[GDIPLUS] Sync with Wine Staging 2.16. CORE-13762
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 17 Sep 2017 12:34:15 +0000 (12:34 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 17 Sep 2017 12:34:15 +0000 (12:34 +0000)
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

reactos/dll/win32/gdiplus/brush.c
reactos/dll/win32/gdiplus/gdiplus.spec
reactos/dll/win32/gdiplus/gdiplus_private.h
reactos/dll/win32/gdiplus/graphics.c
reactos/dll/win32/gdiplus/graphicspath.c
reactos/dll/win32/gdiplus/image.c
reactos/dll/win32/gdiplus/matrix.c
reactos/dll/win32/gdiplus/metafile.c
reactos/dll/win32/gdiplus/region.c
reactos/media/doc/README.WINE

index 2c3bcc6..7e6d7ef 100644 (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(GpLineGradientbrush,
+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);
 }
 
 /******************************************************************************
index 09a6109..b29e41e 100644 (file)
 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)
index 0825129..633ef14 100644 (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;
index be16931..79f9b11 100644 (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 = 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;
 
-        points[0].x = rect->X;
-        points[0].y = rect->Y;
-        points[1].x = rect->X + rect->Width;
-        points[1].y = rect->Y + rect->Height;
+        gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
 
-        DPtoLP(graphics->hdc, points, sizeof(points)/sizeof(points[0]));
+        min_point = max_point = points[0];
 
-        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);
+        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;
-
-    EndPath(graphics->hdc);
-    brush_fill_path(graphics, brush);
+    if(retval == Ok)
+    {
+        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;
+}
index 22af560..31a0d3e 100644 (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;
+}
index 0cb4a18..214b95f 100644 (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;
 }
index 68ec523..8893206 100644 (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;
 }
index 2c655a6..6171352 100644 (file)
 
 #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:
-                break;
-            }
+            {
+                ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
 
-            record = heap_alloc_zero(dataSize + 8);
+                if (record)
+                {
+                    record->iType = recordType;
+                    record->nSize = dataSize + 8;
+                    memcpy(record->dParm, data, dataSize);
 
-            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");
 
-                PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
-                    record, metafile->handle_count);
+                    heap_free(record);
+                }
+                else
+                    return OutOfMemory;
 
-                heap_free(record);
+                break;
+            }
             }
-            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);
+
+    emf = GetEnhMetaFileW(file);
+    if(emf)
+        return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
 
-    return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, 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;
+}
index 255237e..b5fa5bc 100644 (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)
     {
index edb8392..a6ba88f 100644 (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