Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1711 lines
49 KiB

/**************************************************************************\
*
* Copyright (c) 1998-2000 Microsoft Corporation
*
* Abstract:
*
* Internal scan class.
* Use ARGB buffer for all scan drawing, and Blt to destination when done.
*
* Revision History:
*
* 07/26/1999 t-wehunt
* Created it.
*
\**************************************************************************/
#include "precomp.hpp"
#ifdef DBGALPHA
const ULONG gDebugAlpha = 0;
#endif
#define ALPHA_BYTE_INDEX 3
#define SRC_PIX_SIZE 4
#define RoundDWORD(x) (x + ((x%sizeof(DWORD))>0?(sizeof(DWORD)-(x%sizeof(DWORD))):0))
#define IsInteger(x) (GpFloor(x) == x)
static int TranslateHTTable = 0;
EpScanDIB::EpScanDIB() :
BufStart(NULL),
CurBuffer(NULL),
MaskStart(NULL),
AlphaStart(NULL),
OutputWidth(-1),
OutputBleed(0),
NextBuffer(NULL),
ZeroOutPad(2)
{
}
/**************************************************************************\
*
* Function Description:
*
* Starts a scan.
*
* Arguments:
*
* [IN] driver - Driver interface
* [IN] context - Drawing context
* [IN] surface - Destination surface
* [IN] compositeMode - Alpha blend mode
* [OUT] nextBuffer - Points to a EpScan:: type function to return
* the next buffer
*
* Return Value:
*
* FALSE if all the necessary buffers couldn't be created
*
* History:
*
* 07/13/1999 t-wehunt
* Created it.
*
\**************************************************************************/
BOOL
EpScanDIB::Start(
DpDriver *driver,
DpContext *context,
DpBitmap *surface,
NEXTBUFFERFUNCTION *nextBuffer,
EpScanType scanType,
PixelFormatID pixFmtGeneral,
PixelFormatID pixFmtOpaque,
ARGB solidColor
)
{
// Inherit initialization
EpScan::Start(
driver,
context,
surface,
nextBuffer,
scanType,
pixFmtGeneral,
pixFmtOpaque,
solidColor
);
// Printer surfaces don't have an alpha channel.
ASSERT(surface->SurfaceTransparency == TransparencyNoAlpha);
*nextBuffer = NextBuffer;
ASSERT(NextBuffer != NULL);
// !! Add more asserts for valid state.
OutputX = -1;
OutputY = -1;
OutputWidth = -1;
OutputBleed = -1;
Rasterizing = TRUE;
return TRUE;
}
/**************************************************************************\
*
* Function Description:
*
* Denotes the end of the use of the scan buffer.
*
* Arguments:
*
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* None.
*
* History:
*
* 07/13/1999 t-wehunt
* Created it.
*
\**************************************************************************/
VOID
EpScanDIB::End(INT updateWidth)
{
// it is the driver's job to decide what to do with us and they
// MUST call ReleaseBuffer() as appropriate
// Flush the last scan... This is always required, since at the very
// least, we unpremultiply the scan line.
Rasterizing = FALSE;
if (RenderAlpha)
{
if (!(ScanOptions & ScanDeviceZeroOut))
{
(this->*NextBuffer)(
DeviceBounds.X + DeviceBounds.Width,
DeviceBounds.Y + DeviceBounds.Height,
0,
updateWidth,
0
);
}
else if (OutputWidth > 0)
{
// we must flush the last zeroArray.
BYTE* bitsPtr = BufStart + CappedStride *
((CappedBounds.Height-1) - ((OutputY/ScaleY) - CappedBounds.Y));
DWORD* zeroPtr = (DWORD*) ZeroStart;
INT count = CappedBounds.Width;
while (count--)
{
if (*zeroPtr++ == 0)
{
*bitsPtr++ = '\0';
*bitsPtr++ = '\0';
*bitsPtr++ = '\0';
}
else
{
bitsPtr += 3;
}
}
}
}
else
{
(this->*NextBuffer)(
CappedBounds.X + CappedBounds.Width,
CappedBounds.Y + CappedBounds.Height,
0,
updateWidth,
0
);
}
// Ensure we don't flush if we're called on another band
OutputWidth = -1;
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFunc32bpp - Handles output when we are rasterizing at capped
* dpi to a 32bpp unpremultiplied DIB
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFunc32bpp(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(!RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= CappedBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= CappedBounds.X && x <= (CappedBounds.X + CappedBounds.Width));
ASSERT(y >= CappedBounds.Y && y <= (CappedBounds.Y + CappedBounds.Height));
ASSERT((x + newWidth) <= (CappedBounds.X + CappedBounds.Width));
ASSERT((ScanOptions & ScanCappedBounds) && (ScanOptions & ScanCapped32bpp));
// !! Remove this when we standardize on unpremultiplied
#if 1
if (OutputWidth > 0)
{
while (OutputWidth--)
{
// unpremultiply
*CurBuffer = Unpremultiply(*CurBuffer);
CurBuffer++;
}
}
OutputWidth = newWidth;
#endif
// return pointer directly into our 32bpp buffer
return (CurBuffer = (((ARGB*)BufStart) +
((CappedBounds.Height - 1) - (y - CappedBounds.Y)) *
CappedBounds.Width +
(x - CappedBounds.X)));
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFunc32bppOver - Handles output when we are rasterizing at capped
* DPI to a 32bpp unpremultiplied DIB. Blends any
* alpha with background WHITENESS.
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFunc32bppOver(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(!RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= CappedBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= CappedBounds.X && x <= (CappedBounds.X + CappedBounds.Width));
ASSERT(y >= CappedBounds.Y && y <= (CappedBounds.Y + CappedBounds.Height));
ASSERT((x + newWidth) <= (CappedBounds.X + CappedBounds.Width));
ASSERT((ScanOptions & ScanCappedBounds) && (ScanOptions & ScanCapped32bppOver));
// !! Remove this when we standardize on unpremultiplied
#if 1
if (OutputWidth > 0)
{
while (OutputWidth--)
{
// An adaptation of the blending code from Andrew Godfrey's
// BlendOver function, but onto a white surface. This is done to
// improve the output quality of postscript.
GpColor color(*CurBuffer);
UINT32 alpha = color.GetAlpha();
UINT32 alphaContrib;
if (alpha == 0)
{
*CurBuffer++ = 0x00FFFFFF;
}
else if (alpha == 255)
{
CurBuffer++;
}
else
{
// Dst = Src + (1-Alpha) * Dst
UINT32 multA = 255 - alpha;
UINT32 D1_000000FF = 0xFF;
UINT32 D2_0000FFFF = D1_000000FF * multA + 0x00000080;
UINT32 D3_000000FF = (D2_0000FFFF & 0x0000ff00) >> 8;
UINT32 D4_0000FF00 = (D2_0000FFFF + D3_000000FF) & 0x0000FF00;
alphaContrib = D4_0000FF00 >> 8;
// store: (1-alpha)*0xFF + color for each B, G, R
*CurBuffer++ = ((DWORD)(alphaContrib + color.GetBlue()) << GpColor::BlueShift) |
((DWORD)(alphaContrib + color.GetGreen()) << GpColor::GreenShift) |
((DWORD)(alphaContrib + color.GetRed()) << GpColor::RedShift) |
(alpha << GpColor::AlphaShift);
}
}
}
OutputWidth = newWidth;
#endif
// return pointer directly into our 32bpp buffer
return (CurBuffer = (((ARGB*)BufStart) +
((CappedBounds.Height - 1) - (y - CappedBounds.Y)) *
CappedBounds.Width +
(x - CappedBounds.X)));
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFunc24bpp - Handles output when we are rasterizing at capped
* dpi to a 24bpp unpremultiplied DIB.
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFunc24bpp(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(!RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= CappedBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= CappedBounds.X && x <= (CappedBounds.X + CappedBounds.Width));
ASSERT(y >= CappedBounds.Y && y <= (CappedBounds.Y + CappedBounds.Height));
ASSERT((x + newWidth) <= (CappedBounds.X + CappedBounds.Width));
ASSERT((ScanOptions & ScanCappedBounds) && !(ScanOptions & ScanCapped32bpp));
if (OutputWidth > 0)
{
// compute destination location into 24bpp buffer
BYTE* dstPos = BufStart + (OutputX - CappedBounds.X) * 3 +
CappedStride * ((CappedBounds.Height - 1) -
(OutputY - CappedBounds.Y));
ARGB* srcPos = Buf32bpp;
while (OutputWidth--)
{
// convert from 32 ARGB to 24bpp RGB
#if 1
// !! Remove this when we standardize on non-premultiplied
GpColor color(Unpremultiply(*srcPos++));
#else
GpColor color(*srcPos++);
#endif
// NOTICE: Bytes are stored as Blue, Green, Red.
*dstPos++ = (BYTE)color.GetBlue();
*dstPos++ = (BYTE)color.GetGreen();
*dstPos++ = (BYTE)color.GetRed();
}
}
// record location of next scan
OutputX = x;
OutputY = y;
OutputWidth = newWidth;
return (ARGB*)Buf32bpp;
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFunc24bppBleed - Handles output when we are rasterizing at capped
* dpi to a 24bpp unpremultiplied DIB. It bleeds
* the output to left and right of the scanned area.
* This prevents black jaggies from appearing in the
* output.
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFunc24bppBleed(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(!RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= CappedBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= CappedBounds.X && x <= (CappedBounds.X + CappedBounds.Width));
ASSERT(y >= CappedBounds.Y && y <= (CappedBounds.Y + CappedBounds.Height));
ASSERT((x + newWidth) <= (CappedBounds.X + CappedBounds.Width));
ASSERT((ScanOptions & ScanCappedBounds) &&
!(ScanOptions & ScanCapped32bpp) &&
(ScanOptions & ScanBleedOut));
if (OutputWidth > 0)
{
ARGB* srcPos = Buf32bpp;
GpColor color(Unpremultiply(*srcPos));
if ((OutputLastY == -1) && ((OutputY-CappedBounds.Y) != 0))
{
// Bleed up all previous subsequent scan lines.
// compute destination location into 24bpp buffer
BYTE* clearPos = BufStart + CappedStride * (CappedBounds.Height -
(OutputY - CappedBounds.Y));
INT capHeight = OutputY - CappedBounds.Y;
for (int cntY=0; cntY<capHeight; cntY++)
{
for (int cntX=0; cntX<CappedBounds.Width; cntX++)
{
clearPos[cntX*3] = (BYTE)color.GetBlue();
clearPos[cntX*3+1] = (BYTE)color.GetGreen();
clearPos[cntX*3+2] = (BYTE)color.GetRed();
}
clearPos += CappedStride;
}
}
// compute destination location into 24bpp buffer
BYTE* dstPos = BufStart + (OutputBleed - CappedBounds.X) * 3 +
CappedStride * ((CappedBounds.Height - 1) -
(OutputY - CappedBounds.Y));
// Bleed to the left
INT count = OutputBleed;
while (count++ < OutputX)
{
*dstPos++ = (BYTE)color.GetBlue();
*dstPos++ = (BYTE)color.GetGreen();
*dstPos++ = (BYTE)color.GetRed();
}
// Output source pixels into destination surface
count = OutputWidth;
while (count--)
{
// convert from 32 ARGB to 24bpp RGB
GpColor refColor = color; // save last ARGB color
color.SetValue(Unpremultiply(*srcPos++));
// NTRAID#NTBUG9-436131-2001-07-13-jerryste "P1CD: Printing:When printing the image, noise will appear in the surrounding of the image."
// Real problem: color bitmap and scaled-up alpha mask misalignment. halftoned low-alpha region let black see through
// Problems in DriverPrint::DrawImage
// 1) Calculation of boundsCap in integers has rounding error
// 2) Low level scanline rendering code offset coordinates by 0.5 before rounding
// 3) Scale integer version of boundsCap to boundsDev introduces more error
// 4) Single precision floating-point number calculation can lose precision
// We do not have a clean way to fix the real problem for the moment (7/28/01).
// Workaround: Change color to (white+neighbour)/2 when alpha is low to remove black pixels. Neighbor is
// either the previous pixel, or the next pixel if the previous pixel has a small alpha
const BYTE smallalpha = 10;
if ( color.GetAlpha()<smallalpha ) // if alpha is low
{
if ( ( refColor.GetAlpha()<smallalpha) && (count!=0) ) // if previous pixel has small alpha and there is next pixel
refColor.SetValue(Unpremultiply(*srcPos)); // use next pixel
if ( refColor.GetAlpha()>=smallalpha )
{
*dstPos++ = (BYTE) ( ( 255 + (UINT32) refColor.GetBlue() ) / 2 ); // blend with white
*dstPos++ = (BYTE) ( ( 255 + (UINT32) refColor.GetGreen() ) / 2 );
*dstPos++ = (BYTE) ( ( 255 + (UINT32) refColor.GetRed() ) / 2 );
}
else
{
*dstPos++ = 255; // set to white
*dstPos++ = 255;
*dstPos++ = 255;
}
}
else
{
*dstPos++ = (BYTE)color.GetBlue();
*dstPos++ = (BYTE)color.GetGreen();
*dstPos++ = (BYTE)color.GetRed();
}
}
// Bleed to the right
if (y != OutputY)
{
count = CappedBounds.X + CappedBounds.Width - OutputX - OutputWidth;
while (count--)
{
*dstPos++ = (BYTE)color.GetBlue();
*dstPos++ = (BYTE)color.GetGreen();
*dstPos++ = (BYTE)color.GetRed();
}
}
// Bleed down all subsequent scan lines. This should only happen when called
// implicitly by EpScanDIB::End()
if ((newWidth == 0) &&
(x == CappedBounds.X + CappedBounds.Width) &&
(y == CappedBounds.Y + CappedBounds.Height) &&
(OutputY != 0))
{
// Bleed down all previous subsequent scan lines.
// compute destination location into 24bpp buffer
BYTE* clearPos = BufStart;
INT capHeight = (CappedBounds.Height - 1) - (OutputY - CappedBounds.Y);
for (int cntY=0; cntY<capHeight; cntY++)
{
for (int cntX=0; cntX<CappedBounds.Width; cntX++)
{
clearPos[cntX*3] = (BYTE)color.GetBlue();
clearPos[cntX*3+1] = (BYTE)color.GetGreen();
clearPos[cntX*3+2] = (BYTE)color.GetRed();
}
clearPos += CappedStride;
}
}
}
// Compute size of bleed scan range
if (y == OutputY)
{
ASSERT(x >= OutputX + OutputWidth);
OutputBleed = OutputX + OutputWidth;
}
else
{
OutputBleed = CappedBounds.X;
}
OutputLastY = OutputY;
OutputX = x;
OutputY = y;
OutputWidth = newWidth;
return (ARGB*)Buf32bpp;
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFunc24bppOver - Handles output when we are rasterizing at capped
* dpi to a 24bpp unpremultiplied DIB. We do an
* implicit blend onto a white opaque surface.
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFunc24bppOver(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(!RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= CappedBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= CappedBounds.X && x <= (CappedBounds.X + CappedBounds.Width));
ASSERT(y >= CappedBounds.Y && y <= (CappedBounds.Y + CappedBounds.Height));
ASSERT((x + newWidth) <= (CappedBounds.X + CappedBounds.Width));
ASSERT((ScanOptions & ScanCappedBounds) &&
!(ScanOptions & ScanCapped32bpp) &&
(ScanOptions & ScanCappedOver));
if (OutputWidth > 0)
{
// compute destination location into 24bpp buffer
BYTE* dstPos = BufStart + (OutputX - CappedBounds.X) * 3 +
CappedStride * ((CappedBounds.Height - 1) -
(OutputY - CappedBounds.Y));
ARGB* srcPos = Buf32bpp;
while (OutputWidth--)
{
// An adaptation of the blending code from Andrew Godfrey's
// BlendOver function, but onto a white surface. This is done to
// improve the output quality of postscript.
GpColor color(*srcPos++);
UINT32 alpha = color.GetAlpha();
UINT32 alphaContrib;
if (alpha == 0)
{
*dstPos++ = 0xFF;
*dstPos++ = 0xFF;
*dstPos++ = 0xFF;
}
else if (alpha == 255)
{
*dstPos++ = color.GetBlue();
*dstPos++ = color.GetGreen();
*dstPos++ = color.GetRed();
}
else
{
// Dst = Src + (1-Alpha) * Dst
UINT32 multA = 255 - alpha;
UINT32 D1_000000FF = 0xFF;
UINT32 D2_0000FFFF = D1_000000FF * multA + 0x00000080;
UINT32 D3_000000FF = (D2_0000FFFF & 0x0000ff00) >> 8;
UINT32 D4_0000FF00 = (D2_0000FFFF + D3_000000FF) & 0x0000FF00;
alphaContrib = D4_0000FF00 >> 8;
// convert from 32 ARGB to 24bpp RGB
// store: (1-alpha)*0xFF + color for each B, G, R
*dstPos++ = (BYTE)(alphaContrib + color.GetBlue());
*dstPos++ = (BYTE)(alphaContrib + color.GetGreen());
*dstPos++ = (BYTE)(alphaContrib + color.GetRed());
}
}
}
// record location of next scan
OutputX = x;
OutputY = y;
OutputWidth = newWidth;
return (ARGB*)Buf32bpp;
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFuncAlpha - Handles output when we are rasterizing at device
* dpi to a 1bpp mask, we generate the mask on the fly
* using DonC's halftoning table.
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFuncAlpha(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= DeviceBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= DeviceBounds.X && x <= (DeviceBounds.X + DeviceBounds.Width));
ASSERT(y >= DeviceBounds.Y && y <= (DeviceBounds.Y + DeviceBounds.Height));
ASSERT((x + newWidth) <= (DeviceBounds.X + DeviceBounds.Width));
ASSERT((ScanOptions & ScanDeviceBounds) && (ScanOptions & ScanDeviceAlpha));
if (OutputWidth > 0)
{
// update bounding box for this band
if (OutputX < MinBound.X) MinBound.X = OutputX;
if (OutputY < MinBound.Y) MinBound.Y = OutputY;
if ((OutputX + OutputWidth) > MaxBound.X) MaxBound.X = OutputX + OutputWidth;
if (OutputY > MaxBound.Y) MaxBound.Y = OutputY;
INT startX = OutputX - DeviceBounds.X;
INT endX = startX + OutputWidth;
// !! Shift '91' into some global constant!?!
INT orgX = OutputX % 91;
INT orgY = (OutputY + TranslateHTTable) % 91;
INT htIndex = orgY*91 + orgX;
// compute destination location into 24bpp buffer
#ifdef PRINT_BOTTOM_UP
BYTE* dstPos = MaskStart +
MaskStride * ((DeviceBounds.Height - 1) -
(OutputY - DeviceBounds.Y)) + (startX >> 3);
#else
BYTE* dstPos = MaskStart +
MaskStride * (OutputY - DeviceBounds.Y) + (startX >> 3);
#endif
ARGB* srcPos = AlphaStart;
BYTE outByte = 0;
// using FOR loop makes it easier to detect relative bit position
for (INT xPos = startX; xPos < endX; xPos++)
{
GpColor color(*srcPos++);
INT maskBit = color.GetAlpha() >
HT_SuperCell_GreenMono[htIndex++] ? 1 : 0;
outByte = (outByte << 1) | maskBit;
if (((xPos+1) % 8) == 0)
*dstPos++ |= outByte;
if (++orgX >= 91)
{
orgX = 0;
htIndex = orgY*91;
}
}
// output the last partial byte
if ((xPos % 8) != 0)
{
*dstPos |= outByte << (8 - (xPos % 8));
}
}
// record location of next scan
OutputX = x;
OutputY = y;
OutputWidth = newWidth;
return (ARGB*)AlphaStart;
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFuncOpaque - Handles output when we are rasterizing at device
* dpi to a 1bpp opaque mask (1 if alpha > 0, 0 otherwise)
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/9/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFuncOpaque(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= DeviceBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= DeviceBounds.X && x <= (DeviceBounds.X + DeviceBounds.Width));
ASSERT(y >= DeviceBounds.Y && y <= (DeviceBounds.Y + DeviceBounds.Height));
ASSERT((x + newWidth) <= (DeviceBounds.X + DeviceBounds.Width));
ASSERT((ScanOptions & ScanDeviceBounds) && !(ScanOptions & ScanDeviceAlpha));
if (OutputWidth > 0)
{
// update bounding box for this band
if (OutputX < MinBound.X) MinBound.X = OutputX;
if (OutputY < MinBound.Y) MinBound.Y = OutputY;
if ((OutputX + OutputWidth) > MaxBound.X) MaxBound.X = OutputX + OutputWidth;
if (OutputY > MaxBound.Y) MaxBound.Y = OutputY;
INT startX = OutputX - DeviceBounds.X;
INT endX = startX + OutputWidth;
// compute destination location into 24bpp buffer
BYTE* dstPos = MaskStart +
MaskStride * ((DeviceBounds.Height - 1) -
(OutputY - DeviceBounds.Y)) + (startX >> 3);
ARGB* srcPos = AlphaStart;
BYTE outByte = 0;
// using FOR loop makes it easier to detect relative bit position
for (INT xPos = startX; xPos < endX; xPos++)
{
GpColor color(*srcPos++);
INT maskBit = (color.GetAlpha() == 0) ? 0 : 1;
outByte = (outByte << 1) | maskBit;
if (((xPos+1) % 8) == 0)
*dstPos++ |= outByte;
}
// output the last partial byte
if ((xPos % 8) != 0)
{
*dstPos |= outByte << (8 - (xPos % 8));
}
}
// record location of next scan
OutputX = x;
OutputY = y;
OutputWidth = newWidth;
return (ARGB*)AlphaStart;
}
/**************************************************************************\
*
* Function Description:
*
* NextBufferFuncZeroOut - Handles output where we aren't rasterizing to a
* DIB section, but only zeroing out portions of the
* the original 24bpp bitmap
*
* Arguments:
*
* [IN] x - Destination pixel coordinate in destination surface
* [IN] y - ""
* [IN] width - Number of pixels needed for the next buffer (can be 0)
* [IN] updateWidth - Number of pixels to update in the current buffer
*
* Return Value:
*
* Points to the resulting scan buffer
*
* History:
*
* 3/10/2k ericvan
* Created it.
*
\**************************************************************************/
VOID*
EpScanDIB::NextBufferFuncZeroOut(
INT x,
INT y,
INT newWidth,
INT updateWidth,
INT blenderNum
)
{
LastBlenderNum = blenderNum;
ASSERT(RenderAlpha);
ASSERT(newWidth >= 0);
ASSERTMSG(newWidth <= DeviceBounds.Width,
("Width exceeds SetBounds() request"));
ASSERT(x >= DeviceBounds.X && x <= (DeviceBounds.X + DeviceBounds.Width));
ASSERT(y >= DeviceBounds.Y && y <= (DeviceBounds.Y + DeviceBounds.Height));
ASSERT((x + newWidth) <= (DeviceBounds.X + DeviceBounds.Width));
ASSERT(!(ScanOptions & ScanDeviceBounds) && !(ScanOptions & ScanDeviceAlpha)
&& !(ScanOptions & (ScanCapped32bpp | ScanCapped32bppOver))
&& (ScanOptions & ScanDeviceZeroOut));
ASSERT(ZeroOutPad >= 0);
// THIS IS AN IMPORTANT CONDITION. If it's untrue, then we may fail to
// generate proper masks in some cases. Also causes problems in zeroing out.
ASSERT(y>=OutputY);
if (newWidth > 0)
{
// update bounding box for this band
if (x < MinBound.X)
{
MinBound.X = x;
}
if (y < MinBound.Y)
{
MinBound.Y = y;
}
if ((x + newWidth) > MaxBound.X)
{
MaxBound.X = x + newWidth;
}
if (y > MaxBound.Y)
{
MaxBound.Y = y;
}
}
if (OutputWidth < 0)
{
OutputX = x;
OutputY = y;
}
if ((y/ScaleY) != (OutputY/ScaleY))
{
// tally counts and zero out
BYTE* bitsPtr = BufStart + CappedStride *
((CappedBounds.Height - 1) -
((OutputY/ScaleY) - CappedBounds.Y));
DWORD* zeroPtr = (DWORD*) ZeroStart;
INT count = CappedBounds.Width;
while (count--)
{
if (*zeroPtr++ == 0)
{
*bitsPtr++ = '\0';
*bitsPtr++ = '\0';
*bitsPtr++ = '\0';
}
else
{
bitsPtr += 3;
}
}
ZeroMemory(ZeroStart, (CappedBounds.Width+ZeroOutPad)*sizeof(DWORD));
}
// bleed the color ZeroOutPad pixels to left and right
INT xPos = (x/ScaleX) - CappedBounds.X;
INT count = (newWidth/ScaleX) + ((newWidth % ScaleX) ? 1 : 0) + 1;
// Calculate how many pixels on the left we can pad
INT subtract = min(xPos, ZeroOutPad);
if (subtract > 0)
{
xPos -= subtract;
count += subtract;
}
count = min(count+ZeroOutPad, CappedBounds.Width + ZeroOutPad - xPos);
DWORD *zeroPtr = ((DWORD*)ZeroStart) + xPos;
ASSERT((xPos+count) <= CappedBounds.Width + ZeroOutPad);
while (count--)
{
*zeroPtr += 1;
zeroPtr++;
}
// record location of next scan
OutputX = x;
OutputY = y;
OutputWidth = newWidth;
return (ARGB*)AlphaStart;
}
/**************************************************************************\
*
* Function Description:
*
* Sets the bounds of the current scan.
*
* Arguments:
*
* [IN] bounds - the bounds.
*
* Return Value:
*
* None.
*
* History:
*
* 07/13/1999 t-wehunt
* Created it.
*
\**************************************************************************/
VOID EpScanDIB::SetRenderMode(
BOOL renderAlpha,
GpRect *newBounds
)
{
RenderAlpha = renderAlpha;
MinBound.X = INFINITE_MAX;
MinBound.Y = INFINITE_MAX;
MaxBound.X = INFINITE_MIN;
MaxBound.Y = INFINITE_MIN;
if (RenderAlpha)
{
DeviceBounds = *newBounds;
ZeroMemory(AlphaStart, DeviceBounds.Width * sizeof(ARGB));
if (ScanOptions & ScanDeviceBounds)
{
ZeroMemory(MaskStart, MaskStride * DeviceBounds.Height);
if (ScanOptions & ScanDeviceAlpha)
{
NextBuffer = (NEXTBUFFERFUNCTION) EpScanDIB::NextBufferFuncAlpha;
}
else
{
NextBuffer = (NEXTBUFFERFUNCTION) EpScanDIB::NextBufferFuncOpaque;
}
}
else
{
ASSERT(ScanOptions & ScanDeviceZeroOut);
ASSERT(!(ScanOptions & (ScanCapped32bpp | ScanCapped32bppOver)));
ZeroMemory(ZeroStart, (CappedBounds.Width + ZeroOutPad)*sizeof(DWORD));
NextBuffer = (NEXTBUFFERFUNCTION) EpScanDIB::NextBufferFuncZeroOut;
}
CurBuffer = AlphaStart;
}
else
{
CappedBounds = *newBounds;
if (ScanOptions & ScanCapped32bpp)
{
ZeroMemory(BufStart, CappedBounds.Width
* CappedBounds.Height * sizeof(ARGB));
NextBuffer = (NEXTBUFFERFUNCTION) NextBufferFunc32bpp;
}
else if (ScanOptions & ScanCapped32bppOver)
{
ZeroMemory(BufStart, CappedBounds.Width
* CappedBounds.Height * sizeof(ARGB));
NextBuffer = (NEXTBUFFERFUNCTION) NextBufferFunc32bppOver;
}
else
{
ASSERT(CappedStride != 0);
ZeroMemory(BufStart, CappedStride * CappedBounds.Height);
if (ScanOptions & ScanCappedOver)
{
NextBuffer = (NEXTBUFFERFUNCTION) NextBufferFunc24bppOver;
}
else
{
if (ScanOptions & ScanBleedOut)
{
NextBuffer = (NEXTBUFFERFUNCTION) NextBufferFunc24bppBleed;
}
else
{
NextBuffer = (NEXTBUFFERFUNCTION) NextBufferFunc24bpp;
}
}
CurBuffer = Buf32bpp;
}
}
OutputWidth = -1;
}
/**************************************************************************\
*
* Function Description:
*
* Flushes the current scan.
*
* Arguments:
*
* None.
*
* Return Value:
*
* None.
*
* History:
*
* 07/13/1999 t-wehunt
* Created it.
*
\**************************************************************************/
VOID
EpScanDIB::Flush()
{
}
/**************************************************************************\
*
* Function Description:
*
* Resets the DIBSection buffer, safely releasing resources and resetting
* them.
*
* Arguments:
*
* None.
*
* Return Value:
*
* None.
*
* History:
*
* 07/26/1999 t-wehunt
* Created it.
*
\**************************************************************************/
VOID
EpScanDIB::DestroyBufferDIB()
{
if (BufStart != NULL)
{
GpFree(BufStart);
}
if (AlphaStart != NULL)
{
GpFree(AlphaStart);
}
BufStart = NULL;
Buf32bpp = NULL;
CurBuffer = NULL;
// Transparency mask
MaskStart = NULL;
// Alpha buffer
AlphaStart = NULL;
ZeroStart = NULL;
RenderAlpha = FALSE;
ScanOptions = 0;
OutputWidth = -1;
NextBuffer = NULL;
}
/**************************************************************************\
*
* Function Description:
*
* In pre-multiplies an ARGB value
*
* Arguments:
*
*
* Return Value:
*
* GpStatus.
*
* History:
*
* 10/08/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
EpScanDIB::CreateBufferDIB(
const GpRect* BoundsCap,
const GpRect* BoundsDev,
DWORD options,
INT scaleX,
INT scaleY)
{
ScanOptions = options;
CappedBounds = *BoundsCap;
DeviceBounds = *BoundsDev;
ScaleX = scaleX;
ScaleY = scaleY;
if (options & ScanCappedBounds)
{
ZeroMemory(&Buf.BMI, sizeof(Buf.BMI));
Buf.BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Buf.BMI.bmiHeader.biWidth = CappedBounds.Width;
Buf.BMI.bmiHeader.biHeight = CappedBounds.Height;
Buf.BMI.bmiHeader.biPlanes = 1;
if (options & (ScanCapped32bpp | ScanCapped32bppOver))
{
RGBQUAD red = { 0, 0, 0xFF, 0}; // red
RGBQUAD green = { 0, 0xFF, 0, 0}; // green
RGBQUAD blue = { 0xFF, 0, 0, 0}; // blue
Buf.BMI.bmiColors[0] = red;
Buf.BMI.bmiColors[1] = green;
Buf.BMI.bmiColors[2] = blue;
Buf.BMI.bmiHeader.biBitCount = 32;
Buf.BMI.bmiHeader.biCompression = BI_BITFIELDS;
}
else
{
Buf.BMI.bmiHeader.biHeight += 2;
Buf.BMI.bmiHeader.biBitCount = 24;
Buf.BMI.bmiHeader.biClrUsed = 0;
Buf.BMI.bmiHeader.biCompression = BI_RGB;
}
if (options & (ScanCapped32bpp | ScanCapped32bppOver))
{
CappedStride = CappedBounds.Width*sizeof(ARGB);
}
else
{
// use extra allocation at the end of DIB for temp 32bpp storage
CappedStride = RoundDWORD((CappedBounds.Width * 3));
}
BufStart = (BYTE*) GpMalloc(CappedStride *
Buf.BMI.bmiHeader.biHeight);
if (BufStart == NULL)
return OutOfMemory;
if (options & (ScanCapped32bpp | ScanCapped32bppOver))
{
Buf32bpp = NULL;
}
else
{
Buf.BMI.bmiHeader.biHeight -= 2;
Buf32bpp = (ARGB*) (BufStart + CappedStride*CappedBounds.Height);
}
}
else
{
BufStart = NULL;
Buf32bpp = NULL;
}
if (options & ScanDeviceBounds)
{
ZeroMemory(&Mask.BMI, sizeof(Mask.BMI));
// if we do zeroing out of the capped bitmap, then we require that
// their sizes be an integer ratio of each other (device>= capped).
ASSERT(!(options & ScanDeviceZeroOut) ||
((options & ScanDeviceZeroOut) &&
IsInteger((REAL)DeviceBounds.Height/(REAL)CappedBounds.Height) &&
IsInteger((REAL)DeviceBounds.Width/(REAL)CappedBounds.Width)));
ASSERT(DeviceBounds.Height > 0);
Mask.BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Mask.BMI.bmiHeader.biWidth = DeviceBounds.Width;
Mask.BMI.bmiHeader.biHeight = DeviceBounds.Height;
Mask.BMI.bmiHeader.biPlanes = 1;
Mask.BMI.bmiHeader.biBitCount = 1;
Mask.BMI.bmiHeader.biCompression = BI_RGB;
RGBQUAD opaque = { 0,0,0,0 };
RGBQUAD transparent = { 0xFF, 0xFF, 0xFF, 0xFF };
Mask.BMI.bmiColors[0] = transparent;
Mask.BMI.bmiColors[1] = opaque;
MaskStride = (DeviceBounds.Width - 1) >> 3;
MaskStride = MaskStride + (sizeof(DWORD) - MaskStride % sizeof(DWORD));
INT AlphaSize = DeviceBounds.Width * sizeof(ARGB);
AlphaStart = (ARGB*) GpMalloc(MaskStride * DeviceBounds.Height +
AlphaSize);
if (AlphaStart == NULL)
{
return OutOfMemory;
}
// device space bounds only for alpha channel
MaskStart = (BYTE*)(AlphaStart) + AlphaSize;
ASSERT(MaskStart != NULL);
ZeroStart = NULL;
}
else
{
MaskStart = NULL;
if (ScanOptions & ScanDeviceZeroOut)
{
// device space bounds only for alpha channel
AlphaStart = (ARGB*) GpMalloc(DeviceBounds.Width * sizeof(ARGB) +
(CappedBounds.Width+ZeroOutPad) * sizeof(DWORD));
if (AlphaStart == NULL)
return OutOfMemory;
// array for maintaining zero out counts
ZeroStart = (BYTE*)(AlphaStart + DeviceBounds.Width);
}
else
{
AlphaStart = NULL;
ZeroStart = NULL;
}
}
// To prevent bad output when overlapping images have same alpha value
// we increment our position in the HT Table matrix.
TranslateHTTable++;
// NOTE: We don't bother filling the monochrome DIB with 0's or 1's
return Ok;
}
BOOL EpScanDIB::GetActualBounds(GpRect *rect)
{
if (!(ScanOptions & (ScanDeviceBounds | ScanDeviceZeroOut)))
{
rect->X = 0;
rect->Y = 0;
rect->Width = DeviceBounds.Width;
rect->Height = DeviceBounds.Height;
return TRUE;
}
if (MaxBound.X <= 0)
{
return FALSE;
}
ASSERT(MaxBound.X > -1 && MaxBound.Y > -1);
GpRect tempRect;
// relative to (0, 0) in device units (not device space)
tempRect.X = (rect->X = MinBound.X - DeviceBounds.X);
tempRect.Y = (rect->Y = MinBound.Y - DeviceBounds.Y);
rect->Width = MaxBound.X - MinBound.X;
rect->Height = MaxBound.Y - MinBound.Y + 1;
// Round bounds to multiples of ScaleX, ScaleY. This is so
// We map between capped and device rectangles easily
rect->X = (rect->X / ScaleX) * ScaleX;
rect->Y = (rect->Y / ScaleY) * ScaleY;
rect->Width = rect->Width + tempRect.X - rect->X;
rect->Height = rect->Height + tempRect.Y - rect->Y;
INT remainderX = rect->Width % ScaleX;
INT remainderY = rect->Height % ScaleY;
if (remainderX > 0) rect->Width += (ScaleX - remainderX);
if (remainderY > 0) rect->Height += (ScaleY - remainderY);
ASSERT((rect->X + rect->Width) <= (DeviceBounds.Width + ScaleX));
ASSERT((rect->Y + rect->Height) <= (DeviceBounds.Height + ScaleY));
return TRUE;
}
// !! Out of commission for the time being.
#if 0
/**************************************************************************\
*
* Function Description:
*
* Creates a monochrome bitmap from the alpha channel of the DIB.
* This code uses DonC's halftoning table cells to determine the pattern
* for use in mask generation.
*
* NOTE: The mask is generated at device Dpi not capped Dpi.
*
* Arguments:
*
* zeroOut - only modify the original DIB for non-Postscript since we
* OR the dib in. For PS, we use imagemask exclusively.
*
* Return Value:
*
* GpStatus.
*
* History:
*
* 10/08/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
EpScanDIB::CreateAlphaMask()
{
DWORD MaskStride;
MaskStride = (ScanBounds.Width - 1) >> 3;
MaskStride = MaskStride + ( 4 - (MaskStride % 4));
// SetBounds() multiplies the ScanBounds for the DPI scaling.
INT width = ScanBounds.Width;
INT height = ScanBounds.Height;
INT orgXsrc = ScanBounds.X + TranslateHTTable;
INT orgYsrc = ScanBounds.Y + TranslateHTTable;
BYTE* dst = MaskStart;
BYTE* src = AlphaStart;
ARGB* orig = BufStart;
INT srcStride = ScanBounds.Width;
INT dstStride = MaskStride;
if (width == 0)
{
return GenericError;
}
for (INT yPos=0; yPos < height; yPos++)
{
src = AlphaStart + yPos*srcStride;
dst = MaskStart + yPos*dstStride;
INT orgX = orgXsrc % 91;
INT orgY = orgYsrc % 91;
INT htStartX = orgX;
INT htStartRow = orgY * 91;
INT htIndex = htStartRow + orgX;
BYTE outByte = 0;
for (INT xPos=0; xPos < width; xPos++)
{
// unpremultiply or zero out only once per pixel of source image
// at capped DPI
if (((yPos % MaskScaleY) == 0) && ((xPos % MaskScaleX) == 0))
{
// Check if we should ZERO out his pixel in the original
// source image. We do so if all alpha values for this pixel
// in the device DPI alpha image are 0. This is done for
// better compression in the postscript output case.
BOOL zeroIt = TRUE;
for (INT xTmp=0; xTmp < MaskScaleX; xTmp++)
{
for (INT yTmp=0; yTmp < MaskScaleY; yTmp++)
{
if (*(src + xTmp + (yTmp * srcStride)) != 0)
{
zeroIt = FALSE;
break;
}
}
}
if (zeroIt)
*orig = 0;
else
*orig = Unpremultiply(*(ARGB*)orig);
orig++;
}
INT maskBit = *src++ > HT_SuperCell_GreenMono[htIndex] ? 0 : 1;
outByte = (outByte << 1) | maskBit;
if (((xPos+1) % 8) == 0)
*dst++ = outByte;
htIndex++;
if (++orgX >= 91)
{
orgX = 0;
htIndex = htStartRow;
}
}
// output last partial byte
if ((xPos % 8) != 0)
{
// shift remaining bits & output
outByte = outByte << (8 - (xPos % 8));
*dst = outByte;
}
orgYsrc++;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Creates a 0-1 bitmap where we know the alpha channel is always
* 0x00 or 0xFF. We iterate through the bits and where-ever pixel is != 0 we
* output a 1 otherwise a 0. This is better than the Floyd-Steinberg
* which still produces spurious 0 bits even though there shouldn't really
* be any.
*
* Arguments:
*
* Return Value:
*
* GpStatus.
*
* History:
*
* 10/08/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
EpScanDIB::CreateOpaqueMask()
{
ARGB* orig = BufStart;
BYTE* AlphaPos = AlphaStart;
BYTE* MaskPos = MaskStart;
INT DstWidth = ScanBounds.Width;
INT DstHeight = ScanBounds.Height;
INT SrcWidth = ScanBounds.Width;
INT &Height = ScanBounds.Height;
LONG BitStride = (DstWidth - 1) >> 3;
BitStride = BitStride + (4 - (BitStride % 4));
BYTE outByte = 0;
for (INT y=0; y<DstHeight; y++)
{
AlphaPos = AlphaStart + SrcWidth*y;
MaskPos = MaskStart + BitStride*y;
for (INT x=0; x<DstWidth; x++)
{
if (((y % MaskScaleY) == 0) && ((x % MaskScaleX) == 0))
{
// Check if we should ZERO out his pixel in the original
// source image. We do so if all alpha values for this pixel
// in the device DPI alpha image are 0. This is done for
// better compression in the postscript output case.
BOOL zeroIt = TRUE;
for (INT xTmp=0; xTmp < MaskScaleX; xTmp++)
{
for (INT yTmp=0; yTmp < MaskScaleY; yTmp++)
{
if (*(AlphaPos + xTmp + (yTmp * SrcWidth)) != 0)
{
zeroIt = FALSE;
break;
}
}
}
// no need to unpremultiply since this is a 0-1 source image
if (zeroIt)
*orig = 0;
orig++;
}
BYTE alpha = *AlphaPos++;
outByte = (outByte << 1) | ((alpha != 0) ? 0:1);
if (((x + 1) % 8) == 0)
*MaskPos++ = outByte;
}
if ((x % 8) != 0)
{
*MaskPos = (BYTE)(outByte << (8 - (x % 8)));
}
}
return Ok;
}
#endif