File:  [WindowsNT SDKs] / mstools / samples / sdktools / imagedit / workwp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:24:28 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntsdk-nov-1993, ntsdk-jul-1993, HEAD
Microsoft Windows NT Build 511 (SDK Final Release) 07-24-1993

/***************************************************************************
 *                                                                         *
 *  MODULE      : WorkWP.c                                                 *
 *                                                                         *
 *  DESCRIPTION : Functions controlling the workspace window.              *
 *                                                                         *
 *  HISTORY     : 6/21/89 LR                                               *
 *                                                                         *
 ***************************************************************************/

#include "imagedit.h"

#include <stdlib.h>


/*
 * 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;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.