|
|
/**************************************************************************\
* * Copyright (c) 1998 Microsoft Corporation * * Abstract: * * Graphics vector fill APIs. * * Revision History: * * 12/02/1998 andrewgo * Created it. * \**************************************************************************/
#include "precomp.hpp"
#include "QuadTransforms.hpp"
/**************************************************************************\
* * Function Description: * * API to clear the surface to a specified color * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 03/13/2000 agodfrey * Created it. * \**************************************************************************/
GpStatus GpGraphics::Clear( const GpColor &color ) { INT i; GpStatus status = Ok;
ASSERT(this->IsValid());
RectF drawRect( static_cast<float>(SurfaceBounds.X), static_cast<float>(SurfaceBounds.Y), static_cast<float>(SurfaceBounds.Width), static_cast<float>(SurfaceBounds.Height));
if (IsRecording()) { status = Metafile->RecordClear(&drawRect, color);
if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; }
// else we need to record down-level GDI EMF records as well
}
GpSolidFill brush(color);
if (!IsTotallyClipped(&SurfaceBounds)) { // Remember the compositing mode, antialiasing mode, and world
// transform, and then set them up for this call.
GpMatrix oldWorldToDevice = Context->WorldToDevice; INT oldAntiAliasMode = Context->AntiAliasMode; GpCompositingMode oldCompositingMode = Context->CompositingMode;
Context->WorldToDevice.Reset(); Context->AntiAliasMode = 0; Context->CompositingMode = CompositingModeSourceCopy;
Devlock devlock(Device);
status = DrvFillRects( &SurfaceBounds, 1, &drawRect, brush.GetDeviceBrush());
// Restore the context state we changed
Context->WorldToDevice = oldWorldToDevice; Context->AntiAliasMode = oldAntiAliasMode; Context->CompositingMode = oldCompositingMode; }
return status; }
/**************************************************************************\
* * Function Description: * * API to fill rectangles using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 12/06/1998 andrewgo * Created it. * \**************************************************************************/
GpStatus GpGraphics::FillRects( GpBrush* brush, const GpRectF* rects, INT count ) { INT i; GpStatus status = Ok;
// Objects returned from the API must always be in a valid state:
ASSERT((brush != NULL) && (rects != NULL)); ASSERT(this->IsValid() && brush->IsValid()); // See RAID bug:
// 301407 GDI+ Globals::DesktopDC has thread affinity
ASSERT(GetObjectType(Globals::DesktopIc) == OBJ_DC);
if (count < 0) { return InvalidParameter; }
if (count == 0) { return Ok; }
// Zoom through the list and accumulate the bounds. What a pain, but
// we have to do this.
REAL left = rects[0].X; REAL top = rects[0].Y; REAL right = rects[0].GetRight(); REAL bottom = rects[0].GetBottom();
// !!![andrewgo] We have a bug here, in that we don't properly handle
// rectangles with negative dimensions (which after the
// transform might be positive dimensions):
for (i = 1; i < count; i++) { if (rects[i].X < left) { left = rects[i].X; } if (rects[i].GetRight() > right) { right = rects[i].GetRight(); } if (rects[i].Y < top) { top = rects[i].Y; } if (rects[i].GetBottom() > bottom) { bottom = rects[i].GetBottom(); } }
// Convert the bounds to device space:
GpRectF bounds;
TransformBounds(&(Context->WorldToDevice), left, top, right, bottom, &bounds);
if (IsRecording()) { status = Metafile->RecordFillRects(&bounds, brush, rects, count); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
if (UseDriverRects()) { status = RenderFillRects(&bounds, count, rects, brush); } else { for (i = 0; i < count; i++) { if ((rects[i].Width > REAL_EPSILON) && (rects[i].Height > REAL_EPSILON) ) { GpPointF points[4];
left = rects[i].X; top = rects[i].Y; right = rects[i].X + rects[i].Width; bottom = rects[i].Y + rects[i].Height;
points[0].X = left; points[0].Y = top; points[1].X = right; points[1].Y = top; points[2].X = right; points[2].Y = bottom; points[3].X = left; points[3].Y = bottom;
const INT stackCount = 10; GpPointF stackPoints[stackCount]; BYTE stackTypes[stackCount];
GpPath path( points, 4, &stackPoints[0], &stackTypes[0], stackCount, FillModeAlternate, DpPath::ConvexRectangle );
path.CloseFigure();
if (path.IsValid()) { // Call internal FillPath so that path doesn't get recorded in
// the metafile again.
status = RenderFillPath(&bounds, &path, brush);
// Terminate if we failed to render.
if(status != Ok) { break; } } } } }
return status; }
/**************************************************************************\
* * Function Description: * * API to fill polygons using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 12/06/1998 andrewgo * \**************************************************************************/
GpStatus GpGraphics::FillPolygon( GpBrush* brush, const GpPointF* points, INT count, GpFillMode fillMode ) { GpStatus status = Ok;
ASSERT((brush != NULL) && (points != NULL));
if ((count < 0) || ((fillMode != FillModeWinding) && (fillMode != FillModeAlternate))) { return InvalidParameter; }
// Two vertices or less constitutes an empty fill:
if (count <= 2) { return Ok; }
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && brush->IsValid());
const stackCount = 30; GpPointF stackPoints[stackCount]; BYTE stackTypes[stackCount];
GpPath path(points, count, &stackPoints[0], &stackTypes[0], stackCount, fillMode);
if (path.IsValid()) { GpRectF bounds;
// If the path is a rectangle, we can draw it much faster and
// save space in spool files and metafiles if we fill it as a
// rect instead of as a path.
if (this->UseDriverRects() && path.IsRectangle(&(Context->WorldToDevice))) { path.GetBounds(&bounds, NULL); return this->FillRects(brush, &bounds, 1); }
path.GetBounds(&bounds, &(Context->WorldToDevice));
if (IsRecording()) { status = Metafile->RecordFillPolygon(&bounds, brush, points, count, fillMode); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal FillPath so that path doesn't get recorded in
// the metafile again.
status = RenderFillPath(&bounds, &path, brush); }
return status; }
/**************************************************************************\
* * Function Description: * * API to fill an ellipse using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::FillEllipse( GpBrush* brush, const GpRectF& rect ) { ASSERT(brush != NULL);
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && brush->IsValid());
GpPath path; GpStatus status = path.AddEllipse(rect);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
path.GetBounds(&bounds, &(Context->WorldToDevice));
if (IsRecording()) { status = Metafile->RecordFillEllipse(&bounds, brush, rect); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal FillPath so that path doesn't get recorded in
// the metafile again.
status = RenderFillPath(&bounds, &path, brush); }
return status; }
/**************************************************************************\
* * Function Description: * * API to fill a pie shape using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::FillPie( GpBrush* brush, const GpRectF& rect, REAL startAngle, REAL sweepAngle ) { ASSERT(brush != NULL);
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && brush->IsValid());
GpPath path; GpStatus status = path.AddPie(rect, startAngle, sweepAngle);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
path.GetBounds(&bounds, &(Context->WorldToDevice));
if (IsRecording()) { status = Metafile->RecordFillPie(&bounds, brush, rect, startAngle, sweepAngle); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal FillPath so that path doesn't get recorded in
// the metafile again.
status = RenderFillPath(&bounds, &path, brush); }
return status; }
/**************************************************************************\
* * Function Description: * * API to fill region using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 12/18/1998 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::FillRegion( GpBrush* brush, GpRegion* region ) { GpStatus status;
ASSERT((brush != NULL) && (region != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && brush->IsValid() && region->IsValid());
BOOL regionIsEmpty;
if ((status = region->IsEmpty(&Context->WorldToDevice, ®ionIsEmpty)) != Ok) { return status; }
if (regionIsEmpty) { return Ok; }
GpRectF bounds;
if ((status = region->GetBounds(this, &bounds, TRUE)) == Ok) { // The region may have very large bounds if it is infinite or if
// an infinite region was combined with another region. We don't
// want to draw a huge region into a metafile, because it will
// mess up the bounds of the metafile. So intersect the region
// with the appropriate bounding rect.
GpRect metafileBounds; // in device units
BOOL isMetafileGraphics = (this->Type == GraphicsMetafile);
if (isMetafileGraphics) { if (this->Metafile != NULL) { this->Metafile->GetMetafileBounds(metafileBounds); metafileBounds.Width++; // make exclusive
metafileBounds.Height++; } else // use size of HDC
{ HDC hdc = Context->GetHdc(Surface); metafileBounds.X = 0; metafileBounds.Y = 0; metafileBounds.Width = ::GetDeviceCaps(hdc, HORZRES); metafileBounds.Height = ::GetDeviceCaps(hdc, VERTRES); Context->ReleaseHdc(hdc); } GpRectF metafileBoundsF; metafileBoundsF.X = (REAL)metafileBounds.X; metafileBoundsF.Y = (REAL)metafileBounds.Y; metafileBoundsF.Width = (REAL)metafileBounds.Width; metafileBoundsF.Height = (REAL)metafileBounds.Height; bounds.Intersect(metafileBoundsF); }
if (IsRecording()) { status = Metafile->RecordFillRegion(&bounds, brush, region); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
if (isMetafileGraphics) { status = RenderFillRegion(&bounds, region, brush, &metafileBounds); } else // not an infinite region
{ // call internal FillRegion that doesn't do recording
status = RenderFillRegion(&bounds, region, brush, NULL); } }
return status; }
/**************************************************************************\
* * Function Description: * * API to fill path using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 12/18/1998 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::FillPath( const GpBrush* brush, GpPath* path ) { GpStatus status = Ok;
ASSERT((brush != NULL) && (path != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && brush->IsValid() && path->IsValid());
// Don't do anything with less then 2 points.
if (path->GetPointCount() < 3) { return status; }
GpRectF bounds;
// If the path is a rectangle, we can draw it much faster and
// save space in spool files and metafiles if we fill it as a
// rect instead of as a path.
if (this->UseDriverRects() && path->IsRectangle(&(Context->WorldToDevice))) { path->GetBounds(&bounds, NULL); return this->FillRects(const_cast<GpBrush *>(brush), &bounds, 1); }
path->GetBounds(&bounds, &(Context->WorldToDevice));
if (IsRecording()) { status = Metafile->RecordFillPath(&bounds, brush, path); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// call internal FillPath that doesn't do recording
status = RenderFillPath(&bounds, path, brush);
return status; }
/**************************************************************************\
* * Function Description: * * API to fill a closed curve using the specified brush * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::FillClosedCurve( GpBrush* brush, const GpPointF* points, INT count, REAL tension, GpFillMode fillMode ) { ASSERT((brush != NULL) && (points != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && brush->IsValid());
if ((count < 0) || ((fillMode != FillModeWinding) && (fillMode != FillModeAlternate))) { return InvalidParameter; }
// Less than three vertices constitutes an empty fill:
if (count < 3) { return Ok; }
GpPath path(fillMode); GpStatus status = path.AddClosedCurve(points, count, tension);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
path.GetBounds(&bounds, &(Context->WorldToDevice));
if (IsRecording()) { status = Metafile->RecordFillClosedCurve(&bounds, brush, points, count, tension, fillMode); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal FillPath so that path doesn't get recorded in
// the metafile again.
status = RenderFillPath(&bounds, &path, brush); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw polygons using the specified pen * * Arguments: * * [IN] pen - the pen for stroking. * [IN] points - the point data. * [IN] count - the number of points given in points array. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 01/06/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawLines( GpPen* pen, const GpPointF* points, INT count, BOOL closed ) { GpStatus status = Ok;
ASSERT((pen != NULL) && (points != NULL));
if (count < 2) { return InvalidParameter; }
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
const stackCount = 30; GpPointF stackPoints[stackCount]; BYTE stackTypes[stackCount];
GpPath path(points, count, stackPoints, stackTypes, stackCount, FillModeWinding); if(closed) path.CloseFigure();
if (path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawLines(&bounds, pen, points, count, closed); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw an arc using the specified pen * * Arguments: * * [IN] pen - the pen for stroking. * [IN] rect - the boundary rect. * [IN] startAndle - the start angle in degrees * [IN] sweepAngle - the sweep angle in degrees in clockwise * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawArc( GpPen* pen, const GpRectF& rect, REAL startAngle, REAL sweepAngle ) { ASSERT(pen != NULL);
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
GpPath path; GpStatus status = path.AddArc(rect, startAngle, sweepAngle);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawArc(&bounds, pen, rect, startAngle, sweepAngle); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw Cubic Bezier curves using the specified pen * * Arguments: * * [IN] pen - the pen for stroking. * [IN] points - the control points. * [IN] count - the number of control points (must be 3n + 1). * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawBeziers( GpPen* pen, const GpPointF* points, INT count ) { ASSERT((pen != NULL) && (points != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
// Nothing to draw
if (count <= 3) { return Ok; }
GpPath path; GpStatus status = path.AddBeziers(points, count);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawBeziers(&bounds, pen, points, count); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw rectangles using the specified brush * * Arguments: * * [IN] pen - the pen for stroking. * [IN] rects - the rectangle array. * [IN] count - the number of rectangles given in rects array. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 01/15/1998 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawRects( GpPen* pen, const GpRectF* rects, INT count ) { INT i; GpStatus status = Ok;
// !!! Change Eng function to do clipping
// !!! Create a stack path
// !!! Fix multiple inheritence thing
// !!! Check tail merging
// !!! Add alignment checks
// !!! Change DDIs to return GpStatus?
// !!! Add ICM hooks?
// !!! Change path constant to include 'single figure'?
// !!! Create .LIB
// !!! Add convention for alpha
// Objects returned from the API must always be in a valid state:
ASSERT((pen != NULL) && (rects != NULL)); ASSERT(this->IsValid() && pen->IsValid());
if (count < 0) { return InvalidParameter; }
if (count == 0) { return Ok; }
// Zoom through the list and accumulate the bounds. What a pain, but
// we have to do this.
// !!! We're doing 'double' goop, so we should ensure correct stack
// alignment
REAL left = rects[0].X; REAL top = rects[0].Y; REAL right = rects[0].GetRight(); REAL bottom = rects[0].GetBottom();
for (i = 1; i < count; i++) { if (rects[i].X < left) { left = rects[i].X; } if (rects[i].GetRight() > right) { right = rects[i].GetRight(); } if (rects[i].Y < top) { top = rects[i].Y; } if (rects[i].GetBottom() > bottom) { bottom = rects[i].GetBottom(); } }
GpRectF bounds;
// Convert the bounds to device space and adjust for the pen width
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY();
DpPen *dpPen = pen->GetDevicePen();
REAL penWidth = 0; Unit penUnit = UnitWorld; REAL delta = 0;
if(dpPen) { penWidth = dpPen->Width; penUnit = dpPen->Unit;
if(penUnit == UnitWorld) { // If the pen is in World unit, strech the rectangle
// by pen width before the transform.
// For a case of the centered pen.
// penWidth/2 is OK. But here, we
// just use penWidth for all pen mode.
delta = penWidth;
left -= delta; top -= delta; right += delta; bottom += delta; } }
TransformBounds(&(Context->WorldToDevice), left, top, right, bottom, &bounds);
if(dpPen) { if(penUnit != UnitWorld) { // If the pen is not in World unit, strech the rectangle
// by pen's device width after the transform.
REAL dpi = max(dpiX, dpiY); penWidth = ::GetDeviceWidth(penWidth, penUnit, dpi);
// For a case of the centered pen.
// penWidth/2 is OK. But here, we
// just use penWidth for all pen mode.
delta = penWidth;
bounds.X -= delta; bounds.Y -= delta; bounds.Width += 2*delta; bounds.Height += 2*delta; } }
if (IsRecording()) { status = Metafile->RecordDrawRects(&bounds, pen, rects, count); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Increase the bounds to account for the widener's minimum pen width.
// For some arcane reason, the widener doesn't use 1.0 as the minimum
// pen width. Rather it uses 1.000001f. Also it has some interesting
// rounding properties, so our epsilon here is much larger 0.001f
bounds.Inflate(1.001f, 1.001f);
for (i = 0; i < count; i++) { if ((rects[i].Width > REAL_EPSILON) && (rects[i].Height > REAL_EPSILON) ) { // !!! Should use a stack-path
// !!! For StrokePath case, should check start of rectangle
// for styled lines
GpPointF points[4];
left = rects[i].X; top = rects[i].Y; right = rects[i].X + rects[i].Width; bottom = rects[i].Y + rects[i].Height;
points[0].X = left; points[0].Y = top; points[1].X = right; points[1].Y = top; points[2].X = right; points[2].Y = bottom; points[3].X = left; points[3].Y = bottom;
const INT stackCount = 10; GpPointF stackPoints[stackCount]; BYTE stackTypes[stackCount];
GpPath path( points, 4, stackPoints, stackTypes, stackCount, FillModeAlternate, DpPath::ConvexRectangle );
path.CloseFigure();
if(path.IsValid()) { // Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen);
if(status != Ok) { break; } } } }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw an ellipse using the specified pen * * Arguments: * * [IN] pen - the pen for stroking. * [IN] rect - the boundary rectangle * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawEllipse( GpPen* pen, const GpRectF& rect ) { ASSERT(pen != NULL);
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
GpPath path; GpStatus status = path.AddEllipse(rect);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawEllipse(&bounds, pen, rect); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw a pie using the specified pen * * Arguments: * * [IN] pen - the pen for stroking. * [IN] rect - the boundary rectangle * [IN] startAngle - the start angle in degrees. * [IN] sweepAngle - the sweep angle in degrees in clockwise. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawPie( GpPen* pen, const GpRectF& rect, REAL startAngle, REAL sweepAngle ) { ASSERT(pen != NULL);
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
GpPath path; GpStatus status = path.AddPie(rect, startAngle, sweepAngle);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawPie(&bounds, pen, rect, startAngle, sweepAngle); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw path using the specified pen * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 01/27/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawPath( GpPen* pen, GpPath* path ) { GpStatus status = Ok;
ASSERT((pen != NULL) && (path != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid() && path->IsValid());
// Don't do anything unless we have at least one point
if (path->GetPointCount() < 1) { return status; }
GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path->GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawPath(&bounds, pen, path); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// call internal DrawPath that doesn't do recording
status = RenderDrawPath(&bounds, path, pen);
return status; }
/**************************************************************************\
* * Function Description: * * API to draw a curve using the specified pen. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
#define DEFAULT_TENSION 0.5
GpStatus GpGraphics::DrawCurve( GpPen* pen, const GpPointF* points, INT count ) { return DrawCurve(pen, points, count, DEFAULT_TENSION, 0, count - 1); }
GpStatus GpGraphics::DrawCurve( GpPen* pen, const GpPointF* points, INT count, REAL tension, INT offset, INT numberOfSegments ) { ASSERT((pen != NULL) && (points != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
if (count < 2) { return InvalidParameter; }
GpPath path; GpStatus status = path.AddCurve(points, count, tension, offset, numberOfSegments);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawCurve(&bounds, pen, points, count, tension, offset, numberOfSegments); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw a closed curve using the specified pen. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 02/18/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawClosedCurve( GpPen* pen, const GpPointF* points, INT count ) { return DrawClosedCurve (pen, points, count, DEFAULT_TENSION); }
GpStatus GpGraphics::DrawClosedCurve( GpPen* pen, const GpPointF* points, INT count, REAL tension ) { ASSERT((pen != NULL) && (points != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && pen->IsValid());
if (count < 3) { return InvalidParameter; }
GpPath path; GpStatus status = path.AddClosedCurve(points, count, tension);
if ((status == Ok) && path.IsValid()) { GpRectF bounds;
REAL dpiX = GetDpiX(); REAL dpiY = GetDpiY(); path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
if (IsRecording()) { status = Metafile->RecordDrawClosedCurve(&bounds, pen, points, count, tension); if (status != Ok) { SetValid(FALSE); // Prevent any more recording
return status; } if (!DownLevel) { return Ok; } // else we need to record down-level GDI EMF records as well
}
// Call internal DrawPath so that path doesn't get recorded in
// the metafile again.
status = RenderDrawPath(&bounds, &path, pen); }
return status; }
/**************************************************************************\
* * Function Description: * * Internal Drawing routine for a path. Various functions will * call RenderFillPath. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 09/18/2000 asecchia * Created it. * \**************************************************************************/
GpStatus GpGraphics::RenderFillPath( GpRectF* bounds, GpPath* path, const GpBrush* brush ) { // Are they asking us to draw nothing?
if( REALABS(bounds->Width) < REAL_EPSILON || REALABS(bounds->Height) < REAL_EPSILON ) { // Yes. Ok, we did it.
return Ok; }
GpRect deviceBounds; GpStatus status = BoundsFToRect(bounds, &deviceBounds);
if (status == Ok && !IsTotallyClipped(&deviceBounds)) { // Now that we've done a bunch of work in accumulating the bounds,
// acquire the device lock before calling the driver:
Devlock devlock(Device);
return DrvFillPath(&deviceBounds, path, brush->GetDeviceBrush()); } return status; }
/**************************************************************************\
* * Function Description: * * Internal Drawing routine for a path. Various functions will * call RenderDrawPath. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 10/28/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::RenderDrawPath( GpRectF * bounds, GpPath * path, GpPen * pen ) { // Are they asking us to draw nothing?
if( REALABS(bounds->Width) < REAL_EPSILON || REALABS(bounds->Height) < REAL_EPSILON ) { // Yes. Ok, we did it.
return Ok; }
GpRect deviceBounds; GpStatus status = BoundsFToRect(bounds, &deviceBounds); INT savedState = 0;
if (status == Ok && !IsTotallyClipped(&deviceBounds)) { // Now that we've done a bunch of work in accumulating the bounds,
// acquire the device lock before calling the driver:
Devlock devlock(Device);
status = DrvStrokePath(&deviceBounds, path, pen->GetDevicePen()); } return status; }
VOID GetEmfDpi( HDC hdc, REAL * dpiX, REAL * dpiY ) { SIZEL szlDevice; // Size of the reference device in pels
SIZEL szlMillimeters; // Size of the reference device in millimeters
szlDevice.cx = GetDeviceCaps(hdc, HORZRES); szlDevice.cy = GetDeviceCaps(hdc, VERTRES);
szlMillimeters.cx = GetDeviceCaps(hdc, HORZSIZE); szlMillimeters.cy = GetDeviceCaps(hdc, VERTSIZE);
if ((szlDevice.cx > 0) && (szlDevice.cy > 0) && (szlMillimeters.cx > 0) && (szlMillimeters.cy > 0)) { *dpiX = (static_cast<REAL>(szlDevice.cx) / static_cast<REAL>(szlMillimeters.cx)) * 25.4f; *dpiY = (static_cast<REAL>(szlDevice.cy) / static_cast<REAL>(szlMillimeters.cy)) * 25.4f; } else { WARNING(("GetDeviceCaps failed"));
*dpiX = DEFAULT_RESOLUTION; *dpiY = DEFAULT_RESOLUTION; } }
/**************************************************************************\
* * Function Description: * * Get the size of the destination image in the current page units. * * Arguments: * * [IN] srcDpiX - horizontal resolution of the source image * [IN] srcDpiY - vertical resolution of the source image * [IN] srcWidth - width of the source image in srcUnit units * [IN] srcHeight - height of the source image in srcUnit units * [IN] srcUnit - units of the srcWidth and srcHeight * [OUT] destWidth - destination width in the current page units * [OUT] destHeight - destination height in the current page units * * Return Value: * * NONE * * Created: * * 05/10/1999 DCurtis * \**************************************************************************/ VOID GpGraphics::GetImageDestPageSize( const GpImage * image, REAL srcWidth, REAL srcHeight, GpPageUnit srcUnit, REAL & destWidth, REAL & destHeight ) { // UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (srcUnit == UnitPixel) { REAL srcDpiX; REAL srcDpiY; REAL destDpiX; REAL destDpiY;
image->GetResolution(&srcDpiX, &srcDpiY);
// We don't want to create a bitmap just to get the dpi
// so check if we can get an Hdc easily from the context.
if ((image->GetImageType() == ImageTypeMetafile) && (((GpMetafile *)(image))->IsEmfOrEmfPlus()) && (Context->Hwnd || Context->Hdc)) { // EMFs use a different style of dpi than other images that
// is based off the screen size instead of the font size.
if (Context->Hwnd) { // We don't need a clean dc to find out the dpi
HDC hdc = GetDC(Context->Hwnd); GetEmfDpi(hdc, &destDpiX, &destDpiY); ReleaseDC(Context->Hwnd, hdc); } else { GetEmfDpi(Context->Hdc, &destDpiX, &destDpiY); } } else { destDpiX = GetDpiX(); destDpiY = GetDpiY(); }
// To get the dest size, convert the width and height from the image
// resolution to the resolution of this graphics and then convert
// them to page units by going through the inverse of the page to
// device transform.
destWidth = (srcWidth * destDpiX) / (srcDpiX * Context->PageMultiplierX); destHeight = (srcHeight * destDpiY) / (srcDpiY * Context->PageMultiplierY); } else { // Just convert from the units of the image to the current
// page units.
REAL unitMultiplierX; REAL unitMultiplierY;
Context->GetPageMultipliers(&unitMultiplierX, &unitMultiplierY, srcUnit);
destWidth = (srcWidth * unitMultiplierX) / Context->PageMultiplierX; destHeight = (srcHeight * unitMultiplierY) / Context->PageMultiplierY; } }
/**************************************************************************\
* * Function Description: * * API to draw image * * Arguments: * * [IN] image - the image to draw. * [IN] point - the top-left corner of the drawing boundary. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 01/06/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawImage( GpImage* image, const GpPointF& point ) { GpStatus status;
ASSERT((image != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && image->IsValid());
GpRectF srcRect; GpPageUnit srcUnit; REAL destWidth; REAL destHeight;
status = image->GetBounds(&srcRect, &srcUnit); if(status != Ok) {return status;}
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (status == Ok) { // Get the dest size in page units
GetImageDestPageSize(image, srcRect.Width, srcRect.Height, srcUnit, destWidth, destHeight);
GpRectF destRect(point.X, point.Y, destWidth, destHeight);
return DrawImage(image, destRect, srcRect, srcUnit); } return status; }
GpStatus GpGraphics::DrawImage( GpImage* image, REAL x, REAL y, const GpRectF & srcRect, GpPageUnit srcUnit ) { // UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
REAL srcDpiX, srcDpiY; REAL destWidth; REAL destHeight;
// Get the dest size in page units
GetImageDestPageSize(image, srcRect.Width, srcRect.Height, srcUnit, destWidth, destHeight);
GpRectF destRect(x, y, destWidth, destHeight);
return DrawImage(image, destRect, srcRect, srcUnit); }
/**************************************************************************\
* * Function Description: * * API to draw image. * * [IN] image - the image to draw. * [IN] rect - the the drawing boundary. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 01/12/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawImage( GpImage* image, const GpRectF& destRect ) { GpStatus status;
ASSERT((image != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && image->IsValid());
GpPageUnit srcUnit; GpRectF srcRect;
status = image->GetBounds(&srcRect, &srcUnit); if(status != Ok) { return status; }
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (status == Ok) { return DrawImage(image, destRect, srcRect, srcUnit); } return status; }
/**************************************************************************\
* * Function Description: * * API to draw image. * * [IN] image - the image to draw. * [IN] destPoints - the destination quad. * [IN] count - the number of count in destPoints[] (3 or 4). * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 04/14/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawImage( GpImage* image, const GpPointF* destPoints, INT count ) { GpStatus status;
// count of 4 is not implemented yet (perspective blt)
if(count == 4) { return NotImplemented; }
if(count != 3) { return InvalidParameter; }
ASSERT(count == 3); // Currently only supports Affine transform.
ASSERT((image != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && image->IsValid());
GpPageUnit srcUnit; GpRectF srcRect;
status = image->GetBounds(&srcRect, &srcUnit); if(status != Ok) { return status; }
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (status == Ok) { return DrawImage(image, destPoints, count, srcRect, srcUnit); } return status; }
/**************************************************************************\
* * Function Description: * * API to draw image. * * [IN] image - the image to draw. * [IN] destRect - the destination rectangle. * [IN] srcRect - the portion of the image to copy. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 01/12/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawImage( GpImage* image, const GpRectF& destRect, const GpRectF& srcRect, GpPageUnit srcUnit, const GpImageAttributes* imageAttributes, DrawImageAbort callback, VOID* callbackData ) { GpStatus status = Ok;
ASSERT((image != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && image->IsValid());
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
GpRectF offsetSrcRect = srcRect ;
GpPointF destPoints[3]; destPoints[0].X = destRect.X; destPoints[0].Y = destRect.Y; destPoints[1].X = destRect.X + destRect.Width; destPoints[1].Y = destRect.Y; destPoints[2].X = destRect.X; destPoints[2].Y = destRect.Y + destRect.Height;
GpRectF bounds; TransformBounds( &(Context->WorldToDevice), destPoints[0].X, destPoints[0].Y, destPoints[1].X, destPoints[2].Y, &bounds );
GpImageType imageType = image->GetImageType();
DriverDrawImageFlags flags = 0;
GpRecolor * recolor = NULL;
if (imageAttributes != NULL) { if (imageAttributes->cachedBackground) flags |= DriverDrawImageCachedBackground;
if (imageType == ImageTypeBitmap) { if (imageAttributes->HasRecoloring(ColorAdjustTypeBitmap)) { goto HasRecoloring; } } else if (imageAttributes->HasRecoloring()) { HasRecoloring: recolor = imageAttributes->recolor; recolor->Flush(); } }
GpImage * adjustedImage = NULL; GpImageAttributes noRecoloring;
if (IsRecording()) { if (recolor != NULL) { // We assume that the image is a bitmap.
// For Bitmaps, we want to recolor into an image that will have an
// alpha. CloneColorAdjusted keeps the same pixel format as the
// original image and therefore might not have an alpha channel.
// recolor will convert to ARGB. When recording to a metafile this
// will only create an ARGB image if the original image is not
// palettized, therefore only for 16bit and higher. The most
// space we can waste is twice the image.
if(image->GetImageType() == ImageTypeBitmap) { GpBitmap * bitmap = reinterpret_cast<GpBitmap*>(image); GpBitmap * adjustedBitmap = NULL; if (bitmap != NULL) { status = bitmap->Recolor(recolor, &adjustedBitmap, NULL, NULL); if (status == Ok) { adjustedImage = adjustedBitmap; } } } else { adjustedImage = image->CloneColorAdjusted(recolor); } if (adjustedImage != NULL) { image = adjustedImage;
// have to set the recolor to NULL in the image attributes
// or else the down-level image will be double recolored.
GpRecolor * saveRecolor = noRecoloring.recolor; noRecoloring = *imageAttributes; noRecoloring.recolor = saveRecolor; imageAttributes = &noRecoloring; recolor = noRecoloring.recolor; } }
// Record recolored image.
status = Metafile->RecordDrawImage( &bounds, image, destRect, srcRect, srcUnit, imageAttributes );
if (status != Ok) { SetValid(FALSE); // Prevent any more recording
goto Done; }
if (!DownLevel) { goto Done; } // else we need to record down-level GDI EMF records as well
}
// Metafiles do not require PixelOffsetting, in fact it results in bad
// side effects in some cases when the source metafile dpi is low. But
// we still need to offset the DestRect to match the rendering with other
// primitives
// GillesK: If we are in HalfPixelMode, then offset the source and the
// destination rects by -0.5 pixels
if ((image->GetImageType() != ImageTypeMetafile) && (!Context->IsPrinter) && ((Context->PixelOffset == PixelOffsetModeHalf) || (Context->PixelOffset == PixelOffsetModeHighQuality))) { offsetSrcRect.Offset(-0.5f, -0.5f); }
{ GpRect deviceBounds; status = BoundsFToRect(&bounds, &deviceBounds);
if (status == Ok && !IsTotallyClipped(&deviceBounds)) { if (imageType == ImageTypeBitmap) { INT numPoints = 3;
if (status == Ok) { // Now that we've done a bunch of work in accumulating the bounds,
// acquire the device lock before calling the driver:
Devlock devlock(Device); ASSERT(srcUnit == UnitPixel); // !!! for now
// Set the fpu state.
FPUStateSaver fpuState;
status = DrvDrawImage( &deviceBounds, (GpBitmap*)(image), numPoints, &destPoints[0], &offsetSrcRect, imageAttributes, callback, callbackData, flags ); } } else if (imageType == ImageTypeMetafile) { // If we are recording to a different metafile, then we have
// already recorded this metafile as an image, and now we just
// want to record the down-level parts, so we have to set
// g->Metafile to NULL so we don't record all the GDI+ records
// in the metafile again -- only the down-level ones.
// Make sure to pass in the imageAttributes recolorer since it
// might have been changed if we already recolored the image
IMetafileRecord * recorder = this->Metafile; this->Metafile = NULL;
status = (static_cast<const GpMetafile *>(image))->Play( destRect, offsetSrcRect, srcUnit, this, recolor, ColorAdjustTypeDefault, callback, callbackData);
this->Metafile = recorder; // restore the recorder (if any)
} else { ASSERT(0); status = NotImplemented; } } }
Done: if (adjustedImage != NULL) { adjustedImage->Dispose(); }
return status; }
/**************************************************************************\
* * Function Description: * * API to draw image. * * [IN] image - the image to draw. * [IN] destPoints - the destination quad. * [IN] count - the number of count in destPoints[] (3 or 4). * [IN] srcRect - the portion of the image to copy. * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 04/14/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpGraphics::DrawImage( GpImage* image, const GpPointF* destPoints, INT count, const GpRectF& srcRect, GpPageUnit srcUnit, const GpImageAttributes* imageAttributes, DrawImageAbort callback, VOID* callbackData ) { GpStatus status = Ok;
// count of 4 is not implemented yet (perspective blt)
if(count == 4) { return NotImplemented; }
if(count != 3) { return InvalidParameter; }
ASSERT(count == 3); // Currently only supports Affine transform.
ASSERT((image != NULL));
// Objects returned from the API must always be in a valid state:
ASSERT(this->IsValid() && image->IsValid());
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
GpRectF offsetSrcRect = srcRect ;
// NOTE: We could do this for all image types, including Bitmaps!!!
// It would save code to do this always.
if (image->GetImageType() != ImageTypeBitmap) { // Metafiles don't handle the destPoints API directly, so we
// have to convert to using the destRect API instead. To do so,
// we assume a canonical destRect and set up the transform to
// map from that destRect to the destPoints.
if (count == 3) { GpMatrix matrix; GpRectF destRect(0.0f, 0.0f, 1000.0f, 1000.0f);
if (matrix.InferAffineMatrix(destPoints, destRect) == Ok) { INT gstate;
if ((gstate = this->Save()) != 0) { if ((status = this->MultiplyWorldTransform( matrix, MatrixOrderPrepend)) == Ok) { status = this->DrawImage(image, destRect, srcRect, srcUnit, imageAttributes, callback, callbackData); } this->Restore(gstate); return status; } } return GenericError; } return NotImplemented; } // else it is a Bitmap
REAL xmin, xmax, ymin, ymax;
ASSERT(count == 3); // Currently only supports Affine transform.
// Set to the fourth corner point.
xmin = xmax = destPoints[1].X + destPoints[2].X - destPoints[0].X; ymin = ymax = destPoints[1].Y + destPoints[2].Y - destPoints[0].Y;
// Compare with the other three corners.
for(INT i = 0; i < 3; i++) { xmin = min(xmin, destPoints[i].X); xmax = max(xmax, destPoints[i].X); ymin = min(ymin, destPoints[i].Y); ymax = max(ymax, destPoints[i].Y); }
GpRectF bounds; TransformBounds(&(Context->WorldToDevice), xmin, ymin, xmax, ymax, &bounds);
INT numPoints = 3;
GpImageType imageType = image->GetImageType();
DriverDrawImageFlags flags = 0;
GpRecolor * recolor = NULL;
if (imageAttributes != NULL) { if (imageAttributes->cachedBackground) flags |= DriverDrawImageCachedBackground;
if (imageType == ImageTypeBitmap) { if (imageAttributes->HasRecoloring(ColorAdjustTypeBitmap)) { goto HasRecoloring; } } else if (imageAttributes->HasRecoloring()) { HasRecoloring: recolor = imageAttributes->recolor; recolor->Flush(); } }
GpImage * adjustedImage = NULL; GpImageAttributes noRecoloring;
if (IsRecording()) { if (recolor != NULL) { // We assume that the image is a bitmap.
// For Bitmaps, we want to recolor into an image that will have an
// alpha. CloneColorAdjusted keeps the same pixel format as the
// original image and therefore might not have an alpha channel.
// recolor will convert to ARGB. When recording to a metafile this
// will only create an ARGB image if the original image is not
// palettized, therefore only for 16bit and higher. The most
// space we can waste is twice the image.
if(image->GetImageType() == ImageTypeBitmap) { GpBitmap * bitmap = reinterpret_cast<GpBitmap*>(image); GpBitmap * adjustedBitmap = NULL; if (bitmap != NULL) { status = bitmap->Recolor(recolor, &adjustedBitmap, NULL, NULL); if (status == Ok) { adjustedImage = adjustedBitmap; } } } else { adjustedImage = image->CloneColorAdjusted(recolor); } if (adjustedImage != NULL) { image = adjustedImage;
// have to set the recolor to NULL in the image attributes
// or else the down-level image will be double recolored.
GpRecolor * saveRecolor = noRecoloring.recolor; noRecoloring = *imageAttributes; noRecoloring.recolor = saveRecolor; imageAttributes = &noRecoloring; } }
// Record recolored image.
status = Metafile->RecordDrawImage( &bounds, image, destPoints, count, srcRect, srcUnit, imageAttributes );
if (status != Ok) { SetValid(FALSE); // Prevent any more recording
goto Done; } if (!DownLevel) { goto Done; } // else we need to record down-level GDI EMF records as well
}
// GillesK: If we are in HalfPixelMode, then offset the source and the
// destination rects by -0.5 pixels
if ((image->GetImageType() != ImageTypeMetafile) && (!Context->IsPrinter) && ((Context->PixelOffset == PixelOffsetModeHalf) || (Context->PixelOffset == PixelOffsetModeHighQuality))) { offsetSrcRect.Offset(-0.5f, -0.5f); }
{ GpRect deviceBounds; status = BoundsFToRect(&bounds, &deviceBounds);
if (status == Ok && !IsTotallyClipped(&deviceBounds)) { // Now that we've done a bunch of work in accumulating the bounds,
// acquire the device lock before calling the driver:
Devlock devlock(Device);
ASSERT(srcUnit == UnitPixel); // !!! for now
// Set the fpu state.
FPUStateSaver fpuState;
// We assume that the image is a bitmap.
ASSERT(image->GetImageType() == ImageTypeBitmap);
status = DrvDrawImage( &deviceBounds, static_cast<GpBitmap*>(image), numPoints, &destPoints[0], &offsetSrcRect, imageAttributes, callback, callbackData, flags ); } }
Done: if (adjustedImage != NULL) { adjustedImage->Dispose(); }
return status; }
GpStatus GpGraphics::EnumerateMetafile( const GpMetafile * metafile, const PointF & destPoint, EnumerateMetafileProc callback, VOID * callbackData, const GpImageAttributes * imageAttributes ) { GpStatus status;
ASSERT(metafile != NULL); ASSERT(callback != NULL);
// Objects from the API must always be in a valid state:
ASSERT(this->IsValid() && metafile->IsValid());
GpPageUnit srcUnit; GpRectF srcRect;
status = metafile->GetBounds(&srcRect, &srcUnit); if(status != Ok) { return status; }
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (status == Ok) { return this->EnumerateMetafile( metafile, destPoint, srcRect, srcUnit, callback, callbackData, imageAttributes ); } return status; }
GpStatus GpGraphics::EnumerateMetafile( const GpMetafile * metafile, const RectF & destRect, EnumerateMetafileProc callback, VOID * callbackData, const GpImageAttributes * imageAttributes ) { GpStatus status;
ASSERT(metafile != NULL); ASSERT(callback != NULL);
// Objects from the API must always be in a valid state:
ASSERT(this->IsValid() && metafile->IsValid());
GpPageUnit srcUnit; GpRectF srcRect;
status = metafile->GetBounds(&srcRect, &srcUnit); if(status != Ok) { return status; }
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (status == Ok) { return this->EnumerateMetafile( metafile, destRect, srcRect, srcUnit, callback, callbackData, imageAttributes ); } return status; }
GpStatus GpGraphics::EnumerateMetafile( const GpMetafile * metafile, const PointF * destPoints, INT count, EnumerateMetafileProc callback, VOID * callbackData, const GpImageAttributes * imageAttributes ) { GpStatus status;
ASSERT(metafile != NULL); ASSERT(callback != NULL);
// Objects from the API must always be in a valid state:
ASSERT(this->IsValid() && metafile->IsValid());
GpPageUnit srcUnit; GpRectF srcRect;
status = metafile->GetBounds(&srcRect, &srcUnit); if(status != Ok) { return status; }
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
if (status == Ok) { return this->EnumerateMetafile( metafile, destPoints, count, srcRect, srcUnit, callback, callbackData, imageAttributes ); } return status; }
GpStatus GpGraphics::EnumerateMetafile( const GpMetafile * metafile, const PointF & destPoint, const RectF & srcRect, Unit srcUnit, EnumerateMetafileProc callback, VOID * callbackData, const GpImageAttributes * imageAttributes ) { ASSERT(metafile != NULL); ASSERT(callback != NULL);
// Objects from the API must always be in a valid state:
ASSERT(this->IsValid() && metafile->IsValid());
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
REAL srcDpiX, srcDpiY; REAL destWidth; REAL destHeight;
// Get the dest size in page units
GetImageDestPageSize(metafile, srcRect.Width, srcRect.Height, srcUnit, destWidth, destHeight);
GpRectF destRect(destPoint.X, destPoint.Y, destWidth, destHeight);
return this->EnumerateMetafile( metafile, destRect, srcRect, srcUnit, callback, callbackData, imageAttributes ); }
// All the EnumerateMetafile methods end up calling this one
GpStatus GpGraphics::EnumerateMetafile( const GpMetafile * metafile, const RectF & destRect, const RectF & srcRect, Unit srcUnit, EnumerateMetafileProc callback, VOID * callbackData, const GpImageAttributes * imageAttributes ) { ASSERT(metafile != NULL); ASSERT(callback != NULL);
// Objects from the API must always be in a valid state:
ASSERT(this->IsValid() && metafile->IsValid());
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
GpStatus status;
GpRecolor * recolor = NULL;
if ((imageAttributes != NULL) && imageAttributes->HasRecoloring()) { recolor = imageAttributes->recolor; recolor->Flush(); }
// NOTE: I don't check the bounds, because even if the entire
// metafile is out of the clip bounds, I still want to enumerate it.
status = metafile->EnumerateForPlayback( destRect, srcRect, srcUnit, this, callback, callbackData, recolor ); return status; }
GpStatus GpGraphics::EnumerateMetafile( const GpMetafile * metafile, const PointF * destPoints, INT count, const RectF & srcRect, Unit srcUnit, EnumerateMetafileProc callback, VOID * callbackData, const GpImageAttributes * imageAttributes ) { ASSERT(metafile != NULL); ASSERT(callback != NULL);
// Objects from the API must always be in a valid state:
ASSERT(this->IsValid() && metafile->IsValid());
// UnitDisplay is device-dependent and cannot be used for a source unit
ASSERT(srcUnit != UnitDisplay);
GpStatus status = Ok;
// Metafiles don't handle the destPoints API directly, so we
// have to convert to using the destRect API instead. To do so,
// we assume a canonical destRect and set up the transform to
// map from that destRect to the destPoints.
ASSERT(count == 3); // Currently only supports Affine transform.
if (count == 3) { GpMatrix matrix; GpRectF destRect(0.0f, 0.0f, 100.0f, 100.0f);
if (matrix.InferAffineMatrix(destPoints, destRect) == Ok) { INT gstate;
if ((gstate = this->Save()) != 0) { if ((status = this->MultiplyWorldTransform( matrix, MatrixOrderPrepend)) == Ok) { status = this->EnumerateMetafile( metafile, destRect, srcRect, srcUnit, callback, callbackData, imageAttributes ); } this->Restore(gstate); return status; } } return GenericError; } return NotImplemented; }
/**************************************************************************\
* * Function Description: * * API to get color ARGB value at pixel x,y. This is private GDI+ API. * * [IN] x - horizontal position * [IN] y - vertical position * [IN] argb - argb color value * * Return Value: * * A GpStatus value indicating success or failure. * * History: * * 05/13/1999 ericvan * Created it. * \**************************************************************************/
GpStatus GpGraphics::GetPixelColor( REAL x, REAL y, ARGB* argb ) const { GpPointF pt(x,y);
if (!IsVisible(pt)) return InvalidParameter;
Devlock devlock(Device);
DpScanBuffer scan(Surface->Scan, Driver, Context, Surface, CompositingModeSourceCopy);
Context->WorldToDevice.Transform(&pt, 1);
ARGB* buffer = scan.NextBuffer((INT)x, (INT)y, 1);
if (buffer) *argb = *buffer; else return InvalidParameter;
return Ok; }
|