You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1659 lines
50 KiB
1659 lines
50 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: floodgdi.cxx
|
|
*
|
|
* Contains FLOODFILL and its helper functions.
|
|
*
|
|
* Created: 20-May-1991 14:33:19
|
|
* Author: Wendy Wu [wendywu]
|
|
*
|
|
* Copyright (c) 1990-1999 Microsoft Corporation
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
// Valid iMode for bExpandScanline().
|
|
|
|
#define EXPAND_MERGE_SCANLINE 1
|
|
#define EXPAND_SCRATCH_SCANLINE 0
|
|
|
|
// Valid iMode fore STACKMEMOBJ().
|
|
|
|
#define ALLOC_MERGE_SCANLINE 1
|
|
#define DONT_ALLOC_MERGE_SCANLINE 0
|
|
|
|
|
|
#define FLOOD_REGION_SIZE (NULL_REGION_SIZE + \
|
|
(NULL_SCAN_SIZE + (sizeof(INDEX_LONG) * 2)) * 200 + \
|
|
NULL_SCAN_SIZE)
|
|
|
|
typedef struct _SPAN
|
|
{
|
|
LONG xLeft; // inclusive left
|
|
LONG xRight; // exclusive right
|
|
}SPAN, *PSPAN;
|
|
|
|
class SCANLINE;
|
|
typedef SCANLINE *PSCANLINE ;
|
|
|
|
/*********************************Class************************************\
|
|
* class SCANLINE
|
|
*
|
|
* This variable length structure describes an area in a scanline which
|
|
* should be filled.
|
|
*
|
|
* History:
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
class SCANLINE
|
|
{
|
|
public:
|
|
LONG y; // y coordinate of this scanline
|
|
COUNT cSpans; // number of spans
|
|
ULONGSIZE_T cjScanline; // size in byte of this scanline
|
|
PSCANLINE psclBelow; // -> the scanline below
|
|
SPAN aSpan[1]; // variable length of spans
|
|
};
|
|
|
|
#define SCANLINEHEADER_SIZE (sizeof(SCANLINE) - sizeof(SPAN))
|
|
|
|
// The stack is empty when psclTop->psclBelow points to psclTop itself.
|
|
// At this time, cjStack must be 0.
|
|
|
|
/*********************************Class************************************\
|
|
* class STACKOBJ
|
|
*
|
|
* Stack to hold SCANLINEs in the order of the flooding.
|
|
*
|
|
* History:
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
class STACKOBJ
|
|
{
|
|
public:
|
|
ULONGSIZE_T cjObj; // size of the memory allocated
|
|
ULONGSIZE_T cjStack; // size of the object used
|
|
PSCANLINE psclTop; // -> the scanline at the top of the stack
|
|
PSCANLINE psclScratch; // -> the sratch scanline
|
|
PSCANLINE psclMerge; // -> the merging scanline
|
|
PBYTE pjStackBase; // the base address of this stack
|
|
|
|
public:
|
|
SCANLINE& scl2ndTop() { return(*(psclTop->psclBelow)); }
|
|
BOOL bValid() { return(pjStackBase != (PBYTE)NULL); }
|
|
LONG yTop() { return(psclTop->y); }
|
|
LONG y2ndTop() { return((psclTop->psclBelow)->y); }
|
|
BOOL bEmpty() { return(cjStack == 0); }
|
|
BOOL bNotEmpty() { return(cjStack != 0); }
|
|
BOOL bMoreThanOneEntry() { return(psclTop->psclBelow != (PSCANLINE)NULL); }
|
|
BOOL bExpand(ULONGSIZE_T cj);
|
|
BOOL bExpandScanline(ULONGSIZE_T cj, ULONG iMode);
|
|
VOID vPop()
|
|
{
|
|
ASSERTGDI(bNotEmpty(), "Pop an empty stack");
|
|
cjStack -= psclTop->cjScanline;
|
|
psclTop = psclTop->psclBelow;
|
|
}
|
|
|
|
BOOL bPushMergeScrScan();
|
|
BOOL bPopPushMergeScrScan()
|
|
{
|
|
vPop();
|
|
return(bPushMergeScrScan());
|
|
}
|
|
|
|
};
|
|
|
|
/*********************************Class************************************\
|
|
* class STACKMEMOBJ
|
|
*
|
|
* Memory object for STACKOBJ.
|
|
*
|
|
* History:
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
class STACKMEMOBJ : public STACKOBJ
|
|
{
|
|
public:
|
|
STACKMEMOBJ(ULONGSIZE_T cj, ULONG iMode, LONG y, LONG xLeft, LONG xRight);
|
|
~STACKMEMOBJ();
|
|
};
|
|
|
|
#define SCRATCH_SCANLINE_SIZE SCANLINEHEADER_SIZE + 20 * sizeof(SPAN)
|
|
#define MERGE_SCANLINE_SIZE SCANLINEHEADER_SIZE + 20 * sizeof(SPAN)
|
|
#define SCANLINE_INC_SIZE 20 * sizeof(SPAN)
|
|
#define DOWNSTACK_SIZE sizeof(STACKOBJ) + 2 * SCRATCH_SCANLINE_SIZE
|
|
#define UPSTACK_SIZE sizeof(STACKOBJ) + 10 * SCRATCH_SCANLINE_SIZE
|
|
#define STACK_INC_SIZE 10 * SCRATCH_SCANLINE_SIZE
|
|
|
|
/*********************************Class************************************\
|
|
* class FLOODBM
|
|
*
|
|
* Contains information about FloodFill and the destination bitmap where
|
|
* it takes place.
|
|
*
|
|
* History:
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
class FLOODBM
|
|
{
|
|
public:
|
|
ULONG iFormat; // format of the bitmap
|
|
RECTL rcl; // clipping rectangle
|
|
ULONG iColor; // color passed in to ExtFloodFill
|
|
DWORD iFillType; // filling mode passed in to ExtFloodFill
|
|
PBYTE pjBits; // pointer to the current scanline
|
|
FLONG flMask; // mask of used bits
|
|
|
|
public:
|
|
FLOODBM(ULONG _iFormat, RECTL& _rcl, ULONG _iColor,
|
|
DWORD _iFillType, PBYTE _pjBits, PALETTE *pPal);
|
|
|
|
~FLOODBM() {}
|
|
|
|
ULONG iColorGet(LONG x);
|
|
VOID vFindExtent(LONG x, LONG& xLeft, LONG& xRight);
|
|
BOOL bSearchAllSpans(LONG xLeft, LONG xRight, LONG& xLeftNew, LONG& xRightNew,
|
|
PBYTE pjStart, STACKOBJ& sto, PSCANLINE pscl);
|
|
BOOL bExtendScanline(STACKOBJ& st, STACKOBJ& stOp, LONG lyNxt,
|
|
PBYTE pjBitsCur, PBYTE pjBitsNxt);
|
|
};
|
|
|
|
/******************************Public*Routine******************************\
|
|
* FLOODBM::FLOODBM(
|
|
*
|
|
* History:
|
|
* 16-Aug-1994 -by- Eric Kutter [erick]
|
|
* Made it out of line.
|
|
\**************************************************************************/
|
|
|
|
FLOODBM::FLOODBM(
|
|
ULONG _iFormat,
|
|
RECTL& _rcl,
|
|
ULONG _iColor,
|
|
DWORD _iFillType,
|
|
PBYTE _pjBits,
|
|
PALETTE *pPal)
|
|
{
|
|
iFormat = _iFormat;
|
|
rcl = _rcl;
|
|
iColor = _iColor;
|
|
iFillType = _iFillType;
|
|
pjBits = _pjBits;
|
|
flMask = 0xffffffff;
|
|
|
|
XEPALOBJ pal(pPal);
|
|
|
|
if (pal.bValid())
|
|
{
|
|
if (pal.bIsRGB() || pal.bIsBGR())
|
|
flMask =0xffffff;
|
|
else if (pal.bIsBitfields())
|
|
flMask = pal.flRed() | pal.flGre() | pal.flBlu();
|
|
}
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* VOID vMergeSpans(pspnSrc1, pspnEndSrc1, pspnSrc2, pspnEndSrc2, pspnTrg)
|
|
*
|
|
* Merge two arrays of spans together and store into the target span.
|
|
*
|
|
* History:
|
|
* 12-Jun-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vMergeSpans(PSPAN pspnSrc1, PSPAN pspnEndSrc1,
|
|
PSPAN pspnSrc2, PSPAN pspnEndSrc2, PSPAN pspnTrg)
|
|
{
|
|
while((pspnSrc1 < pspnEndSrc1) && (pspnSrc2 < pspnEndSrc2))
|
|
{
|
|
if (pspnSrc1->xLeft < pspnSrc2->xLeft)
|
|
{
|
|
ASSERTGDI((pspnSrc1->xRight < pspnSrc2->xLeft),
|
|
"vMergeSpans walls overlapped\n");
|
|
*pspnTrg++ = *pspnSrc1++;
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI((pspnSrc2->xRight < pspnSrc1->xRight),
|
|
"vMergeSpans walls overlapped\n");
|
|
|
|
*pspnTrg++ = *pspnSrc2++;
|
|
}
|
|
}
|
|
|
|
while (pspnSrc1 < pspnEndSrc1)
|
|
*pspnTrg++ = *pspnSrc1++;
|
|
|
|
while (pspnSrc2 < pspnEndSrc2)
|
|
*pspnTrg++ = *pspnSrc2++;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* STACKMEMOBJ::STACKMEMOBJ(cj, bMerge, y, xLeft, xRight)
|
|
*
|
|
* Constructor.
|
|
*
|
|
* History:
|
|
* 08-Sep-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
STACKMEMOBJ::STACKMEMOBJ(ULONGSIZE_T cj, ULONG iMode, LONG y,
|
|
LONG xLeft, LONG xRight)
|
|
{
|
|
psclMerge = (PSCANLINE)NULL;
|
|
pjStackBase = (PBYTE)NULL;
|
|
|
|
psclScratch = (PSCANLINE)PALLOCNOZ(SCRATCH_SCANLINE_SIZE, 'dlFG');
|
|
|
|
if (psclScratch == (PSCANLINE)NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
psclScratch->cjScanline = SCRATCH_SCANLINE_SIZE;
|
|
psclScratch->cSpans = 0;
|
|
|
|
if (iMode == ALLOC_MERGE_SCANLINE)
|
|
{
|
|
psclMerge = (PSCANLINE)PALLOCNOZ(MERGE_SCANLINE_SIZE, 'dlFG');
|
|
if (psclMerge == (PSCANLINE)NULL)
|
|
return;
|
|
psclMerge->cjScanline = MERGE_SCANLINE_SIZE;
|
|
psclMerge->cSpans = 0;
|
|
}
|
|
|
|
cjObj = cj;
|
|
cjStack = sizeof(SCANLINE);
|
|
pjStackBase = (PBYTE)PALLOCNOZ(cj, 'dlFG');
|
|
if (pjStackBase == (PBYTE)NULL)
|
|
return;
|
|
|
|
psclTop = (PSCANLINE)pjStackBase;
|
|
|
|
psclTop->y = y;
|
|
psclTop->cSpans = 1;
|
|
psclTop->cjScanline = sizeof(SCANLINE);
|
|
psclTop->psclBelow = psclTop;
|
|
psclTop->aSpan[0].xLeft = xLeft;
|
|
psclTop->aSpan[0].xRight = xRight;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* STACKMEMOBJ::~STACKMEMOBJ()
|
|
*
|
|
* Destructor.
|
|
*
|
|
* History:
|
|
* 08-Sep-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
STACKMEMOBJ::~STACKMEMOBJ()
|
|
{
|
|
if (pjStackBase != (PBYTE)NULL)
|
|
VFREEMEM(pjStackBase);
|
|
if (psclScratch != (PSCANLINE)NULL)
|
|
VFREEMEM(psclScratch);
|
|
if (psclMerge != (PSCANLINE)NULL)
|
|
VFREEMEM(psclMerge);
|
|
|
|
psclScratch = psclMerge = (PSCANLINE)NULL;
|
|
pjStackBase = (PBYTE)NULL;
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL STACKOBJ::bPushMergeScrScan()
|
|
*
|
|
* Push the scanline pointed to by psclScratch onto the stack. If the y
|
|
* of the given scanline is the same as the y of the scanline on top of the
|
|
* stack, merge them together.
|
|
*
|
|
* History:
|
|
* 06-Sep-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL STACKOBJ::bPushMergeScrScan()
|
|
{
|
|
// Get out of here if nothing to push.
|
|
|
|
if (psclScratch->cSpans == 0)
|
|
return(TRUE);
|
|
|
|
#ifdef DBG_FLOOD
|
|
|
|
DbgPrint("bPushMergeScrScan: y = %ld, cSpans = %ld\n",
|
|
psclScratch->y,psclScratch->cSpans);
|
|
|
|
for (COUNT j = 0; j < psclScratch->cSpans; j++)
|
|
DbgPrint(" Left = %ld, Right = %ld\n",
|
|
psclScratch->aSpan[j].xLeft,psclScratch->aSpan[j].xRight);
|
|
#endif
|
|
|
|
PSCANLINE psclNew = psclScratch;
|
|
|
|
ULONGSIZE_T cjNew = SCANLINEHEADER_SIZE + (ULONGSIZE_T)psclNew->cSpans * sizeof(SPAN);
|
|
ULONGSIZE_T cjInc = cjNew;
|
|
|
|
// If we'll have to do merge later on. The cjInc is SCANLINEHEADER_SIZE
|
|
// bigger than the actual stack size increase after this function. We
|
|
// might end up expanding the stack unnecessarily. But it's OK since
|
|
// we're so close to use up our stack space and subsequent calls might
|
|
// cause stack expansion anyway.
|
|
|
|
if ((cjStack + cjInc) > cjObj)
|
|
{
|
|
if (!bExpand(cjStack + cjNew))
|
|
return(FALSE);
|
|
}
|
|
|
|
psclNew->psclBelow = psclTop;
|
|
|
|
// Check if merge is necessary.
|
|
|
|
if (bNotEmpty())
|
|
{
|
|
if (yTop() == psclScratch->y)
|
|
{
|
|
// We have to do merge here.
|
|
|
|
ASSERTGDI((psclMerge != (PSCANLINE)NULL),
|
|
"bPushMergeScrScan:invalid psclMerge");
|
|
|
|
cjNew += psclTop->cjScanline - SCANLINEHEADER_SIZE;
|
|
cjInc -= SCANLINEHEADER_SIZE;
|
|
|
|
if (cjNew > psclMerge->cjScanline)
|
|
{
|
|
if (!bExpandScanline(cjNew, EXPAND_MERGE_SCANLINE))
|
|
return(FALSE);
|
|
}
|
|
|
|
ASSERTGDI((cjNew <= psclMerge->cjScanline),
|
|
"bPushMergeScrScan: did not alloc enough space\n");
|
|
|
|
psclMerge->y = psclScratch->y;
|
|
psclMerge->psclBelow = psclTop->psclBelow;
|
|
psclMerge->cSpans = psclTop->cSpans + psclScratch->cSpans;
|
|
|
|
vMergeSpans((PSPAN)&psclTop->aSpan[0].xLeft,
|
|
(PSPAN)&psclTop->aSpan[psclTop->cSpans].xLeft,
|
|
(PSPAN)&psclScratch->aSpan[0].xLeft,
|
|
(PSPAN)&psclScratch->aSpan[psclScratch->cSpans].xLeft,
|
|
(PSPAN)&psclMerge->aSpan[0].xLeft);
|
|
|
|
psclNew = psclMerge;
|
|
}
|
|
else
|
|
{
|
|
// No merge is necessary. Update pointers so we'll push to the
|
|
// right spot.
|
|
|
|
PBYTE pj = (PBYTE)psclTop + psclTop->cjScanline;
|
|
psclTop = (PSCANLINE)pj;
|
|
}
|
|
}
|
|
|
|
cjStack += cjInc;
|
|
|
|
ASSERTGDI((cjStack <= cjObj),
|
|
"bPushMergeScrScan: bExpand() failed to alloc enough space\n");
|
|
|
|
ASSERTGDI((cjNew == (psclNew->cSpans * sizeof(SPAN) + SCANLINEHEADER_SIZE)),
|
|
"bPushMergeScrScan: wrong cjNew\n");
|
|
|
|
ASSERTGDI(((pjStackBase + cjStack) ==
|
|
((PBYTE)psclTop + cjNew)),
|
|
"bPushMergeScrScan: stack error\n");
|
|
|
|
psclNew->cjScanline = cjNew;
|
|
RtlCopyMemory(psclTop, psclNew, cjNew);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL STACKOBJ::bExpand(cj)
|
|
*
|
|
* Expand the stack to the given size plus a size defined as STACK_INC_SIZE.
|
|
*
|
|
* History:
|
|
* 08-Sep-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL STACKOBJ::bExpand(ULONGSIZE_T cj)
|
|
{
|
|
#ifdef DBG_FLOOD
|
|
DbgPrint("Enter STACKOBJ::bExpand()\n");
|
|
#endif
|
|
|
|
PBYTE pjStackBaseOld = pjStackBase;
|
|
|
|
pjStackBase = (PBYTE)PALLOCNOZ(cj + STACK_INC_SIZE, 'dlFG');
|
|
if (pjStackBase == (PBYTE)NULL)
|
|
return(FALSE);
|
|
|
|
// Copy the contents of the old stack into the new one.
|
|
|
|
RtlCopyMemory((PLONG)pjStackBase, (PLONG)pjStackBaseOld, cjStack);
|
|
|
|
cjObj = cj + STACK_INC_SIZE;
|
|
|
|
// Update all the pointers in the stack.
|
|
|
|
//Sundown: safe to truncate to ULONG since cjStack won't exceed 4gb
|
|
PTRDIFF pdDiff = (ULONG)(pjStackBase - pjStackBaseOld);
|
|
PBYTE pl = (PBYTE)psclTop + pdDiff;
|
|
PSCANLINE pscl = psclTop = (PSCANLINE)pl;
|
|
|
|
if (cjStack == 0)
|
|
{
|
|
psclTop->psclBelow = psclTop;
|
|
}
|
|
else
|
|
{
|
|
while (pscl->psclBelow != (PSCANLINE)pjStackBase)
|
|
{
|
|
pl = (PBYTE)pscl->psclBelow + pdDiff;
|
|
pscl = (pscl->psclBelow = (PSCANLINE)pl);
|
|
}
|
|
}
|
|
|
|
// Free the old stack.
|
|
|
|
VFREEMEM(pjStackBaseOld);
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL STACKOBJ::bExpandScanline(cj, iMode)
|
|
*
|
|
* Expand the stack.
|
|
*
|
|
* History:
|
|
* 08-Sep-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL STACKOBJ::bExpandScanline(ULONGSIZE_T cj, ULONG iMode)
|
|
{
|
|
#ifdef DBG_FLOOD
|
|
DbgPrint("Enter STACKOBJ::bExpandScanline()\n");
|
|
#endif
|
|
|
|
PSCANLINE pscl, psclNew;
|
|
|
|
if (iMode == EXPAND_MERGE_SCANLINE)
|
|
pscl = psclMerge;
|
|
else
|
|
pscl = psclScratch;
|
|
|
|
ASSERTGDI((pscl != (PSCANLINE)NULL), "bExpandScanline: pscl is NULL");
|
|
|
|
// Allocate memory for the new scanline then copy the contents over.
|
|
|
|
psclNew = (PSCANLINE)PALLOCNOZ((cj + SCANLINE_INC_SIZE), 'dlFG');
|
|
if (psclNew == (PSCANLINE)NULL)
|
|
return(FALSE);
|
|
|
|
RtlCopyMemory(psclNew, pscl, pscl->cjScanline);
|
|
|
|
// Reflect the size of the new scanline and free the old scanline.
|
|
|
|
psclNew->cjScanline = cj + SCANLINE_INC_SIZE;
|
|
VFREEMEM(pscl);
|
|
|
|
// Store the pointer back into the stack.
|
|
|
|
if (iMode == EXPAND_MERGE_SCANLINE)
|
|
psclMerge = psclNew;
|
|
else
|
|
psclScratch = psclNew;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ULONG FLOODBM::iColorGet(LONG x)
|
|
*
|
|
* Get the color of the given x. pjBits points to the first pel of the
|
|
* y scanline. iFormat gives the format of the bitmap.
|
|
*
|
|
* History:
|
|
* 13-Jun-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
ULONG FLOODBM::iColorGet(LONG x)
|
|
{
|
|
ULONG ulColor;
|
|
|
|
switch(iFormat)
|
|
{
|
|
case BMF_1BPP:
|
|
ulColor = (ULONG) *(pjBits + (x >> 3));
|
|
ulColor = ulColor >> (7 - (x & 7));
|
|
return(ulColor & 1);
|
|
|
|
case BMF_4BPP:
|
|
ulColor = (ULONG) *(pjBits + (x >> 1));
|
|
|
|
if (x & 1)
|
|
return(ulColor & 15);
|
|
else
|
|
return(ulColor >> 4);
|
|
|
|
case BMF_8BPP:
|
|
return((ULONG) *(pjBits + x));
|
|
|
|
case BMF_16BPP:
|
|
return(((ULONG) *((PUSHORT) (pjBits + (x << 1)))) & flMask);
|
|
|
|
case BMF_24BPP:
|
|
{
|
|
PBYTE pjX = pjBits + (x * 3);
|
|
ulColor = (ULONG) *(pjX + 2);
|
|
ulColor <<= 8;
|
|
ulColor |= ((ULONG) *(pjX + 1));
|
|
ulColor <<= 8;
|
|
return(ulColor | ((ULONG) *pjX));
|
|
}
|
|
|
|
case BMF_32BPP:
|
|
return(((ULONG) *((PULONG) (pjBits + (x << 2)))) & flMask);
|
|
|
|
default:
|
|
RIP("iColorGet error\n");
|
|
}
|
|
return(0L);
|
|
}
|
|
|
|
/******************************Private*Routine*****************************\
|
|
* VOID FLOODBM::vFindExtent(x, xEnd, &xLeft, &xRight)
|
|
*
|
|
* Find the pixel extent in this scan that should be filled.
|
|
* x is the seed for this scan.
|
|
*
|
|
* History:
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID FLOODBM::vFindExtent(LONG x, LONG& xLeft, LONG& xRight)
|
|
{
|
|
LONG xLeftTemp = x - 1;
|
|
LONG xRightTemp = x + 1;
|
|
|
|
if (iFillType == FLOODFILLBORDER)
|
|
{
|
|
ASSERTGDI((iColorGet(x) != iColor),
|
|
"vFindExtent x has wrong color\n");
|
|
|
|
while((xLeftTemp >= rcl.left) && (iColorGet(xLeftTemp) != iColor))
|
|
xLeftTemp--;
|
|
|
|
while((xRightTemp < rcl.right) && (iColorGet(xRightTemp) != iColor))
|
|
xRightTemp++;
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI((iColorGet(x) == iColor),
|
|
"vFindExtent x has wrong color\n");
|
|
|
|
while((xLeftTemp >= rcl.left) && (iColorGet(xLeftTemp) == iColor))
|
|
xLeftTemp--;
|
|
|
|
while((xRightTemp < rcl.right) && (iColorGet(xRightTemp) == iColor))
|
|
xRightTemp++;
|
|
}
|
|
|
|
xLeft = xLeftTemp + 1; // the extreme left pixel
|
|
xRight = xRightTemp; // the extreme right pixel
|
|
|
|
ASSERTGDI((xLeft != xRight),"vFindExtent error\n");
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL FLOODBM::bSearchAllSpans(xLeft, xRight, xMax, pjStart, psclNew, pscl)
|
|
*
|
|
* Search for all the spans between the given xLeft and xRight.
|
|
*
|
|
* History:
|
|
* 11-Jun-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL FLOODBM::bSearchAllSpans(LONG xLeft, LONG xRight,
|
|
LONG& xLeftNew, LONG& xRightNew,
|
|
PBYTE pjStart, STACKOBJ& sto, PSCANLINE pscl)
|
|
{
|
|
#ifdef DBG_FLOOD
|
|
DbgPrint("bSearchAllSpans xLeft = %ld, xRight = %ld, pjStart = %ld\n",
|
|
xLeft,xRight,pjStart);
|
|
#endif
|
|
|
|
PSCANLINE psclNew = sto.psclScratch; // We'll store the new scanline
|
|
// at the location pointed to by
|
|
// sto.psclScratch.
|
|
LONG x = xLeft;
|
|
COUNT cSpans = psclNew->cSpans;
|
|
ULONGSIZE_T cjNew = SCANLINEHEADER_SIZE + (ULONGSIZE_T)cSpans * sizeof(SPAN);
|
|
// size of scanline that has been used
|
|
pjBits = pjStart; // update pointer to the current
|
|
// scanline in the FLOODBM struct
|
|
while (x < xRight)
|
|
{
|
|
// find the first pixel to fill.
|
|
|
|
if (iFillType == FLOODFILLBORDER)
|
|
{
|
|
if (iColorGet(x) == iColor)
|
|
{
|
|
do { x++; }
|
|
while ((x < xRight) && (iColorGet(x) == iColor));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (iColorGet(x) != iColor)
|
|
{
|
|
do { x++; }
|
|
while ((x < xRight) && (iColorGet(x) != iColor));
|
|
}
|
|
}
|
|
|
|
if (x == xRight)
|
|
break;
|
|
|
|
|
|
// Don't have to search for extent if we already know it.
|
|
|
|
BOOL bNeedSearch = TRUE;
|
|
|
|
if (pscl != (PSCANLINE)NULL)
|
|
{
|
|
// pscl points to a sorted list of spans that we found for this
|
|
// scanline before. See if x is within any of the spans.
|
|
|
|
for (COUNT i = 0; i < pscl->cSpans; i++)
|
|
{
|
|
if (x >= pscl->aSpan[i].xLeft)
|
|
{
|
|
if (x < pscl->aSpan[i].xRight)
|
|
{
|
|
x = pscl->aSpan[i].xRight;
|
|
bNeedSearch = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
|
|
if (bNeedSearch)
|
|
{
|
|
// sclNew contains an un-sorted list of spans that we just found
|
|
// for this scanline. See if x is within any of the spans.
|
|
|
|
for (COUNT i = 0; i < psclNew->cSpans; i++)
|
|
{
|
|
if ((x >= psclNew->aSpan[i].xLeft) &&
|
|
(x < psclNew->aSpan[i].xRight))
|
|
{
|
|
x = psclNew->aSpan[i].xRight;
|
|
bNeedSearch = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bNeedSearch)
|
|
{
|
|
// Find the pixel boundary of the current span
|
|
|
|
if ((cjNew += sizeof(SPAN)) > psclNew->cjScanline)
|
|
{
|
|
if (!sto.bExpandScanline(cjNew, EXPAND_SCRATCH_SCANLINE))
|
|
return(FALSE); // allocation failed
|
|
|
|
psclNew = sto.psclScratch;
|
|
}
|
|
|
|
ASSERTGDI((cjNew <= psclNew->cjScanline),
|
|
"bSearchAllSpans: did not alloc enough space\n");
|
|
|
|
vFindExtent(x, psclNew->aSpan[psclNew->cSpans].xLeft,
|
|
psclNew->aSpan[psclNew->cSpans].xRight);
|
|
|
|
x = psclNew->aSpan[psclNew->cSpans].xRight + 1;
|
|
psclNew->cSpans++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cSpans == psclNew->cSpans)
|
|
xLeftNew = xRightNew = 0;
|
|
else
|
|
{
|
|
// Store the new xLeft and xRight onto the stack.
|
|
|
|
xLeftNew = psclNew->aSpan[cSpans].xLeft;
|
|
xRightNew = psclNew->aSpan[psclNew->cSpans-1].xRight;
|
|
|
|
// Sort the spans out.
|
|
|
|
for (COUNT i = 0; i < (psclNew->cSpans - 1); i++)
|
|
{
|
|
LONG xMin = psclNew->aSpan[i].xLeft;
|
|
ULONG iMin = i;
|
|
|
|
for (COUNT j = i; j < psclNew->cSpans; j++)
|
|
{
|
|
if (psclNew->aSpan[j].xLeft < xMin)
|
|
{
|
|
xMin = psclNew->aSpan[j].xLeft;
|
|
iMin = j;
|
|
}
|
|
}
|
|
|
|
// Swap the first span with the span with the smallest x.
|
|
|
|
if (i != iMin)
|
|
{
|
|
SPAN sp = psclNew->aSpan[i];
|
|
psclNew->aSpan[i] = psclNew->aSpan[iMin];
|
|
psclNew->aSpan[iMin] = sp;
|
|
}
|
|
}
|
|
|
|
#ifdef DBG_FLOOD
|
|
DbgPrint("bSearchAllSpans xLeftNew = %ld, xRightNew = %ld\n",
|
|
xLeftNew,xRightNew);
|
|
#endif
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Private*Routine*****************************\
|
|
* BOOL FLOODBM::bExtendScanline(&sto, &stoOp, lyNxt, pjBitsCur, pjBitsNxt)
|
|
*
|
|
* Check if the next scanline is previously filled or is a boundary. If not,
|
|
* find the span of the next scanline and search for any spillage. Push
|
|
* the resultant spans onto the stack.
|
|
*
|
|
* History:
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL FLOODBM::bExtendScanline(STACKOBJ& sto, STACKOBJ& stoOp, LONG lyNext,
|
|
PBYTE pjBitsCurr, PBYTE pjBitsNext)
|
|
{
|
|
// For each span in the current scanline, find the spans for the next scanline.
|
|
|
|
PSCANLINE pscl = sto.psclTop; // top of current stack
|
|
|
|
// psclCurr and psclNext point to the scanlines we're about to extend
|
|
// to. The space for these scanlines were allocated in STACKMEMOBJ()
|
|
// and are used over and over again in this routine so that reallocation
|
|
// is not needed every time we enter here.
|
|
|
|
PSCANLINE psclCurr = stoOp.psclScratch;
|
|
PSCANLINE psclNext = sto.psclScratch;
|
|
psclNext->cSpans = psclCurr->cSpans = 0;
|
|
psclCurr->y = pscl->y; psclNext->y = lyNext;
|
|
PSCANLINE psclNextOld = (PSCANLINE)NULL;
|
|
|
|
// See if we've handled the next scanline before. If we do, pass the
|
|
// structure to bSearchAllSpans() to eliminate searching for the same
|
|
// extents twice. If the next scanline was dealt with before, its
|
|
// must be stored in the current stack at the 2nd entry from the top.
|
|
|
|
if (sto.bMoreThanOneEntry() && (sto.y2ndTop() == lyNext))
|
|
psclNextOld = &sto.scl2ndTop();
|
|
|
|
BOOL bReturn = TRUE;
|
|
|
|
for (COUNT i = 0; i < pscl->cSpans; i++)
|
|
{
|
|
LONG xLeft = pscl->aSpan[i].xLeft;
|
|
LONG xRight = pscl->aSpan[i].xRight;
|
|
LONG xLeftNew, xRightNew, xTemp;
|
|
|
|
bReturn = bSearchAllSpans(xLeft, xRight, xLeftNew, xRightNew,
|
|
pjBitsNext, sto, psclNextOld);
|
|
|
|
if(!bReturn)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (xLeftNew != xRightNew)
|
|
{
|
|
search_left:
|
|
if (xLeftNew < (xLeft - 1))
|
|
{
|
|
// Check for spillage on the left for the current scanline.
|
|
|
|
xTemp = xLeft - 1;
|
|
xLeft = xLeftNew;
|
|
|
|
bReturn &= bSearchAllSpans(xLeft, xTemp, xLeftNew, xTemp,
|
|
pjBitsCurr, stoOp, pscl);
|
|
|
|
if(!bReturn)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((xLeftNew != xTemp) && (xLeftNew < (xLeft - 1)))
|
|
{
|
|
// Check for spillage on the left for the next scanline.
|
|
|
|
xTemp = xLeft - 1;
|
|
xLeft = xLeftNew;
|
|
|
|
bReturn &= bSearchAllSpans(xLeft, xTemp, xLeftNew, xTemp,
|
|
pjBitsNext, sto, psclNextOld);
|
|
|
|
if(!bReturn)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (xLeftNew != xTemp)
|
|
goto search_left;
|
|
}
|
|
}
|
|
|
|
search_right:
|
|
|
|
if (xRightNew > (xRight + 1))
|
|
{
|
|
// Check for spillage on the right for the current scanline.
|
|
|
|
xTemp = xRight + 1;
|
|
xRight = xRightNew;
|
|
|
|
bReturn &= bSearchAllSpans(xTemp, xRight, xLeftNew, xRightNew,
|
|
pjBitsCurr, stoOp, pscl);
|
|
if(!bReturn)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((xLeftNew != xRightNew) && (xRightNew > (xRight + 1)))
|
|
{
|
|
// Check for spillage on the right for the next scanline.
|
|
|
|
xTemp = xRight + 1;
|
|
xRight = xRightNew;
|
|
|
|
bReturn &= bSearchAllSpans(xTemp, xRight, xLeftNew,
|
|
xRightNew, pjBitsNext, sto, psclNextOld);
|
|
if(!bReturn)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (xLeftNew != xRightNew)
|
|
goto search_right;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Push the newly found extents onto the stack.
|
|
|
|
if( bReturn )
|
|
{
|
|
bReturn &= (stoOp.bPushMergeScrScan() & sto.bPopPushMergeScrScan());
|
|
}
|
|
|
|
return(bReturn);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* BOOL RGNOBJ::bMergeScanline(&sto)
|
|
*
|
|
* Merge a new scanline into the existing region. This is used by
|
|
* ExtFloodFill to construct regions.
|
|
*
|
|
* History:
|
|
* 31-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL RGNMEMOBJ::bMergeScanline(STACKOBJ& sto)
|
|
{
|
|
PSCAN pscn;
|
|
PSPAN pspn;
|
|
COUNT cWalls;
|
|
PSCANLINE psclNew = sto.psclTop;
|
|
|
|
#ifdef DBG_FLOOD
|
|
|
|
DbgPrint("y = %ld, cSpans = %ld\n",psclNew->y,psclNew->cSpans);
|
|
|
|
for (COUNT j = 0; j < psclNew->cSpans; j++)
|
|
DbgPrint(" Left = %ld, Right = %ld\n",
|
|
psclNew->aSpan[j].xLeft,psclNew->aSpan[j].xRight);
|
|
#endif
|
|
|
|
if (prgn->sizeRgn == NULL_REGION_SIZE)
|
|
{
|
|
// We just got the first scanline for this region.
|
|
|
|
ULONGSIZE_T sizeRgn;
|
|
|
|
if ((sizeRgn = NULL_REGION_SIZE + NULL_SCAN_SIZE + NULL_SCAN_SIZE +
|
|
sizeof(INDEX_LONG) * (ULONGSIZE_T)(cWalls = psclNew->cSpans << 1)) >
|
|
prgn->sizeObj)
|
|
{
|
|
// Hopefully this is big enough to avoid reallocation later on.
|
|
|
|
if (!bExpand(sizeRgn + FLOOD_REGION_SIZE))
|
|
return(FALSE);
|
|
}
|
|
|
|
prgn->sizeRgn = sizeRgn;
|
|
prgn->cScans = 3;
|
|
|
|
pscn = prgn->pscnHead(); // first scan
|
|
pscn->yBottom = psclNew->y;
|
|
|
|
pscn = pscnGet(pscn); // second scan
|
|
pscn->cWalls = cWalls;
|
|
|
|
pscn->yTop = psclNew->y;
|
|
pscn->yBottom = psclNew->y+1;
|
|
pspn = (PSPAN)&psclNew->aSpan[0].xLeft;
|
|
COUNT i;
|
|
|
|
for (i = 0; i < cWalls; i+=2)
|
|
{
|
|
pscn->ai_x[i].x = pspn->xLeft;
|
|
pscn->ai_x[i+1].x = pspn->xRight;
|
|
pspn++;
|
|
}
|
|
pscn->ai_x[i].x = cWalls; // This sets cWalls2
|
|
|
|
// Fix the bounding box
|
|
|
|
prgn->rcl.top = psclNew->y;
|
|
prgn->rcl.bottom = psclNew->y+1;
|
|
prgn->rcl.left = pscn->ai_x[0].x;
|
|
prgn->rcl.right = pscn->ai_x[cWalls-1].x;
|
|
ASSERTGDI((prgn->rcl.left < prgn->rcl.right), "bMergeScanline error");
|
|
|
|
pscn = pscnGet(pscn); // third scan
|
|
pscn->cWalls = 0;
|
|
pscn->yTop = psclNew->y+1;
|
|
pscn->yBottom = POS_INFINITY;
|
|
pscn->ai_x[0].x = 0; // This sets cWalls
|
|
|
|
prgn->pscnTail = pscnGet(pscn);
|
|
return TRUE;
|
|
}
|
|
|
|
// Check for nearly full region
|
|
|
|
ULONGSIZE_T sizInc = NULL_SCAN_SIZE + sizeof(SPAN) * (ULONGSIZE_T)psclNew->cSpans;
|
|
|
|
if (sizInc > prgn->sizeObj - prgn->sizeRgn)
|
|
{
|
|
// Lets expand it generously since this region is tossed when FloodFill
|
|
// is done.
|
|
|
|
if (!bExpand(prgn->sizeObj + sizInc + FLOOD_REGION_SIZE))
|
|
return(FALSE);
|
|
}
|
|
|
|
pscn = prgn->pscnHead();
|
|
PSCAN pscnTail = prgn->pscnTail;
|
|
PSCANLINE pscl;
|
|
|
|
// Search for the scan that's right below the scanline to be merged in.
|
|
|
|
while (psclNew->y > pscn->yTop)
|
|
pscn = pscnGet(pscn); // points to the next scan
|
|
|
|
ASSERTGDI((pscn < pscnTail), "search has gone beyond the region\n");
|
|
|
|
if (psclNew->y == pscn->yTop)
|
|
{
|
|
// We have to merge with the scan pointed to by pscn.
|
|
|
|
if (pscn->yTop+1 != pscn->yBottom)
|
|
{
|
|
prgn->cScans += 1; // adjust the bounding box
|
|
prgn->rcl.bottom = psclNew->y+1;
|
|
|
|
pscn->yTop = psclNew->y+1; // adjust yTop of the to-be next scan
|
|
pscl = psclNew;
|
|
}
|
|
else
|
|
{
|
|
// The scan is one pel high. We'll prepare and store the resultant
|
|
// scanline in the space pointed to by psclMerge.
|
|
|
|
ASSERTGDI(!(pscn->cWalls & 1),"Odd Walls in bMergeScanline\n");
|
|
PSCANLINE psclMerge = sto.psclScratch;
|
|
|
|
psclMerge->y = pscn->yTop;
|
|
psclMerge->cSpans = psclNew->cSpans + (pscn->cWalls >> 1);
|
|
|
|
// If the space pointed to by psclMerge is not big enough for
|
|
// the merged scanline, we have to expand it.
|
|
|
|
sizInc -= NULL_SCAN_SIZE; // don't need to store scan header
|
|
ULONGSIZE_T sizeMerge = (ULONGSIZE_T)psclMerge->cSpans * sizeof(SPAN) +
|
|
SCANLINEHEADER_SIZE;
|
|
|
|
if (sizeMerge > psclMerge->cjScanline)
|
|
{
|
|
if (!sto.bExpandScanline(sizeMerge, EXPAND_SCRATCH_SCANLINE))
|
|
return(FALSE);
|
|
|
|
psclMerge = sto.psclScratch;
|
|
}
|
|
|
|
ASSERTGDI((sizeMerge <= psclMerge->cjScanline),
|
|
"bMergeScanline: did not alloc enough space\n");
|
|
|
|
// Call the real merger.
|
|
|
|
vMergeSpans((PSPAN)&pscn->ai_x[0].x,
|
|
(PSPAN)&pscn->ai_x[pscn->cWalls].x,
|
|
(PSPAN)&psclNew->aSpan[0].xLeft,
|
|
(PSPAN)&psclNew->aSpan[psclNew->cSpans].xLeft,
|
|
(PSPAN)&psclMerge->aSpan[0].xLeft);
|
|
pscl = psclMerge;
|
|
}
|
|
}
|
|
else // psclNew->y < pscn->yTop
|
|
{
|
|
// The new scanline is above the scan pointed to by pscn.
|
|
|
|
PSCAN pscnPrev = pscnGot(pscn); // adjust yBottom of prev scan
|
|
pscnPrev->yBottom = psclNew->y;
|
|
|
|
prgn->cScans += 1;
|
|
if (psclNew->y < prgn->rcl.top) // adjust bounding box
|
|
prgn->rcl.top = psclNew->y;
|
|
|
|
pscl = psclNew;
|
|
}
|
|
|
|
register PLONG plDst = (PLONG)((PBYTE)pscnTail + sizInc);
|
|
register PLONG plSrc = (PLONG)pscnTail;
|
|
|
|
prgn->pscnTail = (PSCAN)plDst; // update tail of scan
|
|
|
|
while (plSrc > (PLONG)pscn) // move the scans below to the right place
|
|
*--plDst = *--plSrc;
|
|
|
|
pscn->cWalls = pscl->cSpans << 1;
|
|
pscn->yTop = pscl->y;
|
|
pscn->yBottom = pscl->y+1;
|
|
pspn = (PSPAN)&pscl->aSpan[0].xLeft;
|
|
|
|
// Fill in info for walls.
|
|
|
|
cWalls = pscn->cWalls;
|
|
COUNT i;
|
|
|
|
for (i = 0; i < cWalls; i+=2)
|
|
{
|
|
pscn->ai_x[i].x = pspn->xLeft;
|
|
pscn->ai_x[i+1].x = pspn->xRight;
|
|
pspn++;
|
|
}
|
|
pscn->ai_x[i].x = cWalls; // This sets cWalls2
|
|
|
|
// Recalculate xLeft and xRight in the bounding box.
|
|
|
|
if (prgn->rcl.left > pscn->ai_x[0].x)
|
|
prgn->rcl.left = pscn->ai_x[0].x;
|
|
|
|
if (prgn->rcl.right < pscn->ai_x[cWalls-1].x)
|
|
prgn->rcl.right = pscn->ai_x[cWalls-1].x;
|
|
|
|
prgn->sizeRgn += sizInc;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine*****************************\
|
|
* BOOL NtGdiExtFloodFill (hdc,x,y,crColor,iFillType,pac)
|
|
*
|
|
* Fills an area with the current brush. It begins at the given (x, y)
|
|
* point and continues in all directions. If iFillType is
|
|
* FLOODFILLBORDER, the filling area is bounded by crColor. If iFillType
|
|
* is FLOODFILLSURFACE, the filling area contains the color crColor.
|
|
*
|
|
* History:
|
|
* Tue 10-Sep-1991 -by- Patrick Haluptzok [patrickh]
|
|
* put in different DIBMEMOBJ constructor, no more palette creation
|
|
*
|
|
* Mon 24-Jun-1991 -by- Patrick Haluptzok [patrickh]
|
|
* Check for NULL brush, new brush constructor.
|
|
*
|
|
* 20-May-1991 -by- Wendy Wu [wendywu]
|
|
* Wrote it.
|
|
\*************************************************************************/
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtGdiExtFloodFill(
|
|
HDC hdc,
|
|
INT x,
|
|
INT y,
|
|
COLORREF crColor,
|
|
UINT iFillType
|
|
)
|
|
{
|
|
GDIFunctionID(NtGdiExtFloodFill);
|
|
|
|
DCOBJ dco(hdc);
|
|
|
|
if (!dco.bValidSurf())
|
|
{
|
|
if (!dco.bValid())
|
|
{
|
|
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (dco.fjAccum())
|
|
{
|
|
// Use the device's surface size to accumulate bounds.
|
|
|
|
PDEVOBJ po(dco.hdev());
|
|
ASSERTGDI(po.bValid(),"invalid pdev\n");
|
|
|
|
SIZEL sizl;
|
|
|
|
// if there is no surface, use the dc size. This can happen
|
|
// during metafiling.
|
|
//
|
|
// acquire the devlock to protect us from a dynamic mode
|
|
// change happening while we're munging around in pSurface()
|
|
|
|
GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL);
|
|
|
|
if (po.pSurface())
|
|
sizl = (po.pSurface())->sizl();
|
|
else
|
|
sizl = dco.pdc->sizl();
|
|
|
|
GreReleaseSemaphoreEx(po.hsemDevLock());
|
|
|
|
ERECTL ercl(0, 0, sizl.cx, sizl.cy);
|
|
|
|
dco.vAccumulate(ercl);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
SYNC_DRAWING_ATTRS(dco.pdc);
|
|
|
|
// Lock the Rao region and surface, ensure VisRgn up to date.
|
|
|
|
DEVLOCKOBJ dlo(dco);
|
|
|
|
SURFACE *pSurf = dco.pSurface();
|
|
|
|
if ((pSurf != NULL) && (pSurf->iType() == STYPE_DEVBITMAP))
|
|
{
|
|
//
|
|
// Convert the DFB to a DIB. FloodFills to DFBs are EXTREMELY
|
|
// inefficient so this is how we prevent them. If we don't do
|
|
// this, every floodfill (no matter how big) requires copying
|
|
// the entire DFB to a DIB and then copying the entire DIB back
|
|
// to the DFB (and the floodfills tend to come many at a time).
|
|
//
|
|
|
|
if (bConvertDfbDcToDib(&dco))
|
|
{
|
|
pSurf = dco.pSurface(); // it might have changed
|
|
}
|
|
else
|
|
{
|
|
WARNING("bConvertDfbDcToDib failed\n");
|
|
}
|
|
}
|
|
|
|
// Transform (x,y) from world to device space.
|
|
|
|
EPOINTL eptl(x,y);
|
|
|
|
EXFORMOBJ xo(dco, WORLD_TO_DEVICE);
|
|
|
|
if (!xo.bXform(eptl))
|
|
return(FALSE);
|
|
|
|
// User objects for all our toys.
|
|
|
|
PDEVOBJ pdo(pSurf->hdev());
|
|
XEPALOBJ palSurf(pSurf->ppal());
|
|
XEPALOBJ palDC(dco.ppal());
|
|
|
|
ULONG iSolidColor;
|
|
FLONG flColorType;
|
|
|
|
// If somebody wants to floodfill a printer, they're out of luck. [EricK]
|
|
|
|
if (dco.bPrinter())
|
|
{
|
|
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
|
|
WARNING("FloodFill not allowed on this surface\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
if (dco.pdc->bIsCMYKColor() || dco.pdc->bIsDeviceICM())
|
|
{
|
|
// because, usually, CMYK color and device ICM is for Printer...
|
|
|
|
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
|
|
WARNING("FloodFill not allowed with CMYK color or Device ICM context\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
// Map required color from palette
|
|
|
|
iSolidColor = ulGetNearestIndexFromColorref(palSurf, palDC, crColor);
|
|
|
|
if (dco.pdc->bIsSoftwareICM())
|
|
{
|
|
flColorType = BR_HOST_ICM;
|
|
}
|
|
else
|
|
{
|
|
flColorType = 0;
|
|
}
|
|
|
|
// Realize the brush
|
|
|
|
EBRUSHOBJ *peboFill = dco.peboFill();
|
|
|
|
if ((dco.ulDirty() & DIRTY_FILL) || (dco.pdc->flbrush() & DIRTY_FILL))
|
|
{
|
|
dco.ulDirtySub(DIRTY_FILL);
|
|
dco.pdc->flbrushSub(DIRTY_FILL);
|
|
|
|
peboFill->vInitBrush(dco.pdc,
|
|
dco.pdc->pbrushFill(),
|
|
palDC,
|
|
palSurf,
|
|
pSurf);
|
|
}
|
|
|
|
if (peboFill->bIsNull())
|
|
return(TRUE);
|
|
|
|
if (!dlo.bValid())
|
|
return(dco.bFullScreen());
|
|
|
|
// Convert (x,y) to SCREEN coordinates. Return FALSE if the given point
|
|
// is clipped out.
|
|
|
|
eptl += dco.eptlOrigin();
|
|
|
|
RGNOBJ roRao(dco.prgnEffRao());
|
|
|
|
if (roRao.bInside(&eptl) != REGION_POINT_INSIDE)
|
|
return(FALSE);
|
|
|
|
PBYTE pjBits;
|
|
LONG xLeft, xRight, lDelta;
|
|
ULONG iFormat;
|
|
DEVBITMAPINFO dbmi;
|
|
ERECTL erclRao;
|
|
SURFMEM dimo;
|
|
|
|
// Synchronize with the device driver before touching the device surface.
|
|
|
|
{
|
|
PDEVOBJ po(pSurf->hdev());
|
|
po.vSync(pSurf->pSurfobj(),NULL,0);
|
|
}
|
|
|
|
// Exclude the pointer before calling CopyBits so that pointer won't be
|
|
// copied.
|
|
|
|
roRao.vGet_rcl((PRECTL)&erclRao);
|
|
DEVEXCLUDEOBJ dxo(dco, &erclRao);
|
|
|
|
|
|
// If the surface is not a bitmap, we will create a temporary
|
|
// DIB, the size of the RaoRegion bounding box, to create the
|
|
// that's used to create the final painting region. When this is
|
|
// done we need to offset the RaoRegion, eptl and erclRao to
|
|
// correspond to the new coodinate system. We set the flag below to indicate
|
|
// we need to offset the resulting region back when drawing to
|
|
// the actual destination surface. This was done to fix bug #139701
|
|
|
|
BOOL bOffsetNeeded = FALSE;
|
|
POINTL ptlOffset;
|
|
|
|
if ((pSurf->iType() != STYPE_BITMAP) || (roRao.iComplexity() == COMPLEXREGION))
|
|
{
|
|
// Allocate up an RGB palette and a DIB of the size of the RaoRegion.
|
|
|
|
// Figure out what format the engine should use by looking at the
|
|
// size of palette. This is a clone from CreateCompatibleBitmap().
|
|
|
|
dbmi.iFormat = iFormat = pSurf->iFormat();
|
|
dbmi.cxBitmap = erclRao.right - erclRao.left;
|
|
dbmi.cyBitmap = erclRao.bottom - erclRao.top;
|
|
dbmi.hpal = 0;
|
|
dbmi.fl = BMF_TOPDOWN;
|
|
|
|
if (pSurf->bUMPD())
|
|
dbmi.fl |= UMPD_SURFACE;
|
|
|
|
dimo.bCreateDIB(&dbmi, (PVOID)NULL);
|
|
|
|
if (!dimo.bValid())
|
|
{
|
|
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(FALSE);
|
|
}
|
|
|
|
// Copy the area as big as the size of the RaoRegion.
|
|
|
|
BOOL bRes;
|
|
|
|
ERECTL erclDIB;
|
|
|
|
erclDIB.left = 0;
|
|
erclDIB.top = 0;
|
|
erclDIB.right= dbmi.cxBitmap;
|
|
erclDIB.bottom = dbmi.cyBitmap;
|
|
|
|
bRes = (*PPFNGET(pdo, CopyBits, pSurf->flags()))
|
|
(dimo.pSurfobj(),
|
|
pSurf->pSurfobj(),
|
|
(CLIPOBJ *) NULL,
|
|
&xloIdent,
|
|
(RECTL *) &erclDIB,
|
|
(POINTL *) &erclRao);
|
|
|
|
// Calculate the offset necessary to make all coordinated relative
|
|
// to the DIB surface's coordinate system.
|
|
|
|
ptlOffset.x = -erclRao.left;
|
|
ptlOffset.y = -erclRao.top;
|
|
|
|
if ((bRes) && (roRao.iComplexity() == COMPLEXREGION))
|
|
{
|
|
// Color the boundary of the clip region so we don't flood over the
|
|
// area outside.
|
|
|
|
bRes = FALSE;
|
|
RGNMEMOBJTMP rmoRaoBounds;
|
|
RGNMEMOBJTMP rmoDiff;
|
|
|
|
if (rmoRaoBounds.bValid() && rmoDiff.bValid())
|
|
{
|
|
rmoRaoBounds.vSet((RECTL *)&erclRao);
|
|
if (rmoDiff.bMerge(rmoRaoBounds, roRao, gafjRgnOp[RGN_DIFF]))
|
|
{
|
|
// Make the resulting region's and the raoRectangle
|
|
// relative to the ccorinate system of the DIB
|
|
|
|
if(!rmoDiff.bOffset(&ptlOffset))
|
|
return(FALSE);
|
|
|
|
erclRao += ptlOffset;
|
|
|
|
// Fill the area outside the rao region but inside the
|
|
// rao bounding box with the border color if in border mode.
|
|
// Fill with a different than the surface color (1 if surface color
|
|
// is an even index and 0 otherwise) if in surface mode.
|
|
|
|
ECLIPOBJ co(rmoDiff.prgnGet(), erclRao);
|
|
BBRUSHOBJ bo;
|
|
|
|
bo.flColorType = flColorType;
|
|
bo.pvRbrush = (PVOID)NULL;
|
|
|
|
if (iFillType == FLOODFILLBORDER)
|
|
{
|
|
bo.iSolidColor = iSolidColor;
|
|
|
|
if (gbMultiMonMismatchColor)
|
|
{
|
|
bo.crRealized(crColor);
|
|
bo.crDCPalColor(crColor);
|
|
}
|
|
}
|
|
else // if (iFillType == FLOODFILLSURFACE)
|
|
{
|
|
bo.iSolidColor = ~iSolidColor & 1;
|
|
|
|
if (gbMultiMonMismatchColor)
|
|
{
|
|
// Get corresponding RGB value for iSolidColor.
|
|
//
|
|
// NOTE: Color will be quantaized by primary
|
|
|
|
ULONG ulRGB = ulIndexToRGB(palSurf,palDC,bo.iSolidColor);
|
|
bo.crRealized(ulRGB);
|
|
bo.crDCPalColor(ulRGB);
|
|
}
|
|
}
|
|
|
|
bRes = EngPaint(
|
|
dimo.pSurfobj(), // Destination surface
|
|
(CLIPOBJ *) &co, // Clip object
|
|
&bo, // Realized brush
|
|
(POINTL *) NULL, // Brush origin
|
|
((R2_COPYPEN << 8) | R2_COPYPEN) // ROP
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Offset just the rectangle since the raoRegion is not
|
|
// needed in this case
|
|
|
|
erclRao += ptlOffset;
|
|
}
|
|
|
|
if (!bRes)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
// Make the flood point relative to the DIB's coordinates
|
|
|
|
eptl.x += ptlOffset.x;
|
|
eptl.y += ptlOffset.y;
|
|
|
|
// Setup for the inversion of this offset when drawing to
|
|
// the actual destination surface.
|
|
|
|
bOffsetNeeded = TRUE;
|
|
|
|
ptlOffset.x = -ptlOffset.x;
|
|
ptlOffset.y = -ptlOffset.y;
|
|
|
|
|
|
lDelta = dimo.ps->lDelta();
|
|
pjBits = (PBYTE) dimo.ps->pvScan0();
|
|
}
|
|
else
|
|
{
|
|
pjBits = (PBYTE) pSurf->pvScan0();
|
|
lDelta = pSurf->lDelta();
|
|
iFormat = pSurf->iFormat();
|
|
}
|
|
|
|
#if DEBUG_FLOOD
|
|
DbgPrint("lDelta = %lx, pjBits = %lx, color = %lx\n",
|
|
lDelta, pjBits, iSolidColor);
|
|
#endif
|
|
|
|
// Check if (x,y) is boundary color. Return FALSE if the given point
|
|
// has the wrong color.
|
|
|
|
PBYTE pjBitsY = pjBits + (lDelta * eptl.y);
|
|
|
|
FLOODBM fd(iFormat, erclRao, iSolidColor, iFillType, pjBitsY, pSurf->ppal());
|
|
|
|
ULONG iColorGivenPt = fd.iColorGet(eptl.x);
|
|
|
|
if (((iFillType == FLOODFILLBORDER) && (iColorGivenPt == iSolidColor)) ||
|
|
((iFillType == FLOODFILLSURFACE) && (iColorGivenPt != iSolidColor)))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
// Find the extent of the span in the starting scanline.
|
|
|
|
fd.vFindExtent(eptl.x, xLeft, xRight);
|
|
|
|
// Initialize the Up/Down stacks with the initial extents.
|
|
|
|
STACKMEMOBJ stoUp(UPSTACK_SIZE, ALLOC_MERGE_SCANLINE, eptl.y, xLeft, xRight);
|
|
if (!stoUp.bValid())
|
|
{
|
|
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(FALSE);
|
|
}
|
|
|
|
STACKMEMOBJ stoDown(DOWNSTACK_SIZE, DONT_ALLOC_MERGE_SCANLINE, eptl.y,
|
|
xLeft, xRight);
|
|
if (!stoDown.bValid())
|
|
{
|
|
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(FALSE);
|
|
}
|
|
|
|
RGNMEMOBJTMP ro((ULONGSIZE_T)FLOOD_REGION_SIZE);
|
|
if (!ro.bValid())
|
|
{
|
|
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(FALSE);
|
|
}
|
|
|
|
BOOL bReturn = TRUE;
|
|
LONG yBottom = erclRao.bottom-1; // make bottom inclusive
|
|
LONG yTop = erclRao.top;
|
|
|
|
if (eptl.y < yBottom)
|
|
bReturn &= fd.bExtendScanline(stoDown, stoUp, eptl.y+1, pjBitsY,
|
|
pjBitsY+lDelta);
|
|
else
|
|
stoDown.vPop();
|
|
|
|
if(bReturn)
|
|
{
|
|
while (stoDown.bNotEmpty() || stoUp.bNotEmpty())
|
|
{
|
|
LONG y;
|
|
|
|
if (stoDown.bNotEmpty())
|
|
{
|
|
if (!ro.bMergeScanline(stoDown))
|
|
{
|
|
bReturn = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Extend the scanline below
|
|
|
|
if ((y = stoDown.yTop()) < yBottom)
|
|
{
|
|
pjBitsY = pjBits + (lDelta * y);
|
|
if (!fd.bExtendScanline(stoDown, stoUp, y+1,
|
|
pjBitsY, pjBitsY+lDelta))
|
|
{
|
|
bReturn = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
stoDown.vPop(); // hit border, pop the stack
|
|
}
|
|
else
|
|
{
|
|
bReturn &= ro.bMergeScanline(stoUp);
|
|
|
|
// Extend the scanline above
|
|
|
|
if ((y = stoUp.yTop()) > yTop)
|
|
{
|
|
pjBitsY = pjBits + (lDelta * y);
|
|
if (!fd.bExtendScanline(stoUp, stoDown, y-1,
|
|
pjBitsY, pjBitsY-lDelta))
|
|
{
|
|
bReturn = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
stoUp.vPop(); // hit border, pop the stack
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((bReturn) && (ro.iComplexity() != NULLREGION))
|
|
{
|
|
// Invert the offseting if necessary
|
|
|
|
if(bOffsetNeeded)
|
|
{
|
|
if(!ro.bOffset(&ptlOffset))
|
|
return FALSE;
|
|
erclRao += ptlOffset;
|
|
}
|
|
|
|
// Accumulate bounds in device space.
|
|
|
|
if (dco.fjAccum())
|
|
{
|
|
RECTL rcl;
|
|
|
|
ro.vGet_rcl(&rcl);
|
|
dco.vAccumulate(*((ERECTL *)&rcl));
|
|
}
|
|
|
|
MIX mix = peboFill->mixBest(dco.pdc->jROP2(), dco.pdc->jBkMode());
|
|
|
|
// Inc the target surface uniqueness
|
|
|
|
INC_SURF_UNIQ(pSurf);
|
|
ECLIPOBJ co(ro.prgnGet(), erclRao);
|
|
|
|
// Call Paint to draw to the destination surface.
|
|
|
|
bReturn = EngPaint(
|
|
pSurf->pSurfobj(),
|
|
&co,
|
|
peboFill,
|
|
&dco.pdc->ptlFillOrigin(),
|
|
mix);
|
|
}
|
|
|
|
if (!bReturn)
|
|
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
return(bReturn);
|
|
}
|