/*************************************************************************** * * * MODULE : WorkWP.c * * * * DESCRIPTION : Functions controlling the workspace window. * * * * HISTORY : 6/21/89 LR * * * ***************************************************************************/ #include "imagedit.h" #include /* * This structure is used in conjunction with DeltaGenInit() and DeltaGen(). */ typedef struct _DELTAGEN { /* dg */ BOOL fSwap; INT xf; INT yf; INT dx; INT dy; INT d; INT incx; INT incy; INT inc1; INT inc2; } DELTAGEN; typedef DELTAGEN *PDELTAGEN; STATICFN VOID NEAR WorkPaint(HWND hwnd); STATICFN VOID WorkButtonDown(HWND hwnd, UINT msg, PPOINT ppt); STATICFN VOID WorkButtonMouseMove(HWND hwnd, UINT msg, PPOINT ppt); STATICFN VOID WorkButtonUp(HWND hwnd, UINT msg, PPOINT ppt); STATICFN VOID NEAR SnapPointToGrid(PPOINT ppt); STATICFN VOID DrawToPoint(HWND hwnd, PPOINT ppt, BOOL fBrush); STATICFN BOOL NEAR DeltaGenInit(PDELTAGEN pdg, INT x0, INT y0, INT xf, INT yf, PINT px, PINT py); STATICFN BOOL NEAR DeltaGen(PDELTAGEN pdg, PINT px, PINT py); STATICFN VOID DrawPoint(HWND hwnd, PPOINT ppt, BOOL fBrush); STATICFN VOID NEAR RubberBandLine(BOOL fFirstTime); STATICFN VOID NEAR RectDPDraw(HWND hwnd); STATICFN VOID NEAR RubberBandRect(BOOL fFirstTime); STATICFN VOID NEAR CircleDPDraw(HWND hwnd); STATICFN VOID NEAR RubberBandCircle(BOOL fFirstTime); STATICFN VOID NEAR MarkHotSpotPosition(INT x, INT y); STATICFN VOID NEAR StartRubberBanding(HWND hwnd); STATICFN VOID NEAR EndRubberBanding(HWND hwnd); static BOOL fDrawing = FALSE; // TRUE if mouse button is down. static BOOL fLeftButtonDown; // TRUE if left button was pressed. static POINT ptStart; // Saves the starting point. static POINT ptEnd; // Saves the ending point. static POINT ptPrev; // Saves the previous point. static HDC hdcRubberBand; // DC used during rubber banding. static BOOL fRubberBanding = FALSE; // Tracking is in progress. /**************************************************************************** * WorkWndProc * * * purpose: Processes basic create and size and paint messages for the * * workspace window. * * ****************************************************************************/ WINDOWPROC WorkWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { LPCREATESTRUCT cs; POINT pt; switch (msg) { case WM_CREATE: /* set up image variables */ cs = (LPCREATESTRUCT)lParam; gcxWorkSpace = cs->cx; gcyWorkSpace = cs->cy; break; case WM_SIZE: gcxWorkSpace = LOWORD(lParam); gcyWorkSpace = HIWORD(lParam); break; case WM_PAINT: WorkPaint(hwnd); break; case WM_MOUSEMOVE: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); WorkButtonMouseMove(hwnd, msg, &pt); break; case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); WorkButtonDown(hwnd, msg, &pt); break; case WM_LBUTTONUP: case WM_RBUTTONUP: ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y); WorkButtonUp(hwnd, msg, &pt); break; case WM_KEYDOWN: switch (wParam) { case VK_ESCAPE: if (fDrawing) { if (fRubberBanding) { EndRubberBanding(hwnd); WorkUpdate(); } PropBarClearSize(); ReleaseCapture(); fDrawing = FALSE; } break; } break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } /**************************************************************************** * WorkPaint * * Handles WM_PAINT for the workspace window. * * History: * ****************************************************************************/ STATICFN VOID NEAR WorkPaint( HWND hwnd) { register INT i; PAINTSTRUCT ps; HCURSOR hcurOld; HDC hdcTemp; HBITMAP hbmTemp; hcurOld = SetCursor(hcurWait); BeginPaint(hwnd, &ps); /* * Do they want a grid and is there enough room to show the lines? */ if (gfGrid && gZoomFactor > 1) { /* * Stretch the bits onto a temporary DC. */ hdcTemp = CreateCompatibleDC(ghdcImage); hbmTemp = CreateCompatibleBitmap(ghdcImage, gcxWorkSpace, gcyWorkSpace); SelectObject(hdcTemp, hbmTemp); StretchBlt(hdcTemp, 0, 0, gcxWorkSpace, gcyWorkSpace, ghdcImage, 0, 0, gcxImage, gcyImage, SRCCOPY); /* * Draw the grid lines on the temp DC. */ for (i = gZoomFactor - 1; i < gcxWorkSpace; i += gZoomFactor) PatBlt(hdcTemp, i, 0, 1, gcyWorkSpace, BLACKNESS); for (i = gZoomFactor - 1; i < gcyWorkSpace; i += gZoomFactor) PatBlt(hdcTemp, 0, i, gcxWorkSpace, 1, BLACKNESS); /* * Copy the bits to the screen. */ BitBlt(ps.hdc, 0, 0, gcxWorkSpace, gcyWorkSpace, hdcTemp, 0, 0, SRCCOPY); DeleteDC(hdcTemp); DeleteObject(hbmTemp); } else { /* * No grid. Just stretch the image to the screen. */ StretchBlt(ps.hdc, 0, 0, gcxWorkSpace, gcyWorkSpace, ghdcImage, 0, 0, gcxImage, gcyImage, SRCCOPY); } EndPaint(hwnd, &ps); SetCursor(hcurOld); } /**************************************************************************** * WorkUpdate * * This function updates the workspace window. * * History: * ****************************************************************************/ VOID WorkUpdate(VOID) { /* * Invalidate the workspace window. Because the image will be * be blt'ed onto it, we do not need to force the background to * be cleared first. */ InvalidateRect(ghwndWork, NULL, FALSE); } /**************************************************************************** * WorkReset * * This function reset the workspace window. It should be called * any time that a new image is loaded (because the size could * change) or the size of the main window changes (because the * workspace window needs to be resized to fit). * * History: * ****************************************************************************/ VOID WorkReset(VOID) { RECT rcClient; INT cx; INT cy; INT xScale; INT yScale; INT cxBorder; INT cyBorder; cxBorder = GetSystemMetrics(SM_CXBORDER); cyBorder = GetSystemMetrics(SM_CYBORDER); if (!gcxImage || !gcyImage) { gZoomFactor = 1; } else { GetClientRect(ghwndMain, &rcClient); cx = rcClient.right - (2 * PALETTEMARGIN) - (2 * cxBorder); cy = rcClient.bottom - (2 * PALETTEMARGIN) - (2 * cyBorder) - gcyPropBar; xScale = cx / gcxImage; yScale = cy / gcyImage; if (xScale > 0 && yScale > 0) gZoomFactor = min(xScale, yScale); else gZoomFactor = 1; } SetWindowPos(ghwndWork, NULL, PALETTEMARGIN, PALETTEMARGIN + gcyPropBar, (gZoomFactor * gcxImage) + (2 * cxBorder), (gZoomFactor * gcyImage) + (2 * cyBorder), SWP_NOACTIVATE | SWP_NOZORDER); WorkUpdate(); } /************************************************************************ * WorkButtonDown * * * * Arguments: * * History: * ************************************************************************/ STATICFN VOID WorkButtonDown( HWND hwnd, UINT msg, PPOINT ppt) { /* * If the other button is already down, just ignore this one. */ if (fDrawing) return; SetFocus(hwnd); fLeftButtonDown = (msg == WM_LBUTTONDOWN) ? TRUE : FALSE; SnapPointToGrid(ppt); ptStart = ptPrev = ptEnd = *ppt; if (fLeftButtonDown) { ghbrDraw = ghbrLeft; ghbrDrawSolid = ghbrLeftSolid; gfDrawMode = gfModeLeft; ghpenDraw = ghpenLeft; } else { ghbrDraw = ghbrRight; ghbrDrawSolid = ghbrRightSolid; gfDrawMode = gfModeRight; ghpenDraw = ghpenRight; } /* * If this tool draws on the down-click, update the undo * buffer now. */ if (gaTools[gCurTool].fDrawOnDown) ImageUpdateUndo(); SetCapture(ghwndWork); fDrawing = TRUE; (*gpfnDrawProc)(hwnd, msg, *ppt); PropBarSetSize(ptStart, ptEnd); } /************************************************************************ * WorkButtonMouseMove * * * * Arguments: * * History: * ************************************************************************/ STATICFN VOID WorkButtonMouseMove( HWND hwnd, UINT msg, PPOINT ppt) { static POINT ptNZLast; // Saves the last point (non-zoomed). POINT ptNZ; SetCursor(gaTools[gCurTool].hcur); SnapPointToGrid(ppt); /* * Calculate the point as it would be on the actual image * (non-zoomed). */ ptNZ.x = ppt->x / gZoomFactor; ptNZ.y = ppt->y / gZoomFactor; /* * Only call the drawing proc if the point changed enough to * jump over a zoomed pixels width (it jumped a grid square). * This prevents calling the DrawProc for a mouse move of * a single pixel (unless the zoom factor is 1, of course). */ if (ptNZLast.x != ptNZ.x || ptNZLast.y != ptNZ.y) { ptEnd = *ppt; (*gpfnDrawProc)(hwnd, msg, *ppt); ptPrev = ptEnd; PropBarSetPos(ptNZ.x, ptNZ.y); if (fDrawing) PropBarSetSize(ptStart, ptEnd); ptNZLast = ptNZ; } } /************************************************************************ * WorkButtonUp * * * * Arguments: * * History: * ************************************************************************/ STATICFN VOID WorkButtonUp( HWND hwnd, UINT msg, PPOINT ppt) { /* * Pass this on to the draw procs, but only if we are still drawing. * The drawing could have been cancelled by the Escape key, in * which case we just ignore the button up message. */ if (fDrawing) { SnapPointToGrid(ppt); /* * If this tool draws on the up-click, update the undo * buffer now. */ if (gaTools[gCurTool].fDrawOnUp) ImageUpdateUndo(); (*gpfnDrawProc)(hwnd, msg, *ppt); ReleaseCapture(); fDrawing = FALSE; } } /****************************************************************************** * SnapPointToGrid * * PURPOSE : Snap the current mouse coordinate to the nearest grid intersection. * * PARAMS : PPOINT ppt : current mouse coordinates * *****************************************************************************/ STATICFN VOID NEAR SnapPointToGrid( PPOINT ppt) { /* * Scale the point down (this gridizes it at the same time). */ ppt->x = ppt->x / gZoomFactor; ppt->y = ppt->y / gZoomFactor; /* * Limit the point to within the image. */ if (ppt->x < 0) ppt->x = 0; if (ppt->y < 0) ppt->y = 0; if (ppt->x >= gcxImage) ppt->x = gcxImage - 1; if (ppt->y >= gcyImage) ppt->y = gcyImage - 1; /* * Finally, scale it back up to the workspace window size. */ ppt->x *= gZoomFactor; ppt->y *= gZoomFactor; } /************************************************************************ * PencilDP * * * * Arguments: * * History: * ************************************************************************/ VOID PencilDP( HWND hwnd, UINT msg, POINT ptNew) { switch (msg) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: DrawPoint(hwnd, &ptNew, FALSE); break; case WM_MOUSEMOVE: if (fDrawing) DrawToPoint(hwnd, &ptNew, FALSE); break; } } /************************************************************************ * BrushDP * * * * Arguments: * * History: * ************************************************************************/ VOID BrushDP( HWND hwnd, UINT msg, POINT ptNew) { switch (msg) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: DrawPoint(hwnd, &ptNew, TRUE); break; case WM_MOUSEMOVE: if (fDrawing) DrawToPoint(hwnd, &ptNew, TRUE); break; } } /************************************************************************ * DrawToPoint * * This function draws from the previous point to the given point. * This includes all points between. * * The global ptPrev must have been initialized prior to the first time * this function is called during a drawing operation. * * Arguments: * * History: * ************************************************************************/ STATICFN VOID DrawToPoint( HWND hwnd, PPOINT ppt, BOOL fBrush) { DELTAGEN dg; BOOL fContinue; POINT pt; INT x; INT y; x = ppt->x / gZoomFactor; y = ppt->y / gZoomFactor; DeltaGenInit(&dg, ptPrev.x / gZoomFactor, ptPrev.y / gZoomFactor, ppt->x / gZoomFactor, ppt->y / gZoomFactor, &x, &y); do { pt.x = x * gZoomFactor; pt.y = y * gZoomFactor; DrawPoint(hwnd, &pt, fBrush); fContinue = DeltaGen(&dg, &x, &y); } while (fContinue); } /***************************** Public Function ****************************\ * DeltaGenInit * * This routine initializes the pdg, px and py in preparation for using * DeltaGen(). Returns fContinue. * * Algorithm derived from BRESENHAM line algorighm on p. 435 of Fund. of * interactive computer graphics, Foley/VanDam, addison-wesley 1983. * * History: * 3/7/89 sanfords created \***************************************************************************/ STATICFN BOOL NEAR DeltaGenInit( PDELTAGEN pdg, INT x0, INT y0, INT xf, INT yf, PINT px, PINT py) { INT nT; pdg->xf = xf; pdg->yf = yf; if (x0 == xf && y0 == yf) return FALSE; if (xf >= x0) pdg->incx = 1; else pdg->incx = -1; if (yf >= y0) pdg->incy = 1; else pdg->incy = -1; pdg->dx = (xf - x0) * pdg->incx; pdg->dy = (yf - y0) * pdg->incy; if (pdg->dy > pdg->dx) { nT = pdg->dy; pdg->dy = pdg->dx; pdg->dx = nT; nT = pdg->incx; pdg->incx = pdg->incy; pdg->incy = nT; pdg->fSwap = TRUE; } else { pdg->fSwap = FALSE; } pdg->inc1 = pdg->dy * 2; pdg->inc2 = (pdg->dy - pdg->dx) * 2; pdg->d = pdg->inc1 - pdg->dx; pdg->xf = xf; pdg->yf = yf; *px = x0; *py = y0; return TRUE; } /***************************** Public Function ****************************\ * DeltaGen * * This routine generates the next coordinates for px,py assuming that this * point is proceeding linearly from x0,y0 to xf, yf. It returns FALSE only * if *px == xf and *py == yf on entry. (ie returns fContinue) pdg should * have been previously set by DeltaGenInit(). * * Algorithm derived from BRESENHAM line algorighm on p. 435 of Fund. of * interactive computer graphics, Foley/VanDam, addison-wesley 1983. * * History: * 3/7/89 sanfords created \***************************************************************************/ STATICFN BOOL NEAR DeltaGen( PDELTAGEN pdg, PINT px, PINT py) { PINT pnT; if ((*px == pdg->xf) && (*py == pdg->yf)) return FALSE; if (pdg->fSwap) { pnT = px; px = py; py = pnT; } *px += pdg->incx; if (pdg->d < 0) { pdg->d += pdg->inc1; } else { *py += pdg->incy; pdg->d += pdg->inc2; } return TRUE; } /************************************************************************ * DrawPoint * * This function is called to draw a point on the image. It is used * by the Pencil and Brush tools. * * Arguments: * * History: * ************************************************************************/ STATICFN VOID DrawPoint( HWND hwnd, PPOINT ppt, BOOL fBrush) { HDC hDC; HBRUSH hbrOld; INT wx; INT wy; INT iStartY; INT wStep; INT i; INT j; INT nBrushSize; if (ppt->x < 0 || ppt->y < 0) return; hDC = GetDC(hwnd); /* * If this is a point from the brush tool, use the current * brush width. Otherwise, draw a single pixel point. */ if (fBrush) nBrushSize = gnBrushSize; else nBrushSize = 1; /* * Determine some starting factors, then draw the point in * the workspace window. */ hbrOld = SelectObject(hDC, ghbrDrawSolid); wy = iStartY = ppt->y - (ppt->y % gZoomFactor) -(nBrushSize / 2) * gZoomFactor; wx = ppt->x - (ppt->x % gZoomFactor) -(nBrushSize / 2) * gZoomFactor; wStep = gZoomFactor; if (gfGrid && gZoomFactor > 1) wStep -= 1; for (i = 0; i < nBrushSize; i++, wx += gZoomFactor) { wy = iStartY; for (j = 0; j < nBrushSize; j++, wy += gZoomFactor) PatBlt(hDC, wx, wy, wStep, wStep, PATCOPY); } SelectObject(hDC, hbrOld); ReleaseDC(hwnd, hDC); /* * Set the point in the bitmap directly as an optimization. */ wx = ppt->x / gZoomFactor; wy = ppt->y / gZoomFactor; if (wx < gcxImage && wy < gcyImage) { hbrOld = SelectObject(ghdcImage, ghbrDrawSolid); PatBlt(ghdcImage, wx - nBrushSize / 2, wy - nBrushSize / 2, nBrushSize, nBrushSize, PATCOPY); if (giType != FT_BITMAP) { /* * If in color mode, set the mask bits black. Otherwise make * them white. */ PatBlt(ghdcANDMask, wx - nBrushSize / 2, wy - nBrushSize / 2, nBrushSize, nBrushSize, (gfDrawMode == MODE_COLOR) ? BLACKNESS : WHITENESS); } SelectObject(ghdcImage, hbrOld); /* * Draw the point in the view window directly as an optimization. */ if (ghwndView) ViewSetPixel(wx, wy, nBrushSize); } /* * Mark the image as changed. */ fImageDirty = TRUE; } /************************************************************************ * PickDP * * Drawing proc that selects a rectangular portion of the image. * It updates the global picking rectangle. * * Arguments: * * History: * ************************************************************************/ VOID PickDP( HWND hwnd, UINT msg, POINT ptNew) { POINT ptTL; // Top-Left point. POINT ptBR; // Bottom-Right point switch (msg) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: /* erase any previous ghost rectangle */ WorkUpdate(); UpdateWindow(ghwndWork); /* * Initialize the pick rectangle to cover the entire screen. */ PickSetRect(0, 0, gcxImage - 1, gcyImage - 1); StartRubberBanding(hwnd); RubberBandRect(TRUE); break; case WM_MOUSEMOVE: if (fRubberBanding) RubberBandRect(FALSE); break; case WM_LBUTTONUP: case WM_RBUTTONUP: EndRubberBanding(hwnd); /* * Flip the points (if needed) and scale down. */ ptTL = ptStart; ptBR = ptEnd; NormalizePoints(&ptTL, &ptBR); ptTL.x /= gZoomFactor; ptTL.y /= gZoomFactor; ptBR.x /= gZoomFactor; ptBR.y /= gZoomFactor; PickSetRect(ptTL.x, ptTL.y, ptBR.x, ptBR.y); break; } } /****************************************************************************** * VOID LineDP(hwnd, msg, ptNew) * * PURPOSE: Draw a straight line according to tracking line. * * PARAMS : HWND hwnd : handle to dest. DC * unsigned msg : Upper left corner of rect; * POINT ptNew : end pt. of line * * SIDE EFFECTS: may change bits in image DC * *****************************************************************************/ VOID LineDP( HWND hwnd, UINT msg, POINT ptNew) { INT sx; INT sy; INT ex; INT ey; HPEN hpen; HPEN hpenOld; HBRUSH hbrOld; switch (msg) { case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: StartRubberBanding(hwnd); RubberBandLine(TRUE); break; case WM_MOUSEMOVE: if (fRubberBanding) RubberBandLine(FALSE); break; case WM_LBUTTONUP: case WM_RBUTTONUP: EndRubberBanding(hwnd); /* transform selected coordinates to those of actual image */ sx = ptStart.x / gZoomFactor; sy = ptStart.y / gZoomFactor; ex = ptEnd.x / gZoomFactor; ey = ptEnd.y / gZoomFactor; hpenOld = SelectObject(ghdcImage, ghpenDraw); MoveToEx(ghdcImage, sx, sy, NULL); LineTo(ghdcImage, ex, ey); SelectObject(ghdcImage, hpenOld); if (giType != FT_BITMAP) { /* for icons and cursors draw the line on the AND DC (memory) * in black (if in color mode) or white (otherwise) */ hpen = CreatePen(PS_INSIDEFRAME, 1, (gfDrawMode == MODE_COLOR) ? RGB_BLACK : RGB_WHITE); hpenOld = SelectObject(ghdcANDMask, hpen); hbrOld = SelectObject(ghdcANDMask, GetStockObject(NULL_BRUSH)); MoveToEx(ghdcANDMask, sx, sy, NULL); LineTo(ghdcANDMask, ex, ey); SelectObject(ghdcANDMask, hbrOld); SelectObject(ghdcANDMask, hpenOld); DeleteObject(hpen); } /* * Because the LineTo function does not draw the ending * point, we must do it manually here. */ DrawPoint(hwnd, &ptEnd, FALSE); fImageDirty = TRUE; ViewUpdate(); break; } } /************************************************************************ * RubberBandLine * * This function erases the old line and draws the new tracking line * when using the "Line" tool. * * Arguments: * BOOL fFirstTime - TRUE if starting to track the line (it doesn't * need to erase the old line). * * History: * ************************************************************************/ STATICFN VOID NEAR RubberBandLine( BOOL fFirstTime) { INT nOffset; /* * Set the raster-op to invert. */ SetROP2(hdcRubberBand, R2_NOT); /* * If we are magnifying the image at all, the line needs to be * slightly offset so that it will be draw exactly in between * the grid lines. */ if (gZoomFactor > 1) nOffset = -1; else nOffset = 0; if (!fFirstTime) { /* * Erase the old line. */ MoveToEx(hdcRubberBand, ptStart.x + (gZoomFactor / 2) + nOffset, ptStart.y + (gZoomFactor / 2) + nOffset, NULL); LineTo(hdcRubberBand, ptPrev.x + (gZoomFactor / 2) + nOffset, ptPrev.y + (gZoomFactor / 2) + nOffset); } /* * Draw the new one. */ MoveToEx(hdcRubberBand, ptStart.x + (gZoomFactor / 2) + nOffset, ptStart.y + (gZoomFactor / 2) + nOffset, NULL); LineTo(hdcRubberBand, ptEnd.x + (gZoomFactor / 2) + nOffset, ptEnd.y + (gZoomFactor / 2) + nOffset); } /****************************************************************************** * VOID RectDP(hwnd, msg, ptNew) * * PURPOSE: Draw a rectangle (filled/hollow) in the area specified * * PARAMS : HWND hwnd : handle to dest. DC * WORD msg : * POINT ptNew : end pt. of line * * SIDE EFFECTS: may change bits in image DC * *****************************************************************************/ VOID RectDP( HWND hwnd, UINT msg, POINT ptNew) { switch (msg) { case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: StartRubberBanding(hwnd); RubberBandRect(TRUE); break; case WM_MOUSEMOVE: if (fRubberBanding) RubberBandRect(FALSE); break; case WM_LBUTTONUP: case WM_RBUTTONUP: RectDPDraw(hwnd); break; } } /************************************************************************ * RectDPDraw * * Does the final drawing of a rectangle when using the Rectangle tool. * * Arguments: * HWND hwnd - Window handle to the workspace. * * History: * ************************************************************************/ STATICFN VOID NEAR RectDPDraw( HWND hwnd) { POINT ptTL; // Top-Left point. POINT ptBR; // Bottom-Right point HBRUSH hbr; HBRUSH hbrOld; HPEN hpen; HPEN hpenOld; INT nOutset; EndRubberBanding(hwnd); /* * Flip the points (if needed) and scale down. */ ptTL = ptStart; ptBR = ptEnd; NormalizePoints(&ptTL, &ptBR); ptTL.x /= gZoomFactor; ptTL.y /= gZoomFactor; ptBR.x /= gZoomFactor; ptBR.y /= gZoomFactor; if (gCurTool == TOOL_RECT) { hpen = ghpenDraw; hbr = GetStockObject(NULL_BRUSH); nOutset = 1; } else { hpen = GetStockObject(NULL_PEN); hbr = ghbrDraw; nOutset = 2; } hpenOld = SelectObject(ghdcImage, hpen); hbrOld = SelectObject(ghdcImage, hbr); Rectangle(ghdcImage, ptTL.x, ptTL.y, ptBR.x + nOutset, ptBR.y + nOutset); SelectObject(ghdcImage, hpenOld); SelectObject(ghdcImage, hbrOld); if (giType != FT_BITMAP) { /* for icons and cursors draw the shape on the AND DC (memory) * in black (if in color mode) or white (otherwise) */ if (gCurTool == TOOL_RECT) { hpen = CreatePen(PS_INSIDEFRAME, 1, (gfDrawMode == MODE_COLOR) ? RGB_BLACK : RGB_WHITE); hbr = GetStockObject(NULL_BRUSH); } else { hpen = GetStockObject(NULL_PEN); hbr = GetStockObject((gfDrawMode == MODE_COLOR) ? BLACK_BRUSH : WHITE_BRUSH); } hpenOld = SelectObject(ghdcANDMask, hpen); hbrOld = SelectObject(ghdcANDMask, hbr); Rectangle(ghdcANDMask, ptTL.x, ptTL.y, ptBR.x + nOutset, ptBR.y + nOutset); SelectObject(ghdcANDMask, hpenOld); SelectObject(ghdcANDMask, hbrOld); if (gCurTool == TOOL_RECT) DeleteObject(hpen); } fImageDirty = TRUE; ViewUpdate(); } /****************************************************************************** * VOID PASCAL RubberBandRect() * * PURPOSE: Draw rubberbanding rect. * * PARAMS : HANDLE hDst : handle to dest. DC * *****************************************************************************/ STATICFN VOID NEAR RubberBandRect( BOOL fFirstTime) { POINT ptTL; // Top-Left point. POINT ptBR; // Bottom-Right point /* * Set the raster-op to invert. */ SetROP2(hdcRubberBand, R2_NOT); if (!fFirstTime) { /* * Erase the old rectangle. */ ptTL = ptStart; ptBR = ptPrev; NormalizePoints(&ptTL, &ptBR); Rectangle(hdcRubberBand, ptTL.x, ptTL.y, ptBR.x + gZoomFactor, ptBR.y + gZoomFactor); } /* * Draw the new one. */ ptTL = ptStart; ptBR = ptEnd; NormalizePoints(&ptTL, &ptBR); Rectangle(hdcRubberBand, ptTL.x, ptTL.y, ptBR.x + gZoomFactor, ptBR.y + gZoomFactor); } /****************************************************************************** * VOID CircleDP(hwnd, msg, ptNew) * * PURPOSE: Draw an ellipse (filled/hollow) in the area specified. * * PARAMS : HWND hwnd : handle to dest. DC * unsigned msg : Upper left corner of rect; * POINT ptNew : end pt. of line * * SIDE EFFECTS: may change bits in image DC * *****************************************************************************/ VOID CircleDP( HWND hwnd, UINT msg, POINT ptNew) { switch (msg) { case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: StartRubberBanding(hwnd); RubberBandCircle(TRUE); break; case WM_MOUSEMOVE: if (fRubberBanding) RubberBandCircle(FALSE); break; case WM_LBUTTONUP: case WM_RBUTTONUP: CircleDPDraw(hwnd); break; } } /************************************************************************ * CircleDPDraw * * Does the final drawing of an ellipse when using the Ellipse tool. * * Arguments: * HWND hwnd - Window handle to the workspace. * * History: * ************************************************************************/ STATICFN VOID NEAR CircleDPDraw( HWND hwnd) { POINT ptTL; // Top-Left point. POINT ptBR; // Bottom-Right point HBRUSH hbr; HBRUSH hbrOld; HPEN hpen; HPEN hpenOld; INT nOutset; EndRubberBanding(hwnd); /* * Flip the points (if needed) and scale down. */ ptTL = ptStart; ptBR = ptEnd; NormalizePoints(&ptTL, &ptBR); ptTL.x /= gZoomFactor; ptTL.y /= gZoomFactor; ptBR.x /= gZoomFactor; ptBR.y /= gZoomFactor; #ifdef WIN16 /* * The win 3.x code does not properly draw an ellipse if it * has a NULL pen selected in (to not draw the border). For * this platform, we must select in the drawing pen. This is * not necessary for NT (we can use a NULL pen to avoid * drawing the solid border). */ if (gCurTool == TOOL_CIRCLE) hbr = GetStockObject(NULL_BRUSH); else hbr = ghbrDraw; hpenOld = SelectObject(ghdcImage, ghpenDraw); hbrOld = SelectObject(ghdcImage, hbr); Ellipse(ghdcImage, ptTL.x, ptTL.y, ptBR.x + 1, ptBR.y + 1); SelectObject(ghdcImage, hbrOld); SelectObject(ghdcImage, hpenOld); if (giType != FT_BITMAP) { /* for icons and cursors draw the shape on the AND DC (memory) * in black (if in color mode) or white (otherwise) */ hpen = CreatePen(PS_INSIDEFRAME, 1, (gfDrawMode == MODE_COLOR) ? RGB_BLACK : RGB_WHITE); if (gCurTool == TOOL_CIRCLE) hbr = GetStockObject(NULL_BRUSH); else hbr = GetStockObject((gfDrawMode == MODE_COLOR) ? BLACK_BRUSH : WHITE_BRUSH); hpenOld = SelectObject(ghdcANDMask, hpen); hbrOld = SelectObject(ghdcANDMask, hbr); Ellipse(ghdcANDMask, ptTL.x, ptTL.y, ptBR.x + 1, ptBR.y + 1); SelectObject(ghdcANDMask, hpenOld); SelectObject(ghdcANDMask, hbrOld); DeleteObject(hpen); } #else if (gCurTool == TOOL_CIRCLE) { hpen = ghpenDraw; hbr = GetStockObject(NULL_BRUSH); nOutset = 1; } else { hpen = GetStockObject(NULL_PEN); hbr = ghbrDraw; nOutset = 2; } hpenOld = SelectObject(ghdcImage, hpen); hbrOld = SelectObject(ghdcImage, hbr); Ellipse(ghdcImage, ptTL.x, ptTL.y, ptBR.x + nOutset, ptBR.y + nOutset); SelectObject(ghdcImage, hpenOld); SelectObject(ghdcImage, hbrOld); if (giType != FT_BITMAP) { /* for icons and cursors draw the shape on the AND DC (memory) * in black (if in color mode) or white (otherwise) */ if (gCurTool == TOOL_CIRCLE) { hpen = CreatePen(PS_INSIDEFRAME, 1, (gfDrawMode == MODE_COLOR) ? RGB_BLACK : RGB_WHITE); hbr = GetStockObject(NULL_BRUSH); } else { hpen = GetStockObject(NULL_PEN); hbr = GetStockObject((gfDrawMode == MODE_COLOR) ? BLACK_BRUSH : WHITE_BRUSH); } hpenOld = SelectObject(ghdcANDMask, hpen); hbrOld = SelectObject(ghdcANDMask, hbr); Ellipse(ghdcANDMask, ptTL.x, ptTL.y, ptBR.x + nOutset, ptBR.y + nOutset); SelectObject(ghdcANDMask, hpenOld); SelectObject(ghdcANDMask, hbrOld); if (gCurTool == TOOL_CIRCLE) DeleteObject(hpen); } #endif fImageDirty = TRUE; ViewUpdate(); } /****************************************************************************** * VOID PASCAL RubberBandCircle() * * PURPOSE: Draw rubberbanding circle * * *****************************************************************************/ STATICFN VOID NEAR RubberBandCircle( BOOL fFirstTime) { POINT ptTL; // Top-Left point. POINT ptBR; // Bottom-Right point /* * Set the raster-op to invert. */ SetROP2(hdcRubberBand, R2_NOT); if (!fFirstTime) { /* * Erase the old circle. */ ptTL = ptStart; ptBR = ptPrev; NormalizePoints(&ptTL, &ptBR); Ellipse(hdcRubberBand, ptTL.x, ptTL.y, ptBR.x + gZoomFactor, ptBR.y + gZoomFactor); } /* * Draw the new one. */ ptTL = ptStart; ptBR = ptEnd; NormalizePoints(&ptTL, &ptBR); Ellipse(hdcRubberBand, ptTL.x, ptTL.y, ptBR.x + gZoomFactor, ptBR.y + gZoomFactor); } /************************************************************************ * FloodDP * * * * Arguments: * * History: * ************************************************************************/ VOID FloodDP( HWND hwnd, UINT msg, POINT ptNew) { HDC dc; HDC bwdc; HBRUSH hbrOld; HBITMAP bwbit; HCURSOR hcurSave; switch (msg) { case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: hcurSave = SetCursor(hcurWait); dc = GetDC(hwnd); /* create temporary DC */ bwdc = CreateCompatibleDC(dc); /* create temporary monochrome bitmap */ if (!(bwbit = CreateBitmap(gcxImage, gcyImage, 1, 1, NULL))) { DeleteDC(bwdc); ReleaseDC(hwnd, dc); Message(MSG_OUTOFMEMORY); return; } SelectObject(bwdc, bwbit); /* Set background color of image DC to desired floodfill color.*/ SetBkColor(ghdcImage, GetPixel(ghdcImage, (ptNew.x / gZoomFactor), (ptNew.y / gZoomFactor))); /******* OPERATION 0 ******/ /* First create a monochrome mask of the image after setting background * color to the floodfill color. This will make the region to be * flooded white(background), and it's boundary black (foreground) in the * mask. */ BitBlt(bwdc, 0, 0, gcxImage, gcyImage, ghdcImage, 0, 0, SRCCOPY); /******* OPERATION 1 ******/ /* floodfill selected region in mask (which is white bounded by black) * with black. */ SelectObject(bwdc, GetStockObject(BLACK_BRUSH)); ExtFloodFill(bwdc, ptNew.x / gZoomFactor, ptNew.y / gZoomFactor, RGB_BLACK, FLOODFILLBORDER); /******* OPERATION 2 ******/ /* Now XOR the original image on the mask , inverting the * flood-filled pixels on mask (black --> white). */ BitBlt(bwdc, 0, 0, gcxImage, gcyImage, ghdcImage, 0, 0, SRCINVERT); /* the AND mask needs to be updated only if in screen or inverse mode */ if ((giType == FT_CURSOR) || (giType == FT_ICON)) { if (gfDrawMode == MODE_COLOR) { SetBkColor(ghdcANDMask, RGB(0, 0, 0)); BitBlt(ghdcANDMask, 0, 0, gcxImage, gcyImage, bwdc, 0, 0, ROP_DSna); SelectObject(ghdcANDMask, GetStockObject(BLACK_BRUSH)); BitBlt(ghdcANDMask, 0, 0, gcxImage, gcyImage, bwdc, 0, 0, ROP_DSPao); } else { SetBkColor(ghdcANDMask, RGB(0xff, 0xff, 0xff)); BitBlt(ghdcANDMask, 0, 0, gcxImage, gcyImage, bwdc, 0, 0, ROP_DSna); SelectObject(ghdcANDMask, GetStockObject(WHITE_BRUSH)); BitBlt(ghdcANDMask, 0, 0, gcxImage, gcyImage, bwdc, 0, 0, ROP_DSPao); } } SetBkColor(ghdcImage, RGB_WHITE); /****** OPERATION 3 ******/ /* The following operation turns the flooded area on-screen black, * on the image, preserving the rest of the it. */ BitBlt(ghdcImage, 0, 0, gcxImage, gcyImage, bwdc, 0, 0, ROP_DSna); /****** OPERATION 4 ******/ /* Rop_DSPao ANDs the pattern (current brush which is the floodfill * color) on the source making flooded area (which was white as a * result of operation 2 ) the current brush color, and keeps the rest * of the source black. The source is then ORed into the original image * (whose flooded area is black as a result of operation 3) to get * the desired end result. */ hbrOld = SelectObject(ghdcImage, ghbrDraw); BitBlt(ghdcImage, 0, 0, gcxImage, gcyImage, bwdc, 0, 0, ROP_DSPao); SelectObject(ghdcImage, hbrOld); /* clean up */ DeleteDC(bwdc); DeleteObject(bwbit); ReleaseDC(hwnd, dc); /* * Mark the image as changed. */ fImageDirty = TRUE; ViewUpdate(); SetCursor(hcurSave); break; } } /****************************************************************************** * VOID HotSpotDP(hwnd, msg, ptNew) * * PURPOSE: Sets the hotspot. * * PARAMS : HWND hwnd : handle to dest. DC * WORD msg : * POINT ptNew : end pt. * *****************************************************************************/ VOID HotSpotDP( HWND hwnd, UINT msg, POINT ptNew) { switch (msg) { case WM_LBUTTONDOWN: PropBarSetHotSpot(ptNew.x / gZoomFactor, ptNew.y / gZoomFactor); break; case WM_MOUSEMOVE: if (fDrawing && fLeftButtonDown) PropBarSetHotSpot(ptNew.x / gZoomFactor, ptNew.y / gZoomFactor); break; case WM_LBUTTONUP: MarkHotSpotPosition(ptNew.x / gZoomFactor, ptNew.y / gZoomFactor); break; } } /************************************************************************ * MarkHotSpotPosition * * Updates the hotspot location in the currently selected cursor image. * * Arguments: * * History: * ************************************************************************/ STATICFN VOID NEAR MarkHotSpotPosition( INT x, INT y) { gpImageCur->iHotspotX = x; gpImageCur->iHotspotY = y; PropBarSetHotSpot(x, y); /* * Mark the image as changed. */ fImageDirty = TRUE; } /****************************************************************************** * NormalizePoints * * PURPOSE : interchange start and end pts * if start point is > end point. * *****************************************************************************/ VOID NormalizePoints( PPOINT pptStart, PPOINT pptEnd) { INT n; if (pptStart->x > pptEnd->x) { n = pptEnd->x; pptEnd->x = pptStart->x; pptStart->x = n; } if (pptStart->y > pptEnd->y) { n = pptEnd->y; pptEnd->y = pptStart->y; pptStart->y = n; } } /****************************************************************************** * HDC PASCAL StartRubberBanding(hwnd) * * PURPOSE: Sets up rubberbanding for all tools. * * PARAMS : HANDLE hDst : handle to box DC * * RETURNS :handle to destination display context * * SIDE EFFECTS: alters a few global flags for tracking * *****************************************************************************/ STATICFN VOID NEAR StartRubberBanding( HWND hwnd) { hdcRubberBand = GetDC(hwnd); /* * Select a white pen, and a null brush (prevents drawing the * interior of rectangles and ellipses). */ SelectObject(hdcRubberBand, GetStockObject(WHITE_PEN)); SelectObject(hdcRubberBand, GetStockObject(NULL_BRUSH)); fRubberBanding = TRUE; } /****************************************************************************** * VOID PASCAL EndRubberBanding() * * PURPOSE: Stops rubberbanding rect. and cleans up * *****************************************************************************/ STATICFN VOID NEAR EndRubberBanding( HWND hwnd) { ReleaseDC(hwnd, hdcRubberBand); fRubberBanding = FALSE; }