2020-09-30 16:53:55 +02:00

3798 lines
111 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1998 Microsoft Corporation
*
* Abstract:
*
* Contains the GDI virtual driver that takes DDI calls and leverages
* existing GDI calls wherever possible to improve performance.
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
#include "precomp.hpp"
#include "..\Render\scan.hpp"
#include "..\entry\device.hpp"
#include "..\entry\metafile.hpp"
#include "..\fondrv\tt\ttfd\fontddi.h"
#include "..\entry\graphics.hpp"
#include "..\entry\regiontopath.hpp"
// font stuff
#define _NO_DDRAWINT_NO_COM
// This is to use GpGlyphPath
extern "C" {
#include "..\fondrv\tt\ttfd\fdsem.h"
#include "..\fondrv\tt\ttfd\mapfile.h"
};
#include "..\entry\intMap.hpp"
#include "..\entry\fontface.hpp"
#include "..\entry\facerealization.hpp"
#include "..\entry\fontfile.hpp"
#include "..\entry\fontable.hpp"
#include "..\entry\FontLinking.hpp"
#include "..\entry\family.hpp"
#include "..\entry\font.hpp"
#include <ole2.h>
#include <objidl.h>
#include <winerror.h>
#include <tchar.h>
//#define NO_PS_CLIPPING 1
//#define DO_PS_COALESING 1
//
// Structures necessary for (postscript) escape clipping setup
/* Types for postscript written to metafiles */
#define CLIP_SAVE 0
#define CLIP_RESTORE 1
#define CLIP_INCLUSIVE 2
#define CLIP_EXCLUSIVE 3
#define RENDER_NODISPLAY 0
#define RENDER_OPEN 1
#define RENDER_CLOSED 2
#define FILL_ALTERNATE 1 // == ALTERNATE
#define FILL_WINDING 2 // == WINDING
#pragma pack(2)
/* Win16 structures for escapes. */
struct POINT16
{
SHORT x;
SHORT y;
};
struct LOGPEN16
{
WORD lopnStyle;
POINT16 lopnWidth;
COLORREF lopnColor;
};
struct LOGBRUSH16
{
WORD lbStyle;
COLORREF lbColor;
SHORT lbHatch;
};
struct PathInfo16
{
WORD RenderMode;
BYTE FillMode;
BYTE BkMode;
LOGPEN16 Pen;
LOGBRUSH16 Brush;
DWORD BkColor;
};
#pragma pack()
/**************************************************************************\
*
* Function Description:
*
* MemoryStream class. Wrap an IStream* around an existing chunk of memory
*
*
* History:
*
* 6/14/1999 ericvan
* Created it.
*
\**************************************************************************/
class MemoryStream : public IStream
{
public:
LPBYTE memory;
LPBYTE position;
DWORD size;
DWORD count;
MemoryStream(LPBYTE memoryPtr, DWORD memorySize)
{
memory = memoryPtr;
position = memory;
size = memorySize;
count = 1;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
};
virtual ULONG STDMETHODCALLTYPE AddRef( void)
{
InterlockedIncrement((LPLONG)&count);
return count;
};
virtual ULONG STDMETHODCALLTYPE Release( void)
{
InterlockedDecrement((LPLONG)&count);
if (!count)
{
delete this;
return 0;
}
else
return count;
};
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read(
/* [length_is][size_is][out] */ void __RPC_FAR *pv,
/* [in] */ ULONG cb,
/* [out] */ ULONG __RPC_FAR *pcbRead)
{
if (!pv)
return STG_E_INVALIDPOINTER;
DWORD readBytes = cb;
if ((ULONG)cb > (ULONG)(memory+size-position))
// !!! IA64 - it's theoretically possible that memory and position
// more than maxint apart and then this arithmetic breaks down.
// We need to verify that this is not possible.
readBytes = (DWORD)(memory+size-position);
if (!readBytes)
{
if (pcbRead)
*pcbRead = 0;
return S_OK;
}
memcpy((LPVOID) pv, (LPVOID) position, readBytes);
position += readBytes;
if (pcbRead)
*pcbRead += readBytes;
return S_OK;
}
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Write(
/* [size_is][in] */ const void __RPC_FAR *pv,
/* [in] */ ULONG cb,
/* [out] */ ULONG __RPC_FAR *pcbWritten)
{
return STG_E_WRITEFAULT;
}
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Seek(
/* [in] */ LARGE_INTEGER dlibMove,
/* [in] */ DWORD dwOrigin,
/* [out] */ ULARGE_INTEGER __RPC_FAR *plibNewPosition)
{
switch (dwOrigin)
{
case STREAM_SEEK_SET:
position = memory+dlibMove.QuadPart;
break;
case STREAM_SEEK_CUR:
position = position+dlibMove.QuadPart;
break;
case STREAM_SEEK_END:
if (dlibMove.QuadPart<0) dlibMove.QuadPart = -dlibMove.QuadPart;
position = memory+size-dlibMove.QuadPart;
break;
default:
return STG_E_INVALIDPARAMETER;
}
if (position>memory+size)
{
position = memory+size;
return S_FALSE;
}
if (position<0)
{
position = 0;
return S_FALSE;
}
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE SetSize(
/* [in] */ ULARGE_INTEGER libNewSize)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual /* [local] */ HRESULT STDMETHODCALLTYPE CopyTo(
/* [unique][in] */ IStream __RPC_FAR *pstm,
/* [in] */ ULARGE_INTEGER cb,
/* [out] */ ULARGE_INTEGER __RPC_FAR *pcbRead,
/* [out] */ ULARGE_INTEGER __RPC_FAR *pcbWritten)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual HRESULT STDMETHODCALLTYPE Commit(
/* [in] */ DWORD grfCommitFlags)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual HRESULT STDMETHODCALLTYPE Revert( void)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual HRESULT STDMETHODCALLTYPE LockRegion(
/* [in] */ ULARGE_INTEGER libOffset,
/* [in] */ ULARGE_INTEGER cb,
/* [in] */ DWORD dwLockType)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual HRESULT STDMETHODCALLTYPE UnlockRegion(
/* [in] */ ULARGE_INTEGER libOffset,
/* [in] */ ULARGE_INTEGER cb,
/* [in] */ DWORD dwLockType)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual HRESULT STDMETHODCALLTYPE Stat(
/* [out] */ STATSTG __RPC_FAR *pstatstg,
/* [in] */ DWORD grfStatFlag)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
virtual HRESULT STDMETHODCALLTYPE Clone(
/* [out] */ IStream __RPC_FAR *__RPC_FAR *ppstm)
{
return STG_E_UNIMPLEMENTEDFUNCTION;
}
};
/**************************************************************************\
*
* Function Description:
*
* GDI+ Printer callback
*
* Arguments:
*
* [IN] GDIPPRINTDATA block
*
* Return Value:
*
* status
*
* History:
*
* 6/14/1999 ericvan
* Created it.
*
\**************************************************************************/
#ifndef DCR_REMOVE_OLD_186091
GpStatus
__stdcall
GdipDecodePrinterCallback(DWORD size,
LPVOID emfBlock,
SURFOBJ* surfObj,
HDC hdc,
RECTL* bandClip,
SIZEL* bandSize
)
{
#ifdef DCR_DISABLE_OLD_186091
WARNING(("DCR: Using disabled functionality 186091"));
return NotImplemented;
#else
INT numBits = 4;
if (!emfBlock || size == 0 || !surfObj || hdc == NULL || !bandClip ||!bandSize)
{
return InvalidParameter;
}
FPUStateSaver fpuState;
// create Graphics and draw Metafile into it.
GpMetafile* metafile;
GpGraphics* graphics;
// use banding information to create a temporary pseudo-HDC surface
graphics = GpGraphics::GetFromHdcSurf(hdc, surfObj, bandClip);
if (CheckValid(graphics))
{
{
GpLock lockGraphics(graphics->GetObjectLock());
// wrap memory block in stream object
MemoryStream *emfStream = new MemoryStream((LPBYTE)emfBlock, size);
// create metafile
metafile = new GpMetafile((IStream *)emfStream);
if (metafile)
{
// play metafile into the printer graphics DC
// !! destination point - relative to band or surface origin (0,0) ??
graphics->DrawImage(metafile,
GpPointF(0.0, 0.0));
metafile->Dispose();
}
emfStream->Release();
}
delete graphics;
}
return Ok;
#endif
}
#endif
BOOL
DriverPrint::SetupBrush(
DpBrush* brush,
DpContext* context,
DpBitmap* surface
)
{
GpBrush *gpBrush = GpBrush::GetBrush(brush);
if (IsSolid = gpBrush->IsSolid())
{
ASSERT(gpBrush->GetBrushType() == BrushTypeSolidColor);
if (((GpSolidFill *)gpBrush)->GetColor().GetAlpha() == 0)
{
// yes, this did come up... hey it's a cheap test.
return TRUE;
}
SolidColor = gpBrush->ToCOLORREF();
}
IsOpaque = (context->CompositingMode == CompositingModeSourceCopy) ||
gpBrush->IsOpaque(TRUE);
// Currently only DriverPS uses this
//IsNearConstant = gpBrush->IsNearConstant(&MinAlpha, &MaxAlpha);
IsNearConstant = FALSE;
if (!IsOpaque &&
(brush->Type == BrushTypeTextureFill))
{
GpTexture *textureBrush;
DpTransparency transparency;
textureBrush = static_cast<GpTexture*>(GpBrush::GetBrush(brush));
GpBitmap* bitmap = textureBrush->GetBitmap();
Is01Bitmap = ((bitmap != NULL) &&
(bitmap->GetTransparencyHint(&transparency) == Ok) &&
(transparency == TransparencySimple));
}
else
{
Is01Bitmap = FALSE;
}
return FALSE;
}
VOID
DriverPrint::RestoreBrush(
DpBrush * brush,
DpContext * context,
DpBitmap * surface
)
{
}
/**************************************************************************\
*
* Function Description:
*
* GDI driver class constructor.
*
* Arguments:
*
* [IN] device - Associated device
*
* Return Value:
*
* IsValid() is FALSE in the event of failure.
*
* History:
*
* 12/04/1998 andrewgo
* Created it.
*
\**************************************************************************/
DriverPrint::DriverPrint(
GpPrinterDevice *device
)
{
IsLockable = FALSE;
SetValid(TRUE);
Device = (GpDevice*)device;
Uniqueness = -1;
ImageCache = NULL;
IsPathClip = FALSE;
REAL dpix = TOREAL(GetDeviceCaps(device->DeviceHdc, LOGPIXELSX));
REAL dpiy = TOREAL(GetDeviceCaps(device->DeviceHdc, LOGPIXELSY));
if (dpix > PostscriptImagemaskDPICap)
PostscriptScalerX = GpCeiling(dpix / PostscriptImagemaskDPICap);
else
PostscriptScalerX = 1;
if (dpiy > PostscriptImagemaskDPICap)
PostscriptScalerY = GpCeiling(dpiy / PostscriptImagemaskDPICap);
else
PostscriptScalerY = 1;
#ifdef DBG
OutputText("GDI+ PrintDriver Created\n");
#endif
}
/**************************************************************************\
*
* Function Description:
*
* GDI driver class destructor.
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
DriverPrint::~DriverPrint(
VOID
)
{
if (ImageCache)
delete ImageCache;
}
/**************************************************************************\
*
* Function Description:
*
* Computes band size and saves original clipping bounds
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus
*
* History:
*
* 11/23/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::SetupPrintBanding(
DpContext* context,
GpRect* drawBoundsCap,
GpRect* drawBoundsDev
)
{
// determine band size from MAX_BAND_ALLOC
NumBands = GpCeiling((REAL)(drawBoundsCap->Width *
drawBoundsCap->Height *
sizeof(ARGB)) / (REAL)MAX_BAND_ALLOC);
BandHeightCap = GpCeiling((REAL)drawBoundsCap->Height / (REAL)NumBands);
BandHeightDev = BandHeightCap * ScaleY;
// Band bounds for capped DPI rendering
BandBoundsCap.X = drawBoundsCap->X;
BandBoundsCap.Y = drawBoundsCap->Y;
BandBoundsCap.Width = drawBoundsCap->Width;
BandBoundsCap.Height = BandHeightCap;
// Band bounds for device DPI rendering
BandBoundsDev.X = drawBoundsDev->X;
BandBoundsDev.Y = drawBoundsDev->Y;
BandBoundsDev.Width = drawBoundsDev->Width;
BandBoundsDev.Height = BandHeightDev;
ASSERT(NumBands >= 1 && BandHeightCap >= 1 && BandHeightDev >= 1);
context->VisibleClip.StartBanding();
// Tweak the original capped and device bounds to force
// DpOutputClipRegion in our rendering pipeline. This is necessary since
// we clip to each band.
drawBoundsCap->Y--; drawBoundsCap->Height += 2;
drawBoundsCap->X--; drawBoundsCap->Width += 2;
drawBoundsDev->Y--; drawBoundsDev->Height += 2;
drawBoundsDev->X--; drawBoundsDev->Width += 2;
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Computes band size and sets up clipping bounds
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus
*
* History:
*
* 11/23/1999 ericvan
* Created it.
*
\**************************************************************************/
VOID DriverPrint::EndPrintBanding(
DpContext* context
)
{
// restore state of clip region
context->VisibleClip.EndBanding();
}
/**************************************************************************\
*
* Function Description:
*
* Helper function for SetupEscapeClipping (see return value section)
*
* Arguments:
*
* points - array of POINTs
* types - array of BYTE types
*
* Return Value:
*
* 1 if the points indicate a clockwise described rectangle, 2 if
* counterclockwise and 0 if not a rectangle
*
* History:
*
* 10/10/2000 ericvan
* Created it.
*
\**************************************************************************/
INT isSimpleRect(DynPointArray &points, DynByteArray &types)
{
if (points.GetCount() != 4)
{
return 0;
}
//specified in clockwise order
if (points[0].Y == points[1].Y && points[2].Y == points[3].Y &&
points[0].X == points[3].X && points[1].X == points[2].X &&
types[0] == PathPointTypeStart &&
types[1] == PathPointTypeLine &&
types[2] == PathPointTypeLine &&
types[3] == (PathPointTypeLine | PathPointTypeCloseSubpath))
return 1;
//specified in counterclockwise order
if (points[0].X == points[1].X && points[2].X == points[3].X &&
points[0].Y == points[3].Y && points[1].Y == points[2].Y &&
types[0] == PathPointTypeStart &&
types[1] == PathPointTypeLine &&
types[2] == PathPointTypeLine &&
types[3] == (PathPointTypeLine | PathPointTypeCloseSubpath))
return 2;
return 0;
}
/**************************************************************************\
*
* Function Description:
*
* Setup clipping to a given arbitrary path. On Win98 the path must already
* be flattened. The points are specified in POINT units (not floating point),
* consistent with output of RegionToPath.
*
* The path can contain subpaths. For Win9x, we coalesce the subpaths into
* a single path to avoid poor performance on GDI. The path is ANDed into
* any existing postscript clip paths.
*
* Arguments:
*
* HDC - hdc to send escapes to.
* points - array of POINTs
* types - array of BYTE types
*
* CLIP_SAVE
* BEGIN_PATH
* Render path using GDI (use NULL pen + brush to ensure nothings drawn)
* END_PATH
* CLIP_RESTORE
*
* Return Value:
*
* GpStatus
*
* History:
*
* 3/3/2000 ericvan
* Created it.
*
\**************************************************************************/
VOID
DriverPrint::SetupEscapeClipping(
HDC hdc,
DynPointArray& points,
DynByteArray& types,
GpFillMode fillMode
)
{
PathInfo16 pi;
pi.RenderMode = RENDER_NODISPLAY;
pi.FillMode = (fillMode == FillModeAlternate) ?
FILL_ALTERNATE : FILL_WINDING;
pi.BkMode = TRANSPARENT;
pi.Pen.lopnStyle = PS_NULL;
pi.Pen.lopnWidth.x = 0;
pi.Pen.lopnWidth.y = 0;
pi.Pen.lopnColor = RGB(0,0,0);
pi.Brush.lbStyle = BS_NULL;
pi.Brush.lbColor = RGB(0,0,0);
pi.Brush.lbHatch = 0;
ASSERT((fillMode == FillModeAlternate) || (fillMode == FillModeWinding));
GpPoint* pointPtr = points.GetDataBuffer();
BYTE* typePtr = types.GetDataBuffer();
GpPoint* freeThisPtr = NULL;
INT count = points.GetCount();
// We are partially visible, so we expect something!
ASSERT(count > 0);
if (count <= 1)
{
return;
}
// There is a bug on some printers (eg. hplj8550) where they incorrectly
// cache simple clipping regions. To work around this we take simple
// clipping regions and make them complex.
GpPoint simplerect[5];
BYTE simpletypes[] = {
PathPointTypeStart,
PathPointTypeLine,
PathPointTypeLine,
PathPointTypeLine,
PathPointTypeLine | PathPointTypeCloseSubpath};
if (isSimpleRect(points, types)>0)
{
//inserting a new point between the third and fourth points which is
//between the two of them
simplerect[0] = points[0];
simplerect[1] = points[1];
simplerect[2] = points[2];
simplerect[4] = points[3];
//we take the average of the two last points to make a point inbetween
//them. This works whether the rectangle is specified clockwise or
//counterclockwise
simplerect[3].X = (points[2].X + points[3].X) / 2;
simplerect[3].Y = (points[2].Y + points[3].Y) / 2;
count = 5;
pointPtr = simplerect;
typePtr = simpletypes;
}
INT subCount;
BYTE curType;
// save original clip
WORD clipMode = CLIP_SAVE;
Escape(hdc, CLIP_TO_PATH, sizeof(clipMode), (LPSTR)&clipMode, NULL);
// send path to PS printer as an escape
Escape(hdc, BEGIN_PATH, 0, NULL, NULL);
// !! Notice the lack of error checking when we call GDI...
// Win95 and WinNT are subtly different in processing postscript escapes.
if (Globals::IsNt)
{
BYTE lastType = 0;
::BeginPath(hdc);
INT startIndex;
while (count-- > 0)
{
switch ((curType = *typePtr++) & PathPointTypePathTypeMask)
{
case PathPointTypeStart:
::MoveToEx(hdc, pointPtr->X, pointPtr->Y, NULL);
pointPtr++;
ASSERT(count > 0); // no illformed paths please...
lastType = *typePtr & PathPointTypePathTypeMask;
subCount = 0;
break;
case PathPointTypeLine:
if (lastType == PathPointTypeBezier)
{
ASSERT(subCount % 3 == 0);
if (subCount % 3 == 0)
{
::PolyBezierTo(hdc, (POINT*)pointPtr, subCount);
}
pointPtr += subCount;
subCount = 1;
lastType = PathPointTypeLine;
}
else
{
subCount++;
}
break;
case PathPointTypeBezier:
if (lastType == PathPointTypeLine)
{
::PolylineTo(hdc, (POINT*)pointPtr, subCount);
pointPtr += subCount;
subCount = 1;
lastType = PathPointTypeBezier;
}
else
{
subCount++;
}
break;
}
if (curType & PathPointTypeCloseSubpath)
{
ASSERT(subCount > 0);
if (lastType == PathPointTypeBezier)
{
ASSERT(subCount % 3 == 0);
if (subCount % 3 == 0)
{
::PolyBezierTo(hdc, (POINT*)pointPtr, subCount);
}
}
else
{
ASSERT(lastType == PathPointTypeLine);
::PolylineTo(hdc, (POINT*)pointPtr, subCount);
}
pointPtr += subCount;
subCount = 0;
::CloseFigure(hdc);
}
}
::EndPath(hdc);
::StrokePath(hdc);
}
else
{
while (count-- > 0)
{
curType = *typePtr++;
ASSERT((curType & PathPointTypePathTypeMask) != PathPointTypeBezier);
if ((curType & PathPointTypePathTypeMask) == PathPointTypeStart)
{
subCount = 1;
}
else
{
subCount ++;
}
if (curType & PathPointTypeCloseSubpath)
{
ASSERT(subCount > 0);
if (subCount == 4)
{
// Win98 postscript drivers are known for recognizing
// rectangle polygons at driver level and converting them
// to rectfill or rectclip calls. Unfortunately, there is
// a bug that they don't preserve the orientation and so
// when winding fill is used, the fill is bad.
// We fix this by hacking it into a path of five points.
GpPoint rectPts[5];
rectPts[0].X = pointPtr[0].X;
rectPts[0].Y = pointPtr[0].Y;
rectPts[1].X = (pointPtr[0].X + pointPtr[1].X)/2;
rectPts[1].Y = (pointPtr[0].Y + pointPtr[1].Y)/2;
rectPts[2].X = pointPtr[1].X;
rectPts[2].Y = pointPtr[1].Y;
rectPts[3].X = pointPtr[2].X;
rectPts[3].Y = pointPtr[2].Y;
rectPts[4].X = pointPtr[3].X;
rectPts[4].Y = pointPtr[3].Y;
::Polygon(hdc, (POINT*)&rectPts[0], 5);
}
else
{
::Polygon(hdc, (POINT*)pointPtr, subCount);
}
pointPtr += subCount;
subCount = 0;
// send END_PATH, BEGIN_PATH escapes
if (count > 0)
{
Escape(hdc, END_PATH, sizeof(PathInfo16), (LPSTR)&pi, NULL);
Escape(hdc, BEGIN_PATH, 0, NULL, NULL);
}
}
}
}
// we should end on a closed subpath
Escape(hdc, END_PATH, sizeof(PathInfo16), (LPSTR)&pi, NULL);
// end the path and set up for clipping
// NT driver ignoes the high WORD - always uses eoclip, but according to
// Win31 documentation it should be set to the fillmode.
DWORD inclusiveMode = CLIP_INCLUSIVE | pi.FillMode <<16;
Escape(hdc, CLIP_TO_PATH, sizeof(inclusiveMode), (LPSTR)&inclusiveMode, NULL);
#if 0
SelectObject(hdc, oldhPen);
SelectObject(hdc, oldhBrush);
SetPolyFillMode(hdc, oldFillMode);
#endif
#ifdef DBG
OutputText("GDI+ End Setup Escape Clipping");
#endif
}
/**************************************************************************\
*
* Function Description:
*
* Setup simple path clipping. On Win98 the path must already
* be flattened. The points are specified in POINT units (not floating point),
* consistent with output of RegionToPath.
*
* This differs from SetupEscapeClipping() in following way. The API can
* be called multiple times, each time specifying a new path, which is ORed
* into the previous path. On Win98, no coalescing of the subpath is done.
*
* Arguments:
*
* HDC - hdc to send escapes to.
* points - array of POINTs
* types - array of BYTE types
*
* Return Value:
*
* GpStatus
*
* History:
*
* 5/22/2000 ericvan
* Created it.
*
\**************************************************************************/
VOID
DriverPrint::SimpleEscapeClipping(
HDC hdc,
DynPointArray& points,
DynByteArray& types,
GpFillMode fillMode,
DWORD flags
)
{
#ifdef NO_PS_CLIPPING
return;
#endif
PathInfo16 pi;
WORD clipMode;
pi.RenderMode = RENDER_NODISPLAY;
pi.FillMode = (fillMode == FillModeAlternate) ?
FILL_ALTERNATE : FILL_WINDING;
pi.BkMode = TRANSPARENT;
pi.Pen.lopnStyle = PS_NULL;
pi.Pen.lopnWidth.x = 0;
pi.Pen.lopnWidth.y = 0;
pi.Pen.lopnColor = RGB(0,0,0);
pi.Brush.lbStyle = BS_NULL;
pi.Brush.lbColor = RGB(0,0,0);
pi.Brush.lbHatch = 0;
ASSERT((fillMode == FillModeAlternate) || (fillMode == FillModeWinding));
#ifdef DBG
OutputText("GDI+ Setup Simple Escape Clipping");
#endif
GpPoint* pointPtr = points.GetDataBuffer();
BYTE* typePtr = types.GetDataBuffer();
GpPoint* freeThisPtr = NULL;
INT count = points.GetCount();
INT subCount = 0;
BYTE curType;
// we are partially visible, so we expect something!
ASSERT(count > 0);
if (count <= 1)
{
return;
}
// Win95 and WinNT are subtly different in processing postscript escapes.
if (Globals::IsNt)
{
BYTE lastType = 0;
INT startIndex;
while (count-- > 0)
{
switch ((curType = *typePtr++) & PathPointTypePathTypeMask)
{
case PathPointTypeStart:
::MoveToEx(hdc, pointPtr->X, pointPtr->Y, NULL);
pointPtr++;
ASSERT(count > 0); // no illformed paths please...
lastType = *typePtr & PathPointTypePathTypeMask;
subCount = 0;
break;
case PathPointTypeLine:
if (lastType == PathPointTypeBezier)
{
ASSERT(subCount % 3 == 0);
if (subCount % 3 == 0)
{
::PolyBezierTo(hdc, (POINT*)pointPtr, subCount);
}
pointPtr += subCount;
subCount = 1;
lastType = PathPointTypeLine;
}
else
{
subCount++;
}
break;
case PathPointTypeBezier:
if (lastType == PathPointTypeLine)
{
::PolylineTo(hdc, (POINT*)pointPtr, subCount);
pointPtr += subCount;
subCount = 1;
lastType = PathPointTypeBezier;
}
else
{
subCount++;
}
break;
}
if (curType & PathPointTypeCloseSubpath)
{
ASSERT(subCount > 0);
if (lastType == PathPointTypeBezier)
{
ASSERT(subCount % 3 == 0);
if (subCount % 3 == 0)
{
::PolyBezierTo(hdc, (POINT*)pointPtr, subCount);
}
}
else
{
ASSERT(lastType == PathPointTypeLine);
::PolylineTo(hdc, (POINT*)pointPtr, subCount);
}
pointPtr += subCount;
subCount = 0;
::CloseFigure(hdc);
}
}
}
else
{
// Win98 equivalent code
// !! Win98 doesn't support bezier points in postscript clipping
while (count-- > 0)
{
curType = *typePtr++;
ASSERT((curType & PathPointTypePathTypeMask) != PathPointTypeBezier);
if ((curType & PathPointTypePathTypeMask) == PathPointTypeStart)
{
subCount = 1;
}
else
{
subCount ++;
}
if (curType & PathPointTypeCloseSubpath)
{
ASSERT(subCount > 0);
::Polygon(hdc, (POINT*)pointPtr, subCount);
pointPtr += subCount;
subCount = 0;
// send END_PATH, BEGIN_PATH escapes
if (count > 0)
{
Escape(hdc, END_PATH, sizeof(PathInfo16), (LPSTR)&pi, NULL);
Escape(hdc, BEGIN_PATH, 0, NULL, NULL);
}
}
}
}
}
/**************************************************************************\
*
* Function Description:
*
* Setups up postscript clipping path given GlyphPos (outline of glyph
* characters).
*
* Arguments:
*
*
* Return Value:
*
* status
*
* History:
*
* 3/20/2k ericvan
* Created it.
*
\**************************************************************************/
VOID
DriverPrint::SetupGlyphPathClipping(
HDC hdc,
DpContext * context,
const GpGlyphPos * glyphPathPos,
INT glyphCount
)
{
ASSERT(hdc != NULL);
ASSERT(glyphCount > 0);
ASSERT(glyphPathPos != NULL);
DynByteArray flattenTypes;
DynPointFArray flattenPoints;
GpPointF *pointsPtr;
BYTE *typesPtr;
INT count;
REAL m[6];
PathInfo16 pi;
DWORD clipMode;
pi.RenderMode = RENDER_NODISPLAY;
pi.FillMode = FILL_ALTERNATE;
pi.BkMode = TRANSPARENT;
pi.Pen.lopnStyle = PS_NULL;
pi.Pen.lopnWidth.x = 0;
pi.Pen.lopnWidth.y = 0;
pi.Pen.lopnColor = RGB(0,0,0);
pi.Brush.lbStyle = BS_NULL;
pi.Brush.lbColor = RGB(0,0,0);
pi.Brush.lbHatch = 0;
GpGlyphPos *curGlyph = const_cast<GpGlyphPos*>(&glyphPathPos[0]);
if (glyphCount > 0)
{
// save original clip
// NT driver ignoes the high WORD - always uses eoclip, but according to
// Win31 documentation it should be set to the fillmode.
clipMode = CLIP_SAVE;
Escape(hdc, CLIP_TO_PATH, sizeof(clipMode), (LPSTR)&clipMode, NULL);
if (Globals::IsNt)
{
// send path to PS printer as an escape
Escape(hdc, BEGIN_PATH, 0, NULL, NULL);
::BeginPath(hdc);
}
}
for (INT pos=0; pos<glyphCount; pos++, curGlyph++)
{
// get path for glyph character
GpGlyphPath *glyphPath = (GpGlyphPath*)curGlyph->GetPath();
if ((glyphPath != NULL) && glyphPath->IsValid() && !glyphPath->IsEmpty())
{
// !! Perf improvement. Avoid copying this point array somehow.
GpPath path(glyphPath->points,
glyphPath->types,
glyphPath->pointCount,
FillModeAlternate); // !! Is this right?
ASSERT(path.IsValid());
if (path.IsValid())
{
// create transform to translate path to correct position
GpMatrix matrix;
BOOL doFlatten = !Globals::IsNt && path.HasCurve();
if (doFlatten)
{
// This makes a Flattened copy of the points... (stored
// independent of the original points)
path.Flatten(&flattenTypes, &flattenPoints, &matrix);
pointsPtr = flattenPoints.GetDataBuffer();
typesPtr = flattenTypes.GetDataBuffer();
count = flattenPoints.GetCount();
}
else
{
pointsPtr = const_cast<GpPointF*>(path.GetPathPoints());
typesPtr = const_cast<BYTE*>(path.GetPathTypes());
count = path.GetPointCount();
}
DynPointArray points;
DynByteArray types(typesPtr, count, count);
POINT * transformedPointsPtr = (POINT *) points.AddMultiple(count);
// !!! bhouse This call can fail yet it returns void
if(!transformedPointsPtr)
return;
// translate to proper position in device space.
matrix.Translate(TOREAL(curGlyph->GetLeft()),
TOREAL(curGlyph->GetTop()),
MatrixOrderAppend);
// path is already in device space, but relative to bounding
// box of glyph character.
matrix.Transform(pointsPtr,
transformedPointsPtr,
count);
// send path to PS printer as an escape
if (!Globals::IsNt)
{
Escape(hdc, BEGIN_PATH, 0, NULL, NULL);
}
SimpleEscapeClipping(hdc,
points,
types,
FillModeAlternate,
0);
if (!Globals::IsNt)
{
// we should end on a closed subpath
Escape(hdc, END_PATH, sizeof(PathInfo16), (LPSTR)&pi, NULL);
}
GlyphClipping = TRUE;
}
}
}
if (glyphCount > 0)
{
if (Globals::IsNt)
{
::EndPath(hdc);
::StrokePath(hdc);
// we should end on a closed subpath
Escape(hdc, END_PATH, sizeof(PathInfo16), (LPSTR)&pi, NULL);
}
// end the path and set up for clipping
// NT driver ignoes the high WORD - always uses eoclip, but according to
// Win31 documentation it should be set to the fillmode.
DWORD inclusiveMode = CLIP_INCLUSIVE | pi.FillMode<<16;
Escape(hdc, CLIP_TO_PATH, sizeof(inclusiveMode), (LPSTR)&inclusiveMode, NULL);
}
}
/**************************************************************************\
*
* Function Description:
*
* Restore postscript escape clipping. For use with simple and complex
* clipping.
*
* Arguments:
*
* HDC - printer HDC to send escapes to
*
* Return Value:
*
* GpStatus
*
* History:
*
* 3/3/2000 ericvan - Created it.
*
\**************************************************************************/
VOID
DriverPrint::RestoreEscapeClipping(
HDC hdc
)
{
WORD clipMode = CLIP_RESTORE;
Escape(hdc, CLIP_TO_PATH, sizeof(clipMode), (LPSTR)&clipMode, NULL);
}
/**************************************************************************\
*
* Function Description:
*
* Setup clipping. If the printer (PS and apparently some PCL printers)
* support escape clippings, then use them. Otherwise, revert to GDI
* to do our clipping. NOTE: This is only necessary for cases where
* GDI is doing the rendering.
*
* Arguments:
*
* HDC - printer HDC to send escapes to
* clipRegion - clip region to send
* drawBounds - bounding box for drawing region
* IsClip [OUT] - whether was necessary to send clip region
* hRgnSaveClip - HRGN to save old clip region
*
* Return Value:
*
* GpStatus
*
* History:
*
* 10/28/1999 ericvan - Created it.
* 1/25/2k ericvan - Switch on escape clipping or GDI clipping
*
\**************************************************************************/
VOID
DriverPrint::SetupClipping(
HDC hdc,
DpContext * context,
const GpRect * drawBounds,
BOOL & isClip,
BOOL & usePathClipping, // ignored here
BOOL forceClipping
)
{
// the visible clip is at device resolution so there is no benefit to using paths here.
ASSERT(usePathClipping == FALSE);
DpClipRegion * clipRegion = &(context->VisibleClip);
isClip = FALSE;
if (UseClipEscapes)
{
if (forceClipping ||
(clipRegion->GetRectVisibility(drawBounds->X,
drawBounds->Y,
drawBounds->GetRight(),
drawBounds->GetBottom())
!= DpRegion::TotallyVisible))
{
// If it is a simple region, we draw it directly.
if (Uniqueness != clipRegion->GetUniqueness())
{
RegionToPath convertRegion;
if (convertRegion.ConvertRegionToPath(clipRegion,
ClipPoints,
ClipTypes) == FALSE)
{
ClipPoints.Reset();
ClipTypes.Reset();
UseClipEscapes = FALSE;
goto UseGDIClipping;
}
Uniqueness = clipRegion->GetUniqueness();
}
SetupEscapeClipping(hdc, ClipPoints, ClipTypes);
isClip = TRUE;
}
}
else
{
UseGDIClipping:
DpDriver::SetupClipping(hdc, context, drawBounds, isClip,
usePathClipping, forceClipping);
}
}
/**************************************************************************\
*
* Function Description:
*
* Restore clipping
*
* Arguments:
*
* dstHdc - destination printer device
* surface - surface
* drawBounds - rectangular section of surface to paint
*
* Return Value:
*
* status
*
* History:
*
* 11/30/1999 ericvan
* Created it.
*
\**************************************************************************/
VOID
DriverPrint::RestoreClipping(
HDC hdc,
BOOL isClip,
BOOL usePathClipping
)
{
if (isClip)
{
if (UseClipEscapes)
{
RestoreEscapeClipping(hdc);
}
else
{
DpDriver::RestoreClipping(hdc, isClip, usePathClipping);
}
}
}
/**************************************************************************\
*
* Function Description:
*
* Setup Path Clipping. This routine ANDs the given path into the
* the current clip region.
*
* Arguments:
*
* HDC - printer HDC
* clipPath - path to clip
* IsClip [OUT] - whether was necessary to send clip region
* hRgnSaveClip - HRGN to save old clip region
*
* Return Value:
*
* GpStatus
*
* History:
*
* 8/17/2k ericvan - Created it.
*
\**************************************************************************/
VOID
DriverPrint::SetupPathClipping(
HDC hdc,
DpContext * context,
const DpPath* clipPath
)
{
ASSERT(IsPathClip == FALSE);
if (UseClipEscapes)
{
BOOL doFlatten = !Globals::IsNt && clipPath->HasCurve();
GpPointF *pointsPtr;
BYTE *typesPtr;
INT count;
DynByteArray flattenTypes;
DynPointFArray flattenPoints;
if (doFlatten)
{
// This makes a Flattened copy of the points... (stored independent
// of original points.
clipPath->Flatten(
&flattenTypes,
&flattenPoints,
&(context->WorldToDevice));
pointsPtr = flattenPoints.GetDataBuffer();
typesPtr = flattenTypes.GetDataBuffer();
count = flattenPoints.GetCount();
}
else
{
pointsPtr = const_cast<GpPointF*>(clipPath->GetPathPoints());
typesPtr = const_cast<BYTE*>(clipPath->GetPathTypes());
count = clipPath->GetPointCount();
}
DynPointArray points;
DynByteArray types(typesPtr, count, count);
POINT * transformedPointsPtr = (POINT *) points.AddMultiple(count);
// !!! bhouse This call can fail yet it returns void
if(!transformedPointsPtr)
return;
if (doFlatten || context->WorldToDevice.IsIdentity())
{
GpMatrix idMatrix;
idMatrix.Transform(pointsPtr, transformedPointsPtr, count);
}
else
{
// We transform the points here to avoid an extra potentially
// large memory alloc (not flattened, we can't transform in place)
context->WorldToDevice.Transform(pointsPtr, transformedPointsPtr, count);
}
SetupEscapeClipping(hdc, points, types, clipPath->GetFillMode());
IsPathClip = TRUE;
}
else
{
::SaveDC(hdc);
// Windows98 ExtCreateRegion has a 64kb limit, so we can't use
// Region->GetHRgn() to create the HRGN. Incidentlly there is
// also an NT4 SPx bug where ExtCreateRegion fails sometimes.
// Instead we use SelectClipPath()
ConvertPathToGdi gdiPath(clipPath,
&(context->WorldToDevice),
0);
if (gdiPath.IsValid())
{
gdiPath.AndClip(hdc);
}
IsPathClip = TRUE;
}
}
/**************************************************************************\
*
* Function Description:
*
* Restore Path Clipping. This routine restore clip region to original
* representation.
*
* Arguments:
*
* HDC - printer HDC
* clipPath - path to clip
* hRgnSaveClip - HRGN to save old clip region
*
* Return Value:
*
* GpStatus
*
* History:
*
* 8/17/2k ericvan - Created it.
*
\**************************************************************************/
VOID
DriverPrint::RestorePathClipping(HDC hdc)
{
DriverPrint::RestoreClipping(hdc, IsPathClip, FALSE);
IsPathClip = FALSE;
}
/**************************************************************************\
*
* Function Description:
*
* Fills a rectangular region with a pen. Sets the clipping appropriately
* as path INTERSECT visible clip.
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::PrivateFillRect(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpPath *outlinePath,
const DpBrush *brush
)
{
GpStatus status = Ok;
ASSERT(outlinePath != NULL);
// Optimization for LinearGradientBrush fills. We rasterize to a small
// DIB and send that to the printer. For horizontal and vertical gradients
// in particular, this results in significant savings.
GpMatrix savedmatrix;
GpBrush *gpBrush = GpBrush::GetBrush(brush);
GpSpecialGradientType gradientType;
if (IsOpaque &&
((gradientType = gpBrush->GetSpecialGradientType(&context->WorldToDevice))
!= GradientTypeNotSpecial))
{
GpRect bitmapBounds = *drawBounds;
GpBitmap * gpBitmap = NULL;
switch (gradientType)
{
case GradientTypeHorizontal:
bitmapBounds.Width = 1;
break;
case GradientTypeVertical:
bitmapBounds.Height = 1;
break;
case GradientTypePathTwoStep:
case GradientTypeDiagonal:
bitmapBounds.Width = min(drawBounds->Width, 256);
bitmapBounds.Height = min(drawBounds->Height, 256);
break;
case GradientTypePathComplex:
// send the whole drawBounds for now.
break;
default:
ASSERT(0);
break;
}
HDC hdc = context->GetHdc(surface);
if (hdc != NULL)
{
// Transform the destination drawBounds rectangle to
// bitmapBounds size.
bitmapBounds.X = 0;
bitmapBounds.Y = 0;
RectF destRect(0.0f,
0.0f,
TOREAL(bitmapBounds.Width),
TOREAL(bitmapBounds.Height));
RectF srcRect(TOREAL(drawBounds->X),
TOREAL(drawBounds->Y),
TOREAL(drawBounds->Width),
TOREAL(drawBounds->Height));
GpMatrix transform;
transform.InferAffineMatrix(destRect, srcRect);
GpMatrix::MultiplyMatrix(transform,
context->WorldToDevice,
transform);
status = GpBitmap::CreateBitmapAndFillWithBrush(
context->FilterType,
&transform,
&bitmapBounds,
gpBrush,
&gpBitmap);
if ((status == Ok) && (gpBitmap != NULL))
{
GpRect & srcRect = bitmapBounds;
PixelFormatID lockFormat = PixelFormat32bppARGB;
BitmapData bmpDataSrc;
// Lock the bits.
if (gpBitmap->LockBits(NULL,
IMGLOCK_READ,
lockFormat,
&bmpDataSrc) == Ok)
{
DpBitmap driverSurface;
// Fake up a DpBitmap for the driver call.
// We do this because the GpBitmap doesn't maintain the
// DpBitmap as a driver surface - instead it uses a
// GpMemoryBitmap.
gpBitmap->InitializeSurfaceForGdipBitmap(
&driverSurface,
bmpDataSrc.Width,
bmpDataSrc.Height
);
driverSurface.Bits = (BYTE*)bmpDataSrc.Scan0;
driverSurface.Width = bmpDataSrc.Width;
driverSurface.Height = bmpDataSrc.Height;
driverSurface.Delta = bmpDataSrc.Stride;
// Pixel format to match the lockbits above.
driverSurface.PixelFormat = lockFormat;
driverSurface.NumBytes = bmpDataSrc.Width *
bmpDataSrc.Height * 3;
// Must be transparent to get here.
driverSurface.SurfaceTransparency = TransparencyOpaque;
ConvertBitmapToGdi gdiBitmap(hdc,
&driverSurface,
&srcRect,
IsPrinting);
status = GenericError;
if (gdiBitmap.IsValid())
{
BOOL isClip;
BOOL usePathClipping = FALSE;
// Clip to visible region
SetupClipping(hdc, context, drawBounds, isClip, usePathClipping, FALSE);
// Clip to outline path (fill shape)
SetupPathClipping(hdc, context, outlinePath);
// Destination points in POINT co-ordinates
POINT destPoints[3];
destPoints[0].x = drawBounds->X;
destPoints[0].y = drawBounds->Y;
destPoints[1].x = drawBounds->X + drawBounds->Width;
destPoints[1].y = drawBounds->Y;
destPoints[2].x = drawBounds->X;
destPoints[2].y = drawBounds->Y + drawBounds->Height;
// Perform StretchDIBits of bitmap
status = gdiBitmap.StretchBlt(hdc, destPoints) ? Ok : GenericError;
// restore clipping from outline of shape
RestorePathClipping(hdc);
// restore from visible clip region
RestoreClipping(hdc, isClip, usePathClipping);
}
gpBitmap->UnlockBits(&bmpDataSrc);
}
gpBitmap->Dispose();
context->ReleaseHdc(hdc);
return status;
}
context->ReleaseHdc(hdc);
}
}
BOOL SetVisibleClip;
DWORD options = 0;
BOOL AdjustWorldTransform = FALSE;
switch (DriverType)
{
case DriverPCL:
if (IsSolid)
{
options = ScanDeviceBounds;
if (!IsOpaque)
{
options |= ScanDeviceAlpha;
}
}
else
{
if (Is01Bitmap)
{
// rasterize at 32bpp
options = ScanCappedBounds | ScanCapped32bpp;
}
else if (IsOpaque)
{
// rasterize at 24bpp
options = ScanCappedBounds;
}
else
{
// rasterize 24bpp @ cap dpi & 1bpp @ device api
options = ScanCappedBounds | ScanDeviceBounds | ScanDeviceAlpha;
}
}
SetVisibleClip = IsOpaque || Is01Bitmap;
break;
case DriverPostscript:
SetVisibleClip = !IsSolid;
if (IsSolid)
{
options = ScanDeviceBounds;
if (!IsOpaque)
{
options |= ScanDeviceAlpha;
}
if (PostscriptScalerX != 1 || PostscriptScalerY != 1)
{
AdjustWorldTransform = TRUE;
savedmatrix = context->WorldToDevice;
}
}
else
{
// For postscript we currently only support 0-1 alpha or complete
// opaque.
if (Is01Bitmap)
{
options |= ScanCappedBounds | ScanCapped32bpp;
}
else if (IsOpaque)
{
options |= ScanCappedBounds;
}
else
{
options |= ScanCappedBounds | ScanCappedOver | ScanDeviceZeroOut;
}
}
break;
default:
ASSERT(FALSE);
return NotImplemented;
}
EpScanDIB *scanPrint = (EpScanDIB*) surface->Scan;
REAL w2dDev[6];
REAL w2dCap[6];
// To avoid round off errors causing
GpRect roundedBounds;
INT oldScaleX = ScaleX;
INT oldScaleY = ScaleY;
// For texture brush, rasterize at the texture image DPI.
if (brush->Type == BrushTypeTextureFill)
{
GpTexture *gpBrush = (GpTexture*)GpBrush::GetBrush(brush);
ASSERT(gpBrush != NULL);
GpBitmap *gpBitmap = gpBrush->GetBitmap();
if (gpBitmap != NULL)
{
REAL dpiX, dpiY;
gpBitmap->GetResolution(&dpiX, &dpiY);
ScaleX = GpFloor(surface->GetDpiX()/dpiX);
ScaleY = GpFloor(surface->GetDpiY()/dpiY);
// don't rasterize at a dpi higher than the destination surface
if (ScaleX < 1) ScaleX = 1;
if (ScaleY < 1) ScaleY = 1;
}
}
if ((ScaleX == 1) && (ScaleY == 1))
{
roundedBounds.X = drawBounds->X;
roundedBounds.Y = drawBounds->Y;
roundedBounds.Width = drawBounds->Width;
roundedBounds.Height = drawBounds->Height;
}
else
{
// round X,Y to multiple of ScaleX,Y
roundedBounds.X = (drawBounds->X / ScaleX) * ScaleX;
roundedBounds.Y = (drawBounds->Y / ScaleY) * ScaleY;
// adjust width and height to compensate for smaller X,Y.
roundedBounds.Width = drawBounds->Width + (drawBounds->X % ScaleX);
roundedBounds.Height = drawBounds->Height + (drawBounds->Y % ScaleY);
// round width, height to multiple of ScaleX,Y
roundedBounds.Width += (ScaleX - (roundedBounds.Width % ScaleX));
roundedBounds.Height += (ScaleY - (roundedBounds.Height % ScaleY));
}
// DrawBounds in Capped Space
GpRect boundsCap(roundedBounds.X / ScaleX,
roundedBounds.Y / ScaleY,
roundedBounds.Width / ScaleX,
roundedBounds.Height / ScaleY);
GpRect& boundsDev = roundedBounds;
if (AdjustWorldTransform)
{
boundsDev.X = GpCeiling((REAL)boundsDev.X / PostscriptScalerX);
boundsDev.Y = GpCeiling((REAL)boundsDev.Y / PostscriptScalerY);
boundsDev.Width = GpCeiling((REAL)boundsDev.Width / PostscriptScalerX);
boundsDev.Height = GpCeiling((REAL)boundsDev.Height / PostscriptScalerY);
context->WorldToDevice.Scale(1.0f/PostscriptScalerX,
1.0f/PostscriptScalerY,
MatrixOrderAppend);
}
context->WorldToDevice.GetMatrix(&w2dDev[0]);
context->WorldToDevice.Scale(1.0f/TOREAL(ScaleX),
1.0f/TOREAL(ScaleY),
MatrixOrderAppend);
context->WorldToDevice.GetMatrix(&w2dCap[0]);
context->InverseOk = FALSE;
// Infer a rectangle in world space which under the w2dCap transform
// covers our bounding box.
GpPointF dstPts[2];
dstPts[0].X = TOREAL(boundsCap.X);
dstPts[0].Y = TOREAL(boundsCap.Y);
dstPts[1].X = TOREAL(boundsCap.X + boundsCap.Width);
dstPts[1].Y = TOREAL(boundsCap.Y + boundsCap.Height);
GpMatrix matrix;
context->GetDeviceToWorld(&matrix);
matrix.Transform(&dstPts[0], 2);
GpRectF rectCap;
rectCap.X = dstPts[0].X;
rectCap.Y = dstPts[0].Y;
rectCap.Width = dstPts[1].X - dstPts[0].X;
rectCap.Height = dstPts[1].Y - dstPts[0].Y;
// Reorient destination rectangle in the event that it has flipped by
// World to Device transform.
if (rectCap.Width < 0)
{
rectCap.X += rectCap.Width;
rectCap.Width = -rectCap.Width;
}
if (rectCap.Height < 0)
{
rectCap.Y += rectCap.Height;
rectCap.Height = -rectCap.Height;
}
SetupPrintBanding(context, &boundsCap, &boundsDev);
HDC hdc = context->GetHdc(surface);
if (hdc != NULL)
{
status = scanPrint->CreateBufferDIB(&BandBoundsCap,
&BandBoundsDev,
options,
ScaleX,
ScaleY);
if (status == Ok)
{
BOOL isClip = FALSE;
BOOL usePathClipping = FALSE;
if (SetVisibleClip)
{
DriverPrint::SetupClipping(hdc, context, drawBounds,
isClip, usePathClipping, FALSE);
}
ASSERT(NumBands > 0);
for (Band = 0; Band<NumBands; Band++)
{
if (options & ScanCappedFlags)
{
context->VisibleClip.DisableComplexClipping(BandBoundsCap);
// Render at capped DPI
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dCap[0]);
scanPrint->SetRenderMode(FALSE, &BandBoundsCap);
status = DpDriver::FillRects(context,
surface,
&boundsCap,
1,
&rectCap,
brush);
context->VisibleClip.ReEnableComplexClipping();
}
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dDev[0]);
if (status != Ok)
break;
if (options & ScanDeviceFlags)
{
context->VisibleClip.SetBandBounds(BandBoundsDev);
scanPrint->SetRenderMode(TRUE, &BandBoundsDev);
status = DpDriver::FillPath(context,
surface,
&boundsDev,
outlinePath,
brush);
}
if (status == Ok)
{
status = OutputBufferDIB(hdc,
context,
surface,
&BandBoundsCap,
&BandBoundsDev,
const_cast<DpPath*>(outlinePath));
if (status != Ok)
break;
}
else
break;
BandBoundsCap.Y += BandHeightCap;
BandBoundsDev.Y += BandHeightDev;
}
if (SetVisibleClip)
{
DriverPrint::RestoreClipping(hdc, isClip, usePathClipping);
}
scanPrint->DestroyBufferDIB();
}
context->ReleaseHdc(hdc);
}
else
{
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dDev[0]);
}
EndPrintBanding(context);
if (AdjustWorldTransform)
{
context->InverseOk = FALSE;
context->WorldToDevice = savedmatrix;
}
ScaleX = oldScaleX;
ScaleY = oldScaleY;
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Draws filled paths.
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::FillPath(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpPath *path,
const DpBrush *brush
)
{
if (SetupBrush(const_cast<DpBrush*>(brush), context, surface))
return Ok;
GpStatus status;
if (IsOpaque)
{
DWORD convertFlags = IsPrinting | ForFill |
((DriverType == DriverPostscript) ? IsPostscript : 0);
HBRUSH hBrush = GetBrush(brush, convertFlags);
if (hBrush)
{
HDC hdc = context->GetHdc(surface);
if (hdc != NULL)
{
BOOL success = FALSE;
ConvertPathToGdi gdiPath(path,
&context->WorldToDevice,
convertFlags,
drawBounds);
if (gdiPath.IsValid())
{
BOOL isClip;
BOOL usePathClipping = FALSE;
SetupClipping(hdc, context, drawBounds, isClip,
usePathClipping, FALSE);
success = gdiPath.Fill(hdc, hBrush);
RestoreClipping(hdc, isClip, usePathClipping);
}
else
{
// Path is too complicated to use GDI printing with FillPath
// semantics. Instead we AND the outline path into the clip
// path and do a PatBlt.
BOOL isClip;
BOOL usePathClipping = FALSE;
// clip to the visible region
SetupClipping(hdc, context, drawBounds, isClip, usePathClipping);
// clip to the outline path
SetupPathClipping(hdc, context, path);
HBRUSH oldHbr = (HBRUSH)SelectObject(hdc, hBrush);
// PatBlt the destination hdc with outline clip path
success = (BOOL)PatBlt(hdc,
drawBounds->X,
drawBounds->Y,
drawBounds->Width,
drawBounds->Height,
PATCOPY);
SelectObject(hdc, oldHbr);
// restore clipping from outline path
RestorePathClipping(hdc);
// restore clipping from visible region
RestoreClipping(hdc, isClip, usePathClipping);
}
context->ReleaseHdc(hdc);
if (success)
{
status = Ok;
goto Exit;
}
}
}
}
status = PrivateFillRect(context,
surface,
drawBounds,
path,
brush);
Exit:
RestoreBrush(const_cast<DpBrush*>(brush), context, surface);
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Draws filled rectangles.
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::FillRects(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
INT numRects,
const GpRectF *rects,
const DpBrush *brush
)
{
ASSERT(numRects > 0);
ASSERT(context->WorldToDevice.IsTranslateScale());
if (SetupBrush(const_cast<DpBrush*>(brush), context, surface))
return Ok;
GpStatus status;
if (IsOpaque)
{
DWORD convertFlags = IsPrinting | ForFill |
((DriverType == DriverPostscript) ? IsPostscript : 0);
HBRUSH hBrush = GetBrush(brush, convertFlags);
if (hBrush)
{
ConvertRectFToGdi gdiRects(rects, numRects, &context->WorldToDevice);
if (gdiRects.IsValid())
{
HDC hdc = context->GetHdc(surface);
if (hdc != NULL)
{
BOOL isClip;
BOOL success;
BOOL usePathClipping = FALSE;
SetupClipping(hdc, context, drawBounds, isClip,
usePathClipping, FALSE);
success = gdiRects.Fill(hdc, hBrush);
RestoreClipping(hdc, isClip, usePathClipping);
context->ReleaseHdc(hdc);
if (success)
{
status = Ok;
goto Exit;
}
}
}
}
}
// Setting the rectangles to a path is somewhat problematic because their
// intersection may not be interpreted properly. Also, this should result
// in fewer bits being sent and more computation. Here we just set each
// rectangle as the drawBounds and no outline path to clip.
{
PointF pts[4];
BYTE types[4] = {
PathPointTypeStart,
PathPointTypeLine,
PathPointTypeLine,
PathPointTypeLine | PathPointTypeCloseSubpath
};
pts[0].X = rects->X;
pts[0].Y = rects->Y;
pts[1].X = rects->X + rects->Width;
pts[1].Y = rects->Y;
pts[2].X = rects->X + rects->Width;
pts[2].Y = rects->Y + rects->Height;
pts[3].X = rects->X;
pts[3].Y = rects->Y + rects->Height;
GpPath rectPath(&pts[0],
&types[0],
4,
FillModeWinding);
if (rectPath.IsValid())
{
while (numRects > 0)
{
GpRectF rectf = *rects;
context->WorldToDevice.TransformRect(rectf);
GpRect rect(GpRound(rectf.X), GpRound(rectf.Y),
GpRound(rectf.Width), GpRound(rectf.Height));
status = PrivateFillRect(context,
surface,
(GpRect *)&rect,
&rectPath,
brush);
if (--numRects)
{
rects++;
// !! Safer and more efficient way to do this?
GpPointF* pathPts = const_cast<GpPointF*>(rectPath.GetPathPoints());
pathPts[0].X = rects->X;
pathPts[0].Y = rects->Y;
pathPts[1].X = rects->X + rects->Width;
pathPts[1].Y = rects->Y;
pathPts[2].X = rects->X + rects->Width;
pathPts[2].Y = rects->Y + rects->Height;
pathPts[3].X = rects->X;
pathPts[3].Y = rects->Y + rects->Height;
}
}
}
}
Exit:
RestoreBrush(const_cast<DpBrush*>(brush), context, surface);
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Draws filled regions.
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::FillRegion(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpRegion *region,
const DpBrush *brush
)
{
if (SetupBrush(const_cast<DpBrush*>(brush), context, surface))
return Ok;
GpStatus status;
if (IsOpaque)
{
DWORD convertFlags = IsPrinting | ForFill |
((DriverType == DriverPostscript) ? IsPostscript : 0);
HBRUSH hBrush = GetBrush(brush, convertFlags);
if (hBrush)
{
ConvertRegionToGdi gdiRegion(region);
if (gdiRegion.IsValid())
{
HDC hdc = context->GetHdc(surface);
if (hdc != NULL)
{
BOOL isClip;
BOOL success;
BOOL usePathClipping = FALSE;
SetupClipping(hdc, context, drawBounds, isClip,
usePathClipping, FALSE);
success = gdiRegion.Fill(hdc, hBrush);
RestoreClipping(hdc, isClip, usePathClipping);
context->ReleaseHdc(hdc);
if (success)
{
status = Ok;
goto Exit;
}
}
}
}
}
{
// convert region to path
RegionToPath convertRegion;
DynPointArray points;
DynByteArray types;
if (convertRegion.ConvertRegionToPath(const_cast<DpRegion*>(region),
points,
types) == FALSE)
{
status = GenericError;
goto Exit1;
}
{
// unfortunately to create path, our points must be floating point,
// so we allocate and convert
GpPointF *pointFArray;
GpPoint *pointArray = points.GetDataBuffer();
INT numPoints = points.GetCount();
pointFArray = (GpPointF*) GpMalloc(numPoints * sizeof(GpPointF));
if (pointFArray == NULL)
{
status = OutOfMemory;
goto Exit12;
}
{
for (INT i=0; i<numPoints; i++)
{
pointFArray[i].X = TOREAL(pointArray[i].X);
pointFArray[i].Y = TOREAL(pointArray[i].Y);
}
// !! We compute path from region in device space to ensure
// our output is high quality. Perhaps add an option here
// dependent on the QualityMode to convert in world space and
// then transform to device space.
// This is not a high frequency API so I don't care too much
// about perf, but perhaps it could be improved by reworking
// where this transform occurs.
GpMatrix deviceToWorld;
context->GetDeviceToWorld(&deviceToWorld);
deviceToWorld.Transform(pointFArray, numPoints);
// !! What's the fillMode?
// !! Create a DpPath, do we require the knowledge of the inheritance anywhere?
{
GpPath path(pointFArray,
types.GetDataBuffer(),
numPoints);
if (path.IsValid())
{
GpRect newBounds;
path.GetBounds(&newBounds,
&context->WorldToDevice);
status = FillPath(context,
surface,
&newBounds,
(DpPath*)&path,
brush);
}
}
GpFree(pointFArray);
}
Exit12:
;
} // pointFArray
Exit1:
;
} // RegionToPath
Exit:
RestoreBrush(const_cast<DpBrush*>(brush), context, surface);
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Strokes paths.
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus.
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::StrokePath(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpPath *path,
const DpPen *pen
)
{
if (SetupBrush(const_cast<DpBrush*>(pen->Brush), context, surface))
return Ok;
GpStatus status;
// GDI doesn't seem to support HPENs which PATTERN type HBRUSHes
if (IsOpaque && IsSolid)
{
DWORD convertFlags = IsPrinting |
((DriverType == DriverPostscript) ? IsPostscript : 0);
HBRUSH hBrush = GetBrush(pen->Brush, convertFlags);
if (hBrush)
{
HDC hdc = context->GetHdc(surface);
BOOL success = FALSE;
if (hdc != NULL)
{
// Handle non compound pen case
if ((pen->PenAlignment == PenAlignmentCenter) &&
(pen->CompoundCount == 0))
{
ConvertPenToGdi gdiPen(hdc,
pen,
&context->WorldToDevice,
context->GetDpiX(),
convertFlags,
const_cast<LOGBRUSH*>(CachedBrush.GetGdiBrushInfo()));
if (gdiPen.IsValid())
{
ConvertPathToGdi gdiPath(path,
&context->WorldToDevice,
convertFlags,
drawBounds);
if (gdiPath.IsValid())
{
BOOL isClip, success = FALSE;
BOOL usePathClipping = FALSE;
SetupClipping(hdc,
context,
drawBounds,
isClip,
usePathClipping,
FALSE);
success = gdiPath.Draw(hdc, gdiPen.GetGdiPen());
RestoreClipping(hdc,
isClip,
usePathClipping);
}
}
}
context->ReleaseHdc(hdc);
if (success)
{
status = Ok;
goto Exit;
}
}
}
}
// get the widened path, then fill path with pen's interal brush
//
// also the bitmap can be quite hugh for a simple path
{
GpRect newBounds;
GpMatrix identity;
GpMatrix savedMatrix = context->WorldToDevice;
DpBrush *brush = const_cast<DpBrush*>(pen->Brush);
GpMatrix savedBrushTransform = brush->Xform;
// Widening the path transforms the points
DpPath* newPath = path->CreateWidenedPath(
pen,
context,
DriverType == DriverPostscript,
pen->PenAlignment != PenAlignmentInset
);
if (!newPath || !newPath->IsValid())
{
status = OutOfMemory;
goto Exit1;
}
// The path is in device space, which is convenient because the World
// to device is an identity transform. However, because W2D is I, the
// brush transform is composed improperly and so device to texture
// map results in wrong size textures.
GpMatrix::MultiplyMatrix(brush->Xform,
savedBrushTransform,
savedMatrix);
{
HDC hdc = NULL;
if(pen->PenAlignment == PenAlignmentInset)
{
hdc = context->GetHdc(surface);
if(hdc != NULL)
{
SetupPathClipping(hdc, context, path);
}
}
// The widened path is already transformed to the device coordinates.
// Hence, use the identity matrix for the context. 05/23/00 -- ikkof
// Set up the state for the FillPath after setting up the path
// clipping for the inset pen if necessary.
newPath->GetBounds(&newBounds);
context->InverseOk = FALSE;
context->WorldToDevice = identity;
status = FillPath(context, surface, &newBounds, newPath, brush);
context->InverseOk = FALSE;
context->WorldToDevice = savedMatrix;
if(pen->PenAlignment == PenAlignmentInset)
{
if(hdc != NULL)
{
RestorePathClipping(hdc);
context->ReleaseHdc(hdc);
}
}
}
brush->Xform = savedBrushTransform;
newPath->DeletePath();
Exit1:
;
}
Exit:
RestoreBrush(const_cast<DpBrush*>(pen->Brush), context, surface);
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Draw the image at the specified location
*
* Arguments:
*
* [IN] context - The drawing context
* [IN] surface - The surface to draw to
* [IN] drawBounds - The bounds of the object being drawn
* [IN] srcSurface - The image to draw
* [IN] mapMode - The map mode
* [IN] numPoints - The number of points in dstPoints
* [IN] dstPoints - Where to draw the image
* [IN] srcRect - The portion of the image to draw
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 10/28/1999 ericvan
*
\**************************************************************************/
GpStatus
DriverPrint::DrawImage(
DpContext * context,
DpBitmap * srcSurface,
DpBitmap * dstSurface,
const GpRect * drawBounds,
DpImageAttributes imageAttributes,
INT numPoints,
const GpPointF * dstPoints,
const GpRectF * srcRect,
DriverDrawImageFlags flags
)
{
GpStatus status = GenericError;
ASSERT(numPoints == 3);
if (context->CompositingMode == CompositingModeSourceCopy)
{
IsOpaque = TRUE;
Is01Bitmap = FALSE;
}
else if (srcSurface->SurfaceTransparency == TransparencySimple)
{
Is01Bitmap = TRUE;
IsNearConstant = FALSE;
IsOpaque = FALSE;
}
else if ((srcSurface->SurfaceTransparency == TransparencyUnknown) ||
(srcSurface->SurfaceTransparency == TransparencyComplex))
{
// PCL driver doesn't treat 0-1 bitmaps any different.
if (DriverType == DriverPostscript)
{
IsOpaque = FALSE;
IsNearConstant = FALSE;
Is01Bitmap = TRUE;
}
else
{
IsOpaque = FALSE;
IsNearConstant = FALSE;
Is01Bitmap = FALSE;
}
}
else if (srcSurface->SurfaceTransparency == TransparencyNearConstant)
{
if (DriverType == DriverPostscript)
{
IsOpaque = FALSE;
IsNearConstant = FALSE;
Is01Bitmap = TRUE;
}
#if 0
// Disable IsNearConstant right now
if (DriverType == DriverPostscript)
{
IsNearConstant = TRUE;
IsOpaque = FALSE;
Is01Bitmap = FALSE;
MinAlpha = srcSurface->MinAlpha;
MaxAlpha = srcSurface->MaxAlpha;
ASSERT(MinAlpha <= MaxAlpha);
}
#endif
else
{
IsOpaque = FALSE;
IsNearConstant = FALSE;
Is01Bitmap = FALSE;
}
}
else
{
// TransparencyOpaque || TransparencyNoAlpha
IsOpaque = TRUE;
IsNearConstant = FALSE;
Is01Bitmap = FALSE;
}
IsSolid = FALSE;
BOOL tryPassthrough = (srcSurface->CompressedData != NULL) &&
(srcSurface->CompressedData->buffer != NULL);
if (IsOpaque || tryPassthrough)
{
// Scale/translated stretched opaque image, use GDI.
if (context->WorldToDevice.IsTranslateScale() &&
(numPoints == 3) &&
(REALABS(dstPoints[0].X - dstPoints[2].X) < REAL_EPSILON) &&
(REALABS(dstPoints[0].Y - dstPoints[1].Y) < REAL_EPSILON) &&
(dstPoints[1].X > dstPoints[0].X) &&
(dstPoints[2].Y > dstPoints[0].Y))
{
CachedBackground back;
HDC hdc = context->GetHdc(dstSurface);
// Ack, this is just before the Office M1 release, and we want
// blts when printing to have half-decent performance. So we
// convert to a straight GDI StretchBlt. But we only want to
// do this for printers (so that we get bilinear stretches to
// the screen), but DriverPrint is also used for the screen. So
// we hack a check here on the DC.
BOOL success = FALSE;
POINT gdiPoints[3];
context->WorldToDevice.Transform(dstPoints, gdiPoints, 3);
// Make sure there is no flipping
if ((gdiPoints[1].x > gdiPoints[0].x) &&
(gdiPoints[2].y > gdiPoints[0].y))
{
DWORD convertFlags = IsPrinting |
((DriverType == DriverPostscript) ? IsPostscript : 0) |
((!IsOpaque && tryPassthrough) ? IsPassthroughOnly : 0);
GpRect rect(GpRound(srcRect->X),
GpRound(srcRect->Y),
GpRound(srcRect->Width),
GpRound(srcRect->Height));
ConvertBitmapToGdi gdiBitmap(hdc,
srcSurface,
&rect,
convertFlags);
if (gdiBitmap.IsValid())
{
BOOL isClip;
BOOL usePathClipping = FALSE;
DriverPrint::SetupClipping(hdc, context, drawBounds,
isClip, usePathClipping, FALSE);
success = gdiBitmap.StretchBlt(hdc, gdiPoints);
DriverPrint::RestoreClipping(hdc, isClip, usePathClipping);
}
}
if (success)
{
context->ReleaseHdc(hdc);
return Ok;
}
context->ReleaseHdc(hdc);
}
}
// We only process remaining code path if pixel format is >= 32bpp.
if (GetPixelFormatSize(srcSurface->PixelFormat) != 32)
{
return GenericError;
}
// Setup ScanDIB class correctly by specifying proper flags
BOOL SetVisibleClip;
DWORD options = 0;
switch (DriverType)
{
case DriverPCL:
if (Is01Bitmap)
{
// rasterize @ 32bpp
// Due to filtering, we want to blend with WHITENESS, only very
// transparent portions are cut.
options = ScanCappedBounds | ScanCapped32bppOver;
}
else if (IsOpaque)
{
// rasterize @ 24bpp
options = ScanCappedBounds;
}
else
{
options = ScanCappedBounds | ScanDeviceBounds | ScanDeviceAlpha;
}
SetVisibleClip = IsOpaque || Is01Bitmap;
break;
case DriverPostscript:
if (Is01Bitmap)
{
// rasterize @ 32bpp (this 0-1 bitmaps or complex alpha)
options = ScanCappedBounds | ScanCapped32bppOver;
}
else if (IsOpaque || IsNearConstant)
{
// rasterize @ 24bpp
options = ScanCappedBounds;
}
else
{
ASSERT(FALSE);
}
SetVisibleClip = TRUE;
break;
default:
ASSERT(FALSE);
return NotImplemented;
}
EpScanDIB *scanPrint = (EpScanDIB*) dstSurface->Scan;
REAL w2dDev[6];
REAL w2dCap[6];
// If there is alpha blending or 0-1 bitmap,
// it REALLY REALLY helps that the capped DPI divides the device DPI,
// otherwise it's hard to find every other one to output!
ASSERT(srcSurface->DpiX != 0 && srcSurface->DpiY != 0);
ASSERT(dstSurface->DpiY != 0 && dstSurface->DpiY != 0);
REAL srcDpiX = srcSurface->GetDpiX();
REAL srcDpiY = srcSurface->GetDpiY();
INT oldScaleX = ScaleX;
INT oldScaleY = ScaleY;
// !!! what if context->GetDpiX has a different value than the surface?
ScaleX = GpFloor(dstSurface->GetDpiX()/srcDpiX);
ScaleY = GpFloor(dstSurface->GetDpiY()/srcDpiY);
// don't rasterize at a dpi higher than the device.
if (ScaleX < 1) ScaleX = 1;
if (ScaleY < 1) ScaleY = 1;
// Some images have incorrect DPI information, to combat this, we check
// for a lower threshold on the image DPI, DEF_RES/4 seems reasonable. If
// the DPI is lower then we assume it's not accurate and rasterize at
// the default capped dpi for this device. If the DPI is above DEF_RES/4
// then the image should at least look reasonable.
if (srcDpiX < TOREAL((DEFAULT_RESOLUTION/4)))
{
ScaleX = oldScaleX;
}
if (srcDpiY < TOREAL((DEFAULT_RESOLUTION/4)))
{
ScaleY = oldScaleY;
}
// To avoid rounding errors with the underlying DpDriver code, we
// compute the destination bounds in capped device space.
context->WorldToDevice.GetMatrix(&w2dDev[0]);
context->WorldToDevice.Scale(1.0f/TOREAL(ScaleX),
1.0f/TOREAL(ScaleY), MatrixOrderAppend);
context->WorldToDevice.GetMatrix(&w2dCap[0]);
context->InverseOk = FALSE;
GpMatrix xForm;
xForm.InferAffineMatrix(&dstPoints[0], *srcRect);
xForm.Append(context->WorldToDevice); // includes 1/ScaleX,Y
GpPointF corners[4];
corners[0].Y = max(srcRect->Y, 0);
corners[1].Y = min(srcRect->Y + srcRect->Height,
srcSurface->Height);
corners[0].X = max(srcRect->X, 0);
corners[1].X = min(srcRect->X + srcRect->Width,
srcSurface->Width);
corners[2].X = corners[0].X;
corners[2].Y = corners[1].Y;
corners[3].X = corners[1].X;
corners[3].Y = corners[0].Y;
xForm.Transform(&corners[0], 4);
GpPointF topLeft, bottomRight;
topLeft.X = min(min(corners[0].X, corners[1].X), min(corners[2].X, corners[3].X));
topLeft.Y = min(min(corners[0].Y, corners[1].Y), min(corners[2].Y, corners[3].Y));
bottomRight.X = max(max(corners[0].X, corners[1].X), max(corners[2].X, corners[3].X));
bottomRight.Y = max(max(corners[0].Y, corners[1].Y), max(corners[2].Y, corners[3].Y));
// Use same rounding convention as DpDriver::DrawImage
GpRect boundsCap;
boundsCap.X = GpFix4Ceiling(GpRealToFix4(topLeft.X));
boundsCap.Y = GpFix4Ceiling(GpRealToFix4(topLeft.Y));
boundsCap.Width = GpFix4Ceiling(GpRealToFix4(bottomRight.X)) - boundsCap.X;
boundsCap.Height = GpFix4Ceiling(GpRealToFix4(bottomRight.Y)) - boundsCap.Y;
// DrawBounds in device space
GpRect boundsDev(boundsCap.X * ScaleX,
boundsCap.Y * ScaleY,
boundsCap.Width * ScaleX,
boundsCap.Height * ScaleY);
SetupPrintBanding(context, &boundsCap, &boundsDev);
// Setup outline path for clipping in world space
PointF clipPoints[4];
BYTE clipTypes[4] = {
PathPointTypeStart,
PathPointTypeLine,
PathPointTypeLine,
PathPointTypeLine | PathPointTypeCloseSubpath
};
clipPoints[0] = dstPoints[0];
clipPoints[1] = dstPoints[1];
clipPoints[3] = dstPoints[2];
clipPoints[2].X = clipPoints[1].X + (clipPoints[3].X - clipPoints[0].X);
clipPoints[2].Y = clipPoints[1].Y + (clipPoints[3].Y - clipPoints[0].Y);
GpPath clipPath(&clipPoints[0],
&clipTypes[0],
4,
FillModeWinding);
HDC hdc = NULL;
if (clipPath.IsValid())
{
hdc = context->GetHdc(dstSurface);
}
if (hdc != NULL)
{
status = scanPrint->CreateBufferDIB(&BandBoundsCap,
&BandBoundsDev,
options,
ScaleX,
ScaleY);
if (status == Ok)
{
BOOL isClip = FALSE;
BOOL usePathClipping = FALSE;
// Set the visible clip unless it is captured in our mask
if (SetVisibleClip)
{
DriverPrint::SetupClipping(hdc,
context,
drawBounds,
isClip,
usePathClipping,
FALSE);
}
ASSERT(NumBands > 0);
for (Band = 0; Band<NumBands; Band++)
{
// Render a square rectangle without any clipping
scanPrint->SetRenderMode(FALSE, &BandBoundsCap);
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dCap[0]);
context->VisibleClip.DisableComplexClipping(BandBoundsCap);
status = DpDriver::DrawImage(context, srcSurface, dstSurface,
&boundsCap, imageAttributes, numPoints,
dstPoints, srcRect, flags);
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dDev[0]);
context->VisibleClip.ReEnableComplexClipping();
if (status != Ok)
break;
if (options & ScanDeviceFlags)
{
context->VisibleClip.SetBandBounds(BandBoundsDev);
// Render the original path band at device DPI
// outputPath is already transformed into device space
scanPrint->SetRenderMode(TRUE, &BandBoundsDev);
status = DpDriver::DrawImage(context, srcSurface, dstSurface,
&boundsDev, imageAttributes, numPoints,
dstPoints, srcRect, flags);
}
if (status == Ok)
{
status = OutputBufferDIB(hdc,
context,
dstSurface,
&BandBoundsCap,
&BandBoundsDev,
&clipPath);
if (status != Ok)
break;
}
else
break;
BandBoundsCap.Y += BandHeightCap;
BandBoundsDev.Y += BandHeightDev;
// next band is last band
if (Band == (NumBands - 2))
{
#if 0
// only blit out the remaining part of draw bounds
BandBoundsCap.Height = boundsCap.Y + boundsCap.Height - BandBoundsCap.Y - 1;
BandBoundsDev.Height = boundsDev.Y + boundsDev.Height - BandBoundsDev.Y - 1;
ASSERT(BandBoundsCap.Height <= BandHeightCap);
ASSERT(BandBoundsDev.Height <= BandHeightDev);
ASSERT(BandBoundsCap.Height > 0);
ASSERT(BandBoundsDev.Height > 0);
#endif
}
}
if (SetVisibleClip)
{
DriverPrint::RestoreClipping(hdc, isClip, usePathClipping);
}
scanPrint->DestroyBufferDIB();
}
context->ReleaseHdc(hdc);
}
else
{
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dDev[0]);
}
EndPrintBanding(context);
ScaleX = oldScaleX;
ScaleY = oldScaleY;
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Draws text at a position.
*
* Arguments:
*
* [IN] context - the context (matrix and clipping)
* [IN] surface - the surface to fill
* [IN] drawBounds - the surface bounds
* [IN] text - the typeset text to be drawn
* [IN] font - the font to use
* [IN] fgBrush - the brush to use for the text
* [IN] bgBrush - the brush to use for the background (default = NULL)
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 5/22/2k ericvan
*
\**************************************************************************/
GpStatus
DriverPrint::DrawGlyphs
(
DrawGlyphData *drawGlyphData
)
{
GpStatus status = GenericError;
// Choose appropriate brush behaviour
switch(drawGlyphData->brush->Type)
{
case BrushTypeSolidColor:
// pass bitmap GlyphPos to SolidText API
status = SolidText(drawGlyphData->context,
drawGlyphData->surface,
drawGlyphData->drawBounds,
drawGlyphData->brush->SolidColor,
drawGlyphData->faceRealization,
drawGlyphData->glyphPos,
drawGlyphData->count,
drawGlyphData->glyphs,
drawGlyphData->glyphOrigins,
TextRenderingHintSingleBitPerPixelGridFit,
drawGlyphData->rightToLeft);
break;
case BrushTypeTextureFill:
case BrushTypeHatchFill:
case BrushTypePathGradient:
case BrushTypeLinearGradient:
// pass path GlyphPos to BrushText API if PostScript (for clipping)
// otherwise pass bitmap GlyphPos to compose bitmaps
status = BrushText(drawGlyphData->context,
drawGlyphData->surface,
drawGlyphData->drawBounds,
drawGlyphData->brush,
drawGlyphData->glyphPos,
drawGlyphData->glyphPathPos,
drawGlyphData->count,
TextRenderingHintSingleBitPerPixelGridFit);
break;
default:
ASSERT(FALSE); // Unknown brush type
break;
}
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Solid Text
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus.
*
* History:
*
* 12/21/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::SolidText(
DpContext* context,
DpBitmap* surface,
const GpRect* drawBounds,
GpColor color,
const GpFaceRealization *faceRealization,
const GpGlyphPos *glyphPos,
INT count,
const UINT16 *glyphs,
const PointF *glyphOrigins,
GpTextRenderingHint textMode,
BOOL rightToLeft
)
{
ASSERT(textMode == TextRenderingHintSingleBitPerPixelGridFit);
GpStatus status = GenericError;
IsSolid = TRUE;
SolidColor = color.ToCOLORREF();
IsOpaque = color.IsOpaque() ||
(context->CompositingMode == CompositingModeSourceCopy);
Is01Bitmap = FALSE;
INT angle; // Passed from GetTextOutputHdc to GdiText
HDC gdiHdc = NULL;
// Try punt to GDI.
gdiHdc = context->GetTextOutputHdc(faceRealization,
color,
surface,
&angle);
GpMatrix savedmatrix;
BOOL AdjustWorldTransform = FALSE;
if (gdiHdc)
{
BOOL isClip;
BOOL usePathClipping = FALSE;
BOOL bUseClipEscapes;
// Win9x and ME PS driver on HP's printer there is a bug to cause
// we need to use GDI clip region to set up the clip path
if (DriverType == DriverPostscript)
{
if (!Globals::IsNt)
{
bUseClipEscapes = UseClipEscapes;
UseClipEscapes = FALSE;
}
if (PostscriptScalerX != 1 || PostscriptScalerY != 1)
{
AdjustWorldTransform = TRUE;
savedmatrix = context->WorldToDevice;
}
}
DriverPrint::SetupClipping(gdiHdc,
context,
drawBounds,
isClip,
usePathClipping,
FALSE);
status = DpDriver::GdiText(gdiHdc,
angle,
glyphs,
glyphOrigins,
count,
rightToLeft);
DriverPrint::RestoreClipping(gdiHdc, isClip, usePathClipping);
if (DriverType == DriverPostscript && !Globals::IsNt)
UseClipEscapes = bUseClipEscapes;
context->ReleaseTextOutputHdc(gdiHdc);
if (status == Ok)
{
return Ok;
}
}
EpScanDIB *scanPrint = (EpScanDIB*) surface->Scan;
// only used for computing band sizes
GpRect boundsCap(drawBounds->X, drawBounds->Y,
drawBounds->Width, drawBounds->Height);
GpRect boundsDev = *drawBounds;
if (AdjustWorldTransform)
{
boundsDev.X = GpCeiling((REAL)boundsDev.X / PostscriptScalerX);
boundsDev.Y = GpCeiling((REAL)boundsDev.Y / PostscriptScalerY);
boundsDev.Width = GpCeiling((REAL)boundsDev.Width / PostscriptScalerX);
boundsDev.Height = GpCeiling((REAL)boundsDev.Height / PostscriptScalerY);
context->WorldToDevice.Scale(1.0f/PostscriptScalerX,
1.0f/PostscriptScalerY,
MatrixOrderAppend);
}
SetupPrintBanding(context, &boundsCap, &boundsDev);
DWORD options;
// works for DriverPCL and DriverPostscript
if (IsOpaque)
{
options = ScanDeviceBounds;
}
else
{
options = ScanDeviceBounds | ScanDeviceAlpha;
}
HDC hdc = context->GetHdc(surface);
if (hdc != NULL)
{
status = scanPrint->CreateBufferDIB(&BandBoundsCap,
&BandBoundsDev,
options,
ScaleX,
ScaleY);
if (status == Ok)
{
ASSERT(NumBands > 0);
for (Band = 0; Band<NumBands; Band++)
{
context->VisibleClip.SetBandBounds(BandBoundsDev);
// Render the solid text at device DPI, this generates
// an alpha channel with only the alpha bits.
scanPrint->SetRenderMode(TRUE, &BandBoundsDev);
status = DpDriver::SolidText(context, surface, &boundsDev,
color, glyphPos, count,
TextRenderingHintSingleBitPerPixelGridFit,
rightToLeft);
if (status == Ok)
{
// Don't set clip path here since it is captured in the mask
status = OutputBufferDIB(hdc,
context,
surface,
&BandBoundsCap,
&BandBoundsDev,
NULL);
if (status != Ok)
break;
}
else
break;
BandBoundsCap.Y += BandHeightCap;
BandBoundsDev.Y += BandHeightDev;
#if 0
// next band is last band
if (Band == (NumBands - 2))
{
// only blit out the remaining part of draw bounds
BandBoundsCap.Height = boundsCap.Y + boundsCap.Height - BandBoundsCap.Y - 1;
BandBoundsDev.Height = boundsDev.Y + boundsDev.Height - BandBoundsDev.Y - 1;
ASSERT(BandBoundsCap.Height <= BandHeightCap);
ASSERT(BandBoundsDev.Height <= BandHeightDev);
}
#endif
}
scanPrint->DestroyBufferDIB();
}
context->ReleaseHdc(hdc);
}
EndPrintBanding(context);
if (AdjustWorldTransform)
{
context->InverseOk = FALSE;
context->WorldToDevice = savedmatrix;
}
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Brush Text
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* GpStatus.
*
* History:
*
* 10/28/1999 ericvan
* Created it.
*
\**************************************************************************/
GpStatus
DriverPrint::BrushText(
DpContext* context,
DpBitmap* surface,
const GpRect* drawBounds,
const DpBrush* brush,
const GpGlyphPos* glyphPos,
const GpGlyphPos* glyphPathPos,
INT count,
GpTextRenderingHint textMode
)
{
ASSERT(textMode == TextRenderingHintSingleBitPerPixelGridFit);
// !! Perf. Do context->GetHdc() once at beginning and then not again.
if (SetupBrush(const_cast<DpBrush*>(brush), context, surface))
return Ok;
INT oldScaleX = -1;
INT oldScaleY = -1;
Is01Bitmap = FALSE;
GpStatus status = GenericError;
BOOL SetVisibleClip;
DWORD options;
switch (DriverType)
{
case DriverPCL:
SetVisibleClip = FALSE;
// to ensure we always use mask (XOR-AND-XOR) on PCL, we set ScanDeviceAlpha
options = ScanCappedBounds | ScanDeviceBounds | ScanDeviceAlpha;
IsOpaque = FALSE;
break;
case DriverPostscript:
SetVisibleClip = TRUE;
options = ScanCappedBounds | ScanDeviceZeroOut;
if (!IsOpaque)
{
IsOpaque = TRUE;
// options |= ScanCappedOver;
}
break;
default:
ASSERT(FALSE);
status = NotImplemented;
goto Exit;
}
{
EpScanDIB *scanPrint = (EpScanDIB*) surface->Scan;
REAL w2dDev[6];
REAL w2dCap[6];
// To avoid round off errors causing
GpRect roundedBounds;
if ((ScaleX == 1) && (ScaleY == 1))
{
roundedBounds.X = drawBounds->X;
roundedBounds.Y = drawBounds->Y;
roundedBounds.Width = drawBounds->Width;
roundedBounds.Height = drawBounds->Height;
}
else
{
// round X,Y to multiple of ScaleX,Y
roundedBounds.X = (drawBounds->X / ScaleX) * ScaleX;
roundedBounds.Y = (drawBounds->Y / ScaleY) * ScaleY;
// adjust width and height to compensate for smaller X,Y.
roundedBounds.Width = drawBounds->Width + (drawBounds->X % ScaleX);
roundedBounds.Height = drawBounds->Height + (drawBounds->Y % ScaleY);
// round width, height to multiple of ScaleX,Y
roundedBounds.Width += (ScaleX - (roundedBounds.Width % ScaleX));
roundedBounds.Height += (ScaleY - (roundedBounds.Height % ScaleY));
}
// DrawBounds in Capped Space
GpRect boundsCap(roundedBounds.X / ScaleX,
roundedBounds.Y / ScaleY,
roundedBounds.Width / ScaleX,
roundedBounds.Height / ScaleY);
if (boundsCap.Width == 0 || boundsCap.Height == 0)
{
oldScaleX = ScaleX;
oldScaleY = ScaleY;
ScaleX = 1;
ScaleY = 1;
boundsCap.X = roundedBounds.X;
boundsCap.Y = roundedBounds.Y;
boundsCap.Width = roundedBounds.Width;
boundsCap.Height = roundedBounds.Height;
}
// DrawBounds in Device Space
GpRect& boundsDev = roundedBounds;
// Infer a rectangle in world space which under the w2dCap transform
// covers our bounding box.
GpPointF dstPts[4];
dstPts[0].X = TOREAL(roundedBounds.X);
dstPts[0].Y = TOREAL(roundedBounds.Y);
dstPts[1].X = TOREAL(roundedBounds.X + roundedBounds.Width);
dstPts[1].Y = TOREAL(roundedBounds.Y);
dstPts[2].X = TOREAL(roundedBounds.X);
dstPts[2].Y = TOREAL(roundedBounds.Y + roundedBounds.Height);
dstPts[3].X = TOREAL(roundedBounds.X + roundedBounds.Width);
dstPts[3].Y = TOREAL(roundedBounds.Y + roundedBounds.Height);
GpMatrix matrix;
context->GetDeviceToWorld(&matrix);
matrix.Transform(&dstPts[0], 4);
GpRectF rectCap;
rectCap.X = min(min(dstPts[0].X, dstPts[1].X),
min(dstPts[2].X, dstPts[3].X));
rectCap.Y = min(min(dstPts[0].Y, dstPts[1].Y),
min(dstPts[2].Y, dstPts[3].Y));
rectCap.Width = max(max(dstPts[0].X, dstPts[1].X),
max(dstPts[2].X, dstPts[3].X)) - rectCap.X;
rectCap.Height = max(max(dstPts[0].Y, dstPts[1].Y),
max(dstPts[2].Y, dstPts[3].Y)) - rectCap.Y;
SetupPrintBanding(context, &boundsCap, &boundsDev);
context->WorldToDevice.GetMatrix(&w2dDev[0]);
context->WorldToDevice.Scale(1.0f/TOREAL(ScaleX),
1.0f/TOREAL(ScaleY),
MatrixOrderAppend);
context->WorldToDevice.GetMatrix(&w2dCap[0]);
context->InverseOk = FALSE;
HDC hdc = context->GetHdc(surface);;
if (hdc != NULL)
{
status = scanPrint->CreateBufferDIB(&BandBoundsCap,
&BandBoundsDev,
options,
ScaleX,
ScaleY);
GlyphClipping = FALSE;
if (status == Ok)
{
BOOL isClip = FALSE;
BOOL usePathClipping = FALSE;
// Set the visible clip unless it is captured in our mask
if (SetVisibleClip)
{
DriverPrint::SetupClipping(hdc, context, drawBounds,
isClip, usePathClipping, FALSE);
}
// !! This should be shifted into postscript driver
if (DriverType == DriverPostscript)
{
DriverPrint::SetupGlyphPathClipping(hdc,
context,
glyphPathPos,
count);
}
ASSERT(NumBands > 0);
for (Band = 0; Band<NumBands; Band++)
{
// Render a square rectangle without any clipping
scanPrint->SetRenderMode(FALSE, &BandBoundsCap);
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dCap[0]);
context->VisibleClip.DisableComplexClipping(BandBoundsCap);
status = DpDriver::FillRects(context,
surface,
&boundsCap,
1,
&rectCap,
brush);
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dDev[0]);
context->VisibleClip.ReEnableComplexClipping();
if (status != Ok)
break;
context->VisibleClip.SetBandBounds(BandBoundsDev);
// Render the original path band at device DPI
scanPrint->SetRenderMode(TRUE, &BandBoundsDev);
status = DpDriver::BrushText(context, surface, &boundsDev,
brush, glyphPos, count,
TextRenderingHintSingleBitPerPixelGridFit);
if (status == Ok)
{
status = OutputBufferDIB(hdc,
context,
surface,
&BandBoundsCap,
&BandBoundsDev,
NULL);
if (status != Ok)
break;
}
BandBoundsCap.Y += BandHeightCap;
BandBoundsDev.Y += BandHeightDev;
#if 0
// next band is last band
if (Band == (NumBands - 2))
{
// only blit out the remaining part of draw bounds
BandBoundsCap.Height = boundsCap.Y + boundsCap.Height - BandBoundsCap.Y - 1;
BandBoundsDev.Height = boundsDev.Y + boundsDev.Height - BandBoundsDev.Y - 1;
ASSERT(BandBoundsCap.Height <= BandHeightCap);
ASSERT(BandBoundsDev.Height <= BandHeightDev);
}
#endif
}
// End scope of clipping for individual glyph characters
if ((DriverType == DriverPostscript) &&
(GlyphClipping))
{
DriverPrint::RestoreEscapeClipping(hdc);
}
if (SetVisibleClip)
{
DriverPrint::RestoreClipping(hdc, isClip, usePathClipping);
}
scanPrint->DestroyBufferDIB();
}
context->ReleaseHdc(hdc);
}
else
{
context->InverseOk = FALSE;
context->WorldToDevice.SetMatrix(&w2dDev[0]);
}
EndPrintBanding(context);
}
Exit:
if (oldScaleX != -1)
{
ScaleX = oldScaleX;
ScaleY = oldScaleY;
}
RestoreBrush(const_cast<DpBrush*>(brush), context, surface);
return status;
}