/************************************************************/ /* Windows Write, Copyright 1985-1992 Microsoft Corporation */ /************************************************************/ /* picture.c -- MW format and display routines for pictures */ //#define NOGDICAPMASKS #define NOWINMESSAGES #define NOVIRTUALKEYCODES #define NOWINSTYLES #define NOCLIPBOARD #define NOCTLMGR #define NOSYSMETRICS #define NOMENUS #define NOICON #define NOKEYSTATE //#define NOATOM #define NOCREATESTRUCT #define NODRAWTEXT #define NOFONT #define NOMB #define NOMENUS #define NOOPENFILE #define NOREGION #define NOSCROLL #define NOSOUND #define NOWH #define NOWINOFFSETS #define NOWNDCLASS #define NOCOMM #include #include "mw.h" #define NOKCCODES #include "ch.h" #include "docdefs.h" #include "fmtdefs.h" #include "dispdefs.h" #include "cmddefs.h" #include "propdefs.h" #include "stcdefs.h" #include "wwdefs.h" #include "filedefs.h" #include "editdefs.h" /* #include "str.h" */ #include "prmdefs.h" /* #include "fkpdefs.h" */ /* #include "macro.h" */ #include "winddefs.h" #if defined(OLE) #include "obj.h" #endif extern typeCP cpMacCur; extern int docCur; extern int vfSelHidden; extern struct WWD rgwwd[]; extern int wwCur; extern int wwMac; extern struct FLI vfli; extern struct SEL selCur; extern struct WWD *pwwdCur; extern struct PAP vpapCache; extern typeCP vcpFirstParaCache; extern typeCP vcpLimParaCache; extern int vfPictSel; extern struct PAP vpapAbs; extern struct SEP vsepAbs; extern struct SEP vsepPage; extern struct DOD (**hpdocdod)[]; extern unsigned cwHeapFree; extern int vfInsertOn; extern int vfPMS; extern int dxpLogInch; extern int dypLogInch; extern int dxaPrPage; extern int dyaPrPage; extern int dxpPrPage; extern int dypPrPage; extern HBRUSH hbrBkgrnd; extern long ropErase; extern int vdocBitmapCache; extern typeCP vcpBitmapCache; extern HBITMAP vhbmBitmapCache; extern BOOL vfBMBitmapCache; extern HCURSOR vhcIBeam; extern BOOL vfMonochrome; /* Used in this module only */ #ifdef DEBUG #define STATIC static #else #define STATIC #endif STATIC RECT rcPictInvalid; /* Rectangle (in window coords) that needs refresh */ int vfWholePictInvalid = TRUE; FreeBitmapCache() { vdocBitmapCache = docNil; if (vhbmBitmapCache != NULL) { DeleteObject( vhbmBitmapCache ); vhbmBitmapCache = NULL; } } MarkInvalidDlPict( ww, dlPict ) int ww; int dlPict; { /* Mark the passed dl (presumed to be part of a picture) as requiring eventual update, when DisplayGraphics is called */ register struct WWD *pwwd = &rgwwd [ww]; struct EDL (**hdndl)[] = pwwd->hdndl; struct EDL *pedl = &(**hdndl)[ dlPict ]; RECT rcDl; SetRect( (LPRECT) &rcDl, 0, pedl->yp - pedl->dyp, pwwd->xpMac, pedl->yp ); if (vfWholePictInvalid) { CopyRect( (LPRECT) &rcPictInvalid, (LPRECT) &rcDl ); vfWholePictInvalid = FALSE; } else { RECT rcT; rcT = rcPictInvalid; /* Necessary? i.e. can UnionRect handle source == destination */ UnionRect( (LPRECT) &rcPictInvalid, (LPRECT) &rcT, (LPRECT) &rcDl ); } } DisplayGraphics( ww, dl, fDontDisplay ) int ww; int dl; int fDontDisplay; { /* Display a line of graphics info */ struct WWD *pwwd = &rgwwd[ww]; struct EDL *pedl; typeCP cpPictStart; typeCP cp; typeCP cpMac = (**hpdocdod)[vfli.doc].cpMac; struct PICINFOX picInfo; RECT rcEnclose; RECT rcPict; HANDLE hBits=NULL; HDC hMDC=NULL; HDC hMDCCache=NULL; HANDLE hbm=NULL; HDC hDC=pwwd->hDC; int cchRun; unsigned long cbPict=0; int dxpOrig; /* Size of picture in the original */ int dypOrig; int dxpDisplay; /* Size of picture as we want to show it */ int dypDisplay; int fBitmap; int ilevel=0; /* THIS ROUTINE COULD USE SOME GDI-CALL ERROR CHECKING! ..pault */ int fDrew=false; /* In the case of monochrome devices, this raster op will map white in the bitmap to the background color and black to the foreground color. */ #define ropMonoBm 0x00990066 Assert( dl >= 0 && dl < pwwd->dlMax ); MarkInvalidDlPict( ww, dl ); if (fDontDisplay) { return; } Diag(CommSz("DisplayGraphics: \n\r")); FreezeHp(); pedl = &(**(pwwd->hdndl))[dl]; cpPictStart=pedl->cpMin; GetPicInfo( cpPictStart, cpMac, vfli.doc, &picInfo ); /* Compute desired display size of picture (in device pixels) */ ComputePictRect( &rcPict, &picInfo, pedl, ww ); dxpDisplay = rcPict.right - rcPict.left; dypDisplay = rcPict.bottom - rcPict.top; /* Compute original size of picture (in device pixels) */ /* MM_ANISOTROPIC and MM_ISOTROPIC pictures have no original size */ switch ( picInfo.mfp.mm ) { case MM_ISOTROPIC: case MM_ANISOTROPIC: break; case MM_BITMAP: dxpOrig = picInfo.bm.bmWidth; dypOrig = picInfo.bm.bmHeight; break; #if defined(OLE) case MM_OLE: { extern BOOL vfObjDisplaying; if (lpOBJ_QUERY_INFO(&picInfo) == NULL) goto DontDraw; /* just to be safe */ if (!CheckPointer(lpOBJ_QUERY_INFO(&picInfo),1)) goto DontDraw; if (lpOBJ_QUERY_OBJECT(&picInfo) == NULL) { typeCP cpRet; /* this can require memory, so unlock heap */ MeltHp(); vfObjDisplaying = TRUE; cpRet = ObjLoadObjectInDoc(&picInfo,vfli.doc,cpPictStart); vfObjDisplaying = FALSE; FreezeHp(); pedl = &(**(pwwd->hdndl))[dl]; if (cpRet == cp0) goto DontDraw; } } break; #endif default: dxpOrig = PxlConvert( picInfo.mfp.mm, picInfo.mfp.xExt, GetDeviceCaps( hDC, HORZRES ), GetDeviceCaps( hDC, HORZSIZE ) ); dypOrig = PxlConvert( picInfo.mfp.mm, picInfo.mfp.yExt, GetDeviceCaps( hDC, VERTRES ), GetDeviceCaps( hDC, VERTSIZE ) ); if (! (dxpOrig && dypOrig) ) { goto DontDraw; } break; } /* Save DC as a guard against DC attribute alteration by a metafile */ #ifdef WINDOWS_BUG_FIXED /* Currently 0 is a valid level for Own DC's */ if ((ilevel=SaveDC( hDC )) == 0) goto DontDraw; #endif ilevel = SaveDC( hDC ); SetStretchBltMode( hDC, BLACKONWHITE ); /* Clip out top bar, selection bar */ IntersectClipRect( hDC, ((wwCur == wwClipboard) ? 0 : xpSelBar), pwwdCur->ypMin, pwwdCur->xpMac, pwwdCur->ypMac ); if (!vfWholePictInvalid) /* Repainting less than the whole picture; clip out what we're not drawing */ IntersectClipRect( hDC, rcPictInvalid.left, rcPictInvalid.top, rcPictInvalid.right, rcPictInvalid.bottom ); /* Build rcEnclose, a rect enclosing the picture that includes the "space before" and "space after" fields */ rcEnclose.left = xpSelBar; if ((rcEnclose.top = rcPict.top - DypFromDya( vpapAbs.dyaBefore, FALSE )) < pwwd->ypMin) rcEnclose.top = pwwd->ypMin; rcEnclose.right = pwwd->xpMac; if ((rcEnclose.bottom = rcPict.bottom + DypFromDya( vpapAbs.dyaAfter, FALSE )) > pwwd->ypMac) rcEnclose.bottom = pwwd->ypMac; /* White out enclosing rect */ PatBlt( hDC, rcEnclose.left, rcEnclose.top, rcEnclose.right - rcEnclose.left, rcEnclose.bottom - rcEnclose.top, ropErase ); /* If we have it cached, do display the easy way */ if (pwwd->doc == vdocBitmapCache && cpPictStart == vcpBitmapCache) { Assert( pwwd->doc != docNil && vhbmBitmapCache != NULL); if ( ((hMDC = CreateCompatibleDC( hDC )) != NULL) && SelectObject( hMDC, vhbmBitmapCache )) { Diag(CommSz("DisplayGraphics: BitBlt\n\r")); BitBlt( hDC, rcPict.left, rcPict.top, dxpDisplay, dypDisplay, hMDC, 0, 0, vfMonochrome && vfBMBitmapCache ? ropMonoBm : SRCCOPY ); fDrew = TRUE; goto DontDraw; } else { /* Using the cache failed -- empty it (SelectObject will fail if bitmap was discarded) */ FreeBitmapCache(); } } StartLongOp(); /* Put up an hourglass */ /* Build up all bytes associated with the picture (except the header) into the global Windows handle hBits */ if ( picInfo.mfp.mm != MM_OLE) { if ((hBits=GlobalAlloc( GMEM_MOVEABLE, (long)picInfo.cbSize )) == NULL) { /* Not enough global heap space to load bitmap/metafile */ goto DontDraw; } #ifdef DCLIP { char rgch[200]; wsprintf(rgch,"DisplayGraphics: picinfo.cbSize %lu \n\r", picInfo.cbSize); CommSz(rgch); } #endif for ( cbPict = 0, cp = cpPictStart + picInfo.cbHeader; cbPict < picInfo.cbSize; cbPict += cchRun, cp += (typeCP) cchRun ) { CHAR rgch[ 256 ]; #if WINVER >= 0x300 HPCH lpch; #else LPCH lpch; #endif #define ulmin(a,b) ((unsigned long)(a) < (unsigned long)(b) ? \ (unsigned long)(a) : (unsigned long)(b)) FetchRgch( &cchRun, rgch, vfli.doc, cp, cpMac, (int) ulmin( picInfo.cbSize - cbPict, 256 ) ); if ((lpch=GlobalLock( hBits )) != NULL) { #ifdef DCLIP { char rgch[200]; wsprintf(rgch," copying %d bytes from %lX to %lX \n\r",cchRun,(LPSTR)rgch,lpch+cbPict); CommSz(rgch); } { char rgchT[200]; int i; for (i = 0; i< min(20,cchRun); i++,i++) { wsprintf(rgchT,"%X ",* (int *) &(rgch[i])); CommSz(rgchT); } CommSz("\n\r"); } #endif #if WINVER >= 0x300 bltbh( (LPSTR)rgch, lpch+cbPict, cchRun ); #else bltbx( (LPSTR)rgch, lpch+cbPict, cchRun ); #endif GlobalUnlock( hBits ); } else { goto DontDraw; } } } /* Display the picture */ MeltHp(); #if defined(OLE) /* CASE 0: OLE */ if (picInfo.mfp.mm == MM_OLE) { Diag(CommSz("Case 0:\n\r")); if (ObjDisplayObjectInDoc(&picInfo, vfli.doc, cpPictStart, hDC, &rcPict) == FALSE) goto DontDraw; fDrew = true; } else #endif /* CASE 1: Bitmap */ if (fBitmap = (picInfo.mfp.mm == MM_BITMAP)) { Diag(CommSz("Case 1: \n\r")); if ( ((hMDC = CreateCompatibleDC( hDC )) != NULL) && ((picInfo.bm.bmBits = GlobalLock( hBits )) != NULL) && ((hbm=CreateBitmapIndirect((LPBITMAP)&picInfo.bm))!=NULL)) { picInfo.bm.bmBits = NULL; GlobalUnlock( hBits ); GlobalFree( hBits ); /* Free handle to bits to allow max room */ hBits = NULL; SelectObject( hMDC, hbm ); goto CacheIt; } } /* Case 2: non-scalable metafile pictures which we are, for user interface consistency, scaling by force using StretchBlt */ else if ( ((dxpDisplay != dxpOrig) || (dypDisplay != dypOrig)) && (picInfo.mfp.mm != MM_ISOTROPIC) && (picInfo.mfp.mm != MM_ANISOTROPIC) ) { Diag(CommSz("Case 2: \n\r")); if (((hMDC=CreateCompatibleDC( hDC)) != NULL) && ((hbm=CreateCompatibleBitmap( hDC, dxpOrig, dypOrig ))!=NULL) && SelectObject( hMDC, hbm ) && SelectObject( hMDC, hbrBkgrnd )) { extern int vfOutOfMemory; PatBlt( hMDC, 0, 0, dxpOrig, dypOrig, ropErase ); SetMapMode( hMDC, picInfo.mfp.mm ); /* To cover StretchBlt calls within the metafile */ SetStretchBltMode( hMDC, BLACKONWHITE ); PlayMetaFile( hMDC, hBits ); /* Because we pass pixels to StretchBlt */ SetMapMode( hMDC, MM_TEXT ); CacheIt: Assert( hbm != NULL && hMDC != NULL ); if (vfOutOfMemory) goto NoCache; #ifndef NOCACHE FreeBitmapCache(); /* Among other things, this code caches the current picture. Notice that there are two assumptions: (1) all bitmaps are monochrome, and (2) a newly created memory DC has a monochrome bitmap selected in. */ if ( ((hMDCCache = CreateCompatibleDC( hDC )) != NULL) && ((vhbmBitmapCache = CreateDiscardableBitmap( fBitmap ? hMDCCache : hDC, dxpDisplay, dypDisplay )) != NULL) && SelectObject( hMDCCache, vhbmBitmapCache )) { if (!StretchBlt( hMDCCache, 0, 0, dxpDisplay, dypDisplay, hMDC, 0, 0, dxpOrig, dypOrig, SRCCOPY )) { /* may get here if memory is low */ DeleteDC( hMDCCache ); hMDCCache = NULL; DeleteObject( vhbmBitmapCache ); vhbmBitmapCache = NULL; goto NoCache; } #ifdef DCLIP if (vfMonochrome && fBitmap) CommSzNum("BitBlt using ropMonoBm == ",ropMonoBm); #endif BitBlt( hDC, rcPict.left, rcPict.top, dxpDisplay, dypDisplay, hMDCCache, 0, 0, vfMonochrome && fBitmap ? ropMonoBm : SRCCOPY ); /* Cached bitmap OK, make cache valid */ vdocBitmapCache = pwwd->doc; vcpBitmapCache = cpPictStart; vfBMBitmapCache = fBitmap; } else #endif /* ndef NOCACHE */ { NoCache: StretchBlt( hDC, rcPict.left, rcPict.top, dxpDisplay, dypDisplay, hMDC, 0, 0, dxpOrig, dypOrig, vfMonochrome && fBitmap ? ropMonoBm : SRCCOPY ); } fDrew = TRUE; } } /* Case 3: A metafile picture which can be directly scaled or does not need to be because its size has not changed */ else { fDrew = true; Diag(CommSz("Case 3:\n\r")); SetMapMode( hDC, picInfo.mfp.mm ); SetViewportOrg( hDC, rcPict.left, rcPict.top ); switch( picInfo.mfp.mm ) { case MM_ISOTROPIC: if (picInfo.mfp.xExt && picInfo.mfp.yExt) /* So we get the correct shape rectangle when SetViewportExt gets called */ SetWindowExt( hDC, picInfo.mfp.xExt, picInfo.mfp.yExt ); /* FALL THROUGH */ case MM_ANISOTROPIC: /** (9.17.91) v-dougk Set the window extent in case the metafile is bad and doesn't call it itself. This will prevent possible gpfaults in GDI **/ SetWindowExt( hDC, dxpDisplay, dypDisplay ); SetViewportExt( hDC, dxpDisplay, dypDisplay ); break; } PlayMetaFile( hDC, hBits ); } DontDraw: /* Clean up */ if ( *(pLocalHeap+1) ) MeltHp(); if (ilevel > 0) RestoreDC( hDC, ilevel ); if (hMDCCache != NULL) DeleteDC( hMDCCache ); if (hMDC != NULL) DeleteDC( hMDC ); if (hbm != NULL) DeleteObject( hbm ); if (hBits != NULL) { if (fBitmap && picInfo.bm.bmBits != NULL) GlobalUnlock( hBits ); GlobalFree( hBits ); } if (!fDrew) { void DrawBlank(HDC hDC, RECT FAR *rc); DrawBlank(hDC,&rcPict); } /* Invert the selection */ if (ww == wwDocument && !vfSelHidden && !vfPMS) { extern int vypCursLine; ilevel = SaveDC( hDC ); /* Because of clip calls below */ if (!vfWholePictInvalid) /* Repainting less than the whole picture; clip out what we're not drawing */ IntersectClipRect( hDC, rcPictInvalid.left, rcPictInvalid.top, rcPictInvalid.right, rcPictInvalid.bottom ); /* Clip out top bar, selection bar */ IntersectClipRect( hDC, xpSelBar, pwwdCur->ypMin, pwwdCur->xpMac, pwwdCur->ypMac ); if (selCur.cpLim > cpPictStart && selCur.cpFirst <= cpPictStart) { /* Take into account 'space before' field */ rcEnclose.left = rcPict.left; rcEnclose.right = rcPict.right; InvertRect( hDC, (LPRECT) &rcEnclose ); } else if ((selCur.cpLim == selCur.cpFirst) && (selCur.cpFirst == cpPictStart) && (vfWholePictInvalid || rcPictInvalid.top < vypCursLine)) { /* We erased the insert point */ vfInsertOn = fFalse; } RestoreDC( hDC, ilevel ); } vfWholePictInvalid = TRUE; /* Next picture, start invalidation anew */ { extern int vfPMS; extern HCURSOR vhcPMS; EndLongOp( vfPMS ? vhcPMS : vhcIBeam ); } } #ifdef ENABLE /* Don't use this anymore */ int FPointInPict(pt) POINT pt; { /* Return true if point is within the picture frame */ struct EDL *pedl; struct PICINFOX picInfo; RECT rcPict; GetPicInfo(selCur.cpFirst, cpMacCur, docCur, &picInfo); if (!FGetPictPedl(&pedl)) return false; ComputePictRect( &rcPict, &picInfo, pedl, wwCur ); return PtInRect( (LPRECT)&rcPict, pt ); } #endif /* ENABLE */ /* C O M P U T E P I C T R E C T */ ComputePictRect( prc, ppicInfo, pedl, ww ) RECT *prc; register struct PICINFOX *ppicInfo; struct EDL *pedl; int ww; { /* Compute rect containing picture indicated by passed ppicInfo, pedl, in the indicated ww. Return the computed rect through prc. picInfo structure is not altered. */ int dypTop, xaLeft; struct WWD *pwwd = &rgwwd[ww]; int xaStart; int dxaText, dxa; int dxpSize, dypSize; int dxaSize, dyaSize; CacheSectPic(pwwd->doc, pedl->cpMin); if (ppicInfo->mfp.mm == MM_BITMAP && ((ppicInfo->dxaSize == 0) || (ppicInfo->dyaSize == 0))) { GetBitmapSize( &dxpSize, &dypSize, ppicInfo, FALSE ); dxaSize = DxaFromDxp( dxpSize, FALSE ); dyaSize = DyaFromDyp( dypSize, FALSE ); } #if defined(OLE) else if (ppicInfo->mfp.mm == MM_OLE) { dxpSize = DxpFromDxa(ppicInfo->dxaSize, FALSE ); dypSize = DypFromDya(ppicInfo->dyaSize, FALSE ); dxpSize = MultDiv( dxpSize, ppicInfo->mx, mxMultByOne ); dypSize = MultDiv( dypSize, ppicInfo->my, myMultByOne ); dxaSize = DxaFromDxp( dxpSize, FALSE ); dyaSize = DyaFromDyp( dypSize, FALSE ); } #endif else { dxpSize = DxpFromDxa( dxaSize = ppicInfo->dxaSize, FALSE ); dypSize = DypFromDya( dyaSize = ppicInfo->dyaSize, FALSE ); } dypTop = pedl->dcpMac != 0 ? /* Last line of picture */ DypFromDya( dyaSize + vpapAbs.dyaAfter, FALSE ) : (pedl->ichCpMin + 1) * dypPicSizeMin; dypTop = pedl->yp - dypTop; xaStart = DxaFromDxp( xpSelBar - (int) pwwd->xpMin, FALSE ); dxaText = vsepAbs.dxaText; switch (vpapAbs.jc) { case jcBoth: case jcLeft: dxa = ppicInfo->dxaOffset; break; case jcCenter: dxa = (dxaText - (int)vpapAbs.dxaRight + (int)vpapAbs.dxaLeft - dxaSize) / 2; break; case jcRight: dxa = dxaText - (int)vpapAbs.dxaRight - dxaSize; break; } xaLeft = xaStart + max( (int)vpapAbs.dxaLeft, dxa ); prc->right = (prc->left = DxpFromDxa( xaLeft, FALSE )) + dxpSize; prc->bottom = (prc->top = dypTop) + dypSize; } FGetPictPedl(ppedl) struct EDL **ppedl; { int dlLim = pwwdCur->dlMac; int dl; typeCP cpFirst = selCur.cpFirst; struct EDL *pedl; //Assert(vfPictSel); if (!vfPictSel) return FALSE; pedl = &(**(pwwdCur->hdndl)[0]); for (dl = 0; dl < dlLim; ++dl, ++pedl) { //if (!pedl->fValid) //return false; if (pedl->cpMin == cpFirst) break; } if (dl >= dlLim) return false; /* No part of picture is on screen */ *ppedl = pedl; return true; } /* C P W I N G R A P H I C */ typeCP CpWinGraphic(pwwd) struct WWD *pwwd; { int cdlPict, dl; struct EDL *dndl = &(**(pwwd->hdndl))[0]; Assert( !pwwd->fDirty ); /* So we can rely on dl info */ CachePara(pwwd->doc, dndl->cpMin); for (dl = 0; (dl < pwwd->dlMac - 1 && dndl[dl].fIchCpIncr); ++dl) ; Assert(dndl[dl].fGraphics); cdlPict = dndl[dl].ichCpMin + 1; return (dndl[0].cpMin + (vcpLimParaCache - vcpFirstParaCache) * dndl[0].ichCpMin / cdlPict); } CacheSectPic(doc, cp) int doc; typeCP cp; { /* Cache section and para props, taking into account that footnotes take props from the reference point */ #ifdef FOOTNOTES struct DOD *pdod = &(**hpdocdod)[doc]; struct FNTB (**hfntb) = pdod->hfntb; #endif CachePara(doc, cp); #ifdef FOOTNOTES if ( (hfntb != 0) && (cp >= (**hfntb).rgfnd[0].cpFtn) ) CacheSect( doc, CpRefFromFtn( doc, cp ) ) else #endif CacheSect(doc, cp); /* Normal text */ } void DrawBlank(HDC hDC, RECT FAR *rc) { /* To tell us when the draw tried but failed */ int xpMid=rc->left + (rc->right-rc->left)/2; int ypMid=rc->top + (rc->bottom - rc->top)/2; int dxpQ=(rc->right-rc->left)/4; int dypQ=(rc->bottom-rc->top)/4; HPEN hOldPen; HBRUSH hOldBrush; hOldPen = SelectObject( hDC, GetStockObject( BLACK_PEN ) ); hOldBrush = SelectObject( hDC, GetStockObject( WHITE_BRUSH ) ); Rectangle(hDC,rc->left,rc->top,rc->right,rc->bottom); MoveTo( hDC, rc->left, rc->top ); LineTo( hDC, rc->right, rc->bottom ); MoveTo( hDC, rc->left, rc->bottom ); LineTo( hDC, rc->right, rc->top ); MoveTo( hDC, xpMid, rc->top ); LineTo( hDC, xpMid, rc->bottom ); MoveTo( hDC, rc->left, ypMid ); LineTo( hDC, rc->right, ypMid ); Ellipse( hDC, xpMid-dxpQ, ypMid-dypQ, xpMid+dxpQ, ypMid+dypQ ); SelectObject( hDC, hOldPen ); SelectObject( hDC, hOldBrush ); }