/**************************************************************************\ * * Copyright (c) 1998 Microsoft Corporation * * Abstract: * * Implementation of GpBrush class * * Revision History: * * 12/09/1998 davidx * Flesh out brush interfaces. * * 12/08/1998 andrewgo * Initial placeholders. * \**************************************************************************/ #include "precomp.hpp" // For GetData and SetData methods #define GDIP_BRUSHFLAGS_PATH 0x00000001 #define GDIP_BRUSHFLAGS_TRANSFORM 0x00000002 #define GDIP_BRUSHFLAGS_PRESETCOLORS 0x00000004 #define GDIP_BRUSHFLAGS_BLENDFACTORS 0x00000008 #define GDIP_BRUSHFLAGS_BLENDFACTORSH GDIP_BRUSHFLAGS_BLENDFACTORS #define GDIP_BRUSHFLAGS_BLENDFACTORSV 0x00000010 #define GDIP_BRUSHFLAGS_BLENDFACTORS0 GDIP_BRUSHFLAGS_BLENDFACTORSH #define GDIP_BRUSHFLAGS_BLENDFACTORS1 GDIP_BRUSHFLAGS_BLENDFACTORSV #define GDIP_BRUSHFLAGS_BLENDFACTORS2 0x00000020 #define GDIP_BRUSHFLAGS_FOCUSSCALES 0x00000040 #define GDIP_BRUSHFLAGS_ISGAMMACORRECTED 0x00000080 // Defined in path.cpp extern BOOL IsRectanglePoints( const GpPointF* points, INT count, const GpMatrix * matrix, GpRectF * transformedBounds ); GpStatus GpElementaryBrush::MultiplyTransform(const GpMatrix& matrix, GpMatrixOrder order) { GpStatus status = Ok; if (matrix.IsInvertible()) { if (order == MatrixOrderPrepend) { DeviceBrush.Xform.Prepend(matrix); } else { DeviceBrush.Xform.Append(matrix); } UpdateUid(); } else status = InvalidParameter; return status; } /**************************************************************************\ * * Function Description: * * Calculate the brush transform from a starting point and two directions. * * Arguments: * * [OUT] m - matrix coefficients * [OUT] width - width (the length of dP1) * [OUT] height - height (the length of dP2) * [IN] p0 - the starting point of the brush. * [IN] dP1 - the vector to represent the transformed x-direction. * [IN] dP2 - the vector to represent the transformed y-direction. * * Return Vaule: * * TRUE if the transform matrix is non-degenerate. * Otherwise returns FALSE. * * History: * * 06/03/1999 ikkof * Created it. * \**************************************************************************/ BOOL getLineGradientTransform( REAL* m, REAL* width, REAL* height, const GpPointF& p0, const GpPointF& dP1, const GpPointF& dP2 ) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); REAL l1 = dP1.X*dP1.X + dP1.Y*dP1.Y; REAL l2 = dP2.X*dP2.X + dP2.Y*dP2.Y; REAL test = dP1.X*dP2.Y - dP1.Y*dP2.X; if(l1 == 0 || l2 == 0 || test == 0) return FALSE; l1 = REALSQRT(l1); l2 = REALSQRT(l2); m[0] = TOREAL(dP1.X/l1); // M11 m[1] = TOREAL(dP1.Y/l1); // M12 m[2] = TOREAL(dP2.X/l2); // M21 m[3] = TOREAL(dP2.Y/l2); // M22 m[4] = TOREAL(p0.X - p0.X*m[0] - p0.Y*m[2]); // Dx m[5] = TOREAL(p0.Y - p0.X*m[1] - p0.Y*m[3]); // Dy *width = l1; *height = l2; return TRUE; } static GpStatus LinearGradientRectFromPoints( const GpPointF& point1, const GpPointF& point2, GpRectF & rect ) { // If the API specifies two coincident points, we // can't get information for the gradient, so we // fail the construction here. if( IsClosePointF(point1, point2) ) { return InvalidParameter; } // Compute the bounding rectangle of the two input points. rect.X = min(point1.X, point2.X); rect.Y = min(point1.Y, point2.Y); rect.Width = REALABS(point1.X-point2.X); rect.Height = REALABS(point1.Y-point2.Y); // eliminate degenerate rectangles when the two // input points form a horizontal or vertical line. // This is a very odd way of coercing a 1d linear gradient // into a rect gradient and avoiding later matrix computation error // when we try get an affine warp between this rectangle and a // reference rectangle. if( IsCloseReal(point1.X, point2.X) ) { rect.X -= rect.Height/2; rect.Width = rect.Height; } if( IsCloseReal(point1.Y, point2.Y) ) { rect.Y -= rect.Width/2; rect.Height = rect.Width; } return Ok; } static GpStatus CalcLinearGradientXform( REAL angle, BOOL isAngleScalable, const GpRectF& rect, GpMatrix& xform ) { GpPointF p0, dP1, dP2; angle = GpModF(angle, 360); INT zone; REALD deltaTheta; const REALD degreeToRadian = 3.1415926535897932/180; if(angle < 90) { zone = 0; deltaTheta = angle; } else if(angle < 180) { zone = 1; deltaTheta = 180 - angle; } else if(angle < 270) { zone = 2; deltaTheta = angle - 180; } else { zone = 3; deltaTheta = 360 - angle; } REALD s, c; deltaTheta *= degreeToRadian; s = sin(deltaTheta); c = cos(deltaTheta); // d0 is the distance between p0 and the starting corner of the // original rectangle. // d1 and d2 is the length of dP1 and dP2, respectively. REALD top, left, w, h, d0, d1, d2; REALD x0, y0; // Starting corner of the original rectangle. GpPointD norm; // Direction of dP1. // Direction of dP2 = (-norm.Y, norm.X) which is 90 degree rotation // of dP1. if(!isAngleScalable) { left = rect.X; top = rect.Y; w = rect.Width; h = rect.Height; } else { // Scale to (0, 0, 1, 1) rectangle. top = 0.0; left = 0.0; w = 1.0; h = 1.0; } switch(zone) { case 0: d0 = w*s; norm.X = c; norm.Y = s; x0 = left; y0 = top; break; case 1: d0 = h*c; norm.X = - c; norm.Y = s; x0 = left + w; y0 = top; break; case 2: d0 = w*s; norm.X = - c; norm.Y = - s; x0 = left + w; y0 = top + h; break; case 3: d0 = h*c; norm.X = c; norm.Y = - s; x0 = left; y0 = top + h; break; } d2 = w*s + h*c; d1 = w*c + h*s; p0.X = TOREAL(x0 + d0*norm.Y); p0.Y = TOREAL(y0 - d0*norm.X); dP1.X = TOREAL(d1*norm.X); dP1.Y = TOREAL(d1*norm.Y); dP2.X = TOREAL(- d2*norm.Y); dP2.Y = TOREAL(d2*norm.X); if(isAngleScalable) { // Scale back. p0.X = rect.Width*p0.X + rect.X; p0.Y = rect.Height*p0.Y + rect.Y; dP1.X *= rect.Width; dP1.Y *= rect.Height; dP2.X *= rect.Width; dP2.Y *= rect.Height; } // Set up the transform. GpPointF points[3]; points[0] = p0; points[1].X = p0.X + dP1.X; points[1].Y = p0.Y + dP1.Y; points[2].X = p0.X + dP2.X; points[2].Y = p0.Y + dP2.Y; GpStatus status; if(xform.InferAffineMatrix(&points[0], rect) == Ok) { return Ok; } return InvalidParameter; } GpStatus GpLineGradient::ChangeLinePoints( const GpPointF& point1, const GpPointF& point2, BOOL isAngleScalable ) { GpStatus status; GpRectF rect; if ((status = LinearGradientRectFromPoints(point1, point2, rect)) != Ok) { return status; } REAL angle = GetAngleFromPoints(point1, point2); GpMatrix xform; if ((status = CalcLinearGradientXform(angle, isAngleScalable, rect, xform)) == Ok) { DeviceBrush.Xform = xform; DeviceBrush.Rect = rect; DeviceBrush.IsAngleScalable = isAngleScalable; DeviceBrush.Points[0] = point1; DeviceBrush.Points[1] = point2; return Ok; } return status; } GpLineGradient::GpLineGradient( const GpPointF& point1, const GpPointF& point2, const GpColor& color1, const GpColor& color2, GpWrapMode wrapMode ) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); REAL angle; GpRectF rect; if (LinearGradientRectFromPoints(point1, point2, rect) != Ok) { SetValid(FALSE); return; } // Compute the angle of the line formed by point1 and point2. // Note atan2 is only undefined if dP.Y == 0.0 and dP.X == 0.0 // and then it returns 0 radians. We take care of that case separately // (above). // Also, atan2 correctly computes the quadrant from the two input points. GpPointF dP = point2 - point1; double rad = atan2((double)(dP.Y), (double)(dP.X)); SetLineGradient( point1, point2, rect, color1, color2, // why aren't we working in radians??? (REAL)(rad*180.0/3.1415926535897932), FALSE, wrapMode ); } /**************************************************************************\ * * Function Description: * * Creates a LineGradient which is defined by the rectangle. * * Arguments: * * [IN] rect - the rectangle to define this gradient. * [IN] color1 - the color of the start point. * [IN] color2 - the color of the end point. * [IN] mode - the line gradient mode * [IN] wrapMode - the wrap mode of this brush. * * The start and end points of this gradient is defined as follows * according to the line gradient mode: * * mode start point end point * ------------------------------------------------------------- * LineGradientHorizontal top-left top-right * LineGradientVertical top-right bottom-right * LineGradientForwardDiagonal top-left bottom-right * LineGradientBackwardDiagonal bottom-left top-right * * * History: * * 06/03/1999 ikkof * Created it. * \**************************************************************************/ GpLineGradient::GpLineGradient( const GpRectF& rect, const GpColor& color1, const GpColor& color2, LinearGradientMode mode, GpWrapMode wrapMode ) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); BOOL isAngleScalable = TRUE; REAL angle = 0; GpPointF point1; GpPointF point2; switch(mode) { case LinearGradientModeHorizontal: angle = 0; point1.X = rect.X; point1.Y = (rect.Y + rect.GetBottom()) / 2.0f; point2.X = rect.GetRight(); point2.Y = point1.Y; break; case LinearGradientModeVertical: angle = 90; point1.X = (rect.X + rect.GetRight()) / 2.0f; point1.Y = rect.Y; point2.X = point1.X; point2.Y = rect.GetBottom(); break; case LinearGradientModeForwardDiagonal: angle = 45; point1.X = rect.X; point1.Y = rect.Y; point2.X = rect.GetRight(); point2.Y = rect.GetBottom(); break; case LinearGradientModeBackwardDiagonal: angle = 135; point1.X = rect.GetRight(); point1.Y = rect.Y; point2.X = rect.X; point2.Y = rect.GetBottom(); break; default: // No such a case. ASSERT(0); SetValid(FALSE); return; } SetLineGradient( point1, point2, rect, color1, color2, angle, isAngleScalable, wrapMode); } /**************************************************************************\ * * Function Description: * * Creates a LineGradient which is defined by the rectangle. * * Arguments: * * [IN] rect - the rectangle to define this gradient. * [IN] color1 - the color of the start point. * [IN] color2 - the color of the end point. * [IN] angle - the angle of the gradient * [IN] isAngleScalable - TRUE if 45 degree is corner to corner. * The default value is FALSE. * [IN] wrapMode - the wrap mode of this brush. * * * * History: * * 10/06/1999 ikkof * Created it. * \**************************************************************************/ GpLineGradient::GpLineGradient( const GpRectF& rect, const GpColor& color1, const GpColor& color2, REAL angle, BOOL isAngleScalable, GpWrapMode wrapMode ) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); GpPointF point1; GpPointF point2; // Not an Office scenario, but need to fix at some point // so we can print to PCL better. point1.X = point1.Y = point2.X = point2.Y = 0; SetLineGradient( point1, point2, rect, color1, color2, angle, isAngleScalable, wrapMode); } GpStatus GpLineGradient::SetLineGradient( const GpPointF& point1, const GpPointF& point2, const GpRectF& rect, const GpColor& color1, const GpColor& color2, REAL angle, BOOL isAngleScalable, GpWrapMode wrapMode ) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); DeviceBrush.Wrap = wrapMode; DeviceBrush.Colors[0] = color1; DeviceBrush.Colors[1] = color2; DeviceBrush.Colors[2] = color1; DeviceBrush.Colors[3] = color2; DeviceBrush.BlendCounts[0] = DeviceBrush.BlendCounts[1] = 1; DeviceBrush.BlendFactors[0] = DeviceBrush.BlendFactors[1] = NULL; DeviceBrush.Falloffs[0] = DeviceBrush.Falloffs[1] = 1; GpStatus status; if (CalcLinearGradientXform(angle, isAngleScalable, rect, DeviceBrush.Xform) == Ok) { SetValid(TRUE); DeviceBrush.Rect = rect; DeviceBrush.IsAngleScalable = isAngleScalable; DeviceBrush.Points[0] = point1; DeviceBrush.Points[1] = point2; status = Ok; } else { SetValid(FALSE); GpMemset(&DeviceBrush.Rect, 0, sizeof(DeviceBrush.Rect)); GpMemset(DeviceBrush.Points, 0, sizeof(DeviceBrush.Points[0]) * 2); DeviceBrush.IsAngleScalable = FALSE; status = InvalidParameter; } return status; } GpStatus GpLineGradient::SetLinePoints( const GpPointF& point1, const GpPointF& point2 ) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); GpPointF p0, dP1, dP2; p0 = point1; dP1.X = point2.X - point1.X; dP1.Y = point2.Y - point1.Y; dP2.X = - dP1.Y; dP2.Y = dP1.X; REAL m[6]; REAL width, height; if(getLineGradientTransform(&m[0], &width, &height, p0, dP1, dP2)) { SetValid(TRUE); DeviceBrush.Rect.X = p0.X; DeviceBrush.Rect.Y = p0.Y; DeviceBrush.Rect.Width = width; DeviceBrush.Rect.Height = height; } else { // Don't change the current state. return GenericError; } DeviceBrush.Xform.SetMatrix(m); UpdateUid(); return Ok; } GpStatus GpLineGradient::GetLinePoints(GpPointF* points) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); ASSERT(points); points[0].X = DeviceBrush.Rect.X; points[0].Y = DeviceBrush.Rect.Y; points[1].X = DeviceBrush.Rect.X + DeviceBrush.Rect.Width; points[1].Y = DeviceBrush.Rect.Y + DeviceBrush.Rect.Height; DeviceBrush.Xform.Transform(points, 2); return Ok; } INT GpLineGradient::GetPresetBlendCount() { if(DeviceBrush.UsesPresetColors) return DeviceBrush.BlendCounts[0]; else return 0; } /* ** This returns the premultiplied colors */ GpStatus GpLineGradient::GetPresetBlend( GpColor* blendColors, REAL* blendPositions, INT count) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); if(!blendColors || !blendPositions || count <= 1) return InvalidParameter; if(DeviceBrush.UsesPresetColors && DeviceBrush.PresetColors && DeviceBrush.BlendPositions[0]) { for(INT i = 0; i < count; i++) { blendColors[i].SetColor(DeviceBrush.PresetColors[i]); } GpMemcpy(blendPositions, DeviceBrush.BlendPositions[0], count*sizeof(REAL)); return Ok; } else return GenericError; } GpStatus GpLineGradient::SetPresetBlend( const GpColor* blendColors, const REAL* blendPositions, INT count) { // Make sure the flat API has correctly set the FPU. FPUStateSaver::AssertMode(); if(!blendColors || !blendPositions || count <= 1) return InvalidParameter; ARGB* newColors = (ARGB*) GpRealloc(DeviceBrush.PresetColors, count*sizeof(ARGB)); if (newColors != NULL) { DeviceBrush.PresetColors = newColors; } else { return OutOfMemory; } REAL* newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], count*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[0] = newPositions; } else { return OutOfMemory; } GpFree(DeviceBrush.BlendFactors[0]); // DeviceBrush.BlendFactors[1] is always NULL for LineGradient. DeviceBrush.BlendFactors[0] = NULL; DeviceBrush.UsesPresetColors = TRUE; for(INT i = 0; i < count; i++) { newColors[i] = blendColors[i].GetValue(); } GpMemcpy(newPositions, blendPositions, count*sizeof(REAL)); DeviceBrush.BlendCounts[0] = count; UpdateUid(); return Ok; } /**************************************************************************\ * * Function Description: * * Blend any transparent colors in this brush with white. Note that * colors are premultiplied, since they will become fully opaque. * * Arguments: * * Return Value: * * GpStatus - Ok or failure status * \**************************************************************************/ GpStatus GpLineGradient::BlendWithWhite() { if (DeviceBrush.UsesPresetColors) { GpColor color; for (INT i=0; i (DeviceBrush.Path); if(path) result = path->IsRectangle(NULL); } return result; } INT GpPathGradient::GetPresetBlendCount() const { if(DeviceBrush.UsesPresetColors) return DeviceBrush.BlendCounts[0]; else return 0; } /* ** This returns the premultiplied colors */ GpStatus GpPathGradient::GetPresetBlend( GpColor* blendColors, REAL* blendPositions, INT count) const { if(!blendColors || !blendPositions || count <= 1) return InvalidParameter; if(DeviceBrush.UsesPresetColors && DeviceBrush.PresetColors && DeviceBrush.BlendPositions[0]) { // Users will obtain the preset colors as radial blend colors. // 0 position means the center location and 1 position means the // the outer edge. In order to convert those colors and position arrays // from the weight factor arrays in PathGradient, // we must invert the order of the returned arrays. for(INT i = 0; i < count; i++) { blendColors[count - 1 -i].SetColor(DeviceBrush.PresetColors[i]); blendPositions[count - 1 -i] = TOREAL(1.0 - DeviceBrush.BlendPositions[0][i]); } return Ok; } else return GenericError; } GpStatus GpPathGradient::SetPresetBlend( const GpColor* blendColors, const REAL* blendPositions, INT count) { if(!blendColors || !blendPositions || count <= 1) return InvalidParameter; ARGB* newColors = (ARGB*) GpRealloc(DeviceBrush.PresetColors, count*sizeof(ARGB)); if (newColors != NULL) { DeviceBrush.PresetColors = newColors; } else { return OutOfMemory; } REAL* newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], count*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[0] = newPositions; } else { return OutOfMemory; } GpFree(DeviceBrush.BlendFactors[0]); DeviceBrush.BlendFactors[0] = NULL; DeviceBrush.UsesPresetColors = TRUE; // Users will supply the preset colors as radial blend colors. // 0 position means the center location and 1 position means the // the outer edge. In order to convert those colors and position arrays // to the weight factor arrays in PathGradient, // we must invert the order of the given arrays. for(INT i = 0; i < count; i++) { // PresetColors are stored non-premultiplied. newColors[count - 1 - i] = blendColors[i].GetValue(); newPositions[count - 1 - i] = TOREAL(1.0 - blendPositions[i]); } DeviceBrush.BlendCounts[0] = count; UpdateUid(); return Ok; } //================================================================== // Copy constructors //================================================================== GpElementaryBrush::GpElementaryBrush(const GpElementaryBrush *brush) { if(brush && brush->IsValid()) { // !!! [asecchia] we should really be asking the DeviceBrush to // copy it's members instead of duplicating the code all over // the place. Current code is error prone - each subclass is has to // know all about how to copy and what has or hasn't been updated on // the chain down to it's ancestor. DeviceBrush.Xform = brush->DeviceBrush.Xform; DeviceBrush.Wrap = brush->DeviceBrush.Wrap; DeviceBrush.IsGammaCorrected = brush->DeviceBrush.IsGammaCorrected; SetValid(brush->IsValid()); } else SetValid(FALSE); } GpTexture::GpTexture( const GpTexture *brush ) : GpElementaryBrush(brush) { if(brush && brush->IsValid()) { const DpBrush* devBrush = &(brush->DeviceBrush); InitializeBrush(brush->Image, devBrush->Wrap, NULL); SetTransform(devBrush->Xform); } else SetValid(FALSE); } GpRectGradient::GpRectGradient( const GpRectGradient *brush ) : GpGradientBrush(brush) { if(brush && brush->IsValid()) { const DpBrush* devBrush = &(brush->DeviceBrush); InitializeBrush( devBrush->Rect, &(devBrush->Colors[0]), devBrush->Wrap ); SetTransform(devBrush->Xform); SetHorizontalBlend( devBrush->BlendFactors[0], devBrush->BlendPositions[0], devBrush->BlendCounts[0] ); SetVerticalBlend( devBrush->BlendFactors[1], devBrush->BlendPositions[1], devBrush->BlendCounts[1] ); } else SetValid(FALSE); } GpLineGradient::GpLineGradient( const GpLineGradient *brush ) : GpRectGradient(brush) { if(brush && brush->IsValid()) { // Copy the preset colors. // !!! [asecchia] why isn't this handled in a uniform way? const DpBrush* devBrush = &(brush->DeviceBrush); DeviceBrush.Points[0] = devBrush->Points[0]; DeviceBrush.Points[1] = devBrush->Points[1]; DeviceBrush.IsAngleScalable = devBrush->IsAngleScalable; if(devBrush->UsesPresetColors) { SetPresetBlend( (GpColor*)(devBrush->PresetColors), (REAL*)(devBrush->BlendPositions[0]), devBrush->BlendCounts[0] ); } } } GpPathGradient::GpPathGradient( const GpPathGradient *brush ) : GpGradientBrush(brush) { if(brush && brush->IsValid()) { const DpBrush* devBrush = &(brush->DeviceBrush); // If a path exists for the brush, use that for initialization. // Otherwise, use the points collection. if (devBrush->Path != NULL) { DefaultBrush(); DeviceBrush.Wrap = devBrush->Wrap; DeviceBrush.Path = devBrush->Path->ClonePath(); PrepareBrush(); } else { InitializeBrush(devBrush->PointsPtr, devBrush->Count, devBrush->Wrap); } if(IsValid()) { SetTransform(devBrush->Xform); SetCenterPoint(devBrush->Points[0]); SetCenterColor(devBrush->Colors[0]); SetSurroundColors(devBrush->ColorsPtr); DeviceBrush.Falloffs[0] = devBrush->Falloffs[0]; DeviceBrush.FocusScaleX = devBrush->FocusScaleX; DeviceBrush.FocusScaleY = devBrush->FocusScaleY; DeviceBrush.UsesPresetColors = devBrush->UsesPresetColors; INT blendCount = devBrush->BlendCounts[0]; DeviceBrush.BlendCounts[0] = blendCount; // If we're cloning a brush with preset colors, copy preset colors // and blend positions. Otherwise, copy the blend factors and // blend positions. if (devBrush->UsesPresetColors) { ARGB* newColors = (ARGB*) GpRealloc(DeviceBrush.PresetColors, blendCount*sizeof(ARGB)); if (newColors != NULL) { DeviceBrush.PresetColors = newColors; REAL* newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], blendCount*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[0] = newPositions; GpFree(DeviceBrush.BlendFactors[0]); DeviceBrush.BlendFactors[0] = NULL; memcpy(DeviceBrush.PresetColors, devBrush->PresetColors, blendCount*sizeof(ARGB)); memcpy(DeviceBrush.BlendPositions[0], devBrush->BlendPositions[0], blendCount*sizeof(REAL)); } else { SetValid(FALSE); } } else { SetValid(FALSE); } } else if (devBrush->BlendFactors[0] && devBrush->BlendPositions[0]) { REAL* newFactors = (REAL*) GpRealloc(DeviceBrush.BlendFactors[0], blendCount*sizeof(REAL)); if (newFactors != NULL) { DeviceBrush.BlendFactors[0] = newFactors; REAL* newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], blendCount*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[0] = newPositions; memcpy(DeviceBrush.BlendFactors[0], devBrush->BlendFactors[0], blendCount*sizeof(REAL)); memcpy(DeviceBrush.BlendPositions[0], devBrush->BlendPositions[0], blendCount*sizeof(REAL)); } else { SetValid(FALSE); } } else { SetValid(FALSE); } } } } else SetValid(FALSE); } GpHatch::GpHatch(const GpHatch* brush) { if(brush && brush->IsValid()) { const DpBrush* devBrush = &(brush->DeviceBrush); InitializeBrush(devBrush->Style, devBrush->Colors[0], devBrush->Colors[1]); } else SetValid(FALSE); } /**************************************************************************\ * * Function Description: * * Getting horizontal falloff / blend-factors for * a rectangular gradient brush object * * Arguments: * * [OUT] blendFactors - Buffer for returning the horizontal * falloff or blend-factors. * count - Size of the buffer (in number of REAL elements) * * Return Value: * * Status code * \**************************************************************************/ GpStatus GpRectGradient::GetHorizontalBlend( REAL* blendFactors, REAL* blendPositions, INT count ) { if(!blendFactors || !blendPositions || count < 1) return InvalidParameter; // Check if the input buffer is big enough if (count < DeviceBrush.BlendCounts[0]) return InsufficientBuffer; if (DeviceBrush.BlendCounts[0] == 1) { // Return falloff parameter blendFactors[0] = DeviceBrush.Falloffs[0]; } else { // Return blend factors GpMemcpy( blendFactors, DeviceBrush.BlendFactors[0], DeviceBrush.BlendCounts[0]*sizeof(REAL) ); GpMemcpy( blendPositions, DeviceBrush.BlendPositions[0], DeviceBrush.BlendCounts[0]*sizeof(REAL) ); } return Ok; } /**************************************************************************\ * * Function Description: * * Setting horizontal falloff / blend-factors for * a rectangular gradient brush object * * Arguments: * * [IN] blendFactors - Specify the new blend factors * count - Number of elements in the blend factor array * * Return Value: * * Status code * \**************************************************************************/ GpStatus GpRectGradient::SetHorizontalBlend( const REAL* blendFactors, const REAL* blendPositions, INT count ) { if(!blendFactors || !blendPositions || count < 1) return InvalidParameter; if (count == 1) { // Setting falloff parameter GpFree(DeviceBrush.BlendFactors[0]); DeviceBrush.BlendFactors[0] = NULL; GpFree(DeviceBrush.BlendPositions[0]); DeviceBrush.BlendPositions[0] = NULL; if (blendFactors == NULL) DeviceBrush.Falloffs[0] = 1; else DeviceBrush.Falloffs[0] = blendFactors[0]; DeviceBrush.BlendCounts[0] = 1; } else { ASSERT(blendFactors != NULL && blendPositions != NULL); // blend positions must start at 0.0 and end at 1.0 if (REALABS(blendPositions[0]) > REAL_EPSILON || REALABS(1.0f - blendPositions[count-1]) > REAL_EPSILON) { return InvalidParameter; } // Setting blend factors REAL* newFactors; REAL* newPositions; newFactors = (REAL*) GpRealloc(DeviceBrush.BlendFactors[0], count*sizeof(REAL)); if (newFactors != NULL) { DeviceBrush.BlendFactors[0] = newFactors; } else { return OutOfMemory; } newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], count*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[0] = newPositions; } else { return OutOfMemory; } if (newFactors == NULL || newPositions == NULL) return OutOfMemory; GpMemcpy(newFactors, blendFactors, count*sizeof(REAL)); GpMemcpy(newPositions, blendPositions, count*sizeof(REAL)); DeviceBrush.BlendCounts[0] = count; } DeviceBrush.UsesPresetColors = FALSE; GpFree(DeviceBrush.PresetColors); DeviceBrush.PresetColors = NULL; UpdateUid(); return Ok; } /**************************************************************************\ * * Function Description: * * Getting vertical falloff / blend-factors for * a rectangular gradient brush object * * Arguments: * * [OUT] blendFactors - Buffer for returning the vertical * falloff or blend-factors. * count - Size of the buffer (in number of REAL elements) * * Return Value: * * Status code * \**************************************************************************/ GpStatus GpRectGradient::GetVerticalBlend( REAL* blendFactors, REAL* blendPositions, INT count ) { if(!blendFactors || !blendPositions || count < 1) return InvalidParameter; // Check if the input buffer is big enough if (count < DeviceBrush.BlendCounts[1]) return InsufficientBuffer; if (DeviceBrush.BlendCounts[1] == 1) { // Return falloff parameter blendFactors[0] = DeviceBrush.Falloffs[1]; } else { // Return blend factors GpMemcpy( blendFactors, DeviceBrush.BlendFactors[1], DeviceBrush.BlendCounts[1]*sizeof(REAL)); GpMemcpy( blendPositions, DeviceBrush.BlendPositions[1], DeviceBrush.BlendCounts[1]*sizeof(REAL)); } return Ok; } /**************************************************************************\ * * Function Description: * * Setting vertical falloff / blend-factors for * a rectangular gradient brush object * * Arguments: * * [IN] blendFactors - Specify the new blend factors * count - Number of elements in the blend factor array * * Return Value: * * Status code * \**************************************************************************/ GpStatus GpRectGradient::SetVerticalBlend( const REAL* blendFactors, const REAL* blendPositions, INT count ) { if(!blendFactors || !blendPositions || count < 1) return InvalidParameter; if (count == 1) { // Setting falloff parameter GpFree(DeviceBrush.BlendFactors[1]); DeviceBrush.BlendFactors[1] = NULL; GpFree(DeviceBrush.BlendPositions[1]); DeviceBrush.BlendPositions[1] = NULL; if (blendFactors == NULL) DeviceBrush.Falloffs[1] = 1; else DeviceBrush.Falloffs[1] = blendFactors[0]; DeviceBrush.BlendCounts[1] = 1; } else { ASSERT(blendFactors != NULL && blendPositions != NULL); // Setting blend factors REAL* newFactors; REAL* newPositions; newFactors = (REAL*) GpRealloc(DeviceBrush.BlendFactors[1], count*sizeof(REAL)); if (newFactors != NULL) { DeviceBrush.BlendFactors[1] = newFactors; } else { return OutOfMemory; } newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[1], count*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[1] = newPositions; } else { return OutOfMemory; } GpMemcpy(newFactors, blendFactors, count*sizeof(REAL)); GpMemcpy(newPositions, blendPositions, count*sizeof(REAL)); DeviceBrush.BlendCounts[1] = count; } DeviceBrush.UsesPresetColors = FALSE; GpFree(DeviceBrush.PresetColors); DeviceBrush.PresetColors = NULL; UpdateUid(); return Ok; } /**************************************************************************\ * * Function Description: * * Blend any transparent colors in this brush with white. Note that colors * are converted to premultiplied first, since they will become fully opaque. * * Arguments: * * Return Value: * * GpStatus - Ok or failure status * \**************************************************************************/ GpStatus GpRectGradient::BlendWithWhite() { GpColor color; for (INT i=0; i<4; i++) { color.SetValue(DeviceBrush.Colors[i].GetPremultipliedValue()); color.BlendOpaqueWithWhite(); DeviceBrush.Colors[i] = color.GetValue(); } return Ok; } //-------------------------------------------------------------------------- // Path Gradient //-------------------------------------------------------------------------- VOID GpPathGradient::PrepareBrush() { GpPath* path = static_cast (DeviceBrush.Path); if (path) { DeviceBrush.Count = path->Points.GetCount(); GpPointF* points = path->Points.GetDataBuffer(); if(!DeviceBrush.ColorsPtr) { DeviceBrush.ColorsPtr = (GpColor*)GpMalloc(DeviceBrush.Count*sizeof(GpColor)); if (DeviceBrush.ColorsPtr != NULL) GpMemset(&DeviceBrush.ColorsPtr[0], 255, DeviceBrush.Count*sizeof(GpColor)); } REAL xmin, xmax, ymin, ymax, x0, y0; x0 = xmin = xmax = points[0].X; y0 = ymin = ymax = points[0].Y; for(INT i = 1; i < DeviceBrush.Count; i++) { x0 += points[i].X; y0 += points[i].Y; xmin = min(xmin, points[i].X); xmax = max(xmax, points[i].X); ymin = min(ymin, points[i].Y); ymax = max(ymax, points[i].Y); } DeviceBrush.Rect.X = xmin; DeviceBrush.Rect.Width = xmax - xmin; DeviceBrush.Rect.Y = ymin; DeviceBrush.Rect.Height = ymax - ymin; if(!WrapModeIsValid(DeviceBrush.Wrap) || DeviceBrush.Rect.Width <= 0 || DeviceBrush.Rect.Height <= 0) return; DeviceBrush.Points[0].X = x0/DeviceBrush.Count; DeviceBrush.Points[0].Y = y0/DeviceBrush.Count; SetValid(TRUE); } } GpStatus GpPathGradient::Flatten(GpMatrix* matrix) const { GpPath* path = static_cast (DeviceBrush.Path); if(!path) return Ok; if(path->HasCurve()) { INT origCount = DeviceBrush.Count; GpStatus status = path->Flatten( const_cast(&FlattenTypes), const_cast (&FlattenPoints), matrix); if(status == Ok) { DeviceBrush.Count = FlattenPoints.GetCount(); DeviceBrush.PointsPtr = FlattenPoints.GetDataBuffer(); if ((DeviceBrush.Count > origCount) && (DeviceBrush.ColorsPtr != NULL)) { // The colors array is no longer the proper size. Adjust the // size and copy up the last color. It is the apps responsibility // to estimate and specify the correct number of flattened points. const_cast(DeviceBrush.ColorsPtr) = (GpColor*) GpRealloc((VOID*)DeviceBrush.ColorsPtr, sizeof(GpColor)*DeviceBrush.Count); if (DeviceBrush.ColorsPtr != NULL) { GpColor copyColor = (origCount > 1) ? DeviceBrush.ColorsPtr[origCount-1] : GpColor(0xFFFFFFFF); for (INT i=origCount; iGetPointCount(); DeviceBrush.PointsPtr = const_cast (path->GetPathPoints()); } return Ok; } GpStatus GpPathGradient::GetBlend( REAL* blendFactors, REAL* blendPositions, INT count ) const { if(!blendFactors || !blendPositions || count < 1) return InvalidParameter; // Check if the input buffer is big enough if (count < DeviceBrush.BlendCounts[0]) return InsufficientBuffer; if (DeviceBrush.BlendCounts[0] == 1) { // Return falloff parameter blendFactors[0] = DeviceBrush.Falloffs[0]; } else { // Return blend factors // Users want to obtain the blend factor as radial blend factors. // 0 blend factor means 100 % center color and 0 position means // the center location. In order to return those factor and // position arrays, we must invert the weight and position factor // arrays stored in this PathGradient class. for(INT i = 0; i < DeviceBrush.BlendCounts[0]; i++) { blendFactors[DeviceBrush.BlendCounts[0] - 1 - i] = TOREAL(1.0 - DeviceBrush.BlendFactors[0][i]); blendPositions[DeviceBrush.BlendCounts[0] - 1 - i] = TOREAL(1.0 - DeviceBrush.BlendPositions[0][i]); } } return Ok; } GpStatus GpPathGradient::SetBlend( const REAL* blendFactors, const REAL* blendPositions, INT count ) { if(!blendFactors || !blendPositions || count < 1) return InvalidParameter; if (count == 1) { // Setting falloff parameter GpFree(DeviceBrush.BlendFactors[0]); DeviceBrush.BlendFactors[0] = NULL; GpFree(DeviceBrush.BlendPositions[0]); DeviceBrush.BlendPositions[0] = NULL; if (blendFactors == NULL) DeviceBrush.Falloffs[0] = 1; else DeviceBrush.Falloffs[0] = blendFactors[0]; DeviceBrush.BlendCounts[0] = 1; } else { // blend positions must start at 0.0 and end at 1.0 if (REALABS(blendPositions[0]) > REAL_EPSILON || REALABS(1.0f - blendPositions[count-1]) > REAL_EPSILON) { return InvalidParameter; } // Setting blend factors REAL* newFactors; REAL* newPositions; newFactors = (REAL*) GpRealloc(DeviceBrush.BlendFactors[0], count*sizeof(REAL)); if (newFactors != NULL) { DeviceBrush.BlendFactors[0] = newFactors; } else { return OutOfMemory; } newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], count*sizeof(REAL)); if (newPositions != NULL) { DeviceBrush.BlendPositions[0] = newPositions; } else { return OutOfMemory; } // Users will supply the blend factor as radial blend factors. // 0 blend factor means 100 % center color and 0 position means // the center location. In order to convert those factor and position arrays // to the weight and position factor arrays in PathGradient, // we must invert the given arrays. for(INT i = 0; i < count; i++) { newFactors[count - 1 - i] = TOREAL(1.0 - blendFactors[i]); newPositions[count - 1 - i] = TOREAL(1.0 - blendPositions[i]); } DeviceBrush.BlendCounts[0] = count; } DeviceBrush.UsesPresetColors = FALSE; GpFree(DeviceBrush.PresetColors); DeviceBrush.PresetColors = NULL; UpdateUid(); return Ok; } GpStatus GpGradientBrush::GetSigmaBlendArray( REAL focus, REAL scale, INT* count, REAL* blendFactors, REAL* blendPositions) { // Make sure the FPU is set correctly. FPUStateSaver::AssertMode(); if(!blendFactors || !blendPositions || !count) return InvalidParameter; // This gives 1/4 of the Sigma array. static REAL factors[] = { 0, 59, 120, 182, 247, 314, 383, 454, 527, 602, 680, 759, 841, 926, 1013, 1102, 1194, 1288, 1385, 1485, 1587, 1692, 1800, 1911, 2024, 2141, 2260, 2383, 2508, 2637, 2769, 2904, 3042, 3183, 3328, 3477, 3628, 3783, 3942, 4104, 4270, 4439, 4612, 4789, 4969, 5153, 5341, 5533, 5728, 5928, 6131, 6338, 6549, 6764, 6983, 7206, 7434, 7665, 7900, 8139, 8382, 8630, 8881, 9136, 9396, 9660, 9927, 10199, 10475, 10755, 11039, 11327, 11619, 11916, 12216, 12520, 12828, 13140, 13456, 13776, 14099, 14427, 14758, 15093, 15431, 15774, 16119, 16469, 16822, 17178, 17538, 17901, 18267, 18637, 19009, 19385, 19764, 20146, 20530, 20918, 21308, 21701, 22096, 22494, 22894, 23297, 23702, 24109, 24518, 24929, 25342, 25756, 26173, 26591, 27010, 27431, 27853, 28276, 28701, 29126, 29552, 29979, 30407, 30836, 31264, 31694, 32123, 32553 }; if(focus < 0 || focus > 1 || scale < 0 || scale > 1) return InvalidParameter; if(blendFactors && blendPositions) { INT i, n; scale /= 65536; REAL one = 65536; if(focus > 0 && focus < 1) { for(i = 0; i < 128; i++) { blendFactors[i] = factors[i]; blendPositions[i] = focus*i/255; } for(i = 128; i < 256; i++) { blendFactors[i] = one - factors[255 - i]; blendPositions[i] = focus*i/255; } // skip i = 256 since this gives the same data. for(i = 257; i < 384; i++) { blendFactors[i - 1] = one - factors[i - 256]; blendPositions[i - 1] = TOREAL(focus + (1.0 - focus)*(i - 256)/255); } for(i = 384; i < 512; i++) { blendFactors[i - 1] = factors[511 - i]; blendPositions[i - 1] = TOREAL(focus + (1.0 - focus)*(i - 256)/255); } // Set n to 511 because we skipped index 256 above to avoid // the duplicate 1 entry in the ramp from 0 to 1 to 0. n = 511; } else if(focus == 1) { for(i = 0; i < 128; i++) { blendFactors[i] = factors[i]; blendPositions[i] = TOREAL(i)/255; } for(i = 128; i < 256; i++) { blendFactors[i] = one - factors[255 - i]; blendPositions[i] = TOREAL(i)/255; } n = 256; } else // focus == 0 { for(i = 256; i < 384; i++) { blendFactors[i - 256] = one - factors[i - 256]; blendPositions[i - 256] = TOREAL(i - 256)/255; } for(i = 384; i < 512; i++) { blendFactors[i - 256] = factors[511 - i]; blendPositions[i - 256] = TOREAL(i - 256)/255; } n = 256; } for(i = 0; i < n; i++) blendFactors[i] *= scale; *count = n; return Ok; } else return InvalidParameter; } GpStatus GpGradientBrush::GetLinearBlendArray( REAL focus, REAL scale, INT* count, REAL* blendFactors, REAL* blendPositions) { if(!blendFactors || !blendPositions || !count) return InvalidParameter; if(focus < 0 || focus > 1 || scale < 0 || scale > 1) return InvalidParameter; if(blendFactors && blendPositions) { if(focus > 0 && focus < 1) { blendFactors[0] = 0.0f; blendFactors[1] = scale; blendFactors[2] = 0.0f; blendPositions[0] = 0.0f; blendPositions[1] = focus; blendPositions[2] = 1.0f; *count = 3; } else if(focus == 1) { blendFactors[0] = 0.0f; blendFactors[1] = scale; blendPositions[0] = 0.0f; blendPositions[1] = 1.0f; *count = 2; } else // focus == 0 { blendFactors[0] = scale; blendFactors[1] = 0.0f; blendPositions[0] = 0.0f; blendPositions[1] = 1.0f; *count = 2; } return Ok; } else return InvalidParameter; } GpStatus GpGradientBrush::SetSigmaBlend( REAL focus, REAL scale) { REAL* blendFactors = (REAL*) GpMalloc(512*sizeof(REAL)); REAL* blendPositions = (REAL*) GpMalloc(512*sizeof(REAL)); INT count; GpStatus status; if(blendFactors && blendPositions) { status = GetSigmaBlendArray(focus, scale, &count, blendFactors, blendPositions); if(status == Ok) status = SetBlend(&blendFactors[0], &blendPositions[0], count); } else status = OutOfMemory; GpFree(blendFactors); GpFree(blendPositions); return status; } GpStatus GpGradientBrush::SetLinearBlend( REAL focus, REAL scale) { REAL blendFactors[3]; REAL blendPositions[3]; INT count; GpStatus status = GetLinearBlendArray(focus, scale, &count, &blendFactors[0], &blendPositions[0]); if(status != Ok) return status; return SetBlend(&blendFactors[0], &blendPositions[0], count); } //-------------------------------------------------------------------------- // Hatch Brush //-------------------------------------------------------------------------- const BYTE GdipHatchPatterns8bpp[HatchStyleTotal][64] = { { // HatchStyleHorizontal, 0 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleVertical, 1 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleForwardDiagonal, 2 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, }, { // HatchStyleBackwardDiagonal, 3 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, }, { // HatchStyleCross, 4 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDiagonalCross 5 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0xff, 0x80, 0x00, 0x00, 0x80, 0xff, 0x80, 0x00, 0x80, 0xff, 0x80, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x80, 0xff, 0x80, 0x80, 0xff, 0x80, 0x00, 0x80, 0xff, 0x80, 0x00, 0x00, 0x80, 0xff, 0x80, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, }, { // HatchStyle05Percent, 6 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyle10Percent, 7 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyle20Percent, 8 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyle25Percent, 9 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, }, { // HatchStyle30Percent, 10 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, }, { // HatchStyle40Percent, 11 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, }, { // HatchStyle50Percent, 12 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, }, { // HatchStyle60Percent, 13 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, }, { // HatchStyle70Percent, 14 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, }, { // HatchStyle75Percent, 15 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, { // HatchStyle80Percent, 16 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, { // HatchStyle90Percent, 17 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, { // HatchStyleLightDownwardDiagonal, 18 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, }, { // HatchStyleLightUpwardDiagonal, 19 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleDarkDownwardDiagonal, 20 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, }, { // HatchStyleDarkUpwardDiagonal, 21 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, }, { // HatchStyleWideDownwardDiagonal, 22 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, }, { // HatchStyleWideUpwardDiagonal, 23 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, }, { // HatchStyleLightVertical, 24 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleLightHorizontal, 25 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleNarrowVertical, 26 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, }, { // HatchStyleNarrowHorizontal, 27 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDarkVertical, 28 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, }, { // HatchStyleDarkHorizontal, 29 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDashedDownwardDiagonal, 30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDashedUpwardDiagonal, 31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDashedHorizontal, 32 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDashedVertical, 33 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleSmallConfetti, 34 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, }, { // HatchStyleLargeConfetti, 35 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, }, { // HatchStyleZigZag, 36 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleWave, 37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDiagonalBrick, 38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, }, { // HatchStyleHorizontalBrick, 39 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleWeave, 40 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, }, { // HatchStylePlaid, 41 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDivot, 42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDottedGrid, 43 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleDottedDiamond, 44 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { // HatchStyleShingle, 45 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, }, { // HatchStyleTrellis, 46 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, }, { // HatchStyleSphere, 47 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleSmallGrid, 48 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, }, { // HatchStyleSmallCheckerBoard, 49 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, }, { // HatchStyleLargeCheckerBoard, 50 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, }, { // HatchStyleOutlinedDiamond, 51 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, }, { // HatchStyleSolidDiamond, 52 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }; VOID GpHatch::InitializeData() { if ((DeviceBrush.Style >= HatchStyleMin) && (DeviceBrush.Style <= HatchStyleMax)) { GpMemcpy(DeviceBrush.Data, GdipHatchPatterns8bpp[DeviceBrush.Style], 64); } else { WARNING1("Bad Hatch Style Value"); GpMemset(DeviceBrush.Data, 0x00, 64); // make it transparent } } /***************************************************************************\ * * Equivalence comparsion functions * \***************************************************************************/ /**************************************************************************\ * * Function Description: * * Answer TRUE if brush and the receiver are equivalent (i.e. - they will * render indentically) * * Arguments: * * [IN] brush - GpBrush, or subclass, to compare this against. * * Return Value: * * TRUE if equivalent * * Created - 5/28/99 peterost * \**************************************************************************/ BOOL GpHatch::IsEqual(const GpBrush * brush) const { if(!brush) return FALSE; if (brush == this) return TRUE; if (GpBrush::IsEqual(brush)) { const GpHatch * hbrush = static_cast(brush); return hbrush->DeviceBrush.Style == DeviceBrush.Style && hbrush->DeviceBrush.Colors[0].IsEqual(DeviceBrush.Colors[0]) && hbrush->DeviceBrush.Colors[1].IsEqual(DeviceBrush.Colors[1]); } else { return FALSE; } } /**************************************************************************\ * * Function Description: * * Answer TRUE if brush and the receiver are equivalent (i.e. - they will * render indentically). RectGradient brushes require all four colors and * blend factors to be equal. * * Arguments: * * [IN] brush - GpBrush, or subclass, to compare this against. * * Return Value: * * TRUE if equivalent * * Created - 5/28/99 peterost * \**************************************************************************/ BOOL GpRectGradient::IsEqual(const GpBrush * brush) const { if(!brush) return FALSE; if (brush == this) return TRUE; if (GpGradientBrush::IsEqual(brush)) { const GpRectGradient * rbrush = static_cast(brush); if (rbrush->DeviceBrush.UsesPresetColors == DeviceBrush.UsesPresetColors && rbrush->DeviceBrush.BlendCounts[0] == DeviceBrush.BlendCounts[0] && rbrush->DeviceBrush.BlendCounts[1] == DeviceBrush.BlendCounts[1]) { INT i; if (DeviceBrush.UsesPresetColors) { // For preset colors, only the horizontal blend variables are used. for (INT i=0; iDeviceBrush.PresetColors[i] != DeviceBrush.PresetColors[i] || rbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else { for (i=0; i<4; i++) { if (!rbrush->DeviceBrush.Colors[i].IsEqual(DeviceBrush.Colors[i])) return FALSE; } if (DeviceBrush.BlendCounts[0] > 1) { for (i=0; iDeviceBrush.BlendFactors[0][i] != DeviceBrush.BlendFactors[0][i] || rbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else if (rbrush->DeviceBrush.Falloffs[0] != DeviceBrush.Falloffs[0]) { return FALSE; } if (DeviceBrush.BlendCounts[1] > 1) { for (i=0; iDeviceBrush.BlendFactors[1][i] != DeviceBrush.BlendFactors[1][i] || rbrush->DeviceBrush.BlendPositions[1][i] != DeviceBrush.BlendPositions[1][i]) return FALSE; } } else if (rbrush->DeviceBrush.Falloffs[1] != DeviceBrush.Falloffs[1]) { return FALSE; } } return TRUE; } else { return FALSE; } } else { return FALSE; } } /**************************************************************************\ * * Function Description: * * Answer TRUE if brush and the receiver are equivalent (i.e. - they will * render indentically). * * Arguments: * * [IN] brush - GpBrush, or subclass, to compare this against. * * Return Value: * * TRUE if equivalent * * Created - 6/2/99 peterost * \**************************************************************************/ #if 0 BOOL GpRadialGradient::IsEqual(const GpBrush * brush) const { if(!brush) return FALSE; if (brush == this) return TRUE; if (GpGradientBrush::IsEqual(brush)) { const GpRadialGradient * rbrush = static_cast(brush); if (rbrush->DeviceBrush.UsesPresetColors == DeviceBrush.UsesPresetColors && rbrush->DeviceBrush.BlendCounts[0] == DeviceBrush.BlendCounts[0]) { if (DeviceBrush.UsesPresetColors) { for (INT i=0; iDeviceBrush.PresetColors[i] != DeviceBrush.PresetColors[i] || rbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else { if (rbrush->DeviceBrush.Colors[0].IsEqual(DeviceBrush.Colors[0]) && rbrush->DeviceBrush.Colors[1].IsEqual(DeviceBrush.Colors[1])) { if (DeviceBrush.BlendCounts[0] > 1) { for (INT i=0; iDeviceBrush.BlendFactors[0][i] != DeviceBrush.BlendFactors[0][i] || rbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else if (rbrush->DeviceBrush.Falloffs[0] != DeviceBrush.Falloffs[0]) { return FALSE; } } else { return FALSE; } } return TRUE; } else { return FALSE; } } else { return FALSE; } } /**************************************************************************\ * * Function Description: * * Answer TRUE if brush and the receiver are equivalent (i.e. - they will * render indentically). * * Arguments: * * [IN] brush - GpBrush, or subclass, to compare this against. * * Return Value: * * TRUE if equivalent * * Created - 6/7/99 peterost * \**************************************************************************/ BOOL GpTriangleGradient::IsEqual(const GpBrush * brush) const { if(!brush) return FALSE; if (brush == this) return TRUE; if (GpGradientBrush::IsEqual(brush)) { const GpTriangleGradient * tbrush = static_cast(brush); if (tbrush->DeviceBrush.BlendCounts[0] == DeviceBrush.BlendCounts[0] && tbrush->DeviceBrush.BlendCounts[1] == DeviceBrush.BlendCounts[1] && tbrush->DeviceBrush.BlendCounts[2] == DeviceBrush.BlendCounts[2] && tbrush->DeviceBrush.Rect.Equals(DeviceBrush.Rect)) { INT i; for (i=0; i<3; i++) { if (tbrush->DeviceBrush.Points[i].X != DeviceBrush.Points[i].X || tbrush->DeviceBrush.Points[i].Y != DeviceBrush.Points[i].Y || !(tbrush->DeviceBrush.Colors[i].IsEqual(DeviceBrush.Colors[i]))) return FALSE; } if (DeviceBrush.BlendCounts[0] > 1) { for (i=0; iDeviceBrush.BlendFactors[0][i] != DeviceBrush.BlendFactors[0][i] || tbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else if (tbrush->DeviceBrush.Falloffs[0] != DeviceBrush.Falloffs[0]) { return FALSE; } if (DeviceBrush.BlendCounts[1] > 1) { for (i=0; iDeviceBrush.BlendFactors[1][i] != DeviceBrush.BlendFactors[1][i] || tbrush->DeviceBrush.BlendPositions[1][i] != DeviceBrush.BlendPositions[1][i]) return FALSE; } } else if (tbrush->DeviceBrush.Falloffs[1] != DeviceBrush.Falloffs[1]) { return FALSE; } if (DeviceBrush.BlendCounts[2] > 1) { for (i=0; iDeviceBrush.BlendFactors[2][i] != DeviceBrush.BlendFactors[2][i] || tbrush->DeviceBrush.BlendPositions[2][i] != DeviceBrush.BlendPositions[2][i]) return FALSE; } } else if (tbrush->DeviceBrush.Falloffs[2] != DeviceBrush.Falloffs[2]) { return FALSE; } return TRUE; } else { return FALSE; } } else { return FALSE; } } #endif /**************************************************************************\ * * Function Description: * * Answer TRUE if brush and the receiver are equivalent (i.e. - they will * render indentically). * * Arguments: * * [IN] brush - GpBrush, or subclass, to compare this against. * * Return Value: * * TRUE if equivalent * * Created - 6/7/99 peterost * \**************************************************************************/ BOOL GpPathGradient::IsEqual(const GpBrush * brush) const { if(!brush) return FALSE; if (brush == this) return TRUE; if (GpGradientBrush::IsEqual(brush)) { const GpPathGradient * pbrush = static_cast(brush); if (pbrush->DeviceBrush.BlendCounts[0] == DeviceBrush.BlendCounts[0] && pbrush->DeviceBrush.Count == DeviceBrush.Count && pbrush->DeviceBrush.OneSurroundColor == DeviceBrush.OneSurroundColor && pbrush->DeviceBrush.UsesPresetColors == DeviceBrush.UsesPresetColors && pbrush->DeviceBrush.Points[0].X == DeviceBrush.Points[0].X && pbrush->DeviceBrush.Points[0].Y == DeviceBrush.Points[0].Y && pbrush->DeviceBrush.Rect.Equals(DeviceBrush.Rect) && pbrush->DeviceBrush.Colors[0].IsEqual(DeviceBrush.Colors[0]) ) { INT i; for (i=0; iDeviceBrush.PointsPtr[i].X != DeviceBrush.PointsPtr[i].X || pbrush->DeviceBrush.PointsPtr[i].Y != DeviceBrush.PointsPtr[i].Y || !(pbrush->DeviceBrush.ColorsPtr[i].IsEqual(DeviceBrush.ColorsPtr[i]))) return FALSE; } if (DeviceBrush.UsesPresetColors) { for (i=0; iDeviceBrush.PresetColors[i] != DeviceBrush.PresetColors[i] || pbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else { if (DeviceBrush.BlendCounts[0] > 1) { for (i=0; iDeviceBrush.BlendFactors[0][i] != DeviceBrush.BlendFactors[0][i] || pbrush->DeviceBrush.BlendPositions[0][i] != DeviceBrush.BlendPositions[0][i]) return FALSE; } } else if (pbrush->DeviceBrush.Falloffs[0] != DeviceBrush.Falloffs[0]) { return FALSE; } } } return TRUE; } else { return FALSE; } } DpOutputSpan* GpSolidFill::CreateOutputSpan( DpScanBuffer * scan, DpContext *context, const GpRect *drawBounds) { return new DpOutputSolidColorSpan( DeviceBrush.SolidColor.GetPremultipliedValue(), scan ); } DpOutputSpan* GpRectGradient::CreateOutputSpan( DpScanBuffer * scan, DpContext *context, const GpRect *drawBounds) { DpOutputSpan* span = NULL; ARGB argb[4]; for(INT i = 0; i < 4; i++) { argb[i] = DeviceBrush.Colors[i].GetValue(); } BOOL isHorizontal = FALSE; BOOL isVertical = FALSE; if(HasPresetColors() && DeviceBrush.BlendCounts[0] > 1) isHorizontal = TRUE; if(!isHorizontal && argb[0] == argb[2] && argb[1] == argb[3]) isHorizontal = TRUE; if(!isHorizontal && argb[0] == argb[1] && argb[2] == argb[3]) isVertical = TRUE; if(!isHorizontal && !isVertical) { span = new DpOutputGradientSpan(this, scan, context); } else { // !!![andrewgo] Not sure why a LinearGradient is coming down to us // as BrushRectGrad - if it comes down as a BrushTypeLinearGradient // (as it should) then we don't have to do any of the // above 'isHorizontal', 'isVertical' stuff FPUStateSaver fpuState; // Set the rounding mode. if ((GetBrushType() == BrushTypeLinearGradient) /*|| (GetBrushType() == BrushRectGrad)*/) { if (OSInfo::HasMMX) { span = new DpOutputLinearGradientSpan_MMX(this, scan, context); } else { span = new DpOutputLinearGradientSpan(this, scan, context); } } else { span = new DpOutputOneDGradientSpan(this, scan, context, isHorizontal, isVertical); } } if (span && !span->IsValid()) { delete span; span = NULL; } return span; } #if 0 DpOutputSpan* GpRadialGradient::CreateOutputSpan( DpScanBuffer * scan, DpContext *context, const GpRect *drawBounds) { return new DpOutputOneDGradientSpan( this, scan, context ); } DpOutputSpan* GpTriangleGradient::CreateOutputSpan( DpScanBuffer * scan, DpContext *context, const GpRect *drawBounds) { return new DpOutputTriangleGradientSpan( this, scan, context ); } #endif DpOutputSpan* GpPathGradient::CreateOutputSpan( DpScanBuffer * scan, DpContext *context, const GpRect *drawBounds) { FPUStateSaver::AssertMode(); DpOutputSpan* span = NULL; WrapMode wrap = DeviceBrush.Wrap; // Check to see if a tiled gradient is really needed. It // is not necessary if the transformed drawbounds fit // entirely within the bounds of the brush rectangle. if (drawBounds && wrap != WrapModeClamp) { GpMatrix inverseXForm = context->WorldToDevice; if (Ok == inverseXForm.Invert()) { GpRectF brushRect = DeviceBrush.Rect; GpRectF transformRect; TransformBounds( &inverseXForm, (REAL)drawBounds->GetLeft(), (REAL)drawBounds->GetTop(), (REAL)drawBounds->GetRight(), (REAL)drawBounds->GetBottom(), &transformRect ); if (brushRect.Contains(transformRect)) { wrap = WrapModeClamp; } } } if(wrap == WrapModeClamp) { if(!DeviceBrush.OneSurroundColor) { span = new DpOutputPathGradientSpan( this, scan, context ); } else { span = new DpOutputOneDPathGradientSpan( this, scan, context ); } } else { INT width, height, ix, iy; GpRectF brushRect = DeviceBrush.Rect; // Create a texture brush to represent this path gradient brush. // We do this by creating a texture as close to device resolution // as we can and computing the transform (brush to world) for the // texture brush decomposed into two transforms that take the // brush via device space. The texture brush transform // usually works out to be the inverse of the world to device, so // the final texture brush draws with a resultant identity transform // regardless of the world to device matrix. (exception when there is // a rotation in the w2d). GpPointF worldDestPoints[3]; worldDestPoints[0].X = brushRect.X ; worldDestPoints[0].Y = brushRect.Y; worldDestPoints[1].X = worldDestPoints[0].X + brushRect.Width; worldDestPoints[1].Y = worldDestPoints[0].Y; worldDestPoints[2].X = worldDestPoints[0].X; worldDestPoints[2].Y = worldDestPoints[0].Y + brushRect.Height; // Take into account transformation by both the brush xform and // the world to device. This will handle transforms such as // UnitInch and w2d scales. // First get the destination points in world space by applying the // brush transform. DeviceBrush.Xform.Transform(worldDestPoints, 3); GpPointF deviceDestPoints[3]; GpMemcpy(deviceDestPoints, worldDestPoints, sizeof(worldDestPoints)); // Now get the device space destination points by applying the // world to device transform. context->WorldToDevice.Transform(deviceDestPoints, 3); // Compute the bounds in device space. REAL xmin, xmax, ymin, ymax, nextX, nextY; xmin = xmax = deviceDestPoints[1].X + deviceDestPoints[2].X - deviceDestPoints[0].X; ymin = ymax = deviceDestPoints[1].Y + deviceDestPoints[2].Y - deviceDestPoints[0].Y; for(INT i = 0; i < 3; i++) { nextX = deviceDestPoints[i].X; nextY = deviceDestPoints[i].Y; if(nextX < xmin) xmin = nextX; else if(nextX > xmax) xmax = nextX; if(nextY < ymin) ymin = nextY; else if(nextY > ymax) ymax = nextY; } // Set the optimal bitmap bounds. ix = GpRound(xmin); iy = GpRound(ymin); width = GpRound(xmax) - ix; height = GpRound(ymax) - iy; GpRectF bitmapBounds(0, 0, TOREAL(width), TOREAL(height)); // Decompose brushRect --> worldDestPoints transform into two matrix. // mat1: brushRect --> bitmapBounds (device space) // mat2: bitmapBounds --> worldDestPoints GpMatrix mat1, mat2; mat1.InferAffineMatrix(bitmapBounds, brushRect); mat2.InferAffineMatrix(worldDestPoints, bitmapBounds); if(width <= 0 || height <= 0) return NULL; // Create a bitmap which the gradient will be drawn onto. // Make it the full width and height of the gradient, even // though only a small portion may be used to simplify // handling by downstream functions. GpBitmap* bitmap = new GpBitmap(width, height, PixelFormat32bppARGB); if(bitmap) { GpGraphics* g = bitmap->GetGraphicsContext(); if(g) { GpLock lock(g->GetObjectLock()); // Set the transform to brushRect --> bitmapBounds. g->MultiplyWorldTransform(mat1); WrapMode savedWrapMode = DeviceBrush.Wrap; DeviceBrush.Wrap = WrapModeClamp; GpMatrix savedMat = DeviceBrush.Xform; DeviceBrush.Xform.Reset(); g->FillRect(this, brushRect.X, brushRect.Y, brushRect.Width, brushRect.Height); DeviceBrush.Wrap = savedWrapMode; DeviceBrush.Xform = savedMat; if(MorphedBrush) delete MorphedBrush; // Create a texuture with a unit tile and set the // brush transform to bitmapBounds --> worldDestPoints. GpTexture* texture = new GpTexture(bitmap, savedWrapMode); // span must be NULL at this point. If it's not, we're going // to leak memory when we create it below, or in the case of // an error out, we may end up with uninitialized memory // being returned to the caller. ASSERT(span == NULL); if(texture) { texture->MultiplyTransform(mat2); span = texture->CreateOutputSpan(scan, context, drawBounds); } // Even if we failed to create the texture, we still want to // set a reasonable (NULL) value for MorphedBrush so that we // don't have a dangling pointer. MorphedBrush = texture; } // We're done with this graphics. // NOTE: this is explicitly done outside of the scope of the // GpLock object, so that the GpLock (which modifies the graphics // in its destructor) doesn't touch freed memory. delete g; bitmap->Dispose(); } } return span; } DpOutputSpan* GpTexture::CreateOutputSpan( DpScanBuffer *scan, DpContext *context, const GpRect *drawBounds) { DpOutputBilinearSpan *textureSpan = NULL; GpMatrix brushTransform; GpMatrix worldToDevice; // Figure out the world-to-device transform: worldToDevice = context->WorldToDevice; this->GetTransform(&brushTransform); worldToDevice.Prepend(brushTransform); // Go through our heirarchy of scan drawers: if (worldToDevice.IsIntegerTranslate() && ((this->GetWrapMode() == WrapModeTile) || (this->GetWrapMode() == WrapModeClamp))) { textureSpan = new DpOutputBilinearSpan_Identity(this, scan, &worldToDevice, context); } else if (OSInfo::HasMMX && GpValidFixed16(DeviceBrush.Rect.Width) && GpValidFixed16(DeviceBrush.Rect.Height)) { textureSpan = new DpOutputBilinearSpan_MMX(this, scan, &worldToDevice, context); } // Scan drawer creation may fail, so clean up and try one last time if ((textureSpan) && !textureSpan->IsValid()) { delete textureSpan; textureSpan = NULL; } if (!textureSpan) { textureSpan = new DpOutputBilinearSpan(this, scan, &worldToDevice, context); } if ((textureSpan) && !textureSpan->IsValid()) { delete textureSpan; textureSpan = NULL; } return textureSpan; } DpOutputSpan* GpHatch::CreateOutputSpan( DpScanBuffer * scan, DpContext *context, const GpRect *drawBounds) { if (StretchFactor == 1) { return new DpOutputHatchSpan( this, scan, context ); } else { return new DpOutputStretchedHatchSpan( this, scan, context, StretchFactor ); } } class SolidBrushData : public ObjectTypeData { public: ARGB SolidColor; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpSolidFill::GetData( IStream * stream ) const { ASSERT (stream != NULL); SolidBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.SolidColor = DeviceBrush.SolidColor.GetValue(); stream->Write(&brushData, sizeof(brushData), NULL); return Ok; } UINT GpSolidFill::GetDataSize() const { return sizeof(SolidBrushData); } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpSolidFill::SetData( const BYTE * dataBuffer, UINT size ) { ASSERT ((GpBrushType)(((SolidBrushData *)dataBuffer)->Type) == BrushTypeSolidColor); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(SolidBrushData)) { WARNING(("size too small")); return InvalidParameter; } if (!((SolidBrushData *)dataBuffer)->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } SetColor(GpColor(((SolidBrushData *)dataBuffer)->SolidColor)); return Ok; } GpStatus GpSolidFill::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { if(!recolor) return InvalidParameter; if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } ARGB solidColor32 = Color.GetValue(); recolor->ColorAdjust(&solidColor32, 1, type); this->SetColor(GpColor(solidColor32)); return Ok; } class TextureBrushData : public ObjectTypeData { public: INT32 Flags; INT32 Wrap; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpTexture::GetData( IStream * stream ) const { ASSERT (stream != NULL); if (Image == NULL) { WARNING(("Image is NULL")); return Ok; } INT flags = 0; if (DeviceBrush.IsGammaCorrected) { flags |= GDIP_BRUSHFLAGS_ISGAMMACORRECTED; } if (!DeviceBrush.Xform.IsIdentity()) { flags |= GDIP_BRUSHFLAGS_TRANSFORM; } TextureBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.Flags = flags; brushData.Wrap = DeviceBrush.Wrap; stream->Write(&brushData, sizeof(brushData), NULL); if (flags & GDIP_BRUSHFLAGS_TRANSFORM) { DeviceBrush.Xform.WriteMatrix(stream); } return Image->GetData(stream); } UINT GpTexture::GetDataSize() const { if (Image == NULL) { WARNING(("Image is NULL")); return 0; } UINT size = sizeof(TextureBrushData); if (!DeviceBrush.Xform.IsIdentity()) { size += GDIP_MATRIX_SIZE; } size += Image->GetDataSize(); return size; } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpTexture::SetData( const BYTE * dataBuffer, UINT size ) { ASSERT ((GpBrushType)(((TextureBrushData *)dataBuffer)->Type) == BrushTypeTextureFill); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(TextureBrushData)) { WARNING(("size too small")); return InvalidParameter; } const TextureBrushData * brushData; brushData = reinterpret_cast(dataBuffer); if (!brushData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } DeviceBrush.Type = BrushTypeTextureFill; DeviceBrush.Wrap = (GpWrapMode) brushData->Wrap; DeviceBrush.IsGammaCorrected = ((brushData->Flags & GDIP_BRUSHFLAGS_ISGAMMACORRECTED) != 0); dataBuffer += sizeof(TextureBrushData); size -= sizeof(TextureBrushData); if (brushData->Flags & GDIP_BRUSHFLAGS_TRANSFORM) { if (size < GDIP_MATRIX_SIZE) { WARNING(("size too small")); return InvalidParameter; } DeviceBrush.Xform.SetMatrix((REAL *)dataBuffer); dataBuffer += GDIP_MATRIX_SIZE; size -= GDIP_MATRIX_SIZE; } if (Image != NULL) { Image->Dispose(); Image = NULL; } if (size >= sizeof(ObjectTypeData)) { Image = (GpImage *)GpObject::Factory(ObjectTypeImage, (const ObjectData *)dataBuffer, size); if (Image != NULL) { if ((Image->SetData(dataBuffer, size) == Ok) && Image->IsValid() && ((ImageType = Image->GetImageType()) == ImageTypeBitmap)) { GpPageUnit unit; Image->GetBounds(&DeviceBrush.Rect, &unit); SetValid(TRUE); UpdateUid(); return Ok; } Image->Dispose(); Image = NULL; } } WARNING(("Failure getting image")); GpMemset(&DeviceBrush.Rect, 0, sizeof(DeviceBrush.Rect)); SetValid(FALSE); return GenericError; } GpStatus GpTexture::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } if (Image != NULL) { Image->ColorAdjust(recolor, type); UpdateUid(); } return Ok; } VOID GpTexture::InitializeBrush( GpImage* image, GpWrapMode wrapMode, const GpRectF* rect, const GpImageAttributes *imageAttributes) { ASSERT(image && image->IsValid()); if (!WrapModeIsValid(wrapMode)) { WARNING(("bad wrap mode")); goto Failure; } GpImageType imageType; imageType = image->GetImageType(); if (imageType == ImageTypeBitmap) { InitializeBrushBitmap( static_cast(image), wrapMode, rect, imageAttributes ); } else if (imageType == ImageTypeMetafile) { // For now, convert the metafile into a bitmap image and use that to // create the brush. GpBitmap * bitmapImage; if (rect != NULL) { // !!! we don't handle this case yet if ((rect->X != 0) || (rect->Y != 0)) { WARNING(("No handling for non-zero start in metafiles")); } // Don't apply the imageAttributes now, because WMF/EMF rendering // doesn't support alpha. So wait until it's been converted to // a bitmap to apply the imageAttributes. bitmapImage = ((GpMetafile *)image)->GetBitmap( GpRound(rect->Width), GpRound(rect->Height), NULL); } else { // Let the metafile decide how big the bitmap should be // Don't apply the imageAttributes now, because WMF/EMF rendering // doesn't support alpha. So wait until it's been converted to // a bitmap to apply the imageAttributes. bitmapImage = ((GpMetafile *)image)->GetBitmap(0, 0, NULL); } if (bitmapImage != NULL) { ASSERT (bitmapImage->IsValid()); InitializeBrushBitmap(bitmapImage, wrapMode, NULL, imageAttributes, TRUE); return; } goto Failure; } else // unknown image type { WARNING(("unknown image type")); Failure: Image = NULL; SetValid(FALSE); } } VOID GpTexture::InitializeBrushBitmap( GpBitmap* bitmap, GpWrapMode wrapMode, const GpRectF* rect, const GpImageAttributes *imageAttributes, BOOL useBitmap) { DeviceBrush.Type = BrushTypeTextureFill; DeviceBrush.Wrap = wrapMode; ImageType = ImageTypeBitmap; Image = NULL; FPUStateSaver fpState; // Setup the fpu state. if (bitmap && bitmap->IsValid()) { GpRect *pRectI = NULL; GpRect recti; if(rect) { recti.X = GpRound(rect->X); recti.Y = GpRound(rect->Y); recti.Width = GpRound(rect->Width); recti.Height = GpRound(rect->Height); pRectI = &recti; } if(imageAttributes) { GpBitmap *dst = NULL; if (bitmap->Recolor( imageAttributes->recolor, &dst, NULL, NULL, pRectI ) == Ok) { Image = dst; // If useBitmap is TRUE that means the caller has transferred // ownership of bitmap to us. In this case, Recolor makes // a clone of the bitmap that we're going to use, so we have // to free the bitmap passed in and use the clone instead, // otherwise we leak. if(useBitmap) { bitmap->Dispose(); } } } // !!! note that this should be non-premultiplied ARGB. // we'll fix this when we drop premultiplied data [asecchia] // also note that the output of RecolorImage is 32BPP_ARGB // if it's not NULL it's because the RecolorImage code cloned it already if (Image == NULL) { if (useBitmap) { // This is for the case where we constructed a bitmap // from a metafile image. Image = bitmap; } else { #ifdef NO_PREMULTIPLIED_ALPHA Image = bitmap->Clone(pRectI, PIXFMT_32BPP_ARGB); #else Image = bitmap->Clone(pRectI, PIXFMT_32BPP_PARGB); #endif } } } if (Image && Image->IsValid()) { SetValid(TRUE); // Rect is given as a pixel unit in bitmap. GpPageUnit unit; Image->GetBounds(&DeviceBrush.Rect, &unit); } else { SetValid(FALSE); GpMemset(&DeviceBrush.Rect, 0, sizeof(DeviceBrush.Rect)); } } // See if this texture fill is really a picture fill (with a bitmap, // not a metafile). BOOL GpTexture::IsPictureFill( const GpMatrix * worldToDevice, const GpRect * drawBounds ) const { ASSERT ((drawBounds->Width > 0) && (drawBounds->Height > 0)); BOOL isPictureFill = FALSE; GpMatrix newBrushMatrix; this->GetTransform(&newBrushMatrix); if (worldToDevice != NULL) { newBrushMatrix.Append(*worldToDevice); } newBrushMatrix.Translate( (REAL)-(drawBounds->X), (REAL)-(drawBounds->Y), MatrixOrderAppend ); // See if the texture is supposed to fill the drawBounds. // If so, this is a picture fill. if (newBrushMatrix.IsTranslateScale()) { Size size; // If the texture is not a bitmap, this returns InvalidParameter. if (this->GetBitmapSize(&size) == Ok) { GpRectF transformedRect(0.0f, 0.0f, (REAL)size.Width, (REAL)size.Height); newBrushMatrix.TransformRect(transformedRect); // get the transformed width INT deltaValue = abs(GpRound(transformedRect.Width) - drawBounds->Width); // We might be off a little because of the pixel offset mode // or a matrix that isn't quite right for whatever reason. if (deltaValue <= 2) { // get the transformed height deltaValue = abs(GpRound(transformedRect.Height) - drawBounds->Height); if (deltaValue <= 2) { if ((abs(GpRound(transformedRect.X)) <= 2) && (abs(GpRound(transformedRect.Y)) <= 2)) { isPictureFill = TRUE; } } } } } return isPictureFill; } class RectGradientBrushData : public ObjectTypeData { public: INT32 Flags; INT32 Wrap; GpRectF Rect; UINT32 Color0; UINT32 Color1; UINT32 Color2; UINT32 Color3; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpRectGradient::GetData( IStream * stream ) const { ASSERT (stream != NULL); INT flags = 0; if (DeviceBrush.IsGammaCorrected) { flags |= GDIP_BRUSHFLAGS_ISGAMMACORRECTED; } if (!DeviceBrush.Xform.IsIdentity()) { flags |= GDIP_BRUSHFLAGS_TRANSFORM; } // Note: can't have both blendFactors and presetColors at the same time // PresetColors used for GpLineGradient, but not for GpRectGradient. if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL) && (DeviceBrush.BlendPositions[0] != NULL) && (DeviceBrush.BlendFactors[0] == NULL)) { flags |= GDIP_BRUSHFLAGS_PRESETCOLORS; } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORSH; } if ((DeviceBrush.BlendCounts[1] > 1) && (DeviceBrush.BlendFactors[1] != NULL) && (DeviceBrush.BlendPositions[1] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORSV; } RectGradientBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.Flags = flags; brushData.Wrap = DeviceBrush.Wrap; brushData.Rect = DeviceBrush.Rect; brushData.Color0 = DeviceBrush.Colors[0].GetValue(); brushData.Color1 = DeviceBrush.Colors[1].GetValue(); brushData.Color2 = DeviceBrush.Colors[2].GetValue(); brushData.Color3 = DeviceBrush.Colors[3].GetValue(); stream->Write(&brushData, sizeof(brushData), NULL); if (flags & GDIP_BRUSHFLAGS_TRANSFORM) { DeviceBrush.Xform.WriteMatrix(stream); } if (flags & GDIP_BRUSHFLAGS_PRESETCOLORS) { INT realSize = DeviceBrush.BlendCounts[0] * sizeof(REAL); INT argbSize = DeviceBrush.BlendCounts[0] * sizeof(ARGB); stream->Write(&DeviceBrush.BlendCounts[0], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[0], realSize, NULL); stream->Write(DeviceBrush.PresetColors, argbSize, NULL); } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORSH) { INT realSize = DeviceBrush.BlendCounts[0] * sizeof(REAL); stream->Write(&DeviceBrush.BlendCounts[0], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[0], realSize, NULL); stream->Write(DeviceBrush.BlendFactors[0], realSize, NULL); } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORSV) { INT realSize = DeviceBrush.BlendCounts[1] * sizeof(REAL); stream->Write(&DeviceBrush.BlendCounts[1], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[1], realSize, NULL); stream->Write(DeviceBrush.BlendFactors[1], realSize, NULL); } return Ok; } UINT GpRectGradient::GetDataSize() const { UINT size = sizeof(RectGradientBrushData); if (!DeviceBrush.Xform.IsIdentity()) { size += GDIP_MATRIX_SIZE; } // Note: can't have both blendFactors and presetColors at the same time // PresetColors used for GpLineGradient, but not for GpRectGradient. if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL) && (DeviceBrush.BlendPositions[0] != NULL) && (DeviceBrush.BlendFactors[0] == NULL)) { size += sizeof(INT32) + ((sizeof(ARGB) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } if ((DeviceBrush.BlendCounts[1] > 1) && (DeviceBrush.BlendFactors[1] != NULL) && (DeviceBrush.BlendPositions[1] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[1]); } return size; } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpRectGradient::SetData( const BYTE * dataBuffer, UINT size ) { ASSERT ((GpBrushType)(((RectGradientBrushData *)dataBuffer)->Type) == BrushTypeLinearGradient); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(RectGradientBrushData)) { WARNING(("size too small")); return InvalidParameter; } const RectGradientBrushData * brushData; GpColor colors[4]; brushData = reinterpret_cast(dataBuffer); if (!brushData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } colors[0].SetValue(brushData->Color0); colors[1].SetValue(brushData->Color1); colors[2].SetValue(brushData->Color2); colors[3].SetValue(brushData->Color3); InitializeBrush(brushData->Rect, colors, (GpWrapMode) brushData->Wrap); DeviceBrush.IsGammaCorrected = ((brushData->Flags & GDIP_BRUSHFLAGS_ISGAMMACORRECTED) != 0); dataBuffer += sizeof(RectGradientBrushData); size -= sizeof(RectGradientBrushData); if (brushData->Flags & GDIP_BRUSHFLAGS_TRANSFORM) { if (size < GDIP_MATRIX_SIZE) { WARNING(("size too small")); return InvalidParameter; } DeviceBrush.Xform.SetMatrix((REAL *)dataBuffer); dataBuffer += GDIP_MATRIX_SIZE; size -= GDIP_MATRIX_SIZE; } if (brushData->Flags & GDIP_BRUSHFLAGS_PRESETCOLORS) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); UINT argbSize = count * sizeof(ARGB); if (size < (realSize + argbSize)) { WARNING(("size too small")); return InvalidParameter; } ARGB* newColors = (ARGB*) GpRealloc(DeviceBrush.PresetColors, argbSize); if (newColors != NULL) { // We have to just copy in the ARGB values, because they've already // been premultiplied. // Actually PresetColors is NON-premultiplied, but this code should // still be right because we write them out non-premultiplied too. GpMemcpy(newColors, dataBuffer + realSize, argbSize); DeviceBrush.PresetColors = newColors; REAL* newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], realSize); if (newPositions != NULL) { GpMemcpy(newPositions, dataBuffer, realSize); DeviceBrush.BlendPositions[0] = newPositions; GpFree(DeviceBrush.BlendFactors[0]); DeviceBrush.BlendFactors[0] = NULL; DeviceBrush.UsesPresetColors = TRUE; DeviceBrush.BlendCounts[0] = count; } } dataBuffer += (realSize + argbSize); size -= (realSize + argbSize); } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORSH) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetHorizontalBlend((REAL *)(dataBuffer + realSize),(REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORSV) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetVerticalBlend((REAL *)(dataBuffer + realSize), (REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } UpdateUid(); return Ok; } GpStatus GpRectGradient::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { if(!recolor) return InvalidParameter; if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } ARGB solidColor32[4]; solidColor32[0] = DeviceBrush.Colors[0].GetValue(); solidColor32[1] = DeviceBrush.Colors[1].GetValue(); solidColor32[2] = DeviceBrush.Colors[2].GetValue(); solidColor32[3] = DeviceBrush.Colors[3].GetValue(); recolor->ColorAdjust(solidColor32, 4, type); DeviceBrush.Colors[0].SetValue(solidColor32[0]); DeviceBrush.Colors[1].SetValue(solidColor32[1]); DeviceBrush.Colors[2].SetValue(solidColor32[2]); DeviceBrush.Colors[3].SetValue(solidColor32[3]); if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL)) { recolor->ColorAdjust(DeviceBrush.PresetColors, DeviceBrush.BlendCounts[0], type); } UpdateUid(); return Ok; } #if 0 class RadialGradientBrushData : public ObjectTypeData { public: INT32 Flags; INT32 Wrap; GpRectF Rect; UINT32 CenterColor; UINT32 BoundaryColor; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpRadialGradient::GetData( IStream * stream ) const { ASSERT (stream != NULL); INT flags = 0; if (DeviceBrush.IsGammaCorrected) { flags |= GDIP_BRUSHFLAGS_ISGAMMACORRECTED; } if (!DeviceBrush.Xform.IsIdentity()) { flags |= GDIP_BRUSHFLAGS_TRANSFORM; } // Note: can't have both blendFactors and presetColors at the same time if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL) && (DeviceBrush.BlendPositions[0] != NULL) && (DeviceBrush.BlendFactors[0] == NULL)) { flags |= GDIP_BRUSHFLAGS_PRESETCOLORS; } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORS; } RadialGradientBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.Flags = flags; brushData.Wrap = DeviceBrush.Wrap; brushData.Rect = DeviceBrush.Rect; brushData.CenterColor = DeviceBrush.Colors[0].GetValue(); brushData.BoundaryColor = DeviceBrush.Colors[1].GetValue(); stream->Write(&brushData, sizeof(brushData), NULL); if (flags & GDIP_BRUSHFLAGS_TRANSFORM) { DeviceBrush.Xform.WriteMatrix(stream); } if (flags & GDIP_BRUSHFLAGS_PRESETCOLORS) { INT realSize = DeviceBrush.BlendCounts[0] * sizeof(REAL); INT argbSize = DeviceBrush.BlendCounts[0] * sizeof(ARGB); stream->Write(&DeviceBrush.BlendCounts[0], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[0], realSize, NULL); stream->Write(DeviceBrush.PresetColors, argbSize, NULL); } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORS) { INT realSize = DeviceBrush.BlendCounts[0] * sizeof(REAL); stream->Write(&DeviceBrush.BlendCounts[0], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[0], realSize, NULL); stream->Write(DeviceBrush.BlendFactors[0], realSize, NULL); } return Ok; } UINT GpRadialGradient::GetDataSize() const { UINT size = sizeof(RadialGradientBrushData); if (!DeviceBrush.Xform.IsIdentity()) { size += GDIP_MATRIX_SIZE; } // Note: can't have both blendFactors and presetColors at the same time if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL) && (DeviceBrush.BlendPositions[0] != NULL) && (DeviceBrush.BlendFactors[0] == NULL)) { size += sizeof(INT32) + ((sizeof(ARGB) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } return size; } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpRadialGradient::SetData( const BYTE * dataBuffer, UINT size ) { // ASSERT ((GpBrushType)(((RadialGradientBrushData *)dataBuffer)->Type) == BrushTypeRadialGradient); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(RadialGradientBrushData)) { WARNING(("size too small")); return InvalidParameter; } const RadialGradientBrushData * brushData; GpColor centerColor; GpColor boundaryColor; brushData = reinterpret_cast(dataBuffer); if (!brushData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } centerColor.SetValue(brushData->CenterColor); boundaryColor.SetValue(brushData->BoundaryColor); InitializeBrush( brushData->Rect, centerColor, boundaryColor, (GpWrapMode) brushData->Wrap ); DeviceBrush.IsGammaCorrected = ((brushData->Flags & GDIP_BRUSHFLAGS_ISGAMMACORRECTED) != 0); dataBuffer += sizeof(RadialGradientBrushData); size -= sizeof(RadialGradientBrushData); if (brushData->Flags & GDIP_BRUSHFLAGS_TRANSFORM) { if (size < GDIP_MATRIX_SIZE) { WARNING(("size too small")); return InvalidParameter; } DeviceBrush.Xform.SetMatrix((REAL *)dataBuffer); dataBuffer += GDIP_MATRIX_SIZE; size -= GDIP_MATRIX_SIZE; } if (brushData->Flags & GDIP_BRUSHFLAGS_PRESETCOLORS) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); UINT argbSize = count * sizeof(ARGB); if (size < (realSize + argbSize)) { WARNING(("size too small")); return InvalidParameter; } ARGB* newColors = (ARGB*) GpRealloc(DeviceBrush.PresetColors, argbSize); if (newColors != NULL) { // We have to just copy in the ARGB values, because they've already // been premultiplied. GpMemcpy(newColors, dataBuffer + realSize, argbSize); DeviceBrush.PresetColors = newColors; REAL* newPositions = (REAL*) GpRealloc(DeviceBrush.BlendPositions[0], realSize); if (newPositions != NULL) { GpMemcpy(newPositions, dataBuffer, realSize); DeviceBrush.BlendPositions[0] = newPositions; GpFree(DeviceBrush.BlendFactors[0]); DeviceBrush.BlendFactors[0] = NULL; DeviceBrush.UsesPresetColors = TRUE; DeviceBrush.BlendCounts[0] = count; } } dataBuffer += (realSize + argbSize); size -= (realSize + argbSize); } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORS) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetBlend((REAL *)(dataBuffer + realSize), (REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } UpdateUid(); return Ok; } GpStatus GpRadialGradient::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { if(!recolor) return InvalidParameter; if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } ARGB solidColor32[2]; solidColor32[0] = DeviceBrush.Colors[0].GetValue(); solidColor32[1] = DeviceBrush.Colors[1].GetValue(); recolor->ColorAdjust(solidColor32, 2, type); DeviceBrush.Colors[0].SetValue(solidColor32[0]); DeviceBrush.Colors[1].SetValue(solidColor32[1]); if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL)) { recolor->ColorAdjust(DeviceBrush.PresetColors, DeviceBrush.BlendCounts[0], type); } UpdateUid(); return Ok; } class TriangleGradientBrushData : public ObjectTypeData { public: INT32 Flags; INT32 Wrap; GpPointF Points[3]; UINT32 Color0; UINT32 Color1; UINT32 Color2; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpTriangleGradient::GetData( IStream * stream ) const { ASSERT (stream != NULL); INT flags = 0; if (DeviceBrush.IsGammaCorrected) { flags |= GDIP_BRUSHFLAGS_ISGAMMACORRECTED; } if (!DeviceBrush.Xform.IsIdentity()) { flags |= GDIP_BRUSHFLAGS_TRANSFORM; } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORS0; } if ((DeviceBrush.BlendCounts[1] > 1) && (DeviceBrush.BlendFactors[1] != NULL) && (DeviceBrush.BlendPositions[1] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORS1; } if ((DeviceBrush.BlendCounts[2] > 1) && (DeviceBrush.BlendFactors[2] != NULL) && (DeviceBrush.BlendPositions[2] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORS2; } TriangleGradientBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.Flags = flags; brushData.Wrap = DeviceBrush.Wrap; brushData.Points[0] = DeviceBrush.Points[0]; brushData.Points[1] = DeviceBrush.Points[1]; brushData.Points[2] = DeviceBrush.Points[2]; brushData.Color0 = DeviceBrush.Colors[0].GetValue(); brushData.Color1 = DeviceBrush.Colors[1].GetValue(); brushData.Color2 = DeviceBrush.Colors[2].GetValue(); stream->Write(&brushData, sizeof(brushData), NULL); if (flags & GDIP_BRUSHFLAGS_TRANSFORM) { DeviceBrush.Xform.WriteMatrix(stream); } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORS0) { INT realSize = DeviceBrush.BlendCounts[0] * sizeof(REAL); stream->Write(&DeviceBrush.BlendCounts[0], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[0], realSize, NULL); stream->Write(DeviceBrush.BlendFactors[0], realSize, NULL); } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORS1) { INT realSize = DeviceBrush.BlendCounts[1] * sizeof(REAL); stream->Write(&DeviceBrush.BlendCounts[1], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[1], realSize, NULL); stream->Write(DeviceBrush.BlendFactors[1], realSize, NULL); } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORS2) { INT realSize = DeviceBrush.BlendCounts[2] * sizeof(REAL); stream->Write(&DeviceBrush.BlendCounts[2], sizeof(INT32), NULL); stream->Write(DeviceBrush.BlendPositions[2], realSize, NULL); stream->Write(DeviceBrush.BlendFactors[2], realSize, NULL); } return Ok; } UINT GpTriangleGradient::GetDataSize() const { UINT size = sizeof(RectGradientBrushData); if (!DeviceBrush.Xform.IsIdentity()) { size += GDIP_MATRIX_SIZE; } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } if ((DeviceBrush.BlendCounts[1] > 1) && (DeviceBrush.BlendFactors[1] != NULL) && (DeviceBrush.BlendPositions[1] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[1]); } if ((DeviceBrush.BlendCounts[2] > 1) && (DeviceBrush.BlendFactors[2] != NULL) && (DeviceBrush.BlendPositions[2] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[2]); } return size; } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpTriangleGradient::SetData( const BYTE * dataBuffer, UINT size ) { // ASSERT ((GpBrushType)(((TriangleGradientBrushData *)dataBuffer)->Type) == BrushTypeTriangleGradient); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(TriangleGradientBrushData)) { WARNING(("size too small")); return InvalidParameter; } const TriangleGradientBrushData * brushData; GpColor colors[3]; brushData = reinterpret_cast(dataBuffer); if (!brushData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } colors[0].SetValue(brushData->Color0); colors[1].SetValue(brushData->Color1); colors[2].SetValue(brushData->Color2); InitializeBrush(brushData->Points, colors, (GpWrapMode) brushData->Wrap); DeviceBrush.IsGammaCorrected = ((brushData->Flags & GDIP_BRUSHFLAGS_ISGAMMACORRECTED) != 0); dataBuffer += sizeof(TriangleGradientBrushData); size -= sizeof(TriangleGradientBrushData); if (brushData->Flags & GDIP_BRUSHFLAGS_TRANSFORM) { if (size < GDIP_MATRIX_SIZE) { WARNING(("size too small")); return InvalidParameter; } DeviceBrush.Xform.SetMatrix((REAL *)dataBuffer); dataBuffer += GDIP_MATRIX_SIZE; size -= GDIP_MATRIX_SIZE; } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORS0) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetBlend0((REAL *)(dataBuffer + realSize), (REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORS1) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetBlend1((REAL *)(dataBuffer + realSize), (REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORS2) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetBlend2((REAL *)(dataBuffer + realSize), (REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } UpdateUid(); return Ok; } GpStatus GpTriangleGradient::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { if(!recolor) return InvalidParameter; if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } ARGB solidColor32[3]; solidColor32[0] = DeviceBrush.Colors[0].GetValue(); solidColor32[1] = DeviceBrush.Colors[1].GetValue(); solidColor32[2] = DeviceBrush.Colors[2].GetValue(); recolor->ColorAdjust(solidColor32, 3, type); DeviceBrush.Colors[0].SetValue(solidColor32[0]); DeviceBrush.Colors[1].SetValue(solidColor32[1]); DeviceBrush.Colors[2].SetValue(solidColor32[2]); UpdateUid(); return Ok; } #endif class PathGradientBrushData : public ObjectTypeData { public: INT32 Flags; INT32 Wrap; UINT32 CenterColor; GpPointF CenterPoint; UINT32 SurroundingColorCount; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpPathGradient::GetData( IStream * stream ) const { ASSERT (stream != NULL); UINT pathSize = 0; UINT surroundingColorCount = DeviceBrush.OneSurroundColor ? 1 : DeviceBrush.Count; INT flags = 0; GpPath * path = GpPath::GetPath(DeviceBrush.Path); if (DeviceBrush.IsGammaCorrected) { flags |= GDIP_BRUSHFLAGS_ISGAMMACORRECTED; } if ((DeviceBrush.PointsPtr == NULL) && (path != NULL)) { flags |= GDIP_BRUSHFLAGS_PATH; pathSize = path->GetDataSize(); ASSERT((pathSize & 0x03) == 0); } if (!DeviceBrush.Xform.IsIdentity()) { flags |= GDIP_BRUSHFLAGS_TRANSFORM; } // Note: can't have both blendFactors and presetColors at the same time if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL) && (DeviceBrush.BlendPositions[0] != NULL) && (DeviceBrush.BlendFactors[0] == NULL)) { flags |= GDIP_BRUSHFLAGS_PRESETCOLORS; } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { flags |= GDIP_BRUSHFLAGS_BLENDFACTORS; } if((DeviceBrush.FocusScaleX != 0) || (DeviceBrush.FocusScaleY != 0)) { flags |= GDIP_BRUSHFLAGS_FOCUSSCALES; } PathGradientBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.Flags = flags; brushData.Wrap = DeviceBrush.Wrap; brushData.CenterColor = DeviceBrush.Colors[0].GetValue(); brushData.CenterPoint = DeviceBrush.Points[0]; brushData.SurroundingColorCount = surroundingColorCount; stream->Write(&brushData, sizeof(brushData), NULL); ARGB argb; for (UINT i = 0; i < surroundingColorCount; i++) { argb = DeviceBrush.ColorsPtr[i].GetValue(); stream->Write(&argb, sizeof(argb), NULL); } if (flags & GDIP_BRUSHFLAGS_PATH) { stream->Write(&pathSize, sizeof(INT32), NULL); path->GetData(stream); } else { INT count = DeviceBrush.Count; if (DeviceBrush.PointsPtr == NULL) { count = 0; } stream->Write(&count, sizeof(INT32), NULL); if (count > 0) { INT pointsSize = count * sizeof(DeviceBrush.PointsPtr[0]); stream->Write(DeviceBrush.PointsPtr, pointsSize, NULL); } } if (flags & GDIP_BRUSHFLAGS_TRANSFORM) { DeviceBrush.Xform.WriteMatrix(stream); } if (flags & GDIP_BRUSHFLAGS_PRESETCOLORS) { INT count = DeviceBrush.BlendCounts[0]; INT realSize = count * sizeof(REAL); INT argbSize = count * sizeof(ARGB); REAL *newPositions = (REAL*) GpMalloc(realSize); if (newPositions == NULL ) { return OutOfMemory; } ARGB *newARGB = (ARGB*) GpMalloc(argbSize); if (newARGB == NULL ) { GpFree(newPositions); return OutOfMemory; } GpColor *newPresetColors = new GpColor[count]; if (newPresetColors == NULL) { GpFree(newPositions); GpFree (newARGB); return OutOfMemory; } // Users will supply the preset colors as radial blend colors. // 0 position means the center location and 1 position means the // the outer edge. These are stored inverted internally, so to get back // to the original user values, invert again. GetPresetBlend(newPresetColors, newPositions, count); for (INT i = 0; i < count; i++) { newARGB[i] = newPresetColors[i].GetValue(); } stream->Write(&count, sizeof(INT32), NULL); stream->Write(newPositions, realSize, NULL); stream->Write(newARGB, argbSize, NULL); GpFree(newPositions); GpFree(newARGB); delete newPresetColors; } if (flags & GDIP_BRUSHFLAGS_BLENDFACTORS) { INT count = DeviceBrush.BlendCounts[0]; INT realSize = count * sizeof(REAL); // Users will supply the blend factor as radial blend factors, and these are stored // with inverted values. To get back the original user specified blend factors to // store, they must be inverted again. REAL *newFactors = (REAL*) GpMalloc(realSize); if (newFactors == NULL ) { return OutOfMemory; } REAL *newPositions = (REAL*) GpMalloc(realSize); if (newPositions == NULL ) { GpFree(newFactors); return OutOfMemory; } GetBlend(newFactors, newPositions, count); stream->Write(&count, sizeof(INT32), NULL); stream->Write(newPositions, realSize, NULL); stream->Write(newFactors, realSize, NULL); GpFree(newPositions); GpFree(newFactors); } if (flags & GDIP_BRUSHFLAGS_FOCUSSCALES) { INT count = 2; REAL focusScale[2]; focusScale[0] = DeviceBrush.FocusScaleX; focusScale[1] = DeviceBrush.FocusScaleY; stream->Write(&count, sizeof(INT32), NULL); stream->Write(focusScale, 2 * sizeof(REAL), NULL); } return Ok; } UINT GpPathGradient::GetDataSize() const { UINT pathSize; UINT surroundingColorCount = DeviceBrush.OneSurroundColor ? 1 : DeviceBrush.Count; UINT size = sizeof(PathGradientBrushData) + (surroundingColorCount * sizeof(ARGB)); GpPath* path = static_cast (DeviceBrush.Path); if (DeviceBrush.PointsPtr != NULL) { size += sizeof(INT32) + (DeviceBrush.Count * sizeof(DeviceBrush.PointsPtr[0])); } else if (path != NULL) { pathSize = path->GetDataSize(); ASSERT((pathSize & 0x03) == 0); size += sizeof(INT32) + pathSize; } if (!DeviceBrush.Xform.IsIdentity()) { size += GDIP_MATRIX_SIZE; } // Note: can't have both blendFactors and presetColors at the same time if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL) && (DeviceBrush.BlendPositions[0] != NULL) && (DeviceBrush.BlendFactors[0] == NULL)) { size += sizeof(INT32) + ((sizeof(ARGB) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } if ((DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.BlendFactors[0] != NULL) && (DeviceBrush.BlendPositions[0] != NULL)) { size += sizeof(INT32) + ((sizeof(REAL) + sizeof(REAL)) * DeviceBrush.BlendCounts[0]); } if((DeviceBrush.FocusScaleX != 0) || (DeviceBrush.FocusScaleY != 0)) { size += sizeof(INT32) + 2*sizeof(REAL); } return size; } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpPathGradient::SetData( const BYTE * dataBuffer, UINT size ) { ASSERT ((GpBrushType)(((PathGradientBrushData *)dataBuffer)->Type) == BrushTypePathGradient); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(PathGradientBrushData)) { WARNING(("size too small")); return InvalidParameter; } if (DeviceBrush.PointsPtr != NULL) { GpFree(DeviceBrush.PointsPtr); DeviceBrush.PointsPtr = NULL; } GpPath* path = static_cast (DeviceBrush.Path); if (path != NULL) { delete path; path = NULL; } const PathGradientBrushData * brushData; ARGB * surroundingColors; brushData = reinterpret_cast(dataBuffer); if (!brushData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } dataBuffer += sizeof(PathGradientBrushData); size -= sizeof(PathGradientBrushData); if (size < (brushData->SurroundingColorCount * sizeof(ARGB))) { WARNING(("size too small")); return InvalidParameter; } surroundingColors = (ARGB *)dataBuffer; dataBuffer += (brushData->SurroundingColorCount * sizeof(ARGB)); size -= (brushData->SurroundingColorCount * sizeof(ARGB)); if (brushData->Flags & GDIP_BRUSHFLAGS_PATH) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT pathSize = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); DefaultBrush(); DeviceBrush.Wrap = (GpWrapMode) brushData->Wrap; if (size < pathSize) { WARNING(("size too small")); return InvalidParameter; } path = new GpPath(); if (path) { path->SetData(dataBuffer, pathSize); } DeviceBrush.Path = path; PrepareBrush(); dataBuffer += pathSize; size -= pathSize; } else { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } INT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); if (size < (count * sizeof(GpPointF))) { WARNING(("size too small")); return InvalidParameter; } InitializeBrush((GpPointF *)dataBuffer, count, (GpWrapMode) brushData->Wrap); dataBuffer += (count * sizeof(GpPointF)); size -= (count * sizeof(GpPointF)); } DeviceBrush.IsGammaCorrected = ((brushData->Flags & GDIP_BRUSHFLAGS_ISGAMMACORRECTED) != 0); SetCenterPoint(brushData->CenterPoint); SetCenterColor(GpColor(brushData->CenterColor)); DeviceBrush.OneSurroundColor = (brushData->SurroundingColorCount == 1); if (DeviceBrush.ColorsPtr != NULL) { for (UINT32 i = 0; i < brushData->SurroundingColorCount; i++) { SetSurroundColor(GpColor(surroundingColors[i]), i); } // OneSurroundColor requires n colors and they are all set to the // same value. This is a very weird requirement, but that's the way // it was written. One color simply isn't enough. if (i == 1) { for (i = 1; (INT)i < DeviceBrush.Count; i++) { DeviceBrush.ColorsPtr[i] = GpColor(surroundingColors[0]); } } } if (brushData->Flags & GDIP_BRUSHFLAGS_TRANSFORM) { if (size < GDIP_MATRIX_SIZE) { WARNING(("size too small")); return InvalidParameter; } DeviceBrush.Xform.SetMatrix((REAL *)dataBuffer); dataBuffer += GDIP_MATRIX_SIZE; size -= GDIP_MATRIX_SIZE; } if (brushData->Flags & GDIP_BRUSHFLAGS_PRESETCOLORS) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); UINT argbSize = count * sizeof(ARGB); if (size < (realSize + argbSize)) { WARNING(("size too small")); return InvalidParameter; } ARGB *argbBuffer = (ARGB*)(dataBuffer + realSize); GpColor *colors = new GpColor[count]; if (colors == NULL) { return OutOfMemory; } for (UINT i = 0; i < count; i++) { colors[i].SetValue(argbBuffer[i]); } this->SetPresetBlend(colors, (REAL *)dataBuffer, count); dataBuffer += (realSize + argbSize); size -= (realSize + argbSize); delete colors; } if (brushData->Flags & GDIP_BRUSHFLAGS_BLENDFACTORS) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } UINT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); UINT realSize = count * sizeof(REAL); if (size < (2 * realSize)) { WARNING(("size too small")); return InvalidParameter; } this->SetBlend((REAL *)(dataBuffer + realSize), (REAL *)dataBuffer, count); dataBuffer += (2 * realSize); size -= (2 * realSize); } if (brushData->Flags & GDIP_BRUSHFLAGS_FOCUSSCALES) { if (size < sizeof(INT32)) { WARNING(("size too small")); return InvalidParameter; } INT count = ((INT32 *)dataBuffer)[0]; dataBuffer += sizeof(INT32); size -= sizeof(INT32); if (size < (2 * sizeof(REAL))) { WARNING(("size too small")); return InvalidParameter; } DeviceBrush.FocusScaleX = ((REAL *) dataBuffer)[0]; DeviceBrush.FocusScaleY = ((REAL *) dataBuffer)[1]; dataBuffer += (2 * sizeof(REAL)); size -= (2 * sizeof(REAL)); } UpdateUid(); return Ok; } /**************************************************************************\ * * Function Description: * * Blend any transparent colors in this brush with white. Note that * colors are premultiplied, since they will become fully opaque. * * Arguments: * * Return Value: * * GpStatus - Ok or failure status * \**************************************************************************/ GpStatus GpPathGradient::BlendWithWhite() { DeviceBrush.Colors[0].SetValue( GpColor::ConvertToPremultiplied(DeviceBrush.Colors[0].GetValue())); DeviceBrush.Colors[0].BlendOpaqueWithWhite(); if (DeviceBrush.UsesPresetColors) { GpColor color; for (INT i=0; i= 0 && index < DeviceBrush.Count) { if(DeviceBrush.OneSurroundColor) { if(index == 0) { DeviceBrush.ColorsPtr[0] = color; // OneSurroundColor requires n colors and they are all set to the // same value. This is a very weird requirement, but that's the way // it was written. One color simply isn't enough. for (INT i = 1; i < DeviceBrush.Count; i++) { DeviceBrush.ColorsPtr[i] = GpColor(DeviceBrush.ColorsPtr[0]); } UpdateUid(); } else { if(DeviceBrush.ColorsPtr[0].GetValue() != color.GetValue()) { DeviceBrush.OneSurroundColor = FALSE; DeviceBrush.ColorsPtr[index] = color; UpdateUid(); } } } else { DeviceBrush.ColorsPtr[index] = color; UpdateUid(); } return Ok; } else return InvalidParameter; } /**************************************************************************\ * * Function Description: * * Set the surround colors. * * Arguments: * * [IN] color - the color to set. * * Return Value: * * GpStatus - Ok or failure status * \**************************************************************************/ GpStatus GpPathGradient::SetSurroundColors(const GpColor* colors) { GpStatus status = InvalidParameter; ASSERT(DeviceBrush.Count > 0); if(IsValid() && colors && DeviceBrush.Count > 0) { GpMemcpy( DeviceBrush.ColorsPtr, colors, DeviceBrush.Count*sizeof(GpColor) ); DeviceBrush.OneSurroundColor = TRUE; INT i = 1; ARGB value = colors[0].GetValue(); while((i < DeviceBrush.Count) && (DeviceBrush.OneSurroundColor)) { if(colors[i].GetValue() != value) { DeviceBrush.OneSurroundColor = FALSE; } i++; } UpdateUid(); status = Ok; } return status; } GpStatus GpPathGradient::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { if(!recolor) return InvalidParameter; if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } INT surroundingColorCount = DeviceBrush.OneSurroundColor ? 1 : DeviceBrush.Count; if ((surroundingColorCount > 0) && (DeviceBrush.ColorsPtr != NULL)) { ARGB solidColor32[32]; ARGB * color32 = solidColor32; if (surroundingColorCount > 32) { color32 = new ARGB[surroundingColorCount]; if (color32 == NULL) { return OutOfMemory; } } INT i; for (i = 0; i < surroundingColorCount; i++) { color32[i] = DeviceBrush.ColorsPtr[i].GetValue(); } recolor->ColorAdjust(color32, surroundingColorCount, type); for (i = 0; i < surroundingColorCount; i++) { DeviceBrush.ColorsPtr[i].SetValue(color32[i]); } if (color32 != solidColor32) { delete[] color32; } } if (DeviceBrush.UsesPresetColors && (DeviceBrush.BlendCounts[0] > 1) && (DeviceBrush.PresetColors != NULL)) { recolor->ColorAdjust(DeviceBrush.PresetColors, DeviceBrush.BlendCounts[0], type); } UpdateUid(); return Ok; } class HatchBrushData : public ObjectTypeData { public: INT32 Style; UINT32 ForeColor; UINT32 BackColor; }; /**************************************************************************\ * * Function Description: * * Get the brush data. * * Arguments: * * [IN] dataBuffer - fill this buffer with the data * [IN/OUT] size - IN - size of buffer; OUT - number bytes written * * Return Value: * * GpStatus - Ok or error code * * Created: * * 9/13/1999 DCurtis * \**************************************************************************/ GpStatus GpHatch::GetData( IStream * stream ) const { ASSERT (stream != NULL); HatchBrushData brushData; brushData.Type = DeviceBrush.Type; brushData.Style = DeviceBrush.Style; brushData.ForeColor = DeviceBrush.Colors[0].GetValue(); brushData.BackColor = DeviceBrush.Colors[1].GetValue(); stream->Write(&brushData, sizeof(brushData), NULL); return Ok; } UINT GpHatch::GetDataSize() const { return sizeof(HatchBrushData); } /**************************************************************************\ * * Function Description: * * Read the brush object from memory. * * Arguments: * * [IN] dataBuffer - the data that was read from the stream * [IN] size - the size of the data * * Return Value: * * GpStatus - Ok or failure status * * Created: * * 4/26/1999 DCurtis * \**************************************************************************/ GpStatus GpHatch::SetData( const BYTE * dataBuffer, UINT size ) { ASSERT ((GpBrushType)(((HatchBrushData *)dataBuffer)->Type) == BrushTypeHatchFill); if (dataBuffer == NULL) { WARNING(("dataBuffer is NULL")); return InvalidParameter; } if (size < sizeof(HatchBrushData)) { WARNING(("size too small")); return InvalidParameter; } const HatchBrushData * brushData; brushData = reinterpret_cast(dataBuffer); if (!brushData->MajorVersionMatches()) { WARNING(("Version number mismatch")); return InvalidParameter; } InitializeBrush(static_cast(brushData->Style), GpColor(brushData->ForeColor), GpColor(brushData->BackColor)); UpdateUid(); return Ok; } GpStatus GpHatch::ColorAdjust( GpRecolor * recolor, ColorAdjustType type ) { ASSERT(recolor != NULL); if (type == ColorAdjustTypeDefault) { type = ColorAdjustTypeBrush; } ARGB solidColor32[2]; solidColor32[0] = DeviceBrush.Colors[0].GetValue(); //!!! bhouse: bug? // seems that this should be BackColor ... I'm making the // change! // solidColor32[1] = ForeColor.GetValue(); solidColor32[1] = DeviceBrush.Colors[1].GetValue(); recolor->ColorAdjust(solidColor32, 2, type); DeviceBrush.Colors[0].SetValue(solidColor32[0]); DeviceBrush.Colors[1].SetValue(solidColor32[1]); UpdateUid(); return Ok; } static COLORREF AverageColors( const GpColor * colors, INT count ) { REAL r = 0; REAL g = 0; REAL b = 0; if (count > 0) { for (INT i = 0; i < count; i++) { r += colors->GetRed(); g += colors->GetGreen(); b += colors->GetBlue(); } r /= count; g /= count; b /= count; } INT red = GpRound(r); INT green = GpRound(g); INT blue = GpRound(b); return RGB(red, green, blue); } static COLORREF AverageColors( const GpColor & color1, const GpColor & color2 ) { REAL r = ((REAL)((INT)color1.GetRed() + (INT)color2.GetRed())) / 2.0f; REAL g = ((REAL)((INT)color1.GetGreen()+ (INT)color2.GetGreen())) / 2.0f; REAL b = ((REAL)((INT)color1.GetBlue() + (INT)color2.GetBlue())) / 2.0f; INT red = GpRound(r); INT green = GpRound(g); INT blue = GpRound(b); return RGB(red, green, blue); } COLORREF ToCOLORREF( const DpBrush * deviceBrush ) { switch (deviceBrush->Type) { default: ASSERT(0); // FALLTHRU case BrushTypeSolidColor: return deviceBrush->SolidColor.ToCOLORREF(); case BrushTypeHatchFill: return AverageColors(deviceBrush->Colors[0], deviceBrush->Colors[1]); case BrushTypeTextureFill: return RGB(0x80, 0x80, 0x80); // case BrushRectGrad: case BrushTypeLinearGradient: return AverageColors(deviceBrush->Colors, 4); #if 0 case BrushRadialGrad: return AverageColors(deviceBrush->Colors[0], deviceBrush->Colors[1]); case BrushTriangleGrad: return AverageColors(deviceBrush->Colors, 3); #endif case BrushTypePathGradient: return AverageColors(deviceBrush->Colors[0], deviceBrush->ColorsPtr[0]); } }