[GDIPLUS] Sync with Wine Staging 2.16. CORE-13762

6bf1b63 gdiplus: Account for gdi32 transform in SOFTWARE_GdipDrawThinPath.
e127101 gdiplus: Send paths to gdi32 in device coordinates.
93e8507 gdiplus: Account for gdi32 transform in GdipDrawImage.
be95252 gdiplus: Use SOFTWARE_GdipDrawPath with alpha hdc's.
0914f62 gdiplus: Account for gdi transform in brush_fill_pixels.
399fd55 gdiplus: Account for gdi transform in SOFTWARE_GdipFillRegion.
016dc76 gdiplus: Transform clipping region to gdi device coordinates.
cfa4f08 gdiplus: Replace DPtoLP with an internal coordinate space constant.
5c12ced gdiplus: Check for invalid coordinate space in GdipTransformPoints.
8c593bd gdiplus: Set correct color space flags for grayscale images.
7860d11 gdiplus: Don't call PlayEnhMetaFileRecord for records handled by gdiplus.
5870431 gdiplus: Force conversion of 8 bpp grayscale PNG images to 32 bpp BGRA.
42e5d27 gdiplus: Use defined constants for wrap modes.
79ebd3f gdiplus: Fix copy-paste typo.
a4ab858 gdiplus: GdipCreateMetafileFromWmfFile will also load EMFs.
aac33da gdiplus: Implement transform matrix for line gradient brushes.
14bb8df gdiplus: Support GdipSetClipRegion in metafiles.
4a02870 gdiplus: Add write_region_data helper and use it in GdipGetRegionData.
595959c gdiplus: Add more accurate algorithm for inverting scaling and translation matrices in GdipInvertMatrix.
1744277 gdiplus: Implement stub for GdipGraphicsSetAbort.
331a7af gdiplus: Fix a possible floating point exception in path gradients.
400cfb0 gdiplus: Avoid division by zero in SOFTWARE_GdipDrawThinPath.
2176348 gdiplus: Return success saving path to metafile.
70afb4e gdiplus: Fix saving pen dashed line cap style to metafile.
a172cc6 gdiplus: Free dash_pattern_scaled (Coverity).
58eb74c gdiplus: Use write_path_data helper in GdipGetRegionData.
a892b68 gdiplus: Add write_path_data helper to create EMF+ path object.
5545332 gdiplus: Store newer gdi+ version in created GdipRegions.
cfe2b3f gdiplus: Don't require specific gdi+ versions in metafile structures.
a8b5fdd gdiplus: Use VERSION_MAGIC2 constant in metafiles functions.
8498aa3 gdiplus: Add support for creating image object containing metafile.
9f22041 gdiplus: Fix leak in widen_dashed_figure.
f9b881e gdiplus: Fix GdipGetVisibleClipBounds behavior on metafiles.
de37ced gdiplus: Add partial support for GdipFillPath on metafiles.
e79c4ca gdiplus: Add partial support for GdipDrawPath on metafiles.
7d6896e gdiplus: Add helper for saving pens to metafile.
e502a8d gdiplus: Add helper for saving path objects in metafile.
8608bf5 gdiplus: Add DrawPath stub for metafiles.
29968cf gdiplus: Support GdipSetInterpolationMode in metafiles.
f248374 gdiplus: Support GdipSetCompositingQuality in metafiles.
1cecd47 gdiplus: Support GdipSetCompositingMode in metafiles.
910975a gdiplus: Support GdipSetSmoothingMode in metafiles.
f716029 gdiplus: Support GdipSetPixelOffsetMode in metafiles.
683315d gdiplus: Support GdipSetTextRenderingHint in metafiles.
689268d gdiplus: Add support for ImageAttributes when drawing image to metafile.
ac231b1 gdiplus: Add function for managing metafile objects id.
e1e4dd2 gdiplus: Add partial support for GdipDrawImagePointsRect on metafile.
1a75f76 gdiplus: Remove unused clsid parameter from encode_image_func helpers.
01c9fb9 gdiplus: Remove a duplicate word in a comment.
6ec3cd9 gdiplus: Set flatness more appropriately in GdipDrawPath.
7e1522c gdiplus: Scale widened dashes to the pen width.
c95877d gdiplus: Write API documentation for GdipAddPathArc and GdipAddPathArcI.
f1123f3 gdiplus: Write API for GdipClonePath.
f96e319 gdiplus: Write API for GdipAddPathLine and GdipAddPathLineI.
260cbd0 gdiplus: Implement triangular line caps in widened paths.
a4b7fe6 gdiplus: Initialize containers list in GdipCloneImage.

svn path=/trunk/; revision=75872
This commit is contained in:
Amine Khaldi 2017-09-17 12:34:15 +00:00
parent 2a10a08909
commit c6f32e4a5f
10 changed files with 1654 additions and 332 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 Google (Evan Stade)
* Copyright (C) 2003-2004,2007 Novell, Inc. http://www.novell.com (Ravindra (rkumar@novell.com))
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -162,6 +163,8 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
return OutOfMemory;
}
dest->transform = src->transform;
memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
@ -272,6 +275,41 @@ GpStatus WINGDIPAPI GdipCreateHatchBrush(HatchStyle hatchstyle, ARGB forecol, AR
return Ok;
}
static void linegradient_init_transform(GpLineGradient *line)
{
float trans_x = line->rect.X + (line->rect.Width / 2.f);
float trans_y = line->rect.Y + (line->rect.Height / 2.f);
float dx = line->endpoint.X - line->startpoint.X;
float dy = line->endpoint.Y - line->startpoint.Y;
float t_cos, t_sin, w_ratio, h_ratio;
float h;
GpMatrix rot;
h = sqrtf(dx * dx + dy * dy);
t_cos = dx / h;
t_sin = dy / h;
w_ratio = (fabs(t_cos) * line->rect.Width + fabs(t_sin) * line->rect.Height) / line->rect.Width;
h_ratio = (fabs(t_sin) * line->rect.Width + fabs(t_cos) * line->rect.Height) / line->rect.Height;
GdipSetMatrixElements(&line->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
GdipSetMatrixElements(&rot, t_cos, t_sin, -1.f * t_sin, t_cos, 0, 0);
/* center about the origin */
GdipTranslateMatrix(&line->transform, -trans_x, -trans_y, MatrixOrderAppend);
/* scale to normalize gradient along gradient line (?) */
GdipScaleMatrix(&line->transform, w_ratio, h_ratio, MatrixOrderAppend);
/* rotate so the gradient is horizontal */
GdipMultiplyMatrix(&line->transform, &rot, MatrixOrderAppend);
/* restore original offset in new coords */
GdipTranslateMatrix(&line->transform, trans_x, trans_y, MatrixOrderAppend);
}
/******************************************************************************
* GdipCreateLineBrush [GDIPLUS.@]
*/
@ -338,6 +376,8 @@ GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
(*line)->pblendpos = NULL;
(*line)->pblendcount = 0;
linegradient_init_transform(*line);
TRACE("<-- %p\n", *line);
return Ok;
@ -370,6 +410,7 @@ GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
{
GpPointF start, end;
GpStatus stat;
float far_x, far_y;
TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
wrap, line);
@ -377,31 +418,34 @@ GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
if(!line || !rect)
return InvalidParameter;
far_x = rect->X + rect->Width;
far_y = rect->Y + rect->Height;
switch (mode)
{
case LinearGradientModeHorizontal:
start.X = rect->X;
start.X = min(rect->X, far_x);
start.Y = rect->Y;
end.X = rect->X + rect->Width;
end.X = max(rect->X, far_x);
end.Y = rect->Y;
break;
case LinearGradientModeVertical:
start.X = rect->X;
start.Y = rect->Y;
start.Y = min(rect->Y, far_y);
end.X = rect->X;
end.Y = rect->Y + rect->Height;
end.Y = max(rect->Y, far_y);
break;
case LinearGradientModeForwardDiagonal:
start.X = rect->X;
start.Y = rect->Y;
end.X = rect->X + rect->Width;
end.Y = rect->Y + rect->Height;
start.X = min(rect->X, far_x);
start.Y = min(rect->Y, far_y);
end.X = max(rect->X, far_x);
end.Y = max(rect->Y, far_y);
break;
case LinearGradientModeBackwardDiagonal:
start.X = rect->X + rect->Width;
start.Y = rect->Y;
end.X = rect->X;
end.Y = rect->Y + rect->Height;
start.X = max(rect->X, far_x);
start.Y = min(rect->Y, far_y);
end.X = min(rect->X, far_x);
end.Y = max(rect->Y, far_y);
break;
default:
return InvalidParameter;
@ -510,6 +554,8 @@ GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect
(*line)->startpoint.X = rect->X + exofs;
(*line)->startpoint.Y = rect->Y + eyofs;
}
linegradient_init_transform(*line);
}
return stat;
@ -2018,78 +2064,73 @@ GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
{
static int calls;
TRACE("(%p)\n", brush);
if(!(calls++))
FIXME("not implemented\n");
if(!brush)
return InvalidParameter;
return NotImplemented;
return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
}
GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
GDIPCONST GpMatrix *matrix)
{
static int calls;
TRACE("(%p,%p)\n", brush, matrix);
if(!(calls++))
FIXME("not implemented\n");
if(!brush || !matrix)
return InvalidParameter;
return NotImplemented;
brush->transform = *matrix;
return Ok;
}
GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
{
static int calls;
TRACE("(%p,%p)\n", brush, matrix);
if(!(calls++))
FIXME("not implemented\n");
if(!brush || !matrix)
return InvalidParameter;
return NotImplemented;
*matrix = brush->transform;
return Ok;
}
GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
GpMatrixOrder order)
{
static int calls;
TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
if(!(calls++))
FIXME("not implemented\n");
if(!brush)
return InvalidParameter;
return NotImplemented;
return GdipScaleMatrix(&brush->transform, sx, sy, order);
}
GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
{
static int calls;
TRACE("(%p,%p,%u)\n", brush, matrix, order);
if(!(calls++))
FIXME("not implemented\n");
if(!brush)
return InvalidParameter;
return NotImplemented;
if(!matrix)
return Ok;
return GdipMultiplyMatrix(&brush->transform, matrix, order);
}
GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient* brush,
GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient *brush,
REAL dx, REAL dy, GpMatrixOrder order)
{
static int calls;
TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
if(!(calls++))
FIXME("not implemented\n");
if(!brush)
return InvalidParameter;
return Ok;
return GdipTranslateMatrix(&brush->transform, dx, dy, order);
}
/******************************************************************************

View file

@ -622,7 +622,7 @@
622 stdcall GdipBitmapGetHistogramSize(long ptr)
623 stdcall GdipBitmapConvertFormat(ptr long long long ptr float)
624 stdcall GdipImageSetAbort(ptr ptr)
625 stub GdipGraphicsSetAbort
625 stdcall GdipGraphicsSetAbort(ptr ptr)
626 stub GdipDrawImageFX
627 stdcall GdipConvertToEmfPlus(ptr ptr ptr long ptr ptr)
628 stdcall GdipConvertToEmfPlusToFile(ptr ptr ptr ptr long ptr ptr)

View file

@ -49,6 +49,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
#define VERSION_MAGIC 0xdbc01001
#define VERSION_MAGIC2 0xdbc01002
#define VALID_MAGIC(x) (((x) & 0xfffff000) == 0xdbc01000)
#define TENSION_CONST (0.3)
#define GIF_DISPOSE_UNSPECIFIED 0
@ -86,10 +87,17 @@ extern REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN;
extern REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN;
extern REAL units_scale(GpUnit from, GpUnit to, REAL dpi) DECLSPEC_HIDDEN;
#define WineCoordinateSpaceGdiDevice ((GpCoordinateSpace)4)
extern GpStatus gdi_transform_acquire(GpGraphics *graphics);
extern GpStatus gdi_transform_release(GpGraphics *graphics);
extern GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
GpCoordinateSpace src_space, GpMatrix *matrix) DECLSPEC_HIDDEN;
extern GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
GpCoordinateSpace src_space, GpPointF *points, INT count) DECLSPEC_HIDDEN;
extern GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) DECLSPEC_HIDDEN;
extern GpStatus encode_image_png(GpImage *image, IStream* stream, GDIPCONST EncoderParameters* params) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc) DECLSPEC_HIDDEN;
@ -99,6 +107,7 @@ extern GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
GDIPCONST GpRectF* rects, INT count) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_SetClipRect(GpMetafile* metafile,
REAL x, REAL y, REAL width, REAL height, CombineMode mode) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order) DECLSPEC_HIDDEN;
@ -113,6 +122,13 @@ extern GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex) DE
extern GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_DrawImagePointsRect(GpMetafile* metafile, GpImage *image,
GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
DrawImageAbort callback, VOID *callbackData) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path) DECLSPEC_HIDDEN;
extern GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path) DECLSPEC_HIDDEN;
extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1,
REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN;
@ -123,6 +139,9 @@ extern void free_installed_fonts(void) DECLSPEC_HIDDEN;
extern BOOL lengthen_path(GpPath *path, INT len) DECLSPEC_HIDDEN;
extern DWORD write_region_data(const GpRegion *region, void *data) DECLSPEC_HIDDEN;
extern DWORD write_path_data(GpPath *path, void *data) DECLSPEC_HIDDEN;
extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN;
typedef struct region_element region_element;
@ -249,6 +268,8 @@ struct GpGraphics{
struct list containers;
GraphicsContainer contid; /* last-issued container ID */
INT origin_x, origin_y;
INT gdi_transform_acquire_count, gdi_transform_save;
GpMatrix gdi_transform;
/* For giving the caller an HDC when we technically can't: */
HBITMAP temp_hbitmap;
int temp_hbitmap_width;
@ -307,6 +328,7 @@ struct GpLineGradient{
ARGB* pblendcolor; /* preset blend colors */
REAL* pblendpos; /* preset blend positions */
INT pblendcount;
GpMatrix transform;
};
struct GpTexture{
@ -373,6 +395,7 @@ struct GpMetafile{
IStream *record_stream;
BOOL auto_frame; /* If true, determine the frame automatically */
GpPointF auto_frame_min, auto_frame_max;
DWORD next_object_id;
/* playback */
GpGraphics *playback_graphics;

View file

@ -38,6 +38,10 @@ static volatile LONG g_priv_contid = GDIP_CONTID_STEP;
#define GDIP_GET_NEW_CONTID_FOR(pGpGraphics) \
(UINT)(InterlockedExchangeAdd(&g_priv_contid,GDIP_CONTID_STEP))
/* ReactOS FIXME: Inspect */
#define fmax max
/* looks-right constants */
#define ANCHOR_WIDTH (2.0)
#define MAX_ITERS (50)
@ -245,6 +249,14 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
width *= graphics->scale;
pt[0].X = 0.0;
pt[0].Y = 0.0;
pt[1].X = 1.0;
pt[1].Y = 1.0;
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, pt, 2);
width *= sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
(pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
}
if(pen->dash == DashStyleCustom){
@ -280,43 +292,23 @@ static void restore_dc(GpGraphics *graphics, INT state)
RestoreDC(graphics->hdc, state);
}
/* This helper applies all the changes that the points listed in ptf need in
* order to be drawn on the device context. In the end, this should include at
* least:
* -scaling by page unit
* -applying world transformation
* -converting from float to int
* Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
* SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
* gdi to draw, and these functions would irreparably mess with line widths.
*/
static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
GpPointF *ptf, INT count)
static void round_points(POINT *pti, GpPointF *ptf, INT count)
{
REAL scale_x, scale_y;
GpMatrix matrix;
int i;
scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
/* apply page scale */
if(graphics->unit != UnitDisplay)
{
scale_x *= graphics->scale;
scale_y *= graphics->scale;
}
matrix = graphics->worldtrans;
GdipScaleMatrix(&matrix, scale_x, scale_y, MatrixOrderAppend);
GdipTransformMatrixPoints(&matrix, ptf, count);
for(i = 0; i < count; i++){
pti[i].x = gdip_round(ptf[i].X);
pti[i].y = gdip_round(ptf[i].Y);
}
}
static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
GpPointF *ptf, INT count)
{
gdip_transform_points(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, ptf, count);
round_points(pti, ptf, count);
}
static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
{
@ -344,8 +336,26 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_
static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
{
/* clipping region is in device coords */
return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
GpRegion *rgn;
GpMatrix transform;
GpStatus stat;
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, &transform);
if (stat == Ok)
stat = GdipCloneRegion(graphics->clip, &rgn);
if (stat == Ok)
{
stat = GdipTransformRegion(rgn, &transform);
if (stat == Ok)
stat = GdipGetRegionHRgn(rgn, NULL, hrgn);
GdipDeleteRegion(rgn);
}
return stat;
}
/* Draw ARGB data to the given graphics object */
@ -555,6 +565,7 @@ static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
REAL blendfac;
/* clamp to between 0.0 and 1.0, using the wrap mode */
position = (position - brush->rect.X) / brush->rect.Width;
if (brush->wrap == WrapModeTile)
{
position = fmodf(position, 1.0f);
@ -905,9 +916,8 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
if (y < 0)
y = height*2 + y % (height * 2);
if ((attributes->wrap & 1) == 1)
if (attributes->wrap & WrapModeTileFlipX)
{
/* Flip X */
if ((x / width) % 2 == 0)
x = x % width;
else
@ -916,9 +926,8 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
else
x = x % width;
if ((attributes->wrap & 2) == 2)
if (attributes->wrap & WrapModeTileFlipY)
{
/* Flip Y */
if ((y / height) % 2 == 0)
y = y % height;
else
@ -1220,10 +1229,8 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
case BrushTypeLinearGradient:
{
GpLineGradient *fill = (GpLineGradient*)brush;
GpPointF draw_points[3], line_points[3];
GpPointF draw_points[3];
GpStatus stat;
static const GpRectF box_1 = { 0.0, 0.0, 1.0, 1.0 };
GpMatrix *world_to_gradient; /* FIXME: Store this in the brush? */
int x, y;
draw_points[0].X = fill_area->X;
@ -1236,27 +1243,16 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
/* Transform the points to a co-ordinate space where X is the point's
* position in the gradient, 0.0 being the start point and 1.0 the
* end point. */
stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
CoordinateSpaceDevice, draw_points, 3);
stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
WineCoordinateSpaceGdiDevice, draw_points, 3);
if (stat == Ok)
{
line_points[0] = fill->startpoint;
line_points[1] = fill->endpoint;
line_points[2].X = fill->startpoint.X + (fill->startpoint.Y - fill->endpoint.Y);
line_points[2].Y = fill->startpoint.Y + (fill->endpoint.X - fill->startpoint.X);
stat = GdipCreateMatrix3(&box_1, line_points, &world_to_gradient);
}
if (stat == Ok)
{
stat = GdipInvertMatrix(world_to_gradient);
GpMatrix world_to_gradient = fill->transform;
stat = GdipInvertMatrix(&world_to_gradient);
if (stat == Ok)
stat = GdipTransformMatrixPoints(world_to_gradient, draw_points, 3);
GdipDeleteMatrix(world_to_gradient);
stat = GdipTransformMatrixPoints(&world_to_gradient, draw_points, 3);
}
if (stat == Ok)
@ -1308,8 +1304,8 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
draw_points[2].Y = fill_area->Y+1;
/* Transform the points to the co-ordinate space of the bitmap. */
stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
CoordinateSpaceDevice, draw_points, 3);
stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
WineCoordinateSpaceGdiDevice, draw_points, 3);
if (stat == Ok)
{
@ -1439,7 +1435,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
if (stat != Ok)
return stat;
stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
CoordinateSpaceWorld, &world_to_device);
if (stat == Ok)
{
@ -1575,8 +1571,17 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
REAL blend_amount, pdy, pdx;
pdy = yf - center_point.Y;
pdx = xf - center_point.X;
blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
outer_color = blend_colors(start_color, end_color, blend_amount);
if (fabs(pdx) <= 0.001 && fabs(pdy) <= 0.001)
{
/* Too close to center point, don't try to calculate outer color */
outer_color = start_color;
}
else
{
blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
outer_color = blend_colors(start_color, end_color, blend_amount);
}
}
distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
@ -1662,7 +1667,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
ptf[3].X = x2 - dbig;
ptf[2].X = x2 + dsmall;
transform_and_round_points(graphics, pt, ptf, 4);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
round_points(pt, ptf, 3);
Polygon(graphics->hdc, pt, 4);
break;
@ -1684,7 +1692,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
ptf[2].X = x2;
ptf[2].Y = y2;
transform_and_round_points(graphics, pt, ptf, 3);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
round_points(pt, ptf, 3);
Polygon(graphics->hdc, pt, 3);
break;
@ -1696,7 +1707,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
ptf[1].X = x2 + dx;
ptf[1].Y = y2 + dy;
transform_and_round_points(graphics, pt, ptf, 2);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
round_points(pt, ptf, 3);
Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
break;
@ -1716,7 +1730,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
ptf[2].X = x2 + dx;
ptf[2].Y = y2 + dy;
transform_and_round_points(graphics, pt, ptf, 3);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
round_points(pt, ptf, 3);
Polygon(graphics->hdc, pt, 3);
break;
@ -1736,7 +1753,10 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
ptf[3].X = x2 + dx;
ptf[3].Y = y2 + dy;
transform_and_round_points(graphics, pt, ptf, 4);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
round_points(pt, ptf, 3);
Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
pt[2].y, pt[3].x, pt[3].y);
@ -1762,7 +1782,9 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
GdipTransformMatrixPoints(&matrix, custptf, count);
transform_and_round_points(graphics, custpt, custptf, count);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
round_points(pt, ptf, 3);
for(i = 0; i < count; i++)
tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
@ -1985,7 +2007,9 @@ static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *
}
}
transform_and_round_points(graphics, pti, ptcopy, count);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, count);
round_points(pti, ptcopy, count);
for(i = 0; i < count; i++){
tp[i] = convert_path_point_type(types[i]);
@ -2108,7 +2132,7 @@ static GpStatus restore_container(GpGraphics* graphics,
return Ok;
}
static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
static GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect)
{
RECT wnd_rect;
GpStatus stat=Ok;
@ -2152,21 +2176,39 @@ static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
}
if (graphics->hdc)
return stat;
}
static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
{
GpStatus stat = get_graphics_device_bounds(graphics, rect);
if (stat == Ok && graphics->hdc)
{
POINT points[2];
GpPointF points[4], min_point, max_point;
int i;
points[0].x = rect->X;
points[0].y = rect->Y;
points[1].x = rect->X + rect->Width;
points[1].y = rect->Y + rect->Height;
points[0].X = points[2].X = rect->X;
points[0].Y = points[1].Y = rect->Y;
points[1].X = points[3].X = rect->X + rect->Width;
points[2].Y = points[3].Y = rect->Y + rect->Height;
DPtoLP(graphics->hdc, points, sizeof(points)/sizeof(points[0]));
gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
rect->X = min(points[0].x, points[1].x);
rect->Y = min(points[0].y, points[1].y);
rect->Width = abs(points[1].x - points[0].x);
rect->Height = abs(points[1].y - points[0].y);
min_point = max_point = points[0];
for (i=1; i<4; i++)
{
if (points[i].X < min_point.X) min_point.X = points[i].X;
if (points[i].Y < min_point.Y) min_point.Y = points[i].Y;
if (points[i].X > max_point.X) max_point.X = points[i].X;
if (points[i].Y > max_point.Y) max_point.Y = points[i].Y;
}
rect->X = min_point.X;
rect->Y = min_point.Y;
rect->Width = max_point.X - min_point.X;
rect->Height = max_point.Y - min_point.Y;
}
return stat;
@ -2180,6 +2222,10 @@ static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
GpRectF rectf;
GpRegion* tmp;
/* Ignore graphics image bounds for metafiles */
if (graphics->image && graphics->image_type == ImageTypeMetafile)
return GdipCombineRegionRegion(rgn, graphics->clip, CombineModeReplace);
if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
return stat;
@ -2954,6 +3000,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
debugstr_pointf(&points[2]));
if (graphics->image && graphics->image->type == ImageTypeMetafile)
{
return METAFILE_DrawImagePointsRect((GpMetafile*)graphics->image,
image, points, count, srcx, srcy, srcwidth, srcheight,
srcUnit, imageAttributes, callback, callbackData);
}
memcpy(ptf, points, 3 * sizeof(GpPointF));
/* Ensure source width/height is positive */
@ -2983,7 +3036,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
return Ok;
transform_and_round_points(graphics, pti, ptf, 4);
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
round_points(pti, ptf, 4);
TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
@ -3045,7 +3099,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
}
stat = get_graphics_bounds(graphics, &graphics_bounds);
stat = get_graphics_device_bounds(graphics, &graphics_bounds);
if (stat != Ok) return stat;
if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
@ -3183,10 +3237,14 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
dst_stride = src_stride;
}
gdi_transform_acquire(graphics);
stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
lockeddata.PixelFormat);
gdi_transform_release(graphics);
heap_free(src_data);
heap_free(dst_dyn_data);
@ -3270,6 +3328,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
DeleteObject(hrgn);
}
gdi_transform_acquire(graphics);
if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
{
gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
@ -3281,6 +3341,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
}
gdi_transform_release(graphics);
RestoreDC(graphics->hdc, save_state);
if (temp_hdc)
@ -3506,9 +3568,13 @@ static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *pat
if (hrgn)
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
gdi_transform_acquire(graphics);
retval = draw_poly(graphics, pen, path->pathdata.Points,
path->pathdata.Types, path->pathdata.Count, TRUE);
gdi_transform_release(graphics);
end:
restore_dc(graphics, save_state);
DeleteObject(hrgn);
@ -3541,7 +3607,7 @@ static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPa
if (stat == Ok)
{
stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
CoordinateSpaceWorld, transform);
if (stat == Ok)
@ -3570,7 +3636,7 @@ static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPa
if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
}
stat = get_graphics_bounds(graphics, &gp_bound_rect);
stat = get_graphics_device_bounds(graphics, &gp_bound_rect);
}
if (stat == Ok)
@ -3708,6 +3774,9 @@ static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPa
end_pointi.X = floorf(end_point.X);
end_pointi.Y = floorf(end_point.Y);
if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
continue;
/* draw line segment */
if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
{
@ -3799,9 +3868,13 @@ static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPa
/* draw output image */
if (stat == Ok)
{
gdi_transform_acquire(graphics);
stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
(BYTE*)output_bits, output_width, output_height, output_width * 4,
PixelFormat32bppARGB);
gdi_transform_release(graphics);
}
heap_free(brush_bits);
@ -3819,6 +3892,7 @@ static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *
GpStatus stat;
GpPath *wide_path;
GpMatrix *transform=NULL;
REAL flatness=1.0;
/* Check if the final pen thickness in pixels is too thin. */
if (pen->unit == UnitPixel)
@ -3860,9 +3934,24 @@ static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *
stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
CoordinateSpaceWorld, transform);
}
else
{
/* Set flatness based on the final coordinate space */
GpMatrix t;
stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
CoordinateSpaceWorld, &t);
if (stat != Ok)
return stat;
flatness = 1.0/sqrt(fmax(
t.matrix[0] * t.matrix[0] + t.matrix[1] * t.matrix[1],
t.matrix[2] * t.matrix[2] + t.matrix[3] * t.matrix[3]));
}
if (stat == Ok)
stat = GdipWidenPath(wide_path, pen, transform, 1.0);
stat = GdipWidenPath(wide_path, pen, transform, flatness);
if (pen->unit == UnitPixel)
{
@ -3900,7 +3989,9 @@ GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
if (path->pathdata.Count == 0)
return Ok;
if (!graphics->hdc || !brush_can_fill_path(pen->brush, FALSE))
if (graphics->image && graphics->image->type == ImageTypeMetafile)
retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path);
else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE))
retval = SOFTWARE_GdipDrawPath(graphics, pen, path);
else
retval = GDI32_GdipDrawPath(graphics, pen, path);
@ -4166,17 +4257,19 @@ static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath
if (hrgn)
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
gdi_transform_acquire(graphics);
BeginPath(graphics->hdc);
retval = draw_poly(graphics, NULL, path->pathdata.Points,
path->pathdata.Types, path->pathdata.Count, FALSE);
if(retval != Ok)
goto end;
if(retval == Ok)
{
EndPath(graphics->hdc);
brush_fill_path(graphics, brush);
}
EndPath(graphics->hdc);
brush_fill_path(graphics, brush);
retval = Ok;
gdi_transform_release(graphics);
end:
RestoreDC(graphics->hdc, save_state);
@ -4219,6 +4312,9 @@ GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *p
if(graphics->busy)
return ObjectBusy;
if (graphics->image && graphics->image->type == ImageTypeMetafile)
return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path);
if (!graphics->image && !graphics->alpha_hdc)
stat = GDI32_GdipFillPath(graphics, brush, path);
@ -4425,7 +4521,7 @@ GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GD
for(i = 0; i < count; i++){
rectsF[i].X = (REAL)rects[i].X;
rectsF[i].Y = (REAL)rects[i].Y;
rectsF[i].X = (REAL)rects[i].Width;
rectsF[i].Width = (REAL)rects[i].Width;
rectsF[i].Height = (REAL)rects[i].Height;
}
@ -4502,14 +4598,17 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
if (!brush_can_fill_pixels(brush))
return NotImplemented;
stat = get_graphics_bounds(graphics, &graphics_bounds);
stat = gdi_transform_acquire(graphics);
if (stat == Ok)
stat = get_graphics_device_bounds(graphics, &graphics_bounds);
if (stat == Ok)
stat = GdipCloneRegion(region, &temp_region);
if (stat == Ok)
{
stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
CoordinateSpaceWorld, &world_to_device);
if (stat == Ok)
@ -4527,6 +4626,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
{
DeleteObject(hregion);
gdi_transform_release(graphics);
return Ok;
}
@ -4558,6 +4658,8 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
DeleteObject(hregion);
}
gdi_transform_release(graphics);
return stat;
}
@ -5889,6 +5991,19 @@ GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
if(graphics->busy)
return ObjectBusy;
if(graphics->compmode == mode)
return Ok;
if(graphics->image && graphics->image->type == ImageTypeMetafile)
{
GpStatus stat;
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
EmfPlusRecordTypeSetCompositingMode, mode);
if(stat != Ok)
return stat;
}
graphics->compmode = mode;
return Ok;
@ -5905,6 +6020,19 @@ GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
if(graphics->busy)
return ObjectBusy;
if(graphics->compqual == quality)
return Ok;
if(graphics->image && graphics->image->type == ImageTypeMetafile)
{
GpStatus stat;
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
EmfPlusRecordTypeSetCompositingQuality, quality);
if(stat != Ok)
return stat;
}
graphics->compqual = quality;
return Ok;
@ -5927,6 +6055,19 @@ GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
if (mode == InterpolationModeHighQuality)
mode = InterpolationModeHighQualityBicubic;
if (mode == graphics->interpolation)
return Ok;
if (graphics->image && graphics->image->type == ImageTypeMetafile)
{
GpStatus stat;
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
EmfPlusRecordTypeSetInterpolationMode, mode);
if (stat != Ok)
return stat;
}
graphics->interpolation = mode;
return Ok;
@ -5994,6 +6135,19 @@ GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
if(graphics->busy)
return ObjectBusy;
if(graphics->pixeloffset == mode)
return Ok;
if(graphics->image && graphics->image->type == ImageTypeMetafile)
{
GpStatus stat;
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
EmfPlusRecordTypeSetPixelOffsetMode, mode);
if(stat != Ok)
return stat;
}
graphics->pixeloffset = mode;
return Ok;
@ -6040,6 +6194,20 @@ GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mod
if(graphics->busy)
return ObjectBusy;
if(graphics->smoothing == mode)
return Ok;
if(graphics->image && graphics->image->type == ImageTypeMetafile) {
GpStatus stat;
BOOL antialias = (mode != SmoothingModeDefault &&
mode != SmoothingModeNone && mode != SmoothingModeHighSpeed);
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
EmfPlusRecordTypeSetAntiAliasMode, (mode << 1) + antialias);
if(stat != Ok)
return stat;
}
graphics->smoothing = mode;
return Ok;
@ -6068,6 +6236,18 @@ GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
if(graphics->busy)
return ObjectBusy;
if(graphics->texthint == hint)
return Ok;
if(graphics->image && graphics->image->type == ImageTypeMetafile) {
GpStatus stat;
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
EmfPlusRecordTypeSetTextRenderingHint, hint);
if(stat != Ok)
return stat;
}
graphics->texthint = hint;
return Ok;
@ -6251,6 +6431,13 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
if(graphics->busy)
return ObjectBusy;
if (graphics->image && graphics->image->type == ImageTypeMetafile)
{
status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode);
if (status != Ok)
return status;
}
status = GdipCloneRegion(region, &clip);
if (status == Ok)
{
@ -6536,6 +6723,56 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
return Ok;
}
static void get_gdi_transform(GpGraphics *graphics, GpMatrix *matrix)
{
XFORM xform;
if (graphics->hdc == NULL)
{
GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
return;
}
if (graphics->gdi_transform_acquire_count)
{
*matrix = graphics->gdi_transform;
return;
}
GetTransform(graphics->hdc, 0x204, &xform);
GdipSetMatrixElements(matrix, xform.eM11, xform.eM12, xform.eM21, xform.eM22, xform.eDx, xform.eDy);
}
GpStatus gdi_transform_acquire(GpGraphics *graphics)
{
if (graphics->gdi_transform_acquire_count == 0 && graphics->hdc)
{
get_gdi_transform(graphics, &graphics->gdi_transform);
graphics->gdi_transform_save = SaveDC(graphics->hdc);
SetGraphicsMode(graphics->hdc, GM_COMPATIBLE);
SetMapMode(graphics->hdc, MM_TEXT);
SetWindowOrgEx(graphics->hdc, 0, 0, NULL);
SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
}
graphics->gdi_transform_acquire_count++;
return Ok;
}
GpStatus gdi_transform_release(GpGraphics *graphics)
{
if (graphics->gdi_transform_acquire_count <= 0)
{
ERR("called without matching gdi_transform_acquire");
return GenericError;
}
if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
{
RestoreDC(graphics->hdc, graphics->gdi_transform_save);
}
graphics->gdi_transform_acquire_count--;
return Ok;
}
GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
GpCoordinateSpace src_space, GpMatrix *matrix)
{
@ -6555,23 +6792,29 @@ GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_spac
scale_y *= graphics->scale;
}
/* transform from src_space to CoordinateSpacePage */
switch (src_space)
if (dst_space < src_space)
{
case CoordinateSpaceWorld:
GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
break;
case CoordinateSpacePage:
break;
case CoordinateSpaceDevice:
GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
break;
}
/* transform from CoordinateSpacePage to dst_space */
switch (dst_space)
{
case CoordinateSpaceWorld:
/* transform towards world space */
switch ((int)src_space)
{
case WineCoordinateSpaceGdiDevice:
{
GpMatrix gdixform;
get_gdi_transform(graphics, &gdixform);
stat = GdipInvertMatrix(&gdixform);
if (stat != Ok)
break;
GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
if (dst_space == CoordinateSpaceDevice)
break;
/* else fall-through */
}
case CoordinateSpaceDevice:
GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
if (dst_space == CoordinateSpacePage)
break;
/* else fall-through */
case CoordinateSpacePage:
{
GpMatrix inverted_transform = graphics->worldtrans;
stat = GdipInvertMatrix(&inverted_transform);
@ -6579,23 +6822,54 @@ GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_spac
GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
break;
}
case CoordinateSpacePage:
break;
case CoordinateSpaceDevice:
GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
break;
}
}
else
{
/* transform towards device space */
switch ((int)src_space)
{
case CoordinateSpaceWorld:
GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
if (dst_space == CoordinateSpacePage)
break;
/* else fall-through */
case CoordinateSpacePage:
GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
if (dst_space == CoordinateSpaceDevice)
break;
/* else fall-through */
case CoordinateSpaceDevice:
{
GpMatrix gdixform;
get_gdi_transform(graphics, &gdixform);
GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
break;
}
}
}
}
return stat;
}
GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
GpCoordinateSpace src_space, GpPointF *points, INT count)
GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
GpCoordinateSpace src_space, GpPointF *points, INT count)
{
GpMatrix matrix;
GpStatus stat;
if(!graphics || !points || count <= 0)
stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
if (stat != Ok) return stat;
return GdipTransformMatrixPoints(&matrix, points, count);
}
GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
GpCoordinateSpace src_space, GpPointF *points, INT count)
{
if(!graphics || !points || count <= 0 ||
dst_space < 0 || dst_space > CoordinateSpaceDevice ||
src_space < 0 || src_space > CoordinateSpaceDevice)
return InvalidParameter;
if(graphics->busy)
@ -6605,10 +6879,7 @@ GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace
if (src_space == dst_space) return Ok;
stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
if (stat != Ok) return stat;
return GdipTransformMatrixPoints(&matrix, points, count);
return gdip_transform_points(graphics, dst_space, src_space, points, count);
}
GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
@ -7129,3 +7400,16 @@ GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
return NotImplemented;
}
GpStatus WINGDIPAPI GdipGraphicsSetAbort(GpGraphics *graphics, GdiplusAbort *pabort)
{
TRACE("(%p, %p)\n", graphics, pabort);
if (!graphics)
return InvalidParameter;
if (pabort)
FIXME("Abort callback is not supported.\n");
return Ok;
}

View file

@ -152,6 +152,32 @@ static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, R
return TRUE;
}
/*******************************************************************************
* GdipAddPathArc [GDIPLUS.1]
*
* Add an elliptical arc to the given path.
*
* PARAMS
* path [I/O] Path that the arc is appended to
* x1 [I] X coordinate of the boundary box
* y1 [I] Y coordinate of the boundary box
* x2 [I] Width of the boundary box
* y2 [I] Height of the boundary box
* startAngle [I] Starting angle of the arc, clockwise
* sweepAngle [I] Angle of the arc, clockwise
*
* RETURNS
* InvalidParameter If the given path is invalid
* OutOfMemory If memory allocation fails, i.e. the path cannot be lengthened
* Ok If everything works out as expected
*
* NOTES
* This functions takes the newfigure value of the given path into account,
* i.e. the arc is connected to the end of the given path if it was set to
* FALSE, otherwise the arc's first point gets the PathPointTypeStart value.
* In both cases, the value of newfigure of the given path is FALSE
* afterwards.
*/
GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
REAL y2, REAL startAngle, REAL sweepAngle)
{
@ -186,6 +212,11 @@ GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
return Ok;
}
/*******************************************************************************
* GdipAddPathArcI [GDUPLUS.2]
*
* See GdipAddPathArc
*/
GpStatus WINGDIPAPI GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
INT y2, REAL startAngle, REAL sweepAngle)
{
@ -632,6 +663,30 @@ GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, I
return stat;
}
/*************************************************************************
* GdipAddPathLine [GDIPLUS.21]
*
* Add two points to the given path.
*
* PARAMS
* path [I/O] Path that the line is appended to
* x1 [I] X coordinate of the first point of the line
* y1 [I] Y coordinate of the first point of the line
* x2 [I] X coordinate of the second point of the line
* y2 [I] Y coordinate of the second point of the line
*
* RETURNS
* InvalidParameter If the first parameter is not a valid path
* OutOfMemory If the path cannot be lengthened, i.e. memory allocation fails
* Ok If everything works out as expected
*
* NOTES
* This functions takes the newfigure value of the given path into account,
* i.e. the two new points are connected to the end of the given path if it
* was set to FALSE, otherwise the first point is given the PathPointTypeStart
* value. In both cases, the value of newfigure of the given path is FALSE
* afterwards.
*/
GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
{
INT old_count;
@ -661,6 +716,11 @@ GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REA
return Ok;
}
/*************************************************************************
* GdipAddPathLineI [GDIPLUS.21]
*
* See GdipAddPathLine
*/
GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
{
TRACE("(%p, %d, %d, %d, %d)\n", path, x1, y1, x2, y2);
@ -1044,6 +1104,20 @@ GpStatus WINGDIPAPI GdipAddPathStringI(GpPath* path, GDIPCONST WCHAR* string, IN
return InvalidParameter;
}
/*************************************************************************
* GdipClonePath [GDIPLUS.53]
*
* Duplicate the given path in memory.
*
* PARAMS
* path [I] The path to be duplicated
* clone [O] Pointer to the new path
*
* RETURNS
* InvalidParameter If the input path is invalid
* OutOfMemory If allocation of needed memory fails
* Ok If everything works out as expected
*/
GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
{
TRACE("(%p, %p)\n", path, clone);
@ -1869,6 +1943,27 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint,
}
break;
}
case LineCapTriangle:
{
REAL segment_dy = nextpoint->Y-endpoint->Y;
REAL segment_dx = nextpoint->X-endpoint->X;
REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
REAL distance = pen->width/2.0;
REAL dx, dy;
dx = distance * segment_dx / segment_length;
dy = distance * segment_dy / segment_length;
if (add_first_points) {
add_bevel_point(endpoint, nextpoint, pen, 1, last_point);
*last_point = add_path_list_node(*last_point, endpoint->X - dx,
endpoint->Y - dy, PathPointTypeLine);
}
if (add_last_point)
add_bevel_point(endpoint, nextpoint, pen, 0, last_point);
break;
}
}
}
@ -1954,6 +2049,7 @@ static void widen_dashed_figure(GpPath *path, GpPen *pen, int start, int end,
REAL dash_pos=0.0;
int dash_index=0;
const REAL *dash_pattern;
REAL *dash_pattern_scaled;
int dash_count;
GpPointF *tmp_points;
REAL segment_dy;
@ -1992,8 +2088,17 @@ static void widen_dashed_figure(GpPath *path, GpPen *pen, int start, int end,
break;
}
dash_pattern_scaled = heap_alloc(dash_count * sizeof(REAL));
if (!dash_pattern_scaled) return;
for (i = 0; i < dash_count; i++)
dash_pattern_scaled[i] = pen->width * dash_pattern[i];
tmp_points = heap_alloc_zero((end - start + 2) * sizeof(GpPoint));
if (!tmp_points) return; /* FIXME */
if (!tmp_points) {
heap_free(dash_pattern_scaled);
return; /* FIXME */
}
if (!closed)
draw_start_cap = 1;
@ -2040,7 +2145,7 @@ static void widen_dashed_figure(GpPath *path, GpPen *pen, int start, int end,
}
}
if (dash_pattern[dash_index] - dash_pos > segment_length - segment_pos)
if (dash_pattern_scaled[dash_index] - dash_pos > segment_length - segment_pos)
{
/* advance to next segment */
if ((dash_index % 2) == 0)
@ -2054,7 +2159,7 @@ static void widen_dashed_figure(GpPath *path, GpPen *pen, int start, int end,
else
{
/* advance to next dash in pattern */
segment_pos += dash_pattern[dash_index] - dash_pos;
segment_pos += dash_pattern_scaled[dash_index] - dash_pos;
dash_pos = 0.0;
if (++dash_index == dash_count)
dash_index = 0;
@ -2066,12 +2171,12 @@ static void widen_dashed_figure(GpPath *path, GpPen *pen, int start, int end,
if (dash_index % 2 == 0 && num_tmp_points != 0)
{
/* last dash overflows last segment */
tmp_points[num_tmp_points] = path->pathdata.Points[end];
widen_open_figure(tmp_points, pen, 0, num_tmp_points,
widen_open_figure(tmp_points, pen, 0, num_tmp_points-1,
draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart,
closed ? LineCapFlat : pen->endcap, pen->customend, last_point);
}
heap_free(dash_pattern_scaled);
heap_free(tmp_points);
}
@ -2104,10 +2209,10 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix,
{
last_point = points;
if (pen->endcap > LineCapRound)
if (pen->endcap > LineCapTriangle)
FIXME("unimplemented end cap %x\n", pen->endcap);
if (pen->startcap > LineCapRound)
if (pen->startcap > LineCapTriangle)
FIXME("unimplemented start cap %x\n", pen->startcap);
if (pen->dashcap != DashCapFlat)
@ -2338,3 +2443,77 @@ GpStatus WINGDIPAPI GdipWindingModeOutline(GpPath *path, GpMatrix *matrix, REAL
FIXME("stub: %p, %p, %.2f\n", path, matrix, flatness);
return NotImplemented;
}
#define FLAGS_INTPATH 0x4000
struct path_header
{
DWORD version;
DWORD count;
DWORD flags;
};
/* Test to see if the path could be stored as an array of shorts */
static BOOL is_integer_path(const GpPath *path)
{
int i;
if (!path->pathdata.Count) return FALSE;
for (i = 0; i < path->pathdata.Count; i++)
{
short x, y;
x = gdip_round(path->pathdata.Points[i].X);
y = gdip_round(path->pathdata.Points[i].Y);
if (path->pathdata.Points[i].X != (REAL)x || path->pathdata.Points[i].Y != (REAL)y)
return FALSE;
}
return TRUE;
}
DWORD write_path_data(GpPath *path, void *data)
{
struct path_header *header = data;
BOOL integer_path = is_integer_path(path);
DWORD i, size;
BYTE *types;
size = sizeof(struct path_header) + path->pathdata.Count;
if (integer_path)
size += sizeof(short[2]) * path->pathdata.Count;
else
size += sizeof(float[2]) * path->pathdata.Count;
size = (size + 3) & ~3;
if (!data) return size;
header->version = VERSION_MAGIC2;
header->count = path->pathdata.Count;
header->flags = integer_path ? FLAGS_INTPATH : 0;
if (integer_path)
{
short *points = (short*)(header + 1);
for (i = 0; i < path->pathdata.Count; i++)
{
points[2*i] = path->pathdata.Points[i].X;
points[2*i + 1] = path->pathdata.Points[i].Y;
}
types = (BYTE*)(points + 2*i);
}
else
{
float *points = (float*)(header + 1);
for (i = 0; i < path->pathdata.Count; i++)
{
points[2*i] = path->pathdata.Points[i].X;
points[2*i + 1] = path->pathdata.Points[i].Y;
}
types = (BYTE*)(points + 2*i);
}
for (i=0; i<path->pathdata.Count; i++)
types[i] = path->pathdata.Types[i];
memset(types + i, 0, ((path->pathdata.Count + 3) & ~3) - path->pathdata.Count);
return size;
}

View file

@ -1322,6 +1322,7 @@ GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage)
result->unit = metafile->unit;
result->metafile_type = metafile->metafile_type;
result->hemf = CopyEnhMetaFileW(metafile->hemf, NULL);
list_init(&result->containers);
if (!result->hemf)
{
@ -4165,7 +4166,7 @@ static GpStatus decode_image_emf(IStream *stream, GpImage **image)
}
typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream,
GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params);
GDIPCONST EncoderParameters* params);
typedef GpStatus (*decode_image_func)(IStream *stream, GpImage **image);
@ -4541,31 +4542,31 @@ static GpStatus encode_image_wic(GpImage *image, IStream* stream,
}
static GpStatus encode_image_BMP(GpImage *image, IStream* stream,
GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
GDIPCONST EncoderParameters* params)
{
return encode_image_wic(image, stream, &GUID_ContainerFormatBmp, params);
}
static GpStatus encode_image_tiff(GpImage *image, IStream* stream,
GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
GDIPCONST EncoderParameters* params)
{
return encode_image_wic(image, stream, &GUID_ContainerFormatTiff, params);
}
static GpStatus encode_image_png(GpImage *image, IStream* stream,
GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
GpStatus encode_image_png(GpImage *image, IStream* stream,
GDIPCONST EncoderParameters* params)
{
return encode_image_wic(image, stream, &GUID_ContainerFormatPng, params);
}
static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
GDIPCONST EncoderParameters* params)
{
return encode_image_wic(image, stream, &GUID_ContainerFormatJpeg, params);
}
static GpStatus encode_image_gif(GpImage *image, IStream* stream,
GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
GDIPCONST EncoderParameters* params)
{
return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params);
}
@ -4595,7 +4596,7 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
if (encode_image == NULL)
return UnknownImageFormat;
stat = encode_image(image, stream, clsid, params);
stat = encode_image(image, stream, params);
return stat;
}

View file

@ -184,6 +184,17 @@ GpStatus WINGDIPAPI GdipInvertMatrix(GpMatrix *matrix)
if(!invertible)
return InvalidParameter;
/* optimize inverting simple scaling and translation matrices */
if(matrix->matrix[1] == 0 && matrix->matrix[2] == 0)
{
matrix->matrix[4] = -matrix->matrix[4] / matrix->matrix[0];
matrix->matrix[5] = -matrix->matrix[5] / matrix->matrix[3];
matrix->matrix[0] = 1 / matrix->matrix[0];
matrix->matrix[3] = 1 / matrix->matrix[3];
return Ok;
}
det = matrix_det(matrix);
copy = *matrix;
@ -205,7 +216,10 @@ GpStatus WINGDIPAPI GdipIsMatrixInvertible(GDIPCONST GpMatrix *matrix, BOOL *res
if(!matrix || !result)
return InvalidParameter;
*result = (fabs(matrix_det(matrix)) >= 1e-5);
if(matrix->matrix[1] == 0 && matrix->matrix[2] == 0)
*result = matrix->matrix[0] != 0 && matrix->matrix[3] != 0;
else
*result = (fabs(matrix_det(matrix)) >= 1e-5);
return Ok;
}

View file

@ -18,6 +18,17 @@
#include "gdiplus_private.h"
#include <assert.h>
#include <ole2.h>
typedef struct EmfPlusARGB
{
BYTE Blue;
BYTE Green;
BYTE Red;
BYTE Alpha;
} EmfPlusARGB;
typedef struct EmfPlusRecordHeader
{
WORD Type;
@ -132,6 +143,216 @@ typedef struct container
GpRegion *clip;
} container;
enum PenDataFlags
{
PenDataTransform = 0x0001,
PenDataStartCap = 0x0002,
PenDataEndCap = 0x0004,
PenDataJoin = 0x0008,
PenDataMiterLimit = 0x0010,
PenDataLineStyle = 0x0020,
PenDataDashedLineCap = 0x0040,
PenDataDashedLineOffset = 0x0080,
PenDataDashedLine = 0x0100,
PenDataNonCenter = 0x0200,
PenDataCompoundLine = 0x0400,
PenDataCustomStartCap = 0x0800,
PenDataCustomEndCap = 0x1000
};
typedef struct EmfPlusTransformMatrix
{
REAL TransformMatrix[6];
} EmfPlusTransformMatrix;
enum LineStyle
{
LineStyleSolid,
LineStyleDash,
LineStyleDot,
LineStyleDashDot,
LineStyleDashDotDot,
LineStyleCustom
};
typedef struct EmfPlusPenData
{
DWORD PenDataFlags;
DWORD PenUnit;
REAL PenWidth;
BYTE OptionalData[1];
} EmfPlusPenData;
typedef struct EmfPlusSolidBrushData
{
EmfPlusARGB SolidColor;
} EmfPlusSolidBrushData;
typedef struct EmfPlusBrush
{
DWORD Version;
DWORD Type;
union {
EmfPlusSolidBrushData solid;
} BrushData;
} EmfPlusBrush;
typedef struct EmfPlusPen
{
DWORD Version;
DWORD Type;
/* EmfPlusPenData */
/* EmfPlusBrush */
BYTE data[1];
} EmfPlusPen;
typedef struct EmfPlusPath
{
DWORD Version;
DWORD PathPointCount;
DWORD PathPointFlags;
/* PathPoints[] */
/* PathPointTypes[] */
/* AlignmentPadding */
BYTE data[1];
} EmfPlusPath;
typedef struct EmfPlusRegion
{
DWORD Version;
DWORD RegionNodeCount;
BYTE RegionNode[1];
} EmfPlusRegion;
typedef enum
{
BitmapDataTypePixel,
BitmapDataTypeCompressed,
} BitmapDataType;
typedef struct EmfPlusBitmap
{
DWORD Width;
DWORD Height;
DWORD Stride;
DWORD PixelFormat;
DWORD Type;
BYTE BitmapData[1];
} EmfPlusBitmap;
typedef struct EmfPlusMetafile
{
DWORD Type;
DWORD MetafileDataSize;
BYTE MetafileData[1];
} EmfPlusMetafile;
typedef enum ImageDataType
{
ImageDataTypeUnknown,
ImageDataTypeBitmap,
ImageDataTypeMetafile,
} ImageDataType;
typedef struct EmfPlusImage
{
DWORD Version;
ImageDataType Type;
union
{
EmfPlusBitmap bitmap;
EmfPlusMetafile metafile;
} ImageData;
} EmfPlusImage;
typedef struct EmfPlusImageAttributes
{
DWORD Version;
DWORD Reserved1;
DWORD WrapMode;
EmfPlusARGB ClampColor;
DWORD ObjectClamp;
DWORD Reserved2;
} EmfPlusImageAttributes;
typedef enum ObjectType
{
ObjectTypeInvalid,
ObjectTypeBrush,
ObjectTypePen,
ObjectTypePath,
ObjectTypeRegion,
ObjectTypeImage,
ObjectTypeFont,
ObjectTypeStringFormat,
ObjectTypeImageAttributes,
ObjectTypeCustomLineCap,
} ObjectType;
typedef struct EmfPlusObject
{
EmfPlusRecordHeader Header;
union
{
EmfPlusBrush brush;
EmfPlusPen pen;
EmfPlusPath path;
EmfPlusRegion region;
EmfPlusImage image;
EmfPlusImageAttributes image_attributes;
} ObjectData;
} EmfPlusObject;
typedef struct EmfPlusRectF
{
float X;
float Y;
float Width;
float Height;
} EmfPlusRectF;
typedef struct EmfPlusPointF
{
float X;
float Y;
} EmfPlusPointF;
typedef struct EmfPlusDrawImagePoints
{
EmfPlusRecordHeader Header;
DWORD ImageAttributesID;
DWORD SrcUnit;
EmfPlusRectF SrcRect;
DWORD count;
union
{
/*EmfPlusPointR pointR;
EmfPlusPoint point;*/
EmfPlusPointF pointF;
} PointData[3];
} EmfPlusDrawImagePoints;
typedef struct EmfPlusDrawPath
{
EmfPlusRecordHeader Header;
DWORD PenId;
} EmfPlusDrawPath;
typedef struct EmfPlusFillPath
{
EmfPlusRecordHeader Header;
union
{
DWORD BrushId;
EmfPlusARGB Color;
} data;
} EmfPlusFillPath;
static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
{
return (metafile->next_object_id++) % 64;
}
static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
{
DWORD size_needed;
@ -178,6 +399,12 @@ static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void *
return Ok;
}
static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
{
assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
metafile->comment_data_length -= record->Size;
}
static void METAFILE_WriteRecords(GpMetafile *metafile)
{
if (metafile->comment_data_length > 4)
@ -206,7 +433,7 @@ static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
else
header->Header.Flags = 0;
header->Version = 0xDBC01002;
header->Version = VERSION_MAGIC2;
if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
header->EmfPlusFlags = 1;
@ -606,6 +833,53 @@ GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width,
return Ok;
}
static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
{
EmfPlusObject *object_record;
DWORD size;
GpStatus stat;
*id = -1;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
size = write_region_data(region, NULL);
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
if (stat != Ok) return stat;
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | ObjectTypeRegion << 8;
write_region_data(region, &object_record->ObjectData.region);
return Ok;
}
GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
{
EmfPlusRecordHeader *record;
DWORD region_id;
GpStatus stat;
if (metafile->metafile_type == MetafileTypeEmf)
{
FIXME("stub!\n");
return NotImplemented;
}
stat = METAFILE_AddRegionObject(metafile, region, &region_id);
if (stat != Ok) return stat;
stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
if (stat != Ok) return stat;
record->Type = EmfPlusRecordTypeSetClipRegion;
record->Flags = region_id | mode << 8;
METAFILE_WriteRecords(metafile);
return Ok;
}
GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
{
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
@ -1127,8 +1401,6 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
/* regular EMF record */
if (metafile->playback_dc)
{
ENHMETARECORD *record;
switch (recordType)
{
case EMR_SETMAPMODE:
@ -1173,24 +1445,27 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
return Ok;
}
default:
{
ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
if (record)
{
record->iType = recordType;
record->nSize = dataSize + 8;
memcpy(record->dParm, data, dataSize);
if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
record, metafile->handle_count) == 0)
ERR("PlayEnhMetaFileRecord failed\n");
heap_free(record);
}
else
return OutOfMemory;
break;
}
record = heap_alloc_zero(dataSize + 8);
if (record)
{
record->iType = recordType;
record->nSize = dataSize + 8;
memcpy(record->dParm, data, dataSize);
PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
record, metafile->handle_count);
heap_free(record);
}
else
return OutOfMemory;
}
}
else
@ -1816,7 +2091,7 @@ GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
else
{
memset(header, 0, sizeof(*header));
header->Version = 0xdbc01002;
header->Version = VERSION_MAGIC2;
}
header->Type = metafile->metafile_type;
@ -2065,13 +2340,20 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
{
HMETAFILE hmf = GetMetaFileW(file);
HMETAFILE hmf;
HENHMETAFILE emf;
TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
if(!hmf) return InvalidParameter;
hmf = GetMetaFileW(file);
if(hmf)
return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
emf = GetEnhMetaFileW(file);
if(emf)
return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
return GenericError;
}
GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
@ -2185,3 +2467,585 @@ GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
return NotImplemented;
}
static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
{
LARGE_INTEGER zero;
STATSTG statstg;
GpStatus stat;
HRESULT hr;
*size = 0;
hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
if (FAILED(hr)) return hresult_to_status(hr);
stat = encode_image_png(image, *stream, NULL);
if (stat != Ok)
{
IStream_Release(*stream);
return stat;
}
hr = IStream_Stat(*stream, &statstg, 1);
if (FAILED(hr))
{
IStream_Release(*stream);
return hresult_to_status(hr);
}
*size = statstg.cbSize.u.LowPart;
zero.QuadPart = 0;
hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
{
IStream_Release(*stream);
return hresult_to_status(hr);
}
return Ok;
}
static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
{
HRESULT hr;
record->Width = 0;
record->Height = 0;
record->Stride = 0;
record->PixelFormat = 0;
record->Type = BitmapDataTypeCompressed;
hr = IStream_Read(stream, record->BitmapData, size, NULL);
if (FAILED(hr)) return hresult_to_status(hr);
return Ok;
}
static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
{
EmfPlusObject *object_record;
GpStatus stat;
DWORD size;
*id = -1;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
if (image->type == ImageTypeBitmap)
{
IStream *stream;
DWORD aligned_size;
stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
if (stat != Ok) return stat;
aligned_size = (size + 3) & ~3;
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
(void**)&object_record);
if (stat != Ok)
{
IStream_Release(stream);
return stat;
}
memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | ObjectTypeImage << 8;
object_record->ObjectData.image.Version = VERSION_MAGIC2;
object_record->ObjectData.image.Type = ImageDataTypeBitmap;
stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
IStream_Release(stream);
if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
return stat;
}
else if (image->type == ImageTypeMetafile)
{
HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
EmfPlusMetafile *metafile_record;
if (!hemf) return InvalidParameter;
size = GetEnhMetaFileBits(hemf, 0, NULL);
if (!size) return GenericError;
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
(void**)&object_record);
if (stat != Ok) return stat;
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | ObjectTypeImage << 8;
object_record->ObjectData.image.Version = VERSION_MAGIC2;
object_record->ObjectData.image.Type = ImageDataTypeMetafile;
metafile_record = &object_record->ObjectData.image.ImageData.metafile;
metafile_record->Type = ((GpMetafile*)image)->metafile_type;
metafile_record->MetafileDataSize = size;
if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
{
METAFILE_RemoveLastRecord(metafile, &object_record->Header);
return GenericError;
}
return Ok;
}
else
{
FIXME("not supported image type (%d)\n", image->type);
return NotImplemented;
}
}
static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
{
EmfPlusObject *object_record;
EmfPlusImageAttributes *attrs_record;
GpStatus stat;
*id = -1;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
if (!attrs)
return Ok;
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
(void**)&object_record);
if (stat != Ok) return stat;
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
attrs_record = &object_record->ObjectData.image_attributes;
attrs_record->Version = VERSION_MAGIC2;
attrs_record->Reserved1 = 0;
attrs_record->WrapMode = attrs->wrap;
attrs_record->ClampColor.Blue = attrs->outside_color & 0xff;
attrs_record->ClampColor.Green = (attrs->outside_color >> 8) & 0xff;
attrs_record->ClampColor.Red = (attrs->outside_color >> 16) & 0xff;
attrs_record->ClampColor.Alpha = attrs->outside_color >> 24;
attrs_record->ObjectClamp = attrs->clamp;
attrs_record->Reserved2 = 0;
return Ok;
}
GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
DrawImageAbort callback, VOID *callbackData)
{
EmfPlusDrawImagePoints *draw_image_record;
DWORD image_id, attributes_id;
GpStatus stat;
if (count != 3) return InvalidParameter;
if (metafile->metafile_type == MetafileTypeEmf)
{
FIXME("MetafileTypeEmf metafiles not supported\n");
return NotImplemented;
}
else
FIXME("semi-stub\n");
if (!imageAttributes)
{
stat = METAFILE_AddImageObject(metafile, image, &image_id);
}
else if (image->type == ImageTypeBitmap)
{
INT width = ((GpBitmap*)image)->width;
INT height = ((GpBitmap*)image)->height;
GpGraphics *graphics;
GpBitmap *bitmap;
stat = GdipCreateBitmapFromScan0(width, height,
0, PixelFormat32bppARGB, NULL, &bitmap);
if (stat != Ok) return stat;
stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
if (stat != Ok)
{
GdipDisposeImage((GpImage*)bitmap);
return stat;
}
stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
GdipDeleteGraphics(graphics);
if (stat != Ok)
{
GdipDisposeImage((GpImage*)bitmap);
return stat;
}
stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
GdipDisposeImage((GpImage*)bitmap);
}
else
{
FIXME("imageAttributes not supported (image type %d)\n", image->type);
return NotImplemented;
}
if (stat != Ok) return stat;
stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
if (stat != Ok) return stat;
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
if (stat != Ok) return stat;
draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
draw_image_record->Header.Flags = image_id;
draw_image_record->ImageAttributesID = attributes_id;
draw_image_record->SrcUnit = UnitPixel;
draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
draw_image_record->count = 3;
draw_image_record->PointData[0].pointF.X = points[0].X;
draw_image_record->PointData[0].pointF.Y = points[0].Y;
draw_image_record->PointData[1].pointF.X = points[1].X;
draw_image_record->PointData[1].pointF.Y = points[1].Y;
draw_image_record->PointData[2].pointF.X = points[2].X;
draw_image_record->PointData[2].pointF.Y = points[2].Y;
METAFILE_WriteRecords(metafile);
return Ok;
}
GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
{
EmfPlusRecordHeader *record;
GpStatus stat;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
if (stat != Ok) return stat;
record->Type = prop;
record->Flags = val;
METAFILE_WriteRecords(metafile);
return Ok;
}
static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
{
EmfPlusObject *object_record;
GpStatus stat;
DWORD size;
*id = -1;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
size = write_path_data(path, NULL);
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
(void**)&object_record);
if (stat != Ok) return stat;
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | ObjectTypePath << 8;
write_path_data(path, &object_record->ObjectData.path);
return Ok;
}
static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
{
if (brush->bt == BrushTypeSolidColor)
{
*size = FIELD_OFFSET(EmfPlusBrush, BrushData.solid) + sizeof(EmfPlusSolidBrushData);
return Ok;
}
FIXME("unsupported brush type: %d\n", brush->bt);
return NotImplemented;
}
static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
{
if (brush->bt == BrushTypeSolidColor)
{
GpSolidFill *solid = (GpSolidFill*)brush;
data->Version = VERSION_MAGIC2;
data->Type = solid->brush.bt;
data->BrushData.solid.SolidColor.Blue = solid->color & 0xff;
data->BrushData.solid.SolidColor.Green = (solid->color >> 8) & 0xff;
data->BrushData.solid.SolidColor.Red = (solid->color >> 16) & 0xff;
data->BrushData.solid.SolidColor.Alpha = solid->color >> 24;
}
}
static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
{
DWORD i, data_flags, pen_data_size, brush_size;
EmfPlusObject *object_record;
EmfPlusPenData *pen_data;
GpStatus stat;
BOOL result;
*id = -1;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
data_flags = 0;
pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
GdipIsMatrixIdentity(&pen->transform, &result);
if (!result)
{
data_flags |= PenDataTransform;
pen_data_size += sizeof(EmfPlusTransformMatrix);
}
if (pen->startcap != LineCapFlat)
{
data_flags |= PenDataStartCap;
pen_data_size += sizeof(DWORD);
}
if (pen->endcap != LineCapFlat)
{
data_flags |= PenDataEndCap;
pen_data_size += sizeof(DWORD);
}
if (pen->join != LineJoinMiter)
{
data_flags |= PenDataJoin;
pen_data_size += sizeof(DWORD);
}
if (pen->miterlimit != 10.0)
{
data_flags |= PenDataMiterLimit;
pen_data_size += sizeof(REAL);
}
if (pen->style != GP_DEFAULT_PENSTYLE)
{
data_flags |= PenDataLineStyle;
pen_data_size += sizeof(DWORD);
}
if (pen->dashcap != DashCapFlat)
{
data_flags |= PenDataDashedLineCap;
pen_data_size += sizeof(DWORD);
}
data_flags |= PenDataDashedLineOffset;
pen_data_size += sizeof(REAL);
if (pen->numdashes)
{
data_flags |= PenDataDashedLine;
pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
}
if (pen->align != PenAlignmentCenter)
{
data_flags |= PenDataNonCenter;
pen_data_size += sizeof(DWORD);
}
/* TODO: Add support for PenDataCompoundLine */
if (pen->customstart)
{
FIXME("ignoring custom start cup\n");
}
if (pen->customend)
{
FIXME("ignoring custom end cup\n");
}
stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
if (stat != Ok) return stat;
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
(void**)&object_record);
if (stat != Ok) return stat;
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | ObjectTypePen << 8;
object_record->ObjectData.pen.Version = VERSION_MAGIC2;
object_record->ObjectData.pen.Type = 0;
pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
pen_data->PenDataFlags = data_flags;
pen_data->PenUnit = pen->unit;
pen_data->PenWidth = pen->width;
i = 0;
if (data_flags & PenDataTransform)
{
EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
memcpy(m, &pen->transform, sizeof(*m));
i += sizeof(EmfPlusTransformMatrix);
}
if (data_flags & PenDataStartCap)
{
*(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
i += sizeof(DWORD);
}
if (data_flags & PenDataEndCap)
{
*(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
i += sizeof(DWORD);
}
if (data_flags & PenDataJoin)
{
*(DWORD*)(pen_data->OptionalData + i) = pen->join;
i += sizeof(DWORD);
}
if (data_flags & PenDataMiterLimit)
{
*(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
i += sizeof(REAL);
}
if (data_flags & PenDataLineStyle)
{
switch (pen->style & PS_STYLE_MASK)
{
case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
}
i += sizeof(DWORD);
}
if (data_flags & PenDataDashedLineCap)
{
*(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
i += sizeof(DWORD);
}
if (data_flags & PenDataDashedLineOffset)
{
*(REAL*)(pen_data->OptionalData + i) = pen->offset;
i += sizeof(REAL);
}
if (data_flags & PenDataDashedLine)
{
int j;
*(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
i += sizeof(DWORD);
for (j=0; j<pen->numdashes; j++)
{
*(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
i += sizeof(REAL);
}
}
if (data_flags & PenDataNonCenter)
{
*(REAL*)(pen_data->OptionalData + i) = pen->align;
i += sizeof(DWORD);
}
METAFILE_FillBrushData(pen->brush,
(EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
return Ok;
}
GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
{
EmfPlusDrawPath *draw_path_record;
DWORD path_id;
DWORD pen_id;
GpStatus stat;
if (metafile->metafile_type == MetafileTypeEmf)
{
FIXME("stub!\n");
return NotImplemented;
}
stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
if (stat != Ok) return stat;
stat = METAFILE_AddPathObject(metafile, path, &path_id);
if (stat != Ok) return stat;
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
if (stat != Ok) return stat;
draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
draw_path_record->Header.Flags = path_id;
draw_path_record->PenId = pen_id;
METAFILE_WriteRecords(metafile);
return Ok;
}
static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
{
EmfPlusObject *object_record;
GpStatus stat;
DWORD size;
*id = -1;
if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
return Ok;
stat = METAFILE_PrepareBrushData(brush, &size);
if (stat != Ok) return stat;
stat = METAFILE_AllocateRecord(metafile,
FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
if (stat != Ok) return stat;
*id = METAFILE_AddObjectId(metafile);
object_record->Header.Type = EmfPlusRecordTypeObject;
object_record->Header.Flags = *id | ObjectTypeBrush << 8;
METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
return Ok;
}
GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
{
EmfPlusFillPath *fill_path_record;
DWORD brush_id = -1, path_id;
BOOL inline_color;
GpStatus stat;
if (metafile->metafile_type == MetafileTypeEmf)
{
FIXME("stub!\n");
return NotImplemented;
}
inline_color = brush->bt == BrushTypeSolidColor;
if (!inline_color)
{
stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
if (stat != Ok) return stat;
}
stat = METAFILE_AddPathObject(metafile, path, &path_id);
if (stat != Ok) return stat;
stat = METAFILE_AllocateRecord(metafile,
sizeof(EmfPlusFillPath), (void**)&fill_path_record);
if (stat != Ok) return stat;
fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
if (inline_color)
{
fill_path_record->Header.Flags = 0x8000 | path_id;
fill_path_record->data.Color.Blue = ((GpSolidFill*)brush)->color & 0xff;
fill_path_record->data.Color.Green = (((GpSolidFill*)brush)->color >> 8) & 0xff;
fill_path_record->data.Color.Red = (((GpSolidFill*)brush)->color >> 16) & 0xff;
fill_path_record->data.Color.Alpha = ((GpSolidFill*)brush)->color >> 24;
}
else
{
fill_path_record->Header.Flags = path_id;
fill_path_record->data.BrushId = brush_id;
}
METAFILE_WriteRecords(metafile);
return Ok;
}

View file

@ -62,7 +62,6 @@
*
*/
#define FLAGS_NOFLAGS 0x0
#define FLAGS_INTPATH 0x4000
struct memory_buffer
@ -73,12 +72,17 @@ struct memory_buffer
struct region_header
{
DWORD size;
DWORD checksum;
DWORD magic;
DWORD num_children;
};
struct region_data_header
{
DWORD size;
DWORD checksum;
struct region_header header;
};
struct path_header
{
DWORD size;
@ -87,46 +91,12 @@ struct path_header
DWORD flags;
};
/* Header size as far as header->size is concerned. This doesn't include
* header->size or header->checksum
*/
static const INT sizeheader_size = sizeof(DWORD) * 2;
typedef struct packed_point
{
short X;
short Y;
} packed_point;
/* Test to see if the path could be stored as an array of shorts */
static BOOL is_integer_path(const GpPath *path)
{
int i;
if (!path->pathdata.Count) return FALSE;
for (i = 0; i < path->pathdata.Count; i++)
{
short x, y;
x = gdip_round(path->pathdata.Points[i].X);
y = gdip_round(path->pathdata.Points[i].Y);
if (path->pathdata.Points[i].X != (REAL)x || path->pathdata.Points[i].Y != (REAL)y)
return FALSE;
}
return TRUE;
}
/* Everything is measured in DWORDS; round up if there's a remainder */
static inline INT get_pathtypes_size(const GpPath* path)
{
INT needed = path->pathdata.Count / sizeof(DWORD);
if (path->pathdata.Count % sizeof(DWORD) > 0)
needed++;
return needed * sizeof(DWORD);
}
static inline INT get_element_size(const region_element* element)
{
INT needed = sizeof(DWORD); /* DWORD for the type */
@ -136,17 +106,8 @@ static inline INT get_element_size(const region_element* element)
return needed + sizeof(GpRect);
case RegionDataPath:
{
const GpPath *path = element->elementdata.path;
DWORD flags = is_integer_path(path) ? FLAGS_INTPATH : FLAGS_NOFLAGS;
/* 3 for headers, once again size doesn't count itself */
needed += sizeof(DWORD) * 3;
if (flags & FLAGS_INTPATH)
needed += 2 * sizeof(SHORT) * path->pathdata.Count;
else
needed += 2 * sizeof(FLOAT) * path->pathdata.Count;
needed += get_pathtypes_size(path);
needed += sizeof(DWORD); /* Extra DWORD for pathheader.size */
needed += write_path_data(element->elementdata.path, NULL);
needed += sizeof(DWORD); /* Extra DWORD for path size */
return needed;
}
case RegionDataEmptyRect:
@ -692,29 +653,6 @@ static inline void write_float(DWORD* location, INT* offset, const FLOAT write)
(*offset)++;
}
static inline void write_packed_point(DWORD* location, INT* offset,
const GpPointF* write)
{
packed_point *point = (packed_point *)(location + *offset);
point->X = gdip_round(write->X);
point->Y = gdip_round(write->Y);
(*offset)++;
}
static inline void write_path_types(DWORD* location, INT* offset,
const GpPath* path)
{
INT rounded_size = get_pathtypes_size(path);
memcpy(location + *offset, path->pathdata.Types, path->pathdata.Count);
/* The unwritten parts of the DWORD (if any) must be cleared */
if (rounded_size - path->pathdata.Count)
ZeroMemory(((BYTE*)location) + (*offset * sizeof(DWORD)) +
path->pathdata.Count, rounded_size - path->pathdata.Count);
*offset += rounded_size / sizeof(DWORD);
}
static void write_element(const region_element* element, DWORD *buffer,
INT* filled)
{
@ -738,43 +676,9 @@ static void write_element(const region_element* element, DWORD *buffer,
break;
case RegionDataPath:
{
INT i;
const GpPath* path = element->elementdata.path;
struct path_header *pathheader;
pathheader = (struct path_header *)(buffer + *filled);
pathheader->flags = is_integer_path(path) ? FLAGS_INTPATH : FLAGS_NOFLAGS;
/* 3 for headers, once again size doesn't count itself */
pathheader->size = sizeof(DWORD) * 3;
if (pathheader->flags & FLAGS_INTPATH)
pathheader->size += 2 * sizeof(SHORT) * path->pathdata.Count;
else
pathheader->size += 2 * sizeof(FLOAT) * path->pathdata.Count;
pathheader->size += get_pathtypes_size(path);
pathheader->magic = VERSION_MAGIC;
pathheader->count = path->pathdata.Count;
*filled += 4;
switch (pathheader->flags & FLAGS_INTPATH)
{
case FLAGS_NOFLAGS:
for (i = 0; i < path->pathdata.Count; i++)
{
write_float(buffer, filled, path->pathdata.Points[i].X);
write_float(buffer, filled, path->pathdata.Points[i].Y);
}
break;
case FLAGS_INTPATH:
for (i = 0; i < path->pathdata.Count; i++)
{
write_packed_point(buffer, filled,
&path->pathdata.Points[i]);
}
break;
}
write_path_types(buffer, filled, path);
DWORD size = write_path_data(element->elementdata.path, buffer + *filled + 1);
write_dword(buffer, filled, size);
*filled += size / sizeof(DWORD);
break;
}
case RegionDataEmptyRect:
@ -783,6 +687,24 @@ static void write_element(const region_element* element, DWORD *buffer,
}
}
DWORD write_region_data(const GpRegion *region, void *data)
{
struct region_header *header = data;
INT filled = 0;
DWORD size;
size = sizeof(struct region_header) + get_element_size(&region->node);
if (!data) return size;
header->magic = VERSION_MAGIC2;
header->num_children = region->num_children;
filled += 2;
/* With few exceptions, everything written is DWORD aligned,
* so use that as our base */
write_element(&region->node, (DWORD*)data, &filled);
return size;
}
/*****************************************************************************
* GdipGetRegionData [GDIPLUS.@]
*
@ -819,36 +741,27 @@ static void write_element(const region_element* element, DWORD *buffer,
GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
UINT *needed)
{
struct region_header *region_header;
INT filled = 0;
struct region_data_header *region_data_header;
UINT required;
GpStatus status;
TRACE("%p, %p, %d, %p\n", region, buffer, size, needed);
if (!region || !buffer || !size)
return InvalidParameter;
status = GdipGetRegionDataSize(region, &required);
if (status != Ok) return status;
required = FIELD_OFFSET(struct region_data_header, header) + write_region_data(region, NULL);
if (size < required)
{
if (needed) *needed = size;
return InsufficientBuffer;
}
region_header = (struct region_header *)buffer;
region_header->size = sizeheader_size + get_element_size(&region->node);
region_header->checksum = 0;
region_header->magic = VERSION_MAGIC;
region_header->num_children = region->num_children;
filled += 4;
/* With few exceptions, everything written is DWORD aligned,
* so use that as our base */
write_element(&region->node, (DWORD*)buffer, &filled);
region_data_header = (struct region_data_header *)buffer;
region_data_header->size = write_region_data(region, &region_data_header->header);
region_data_header->checksum = 0;
if (needed)
*needed = filled * sizeof(DWORD);
*needed = required;
return Ok;
}
@ -949,7 +862,7 @@ static GpStatus read_element(struct memory_buffer *mbuf, GpRegion *region, regio
ERR("failed to read path header\n");
return InvalidParameter;
}
if (path_header->magic != VERSION_MAGIC)
if (!VALID_MAGIC(path_header->magic))
{
ERR("invalid path header magic %#x\n", path_header->magic);
return InvalidParameter;
@ -1044,7 +957,7 @@ static GpStatus read_element(struct memory_buffer *mbuf, GpRegion *region, regio
*/
GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region)
{
const struct region_header *region_header;
const struct region_data_header *region_data_header;
struct memory_buffer mbuf;
GpStatus status;
INT count;
@ -1056,9 +969,8 @@ GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRe
init_memory_buffer(&mbuf, data, size);
region_header = buffer_read(&mbuf, sizeof(*region_header));
if (!region_header || (region_header->magic != VERSION_MAGIC &&
region_header->magic != VERSION_MAGIC2))
region_data_header = buffer_read(&mbuf, sizeof(*region_data_header));
if (!region_data_header || !VALID_MAGIC(region_data_header->header.magic))
return InvalidParameter;
status = GdipCreateRegion(region);
@ -1090,7 +1002,7 @@ GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed)
return InvalidParameter;
/* header.size doesn't count header.size and header.checksum */
*needed = sizeof(DWORD) * 2 + sizeheader_size + get_element_size(&region->node);
*needed = FIELD_OFFSET(struct region_data_header, header) + write_region_data(region, NULL);
return Ok;
}
@ -1135,6 +1047,8 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn)
SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
: WINDING));
gdi_transform_acquire(graphics);
stat = trace_path(graphics, path);
if (stat == Ok)
{
@ -1142,6 +1056,8 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn)
stat = *hrgn ? Ok : OutOfMemory;
}
gdi_transform_release(graphics);
RestoreDC(graphics->hdc, save_state);
if (new_hdc)
{

View file

@ -68,7 +68,7 @@ reactos/dll/win32/dciman32 # Synced to WineStaging-2.9
reactos/dll/win32/faultrep # Synced to WineStaging-2.9
reactos/dll/win32/fontsub # Synced to WineStaging-2.9
reactos/dll/win32/fusion # Synced to WineStaging-2.16
reactos/dll/win32/gdiplus # Synced to WineStaging-2.9
reactos/dll/win32/gdiplus # Synced to WineStaging-2.16
reactos/dll/win32/hhctrl.ocx # Synced to WineStaging-2.9
reactos/dll/win32/hlink # Synced to WineStaging-2.9
reactos/dll/win32/hnetcfg # Synced to WineStaging-2.9