[GDIPLUS] Sync with Wine 3.0. CORE-14225
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 18 Jan 2018 23:17:41 +0000 (00:17 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 18 Jan 2018 23:17:41 +0000 (00:17 +0100)
dll/win32/gdiplus/brush.c
dll/win32/gdiplus/font.c
dll/win32/gdiplus/gdiplus.spec
dll/win32/gdiplus/gdiplus_private.h
dll/win32/gdiplus/graphics.c
dll/win32/gdiplus/image.c
dll/win32/gdiplus/imageattributes.c
dll/win32/gdiplus/metafile.c
dll/win32/gdiplus/region.c
media/doc/README.WINE

index 7e6d7ef..dfacfbc 100644 (file)
@@ -243,7 +243,7 @@ static const char HatchBrushes[][8] = {
     { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */
 };
 
     { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */
 };
 
-GpStatus get_hatch_data(HatchStyle hatchstyle, const char **result)
+GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result)
 {
     if (hatchstyle < sizeof(HatchBrushes) / sizeof(HatchBrushes[0]))
     {
 {
     if (hatchstyle < sizeof(HatchBrushes) / sizeof(HatchBrushes[0]))
     {
@@ -257,12 +257,15 @@ GpStatus get_hatch_data(HatchStyle hatchstyle, const char **result)
 /******************************************************************************
  * GdipCreateHatchBrush [GDIPLUS.@]
  */
 /******************************************************************************
  * GdipCreateHatchBrush [GDIPLUS.@]
  */
-GpStatus WINGDIPAPI GdipCreateHatchBrush(HatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
+GpStatus WINGDIPAPI GdipCreateHatchBrush(GpHatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
 {
     TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
 
     if(!brush)  return InvalidParameter;
 
 {
     TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
 
     if(!brush)  return InvalidParameter;
 
+    if(hatchstyle < HatchStyleMin || hatchstyle > HatchStyleMax)
+        return InvalidParameter;
+
     *brush = heap_alloc_zero(sizeof(GpHatch));
     if (!*brush) return OutOfMemory;
 
     *brush = heap_alloc_zero(sizeof(GpHatch));
     if (!*brush) return OutOfMemory;
 
@@ -491,9 +494,12 @@ GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect
     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
           wrap, line);
 
     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
           wrap, line);
 
-    if (!rect || !rect->Width || !rect->Height)
+    if (!rect || !line || wrap == WrapModeClamp)
         return InvalidParameter;
 
         return InvalidParameter;
 
+    if (!rect->Width || !rect->Height)
+        return OutOfMemory;
+
     angle = fmodf(angle, 360);
     if (angle < 0)
         angle += 360;
     angle = fmodf(angle, 360);
     if (angle < 0)
         angle += 360;
@@ -951,7 +957,7 @@ GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
     return Ok;
 }
 
     return Ok;
 }
 
-GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, HatchStyle *hatchstyle)
+GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, GpHatchStyle *hatchstyle)
 {
     TRACE("(%p, %p)\n", brush, hatchstyle);
 
 {
     TRACE("(%p, %p)\n", brush, hatchstyle);
 
index 57f90e7..c0fb51c 100644 (file)
@@ -1378,33 +1378,36 @@ static int match_name_table_language( const tt_name_record *name, LANGID lang )
     return 0;
 }
 
     return 0;
 }
 
-static WCHAR *copy_name_table_string( const tt_name_record *name, const BYTE *data, WCHAR *ret, DWORD len )
+static WCHAR *copy_name_table_string( const tt_name_record *name, const BYTE *data )
 {
     WORD name_len = GET_BE_WORD(name->length);
     WORD codepage;
 {
     WORD name_len = GET_BE_WORD(name->length);
     WORD codepage;
+    WCHAR *ret;
+    int len;
 
     switch (GET_BE_WORD(name->platform_id))
     {
     case TT_PLATFORM_APPLE_UNICODE:
     case TT_PLATFORM_MICROSOFT:
 
     switch (GET_BE_WORD(name->platform_id))
     {
     case TT_PLATFORM_APPLE_UNICODE:
     case TT_PLATFORM_MICROSOFT:
-        if (name_len >= len*sizeof(WCHAR))
-            return NULL;
+        ret = heap_alloc((name_len / 2 + 1) * sizeof(WCHAR));
         for (len = 0; len < name_len / 2; len++)
             ret[len] = (data[len * 2] << 8) | data[len * 2 + 1];
         ret[len] = 0;
         return ret;
     case TT_PLATFORM_MACINTOSH:
         codepage = get_mac_code_page( name );
         for (len = 0; len < name_len / 2; len++)
             ret[len] = (data[len * 2] << 8) | data[len * 2 + 1];
         ret[len] = 0;
         return ret;
     case TT_PLATFORM_MACINTOSH:
         codepage = get_mac_code_page( name );
-        len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, ret, len-1 );
+        len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, NULL, 0 ) + 1;
         if (!len)
             return NULL;
         if (!len)
             return NULL;
+        ret = heap_alloc(len * sizeof(WCHAR));
+        len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, ret, len - 1 );
         ret[len] = 0;
         return ret;
     }
     return NULL;
 }
 
         ret[len] = 0;
         return ret;
     }
     return NULL;
 }
 
-static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
+static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id )
 {
     LANGID lang = GetSystemDefaultLangID();
     const tt_header *header;
 {
     LANGID lang = GetSystemDefaultLangID();
     const tt_header *header;
@@ -1465,8 +1468,9 @@ static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id, WCHAR
 
     if (best_lang)
     {
 
     if (best_lang)
     {
+        WCHAR *ret;
         name_record = (const tt_name_record*)(name_table + 1) + best_index;
         name_record = (const tt_name_record*)(name_table + 1) + best_index;
-        ret = copy_name_table_string( name_record, mem+ofs+GET_BE_WORD(name_record->offset), ret, len );
+        ret = copy_name_table_string( name_record, mem+ofs+GET_BE_WORD(name_record->offset) );
         TRACE( "name %u found platform %u lang %04x %s\n", GET_BE_WORD(name_record->name_id),
                 GET_BE_WORD(name_record->platform_id), GET_BE_WORD(name_record->language_id), debugstr_w( ret ));
         return ret;
         TRACE( "name %u found platform %u lang %04x %s\n", GET_BE_WORD(name_record->name_id),
                 GET_BE_WORD(name_record->platform_id), GET_BE_WORD(name_record->language_id), debugstr_w( ret ));
         return ret;
@@ -1482,43 +1486,45 @@ static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, D
 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
         GDIPCONST void* memory, INT length)
 {
 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
         GDIPCONST void* memory, INT length)
 {
-    WCHAR buf[32], *name;
+    WCHAR *name;
     DWORD count = 0;
     HANDLE font;
     DWORD count = 0;
     HANDLE font;
+    GpStatus ret = Ok;
     TRACE("%p, %p, %d\n", fontCollection, memory, length);
 
     if (!fontCollection || !memory || !length)
         return InvalidParameter;
 
     TRACE("%p, %p, %d\n", fontCollection, memory, length);
 
     if (!fontCollection || !memory || !length)
         return InvalidParameter;
 
-    name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
+    name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME);
     if (!name)
         return OutOfMemory;
 
     font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
     TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
     if (!font || !count)
     if (!name)
         return OutOfMemory;
 
     font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
     TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
     if (!font || !count)
-        return InvalidParameter;
-
-    if (count)
+        ret = InvalidParameter;
+    else
     {
         HDC hdc;
         LOGFONTW lfw;
 
         hdc = CreateCompatibleDC(0);
 
     {
         HDC hdc;
         LOGFONTW lfw;
 
         hdc = CreateCompatibleDC(0);
 
+        /* Truncate name if necessary, GDI32 can't deal with long names */
+        if(lstrlenW(name) > LF_FACESIZE - 1)
+            name[LF_FACESIZE - 1] = 0;
+
         lfw.lfCharSet = DEFAULT_CHARSET;
         lstrcpyW(lfw.lfFaceName, name);
         lfw.lfPitchAndFamily = 0;
 
         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
         lfw.lfCharSet = DEFAULT_CHARSET;
         lstrcpyW(lfw.lfFaceName, name);
         lfw.lfPitchAndFamily = 0;
 
         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
-        {
-            DeleteDC(hdc);
-            return OutOfMemory;
-        }
+            ret = OutOfMemory;
 
         DeleteDC(hdc);
     }
 
         DeleteDC(hdc);
     }
-    return Ok;
+    heap_free(name);
+    return ret;
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
index b29e41e..0e7a78f 100644 (file)
 615 stub GdipGetEffectParameterSize
 616 stub GdipGetEffectParameters
 617 stdcall GdipSetEffectParameters(ptr ptr long)
 615 stub GdipGetEffectParameterSize
 616 stub GdipGetEffectParameters
 617 stdcall GdipSetEffectParameters(ptr ptr long)
-618 stdcall GdipInitializePalette(ptr long long long ptr)
+618 stdcall -stub GdipInitializePalette(ptr long long long ptr)
 619 stdcall GdipBitmapCreateApplyEffect(ptr long ptr ptr ptr ptr long ptr ptr)
 620 stdcall GdipBitmapApplyEffect(ptr ptr ptr long ptr ptr)
 621 stdcall GdipBitmapGetHistogram(ptr long long ptr ptr ptr ptr)
 619 stdcall GdipBitmapCreateApplyEffect(ptr long ptr ptr ptr ptr long ptr ptr)
 620 stdcall GdipBitmapApplyEffect(ptr ptr ptr long ptr ptr)
 621 stdcall GdipBitmapGetHistogram(ptr long long ptr ptr ptr ptr)
index 633ef14..192e367 100644 (file)
@@ -129,6 +129,7 @@ extern GpStatus METAFILE_DrawImagePointsRect(GpMetafile* metafile, GpImage *imag
 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 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 METAFILE_Free(GpMetafile *metafile) DECLSPEC_HIDDEN;
 
 extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1,
     REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN;
 
 extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1,
     REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN;
@@ -147,7 +148,7 @@ extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN;
 typedef struct region_element region_element;
 extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
 
 typedef struct region_element region_element;
 extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
 
-extern GpStatus get_hatch_data(HatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN;
+extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN;
 
 static inline INT gdip_round(REAL x)
 {
 
 static inline INT gdip_round(REAL x)
 {
@@ -270,6 +271,7 @@ struct GpGraphics{
     INT origin_x, origin_y;
     INT gdi_transform_acquire_count, gdi_transform_save;
     GpMatrix gdi_transform;
     INT origin_x, origin_y;
     INT gdi_transform_acquire_count, gdi_transform_save;
     GpMatrix gdi_transform;
+    HRGN gdi_clip;
     /* For giving the caller an HDC when we technically can't: */
     HBITMAP temp_hbitmap;
     int temp_hbitmap_width;
     /* For giving the caller an HDC when we technically can't: */
     HBITMAP temp_hbitmap;
     int temp_hbitmap_width;
@@ -284,7 +286,7 @@ struct GpBrush{
 
 struct GpHatch{
     GpBrush brush;
 
 struct GpHatch{
     GpBrush brush;
-    HatchStyle hatchstyle;
+    GpHatchStyle hatchstyle;
     ARGB forecol;
     ARGB backcol;
 };
     ARGB forecol;
     ARGB backcol;
 };
@@ -378,6 +380,38 @@ struct GpImage{
     LONG busy;
 };
 
     LONG busy;
 };
 
+#define EmfPlusObjectTableSize 64
+
+typedef enum EmfPlusObjectType
+{
+    ObjectTypeInvalid,
+    ObjectTypeBrush,
+    ObjectTypePen,
+    ObjectTypePath,
+    ObjectTypeRegion,
+    ObjectTypeImage,
+    ObjectTypeFont,
+    ObjectTypeStringFormat,
+    ObjectTypeImageAttributes,
+    ObjectTypeCustomLineCap,
+    ObjectTypeMax = ObjectTypeCustomLineCap,
+} EmfPlusObjectType;
+
+/* Deserialized EmfPlusObject record. */
+struct emfplus_object {
+    EmfPlusObjectType type;
+    union {
+        GpBrush *brush;
+        GpPen *pen;
+        GpPath *path;
+        GpRegion *region;
+        GpImage *image;
+        GpFont *font;
+        GpImageAttributes *image_attributes;
+        void *object;
+    } u;
+};
+
 struct GpMetafile{
     GpImage image;
     GpRectF bounds;
 struct GpMetafile{
     GpImage image;
     GpRectF bounds;
@@ -411,6 +445,7 @@ struct GpMetafile{
     GpRegion *base_clip; /* clip region in device space for all metafile output */
     GpRegion *clip; /* clip region within the metafile */
     struct list containers;
     GpRegion *base_clip; /* clip region in device space for all metafile output */
     GpRegion *clip; /* clip region within the metafile */
     struct list containers;
+    struct emfplus_object objtable[EmfPlusObjectTableSize];
 };
 
 struct GpBitmap{
 };
 
 struct GpBitmap{
@@ -454,6 +489,12 @@ struct color_remap_table{
     ColorMap *colormap;
 };
 
     ColorMap *colormap;
 };
 
+enum imageattr_noop{
+    IMAGEATTR_NOOP_UNDEFINED,
+    IMAGEATTR_NOOP_SET,
+    IMAGEATTR_NOOP_CLEAR,
+};
+
 struct GpImageAttributes{
     WrapMode wrap;
     ARGB outside_color;
 struct GpImageAttributes{
     WrapMode wrap;
     ARGB outside_color;
@@ -463,6 +504,7 @@ struct GpImageAttributes{
     struct color_remap_table colorremaptables[ColorAdjustTypeCount];
     BOOL gamma_enabled[ColorAdjustTypeCount];
     REAL gamma[ColorAdjustTypeCount];
     struct color_remap_table colorremaptables[ColorAdjustTypeCount];
     BOOL gamma_enabled[ColorAdjustTypeCount];
     REAL gamma[ColorAdjustTypeCount];
+    enum imageattr_noop noop[ColorAdjustTypeCount];
 };
 
 struct GpFont{
 };
 
 struct GpFont{
@@ -535,6 +577,30 @@ struct GpRegion{
     region_element node;
 };
 
     region_element node;
 };
 
+struct memory_buffer
+{
+    const BYTE *buffer;
+    INT size, pos;
+};
+
+static inline void init_memory_buffer(struct memory_buffer *mbuf, const BYTE *buffer, INT size)
+{
+    mbuf->buffer = buffer;
+    mbuf->size = size;
+    mbuf->pos = 0;
+}
+
+static inline const void *buffer_read(struct memory_buffer *mbuf, INT size)
+{
+    if (mbuf->size - mbuf->pos >= size)
+    {
+        const void *data = mbuf->buffer + mbuf->pos;
+        mbuf->pos += size;
+        return data;
+    }
+    return NULL;
+}
+
 typedef GpStatus (*gdip_format_string_callback)(HDC hdc,
     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
 typedef GpStatus (*gdip_format_string_callback)(HDC hdc,
     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
index 79f9b11..71ea19c 100644 (file)
@@ -302,13 +302,6 @@ static void round_points(POINT *pti, GpPointF *ptf, INT count)
     }
 }
 
     }
 }
 
-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)
 {
 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)
 {
@@ -355,6 +348,17 @@ static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
         GdipDeleteRegion(rgn);
     }
 
         GdipDeleteRegion(rgn);
     }
 
+    if (stat == Ok && graphics->gdi_clip)
+    {
+        if (*hrgn)
+            CombineRgn(*hrgn, *hrgn, graphics->gdi_clip, RGN_AND);
+        else
+        {
+            *hrgn = CreateRectRgn(0,0,0,0);
+            CombineRgn(*hrgn, graphics->gdi_clip, graphics->gdi_clip, RGN_COPY);
+        }
+    }
+
     return stat;
 }
 
     return stat;
 }
 
@@ -505,8 +509,6 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
 
         save = SaveDC(graphics->hdc);
 
 
         save = SaveDC(graphics->hdc);
 
-        SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
-
         ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
 
         if (hregion)
         ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
 
         if (hregion)
@@ -529,23 +531,12 @@ static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
     return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
 }
 
     return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
 }
 
-/* NOTE: start and end pixels must be in pre-multiplied ARGB format */
-static inline ARGB blend_colors_premult(ARGB start, ARGB end, REAL position)
-{
-    UINT pos = position * 255.0f + 0.5f;
-    return
-        (((((start >> 24)       ) << 8) + (((end >> 24)       ) - ((start >> 24)       )) * pos) >> 8) << 24 |
-        (((((start >> 16) & 0xff) << 8) + (((end >> 16) & 0xff) - ((start >> 16) & 0xff)) * pos) >> 8) << 16 |
-        (((((start >>  8) & 0xff) << 8) + (((end >>  8) & 0xff) - ((start >>  8) & 0xff)) * pos) >> 8) <<  8 |
-        (((((start      ) & 0xff) << 8) + (((end      ) & 0xff) - ((start      ) & 0xff)) * pos) >> 8);
-}
-
 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
 {
     INT start_a, end_a, final_a;
     INT pos;
 
 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
 {
     INT start_a, end_a, final_a;
     INT pos;
 
-    pos = (INT)(position * 255.0f + 0.5f);
+    pos = gdip_round(position * 0xff);
 
     start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
     end_a = ((end >> 24) & 0xff) * pos;
 
     start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
     end_a = ((end >> 24) & 0xff) * pos;
@@ -685,6 +676,11 @@ PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
     UINT x, y;
     INT i;
 
     UINT x, y;
     INT i;
 
+    if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
+            attributes->noop[ColorAdjustTypeDefault] == IMAGEATTR_NOOP_SET) ||
+            (attributes->noop[type] == IMAGEATTR_NOOP_SET))
+        return fmt;
+
     if (attributes->colorkeys[type].enabled ||
         attributes->colorkeys[ColorAdjustTypeDefault].enabled)
     {
     if (attributes->colorkeys[type].enabled ||
         attributes->colorkeys[ColorAdjustTypeDefault].enabled)
     {
@@ -946,11 +942,6 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi
     return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
 }
 
     return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
 }
 
-static inline int positive_ceilf(float f)
-{
-    return f - (int)f > 0.0f ? f + 1.0f : f;
-}
-
 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
     UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
     InterpolationMode interpolation, PixelOffsetMode offset_mode)
 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
     UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
     InterpolationMode interpolation, PixelOffsetMode offset_mode)
@@ -971,12 +962,12 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT
         ARGB top, bottom;
         float x_offset;
 
         ARGB top, bottom;
         float x_offset;
 
-        leftx = (INT)point->X;
-        leftxf = (REAL)leftx;
-        rightx = positive_ceilf(point->X);
-        topy = (INT)point->Y;
-        topyf = (REAL)topy;
-        bottomy = positive_ceilf(point->Y);
+        leftxf = floorf(point->X);
+        leftx = (INT)leftxf;
+        rightx = (INT)ceilf(point->X);
+        topyf = floorf(point->Y);
+        topy = (INT)topyf;
+        bottomy = (INT)ceilf(point->Y);
 
         if (leftx == rightx && topy == bottomy)
             return sample_bitmap_pixel(src_rect, bits, width, height,
 
         if (leftx == rightx && topy == bottomy)
             return sample_bitmap_pixel(src_rect, bits, width, height,
@@ -1020,75 +1011,6 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT
     }
 }
 
     }
 }
 
-static ARGB resample_bitmap_pixel_premult(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
-    UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
-    InterpolationMode interpolation, PixelOffsetMode offset_mode)
-{
-    static int fixme;
-
-    switch (interpolation)
-    {
-    default:
-        if (!fixme++)
-            FIXME("Unimplemented interpolation %i\n", interpolation);
-        /* fall-through */
-    case InterpolationModeBilinear:
-    {
-        REAL leftxf, topyf;
-        INT leftx, rightx, topy, bottomy;
-        ARGB topleft, topright, bottomleft, bottomright;
-        ARGB top, bottom;
-        float x_offset;
-
-        leftx = (INT)point->X;
-        leftxf = (REAL)leftx;
-        rightx = positive_ceilf(point->X);
-        topy = (INT)point->Y;
-        topyf = (REAL)topy;
-        bottomy = positive_ceilf(point->Y);
-
-        if (leftx == rightx && topy == bottomy)
-            return sample_bitmap_pixel(src_rect, bits, width, height,
-                leftx, topy, attributes);
-
-        topleft = sample_bitmap_pixel(src_rect, bits, width, height,
-            leftx, topy, attributes);
-        topright = sample_bitmap_pixel(src_rect, bits, width, height,
-            rightx, topy, attributes);
-        bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
-            leftx, bottomy, attributes);
-        bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
-            rightx, bottomy, attributes);
-
-        x_offset = point->X - leftxf;
-        top = blend_colors_premult(topleft, topright, x_offset);
-        bottom = blend_colors_premult(bottomleft, bottomright, x_offset);
-
-        return blend_colors_premult(top, bottom, point->Y - topyf);
-    }
-    case InterpolationModeNearestNeighbor:
-    {
-        FLOAT pixel_offset;
-        switch (offset_mode)
-        {
-        default:
-        case PixelOffsetModeNone:
-        case PixelOffsetModeHighSpeed:
-            pixel_offset = 0.5;
-            break;
-
-        case PixelOffsetModeHalf:
-        case PixelOffsetModeHighQuality:
-            pixel_offset = 0.0;
-            break;
-        }
-        return sample_bitmap_pixel(src_rect, bits, width, height,
-            floorf(point->X + pixel_offset), point->Y + pixel_offset, attributes);
-    }
-
-    }
-}
-
 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
 {
     return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
 {
     return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
@@ -2312,7 +2234,7 @@ static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
         GdipTransformMatrixPoints(&xform, pt, 3);
     }
 
         GdipTransformMatrixPoints(&xform, pt, 3);
     }
 
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
@@ -2342,6 +2264,20 @@ GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
     return GdipCreateFromHDC2(hdc, NULL, graphics);
 }
 
     return GdipCreateFromHDC2(hdc, NULL, graphics);
 }
 
+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;
+    }
+
+    GetTransform(graphics->hdc, 0x204, &xform);
+    GdipSetMatrixElements(matrix, xform.eM11, xform.eM12, xform.eM21, xform.eM22, xform.eDx, xform.eDy);
+}
+
 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
 {
     GpStatus retval;
 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
 {
     GpStatus retval;
@@ -2391,7 +2327,19 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
+#ifdef __REACTOS__
     (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
     (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
+#else
+    (*graphics)->contid = 0;
+#endif
+    get_gdi_transform(*graphics, &(*graphics)->gdi_transform);
+
+    (*graphics)->gdi_clip = CreateRectRgn(0,0,0,0);
+    if (!GetClipRgn(hdc, (*graphics)->gdi_clip))
+    {
+        DeleteObject((*graphics)->gdi_clip);
+        (*graphics)->gdi_clip = NULL;
+    }
 
     TRACE("<-- %p\n", *graphics);
 
 
     TRACE("<-- %p\n", *graphics);
 
@@ -2406,6 +2354,7 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
     if(!*graphics)  return OutOfMemory;
 
     GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
     if(!*graphics)  return OutOfMemory;
 
     GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
+    GdipSetMatrixElements(&(*graphics)->gdi_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
         heap_free(*graphics);
 
     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
         heap_free(*graphics);
@@ -2431,7 +2380,11 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
     (*graphics)->busy = FALSE;
     (*graphics)->textcontrast = 4;
     list_init(&(*graphics)->containers);
+#ifdef __REACTOS__
     (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
     (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
+#else
+    (*graphics)->contid = 0;
+#endif
 
     TRACE("<-- %p\n", *graphics);
 
 
     TRACE("<-- %p\n", *graphics);
 
@@ -2516,6 +2469,8 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
 
     GdipDeleteRegion(graphics->clip);
 
 
     GdipDeleteRegion(graphics->clip);
 
+    DeleteObject(graphics->gdi_clip);
+
     /* Native returns ObjectBusy on the second free, instead of crashing as we'd
      * do otherwise, but we can't have that in the test suite because it means
      * accessing freed memory. */
     /* Native returns ObjectBusy on the second free, instead of crashing as we'd
      * do otherwise, but we can't have that in the test suite because it means
      * accessing freed memory. */
@@ -3151,10 +3106,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
             lockeddata.Scan0 = src_data;
             if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
                 lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
             lockeddata.Scan0 = src_data;
             if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
                 lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
-            else if (imageAttributes != &defaultImageAttributes)
-                lockeddata.PixelFormat = PixelFormat32bppARGB;
             else
             else
-                lockeddata.PixelFormat = PixelFormat32bppPARGB;
+                lockeddata.PixelFormat = PixelFormat32bppARGB;
 
             stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
                 lockeddata.PixelFormat, &lockeddata);
 
             stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
                 lockeddata.PixelFormat, &lockeddata);
@@ -3174,8 +3127,6 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
             if (do_resampling)
             {
 
             if (do_resampling)
             {
-                REAL delta_xx, delta_xy, delta_yx, delta_yy;
-
                 /* Transform the bits as needed to the destination. */
                 dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
                 if (!dst_data)
                 /* Transform the bits as needed to the destination. */
                 dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
                 if (!dst_data)
@@ -3193,42 +3144,24 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
                 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
 
                 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
                 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
 
-                delta_yy = dst_area.top * y_dy;
-                delta_yx = dst_area.top * y_dx;
-
-                for (y=dst_area.top; y<dst_area.bottom; y++)
+                for (x=dst_area.left; x<dst_area.right; x++)
                 {
                 {
-                    delta_xx = dst_area.left * x_dx;
-                    delta_xy = dst_area.left * x_dy;
-
-                    for (x=dst_area.left; x<dst_area.right; x++)
+                    for (y=dst_area.top; y<dst_area.bottom; y++)
                     {
                         GpPointF src_pointf;
                         ARGB *dst_color;
 
                     {
                         GpPointF src_pointf;
                         ARGB *dst_color;
 
-                        src_pointf.X = dst_to_src_points[0].X + delta_xx + delta_yx;
-                        src_pointf.Y = dst_to_src_points[0].Y + delta_xy + delta_yy;
+                        src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
+                        src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
 
                         dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
 
                         if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
 
                         dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
 
                         if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
-                        {
-                            if (lockeddata.PixelFormat != PixelFormat32bppPARGB)
-                                *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
-                                                                   imageAttributes, interpolation, offset_mode);
-                            else
-                                *dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
-                                                                           imageAttributes, interpolation, offset_mode);
-                        }
+                            *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
+                                                               imageAttributes, interpolation, offset_mode);
                         else
                             *dst_color = 0;
                         else
                             *dst_color = 0;
-
-                        delta_xx += x_dx;
-                        delta_yx += y_dx;
                     }
                     }
-
-                    delta_xy += x_dy;
-                    delta_yy += y_dy;
                 }
             }
             else
                 }
             }
             else
@@ -3322,9 +3255,9 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
             stat = get_clip_hrgn(graphics, &hrgn);
 
 
             stat = get_clip_hrgn(graphics, &hrgn);
 
-            if (stat == Ok && hrgn)
+            if (stat == Ok)
             {
             {
-                ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+                ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
                 DeleteObject(hrgn);
             }
 
                 DeleteObject(hrgn);
             }
 
@@ -3565,8 +3498,7 @@ static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *pat
     if (retval != Ok)
         goto end;
 
     if (retval != Ok)
         goto end;
 
-    if (hrgn)
-        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
 
     gdi_transform_acquire(graphics);
 
 
     gdi_transform_acquire(graphics);
 
@@ -3907,7 +3839,7 @@ static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *
         points[1].X = pen->width;
         points[2].Y = pen->width;
 
         points[1].X = pen->width;
         points[2].Y = pen->width;
 
-        stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
+        stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice,
             CoordinateSpaceWorld, points, 3);
 
         if (stat != Ok)
             CoordinateSpaceWorld, points, 3);
 
         if (stat != Ok)
@@ -3931,7 +3863,7 @@ static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *
         stat = GdipCreateMatrix(&transform);
 
         if (stat == Ok)
         stat = GdipCreateMatrix(&transform);
 
         if (stat == Ok)
-            stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
+            stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
                 CoordinateSpaceWorld, transform);
     }
     else
                 CoordinateSpaceWorld, transform);
     }
     else
@@ -3939,7 +3871,7 @@ static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *
         /* Set flatness based on the final coordinate space */
         GpMatrix t;
 
         /* Set flatness based on the final coordinate space */
         GpMatrix t;
 
-        stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
+        stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
             CoordinateSpaceWorld, &t);
 
         if (stat != Ok)
             CoordinateSpaceWorld, &t);
 
         if (stat != Ok)
@@ -4254,8 +4186,7 @@ static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath
     if (retval != Ok)
         goto end;
 
     if (retval != Ok)
         goto end;
 
-    if (hrgn)
-        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
 
     gdi_transform_acquire(graphics);
 
 
     gdi_transform_acquire(graphics);
 
@@ -4542,32 +4473,30 @@ static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
     if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
         return NotImplemented;
 
     if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
         return NotImplemented;
 
-    status = GdipGetRegionHRgn(region, graphics, &hrgn);
-    if(status != Ok)
-        return status;
-
     save_state = SaveDC(graphics->hdc);
     EndPath(graphics->hdc);
 
     save_state = SaveDC(graphics->hdc);
     EndPath(graphics->hdc);
 
-    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
-
-    DeleteObject(hrgn);
-
     hrgn = NULL;
     status = get_clip_hrgn(graphics, &hrgn);
     hrgn = NULL;
     status = get_clip_hrgn(graphics, &hrgn);
-
     if (status != Ok)
     {
         RestoreDC(graphics->hdc, save_state);
         return status;
     }
 
     if (status != Ok)
     {
         RestoreDC(graphics->hdc, save_state);
         return status;
     }
 
-    if (hrgn)
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
+    DeleteObject(hrgn);
+
+    status = GdipGetRegionHRgn(region, graphics, &hrgn);
+    if (status != Ok)
     {
     {
-        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
-        DeleteObject(hrgn);
+        RestoreDC(graphics->hdc, save_state);
+        return status;
     }
 
     }
 
+    ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+    DeleteObject(hrgn);
+
     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
     {
         BeginPath(graphics->hdc);
     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
     {
         BeginPath(graphics->hdc);
@@ -5371,7 +5300,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
     args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
     args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
@@ -5400,9 +5329,13 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
 
     args.regions = regions;
 
 
     args.regions = regions;
 
+    gdi_transform_acquire(graphics);
+
     stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
         (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
 
     stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
         (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
 
+    gdi_transform_release(graphics);
+
     SelectObject(hdc, oldfont);
     DeleteObject(gdifont);
 
     SelectObject(hdc, oldfont);
     DeleteObject(gdifont);
 
@@ -5490,7 +5423,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
     args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
     args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
@@ -5525,9 +5458,13 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
     args.linesfilled = &lines;
     lines = glyphs = 0;
 
     args.linesfilled = &lines;
     lines = glyphs = 0;
 
+    gdi_transform_acquire(graphics);
+
     gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         measure_string_callback, &args);
 
     gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         measure_string_callback, &args);
 
+    gdi_transform_release(graphics);
+
     if (linesfilled) *linesfilled = lines;
     if (codepointsfitted) *codepointsfitted = glyphs;
 
     if (linesfilled) *linesfilled = lines;
     if (codepointsfitted) *codepointsfitted = glyphs;
 
@@ -5657,7 +5594,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
     pt[1].Y = 0.0;
     pt[2].X = 0.0;
     pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
@@ -5667,7 +5604,8 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     rectcpy[1].Y = rectcpy[0].Y = rect->Y;
     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
     rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
     rectcpy[1].Y = rectcpy[0].Y = rect->Y;
     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
     rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
-    transform_and_round_points(graphics, corners, rectcpy, 4);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4);
+    round_points(corners, rectcpy, 4);
 
     margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
     margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
 
     margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
     margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
@@ -5706,12 +5644,16 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string
     args.rel_width = rel_width;
     args.rel_height = rel_height;
 
     args.rel_width = rel_width;
     args.rel_height = rel_height;
 
+    gdi_transform_acquire(graphics);
+
     GetTextMetricsW(hdc, &textmetric);
     args.ascent = textmetric.tmAscent / rel_height;
 
     gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         draw_string_callback, &args);
 
     GetTextMetricsW(hdc, &textmetric);
     args.ascent = textmetric.tmAscent / rel_height;
 
     gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
         draw_string_callback, &args);
 
+    gdi_transform_release(graphics);
+
     DeleteObject(rgn);
     DeleteObject(gdifont);
 
     DeleteObject(rgn);
     DeleteObject(gdifont);
 
@@ -6311,6 +6253,7 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
 {
     GpRegion *region;
     GpStatus status;
 {
     GpRegion *region;
     GpStatus status;
+    GpMatrix transform;
 
     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
 
 
     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
 
@@ -6320,14 +6263,21 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
     if(graphics->busy)
         return ObjectBusy;
 
     if(graphics->busy)
         return ObjectBusy;
 
-    /* hrgn is already in device units */
+    /* hrgn is in gdi32 device units */
     status = GdipCreateRegionHrgn(hrgn, &region);
     status = GdipCreateRegionHrgn(hrgn, &region);
-    if(status != Ok)
-        return status;
 
 
-    status = GdipCombineRegionRegion(graphics->clip, region, mode);
+    if (status == Ok)
+    {
+        status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform);
+
+        if (status == Ok)
+            status = GdipTransformRegion(region, &transform);
+
+        if (status == Ok)
+            status = GdipCombineRegionRegion(graphics->clip, region, mode);
 
 
-    GdipDeleteRegion(region);
+        GdipDeleteRegion(region);
+    }
     return status;
 }
 
     return status;
 }
 
@@ -6723,31 +6673,10 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
     return Ok;
 }
 
     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)
     {
 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);
         graphics->gdi_transform_save = SaveDC(graphics->hdc);
         SetGraphicsMode(graphics->hdc, GM_COMPATIBLE);
         SetMapMode(graphics->hdc, MM_TEXT);
@@ -6762,7 +6691,7 @@ GpStatus gdi_transform_release(GpGraphics *graphics)
 {
     if (graphics->gdi_transform_acquire_count <= 0)
     {
 {
     if (graphics->gdi_transform_acquire_count <= 0)
     {
-        ERR("called without matching gdi_transform_acquire");
+        ERR("called without matching gdi_transform_acquire\n");
         return GenericError;
     }
     if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
         return GenericError;
     }
     if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
@@ -6800,7 +6729,7 @@ GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_spac
             case WineCoordinateSpaceGdiDevice:
             {
                 GpMatrix gdixform;
             case WineCoordinateSpaceGdiDevice:
             {
                 GpMatrix gdixform;
-                get_gdi_transform(graphics, &gdixform);
+                gdixform = graphics->gdi_transform;
                 stat = GdipInvertMatrix(&gdixform);
                 if (stat != Ok)
                     break;
                 stat = GdipInvertMatrix(&gdixform);
                 if (stat != Ok)
                     break;
@@ -6841,9 +6770,7 @@ GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_spac
                 /* else fall-through */
             case CoordinateSpaceDevice:
             {
                 /* else fall-through */
             case CoordinateSpaceDevice:
             {
-                GpMatrix gdixform;
-                get_gdi_transform(graphics, &gdixform);
-                GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
+                GdipMultiplyMatrix(matrix, &graphics->gdi_transform, MatrixOrderAppend);
                 break;
             }
             }
                 break;
             }
             }
@@ -7015,7 +6942,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT
         GpMatrix xform = *matrix;
         GdipTransformMatrixPoints(&xform, pt, 3);
     }
         GpMatrix xform = *matrix;
         GdipTransformMatrixPoints(&xform, pt, 3);
     }
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
@@ -7102,22 +7029,26 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1
 
     status = get_clip_hrgn(graphics, &hrgn);
 
 
     status = get_clip_hrgn(graphics, &hrgn);
 
-    if (status == Ok && hrgn)
+    if (status == Ok)
     {
     {
-        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
+        ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
         DeleteObject(hrgn);
     }
 
     pt = positions[0];
         DeleteObject(hrgn);
     }
 
     pt = positions[0];
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
+    gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &pt, 1);
 
     get_font_hfont(graphics, font, format, &hfont, matrix);
     SelectObject(graphics->hdc, hfont);
 
     SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
 
 
     get_font_hfont(graphics, font, format, &hfont, matrix);
     SelectObject(graphics->hdc, hfont);
 
     SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
 
+    gdi_transform_acquire(graphics);
+
     ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
 
     ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
 
+    gdi_transform_release(graphics);
+
     RestoreDC(graphics->hdc, save_state);
 
     DeleteObject(hfont);
     RestoreDC(graphics->hdc, save_state);
 
     DeleteObject(hfont);
@@ -7165,7 +7096,8 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
     {
         real_position = positions[0];
 
     {
         real_position = positions[0];
 
-        transform_and_round_points(graphics, pti, &real_position, 1);
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1);
+        round_points(pti, &real_position, 1);
     }
     else
     {
     }
     else
     {
@@ -7178,7 +7110,8 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
 
         memcpy(real_positions, positions, sizeof(PointF) * length);
 
 
         memcpy(real_positions, positions, sizeof(PointF) * length);
 
-        transform_and_round_points(graphics, pti, real_positions, length);
+        gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length);
+        round_points(pti, real_positions, length);
 
         heap_free(real_positions);
     }
 
         heap_free(real_positions);
     }
@@ -7318,10 +7251,14 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
 
     heap_free(text_mask);
 
 
     heap_free(text_mask);
 
+    gdi_transform_acquire(graphics);
+
     /* draw the result */
     stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
         pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
 
     /* draw the result */
     stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
         pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
 
+    gdi_transform_release(graphics);
+
     heap_free(pixel_data);
 
     return stat;
     heap_free(pixel_data);
 
     return stat;
index 214b95f..6390bff 100644 (file)
@@ -38,19 +38,13 @@ static const struct
 {
     { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
     { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
 {
     { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
     { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW },
-    { &GUID_WICPixelFormat4bppIndexed, PixelFormat4bppIndexed, WICBitmapPaletteTypeFixedHalftone8 },
     { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 },
     { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 },
     { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
-    { &GUID_WICPixelFormat48bppRGB, PixelFormat48bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedHalftone256 },
     { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, WICBitmapPaletteTypeFixedHalftone256 },
-    { &GUID_WICPixelFormat32bppCMYK, PixelFormat32bppCMYK, WICBitmapPaletteTypeFixedHalftone256 },
-    { &GUID_WICPixelFormat32bppGrayFloat, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedGray256 },
-    { &GUID_WICPixelFormat64bppCMYK, PixelFormat48bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
-    { &GUID_WICPixelFormat64bppRGBA, PixelFormat48bppRGB, WICBitmapPaletteTypeFixedHalftone256 },
     { NULL }
 };
 
     { NULL }
 };
 
@@ -2074,24 +2068,7 @@ static GpStatus free_image_data(GpImage *image)
         heap_free(((GpBitmap*)image)->prop_item);
     }
     else if (image->type == ImageTypeMetafile)
         heap_free(((GpBitmap*)image)->prop_item);
     }
     else if (image->type == ImageTypeMetafile)
-    {
-        GpMetafile *metafile = (GpMetafile*)image;
-        heap_free(metafile->comment_data);
-        DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
-        if (!metafile->preserve_hemf)
-            DeleteEnhMetaFile(metafile->hemf);
-        if (metafile->record_graphics)
-        {
-            WARN("metafile closed while recording\n");
-            /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
-            metafile->record_graphics->image = NULL;
-            metafile->record_graphics->busy = TRUE;
-        }
-        if (metafile->record_stream)
-        {
-            IStream_Release(metafile->record_stream);
-        }
-    }
+        METAFILE_Free((GpMetafile *)image);
     else
     {
         WARN("invalid image: %p\n", image);
     else
     {
         WARN("invalid image: %p\n", image);
@@ -4568,7 +4545,7 @@ static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
 static GpStatus encode_image_gif(GpImage *image, IStream* stream,
     GDIPCONST EncoderParameters* params)
 {
 static GpStatus encode_image_gif(GpImage *image, IStream* stream,
     GDIPCONST EncoderParameters* params)
 {
-    return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params);
+    return encode_image_wic(image, stream, &CLSID_WICGifEncoder, params);
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -4581,7 +4558,7 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
     encode_image_func encode_image;
     int i;
 
     encode_image_func encode_image;
     int i;
 
-    TRACE("%p %p %s %p\n", image, stream, wine_dbgstr_guid(clsid), params);
+    TRACE("%p %p %p %p\n", image, stream, clsid, params);
 
     if(!image || !stream)
         return InvalidParameter;
 
     if(!image || !stream)
         return InvalidParameter;
@@ -5595,111 +5572,3 @@ GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat format, UINT *num
     *num_of_entries = 256;
     return Ok;
 }
     *num_of_entries = 256;
     return Ok;
 }
-
-static GpStatus create_optimal_palette(ColorPalette *palette, INT desired,
-    BOOL transparent, GpBitmap *bitmap)
-{
-    GpStatus status;
-    BitmapData data;
-    HRESULT hr;
-    IWICImagingFactory *factory;
-    IWICPalette *wic_palette;
-
-    if (!bitmap) return InvalidParameter;
-    if (palette->Count < desired) return GenericError;
-
-    status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat24bppRGB, &data);
-    if (status != Ok) return status;
-
-    hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory);
-    if (hr != S_OK)
-    {
-        GdipBitmapUnlockBits(bitmap, &data);
-        return hresult_to_status(hr);
-    }
-
-    hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
-    if (hr == S_OK)
-    {
-        IWICBitmap *bitmap;
-
-        /* PixelFormat24bppRGB actually stores the bitmap bits as BGR. */
-        hr = IWICImagingFactory_CreateBitmapFromMemory(factory, data.Width, data.Height,
-                &GUID_WICPixelFormat24bppBGR, data.Stride, data.Stride * data.Width, data.Scan0, &bitmap);
-        if (hr == S_OK)
-        {
-            hr = IWICPalette_InitializeFromBitmap(wic_palette, (IWICBitmapSource *)bitmap, desired, transparent);
-            if (hr == S_OK)
-            {
-                palette->Flags = 0;
-                IWICPalette_GetColorCount(wic_palette, &palette->Count);
-                IWICPalette_GetColors(wic_palette, palette->Count, palette->Entries, &palette->Count);
-            }
-
-            IWICBitmap_Release(bitmap);
-        }
-
-        IWICPalette_Release(wic_palette);
-    }
-
-    IWICImagingFactory_Release(factory);
-    GdipBitmapUnlockBits(bitmap, &data);
-
-    return hresult_to_status(hr);
-}
-
-/*****************************************************************************
- * GdipInitializePalette [GDIPLUS.@]
- */
-GpStatus WINGDIPAPI GdipInitializePalette(ColorPalette *palette,
-    PaletteType type, INT desired, BOOL transparent, GpBitmap *bitmap)
-{
-    TRACE("(%p,%d,%d,%d,%p)\n", palette, type, desired, transparent, bitmap);
-
-    if (!palette) return InvalidParameter;
-
-    switch (type)
-    {
-    case PaletteTypeCustom:
-        return Ok;
-
-    case PaletteTypeOptimal:
-        return create_optimal_palette(palette, desired, transparent, bitmap);
-
-    /* WIC palette type enumeration matches these gdiplus enums */
-    case PaletteTypeFixedBW:
-    case PaletteTypeFixedHalftone8:
-    case PaletteTypeFixedHalftone27:
-    case PaletteTypeFixedHalftone64:
-    case PaletteTypeFixedHalftone125:
-    case PaletteTypeFixedHalftone216:
-    case PaletteTypeFixedHalftone252:
-    case PaletteTypeFixedHalftone256:
-    {
-        ColorPalette *wic_palette;
-        GpStatus status = Ok;
-
-        wic_palette = get_palette(NULL, type);
-        if (!wic_palette) return OutOfMemory;
-
-        if (palette->Count >= wic_palette->Count)
-        {
-            palette->Flags = wic_palette->Flags;
-            palette->Count = wic_palette->Count;
-            memcpy(palette->Entries, wic_palette->Entries, wic_palette->Count * sizeof(wic_palette->Entries[0]));
-        }
-        else
-            status = GenericError;
-
-        heap_free(wic_palette);
-
-        return status;
-    }
-
-    default:
-        FIXME("unknown palette type %d\n", type);
-        break;
-    }
-
-    return InvalidParameter;
-}
index 12941f7..b30eba9 100644 (file)
@@ -209,14 +209,14 @@ GpStatus WINGDIPAPI GdipSetImageAttributesGamma(GpImageAttributes *imageAttr,
 GpStatus WINGDIPAPI GdipSetImageAttributesNoOp(GpImageAttributes *imageAttr,
     ColorAdjustType type, BOOL enableFlag)
 {
 GpStatus WINGDIPAPI GdipSetImageAttributesNoOp(GpImageAttributes *imageAttr,
     ColorAdjustType type, BOOL enableFlag)
 {
-    static int calls;
-
     TRACE("(%p,%u,%i)\n", imageAttr, type, enableFlag);
 
     TRACE("(%p,%u,%i)\n", imageAttr, type, enableFlag);
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    if (type >= ColorAdjustTypeCount)
+        return InvalidParameter;
 
 
-    return NotImplemented;
+    imageAttr->noop[type] = enableFlag ? IMAGEATTR_NOOP_SET : IMAGEATTR_NOOP_CLEAR;
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipSetImageAttributesOutputChannel(GpImageAttributes *imageAttr,
 }
 
 GpStatus WINGDIPAPI GdipSetImageAttributesOutputChannel(GpImageAttributes *imageAttr,
@@ -323,6 +323,7 @@ GpStatus WINGDIPAPI GdipResetImageAttributes(GpImageAttributes *imageAttr,
     GdipSetImageAttributesColorKeys(imageAttr, type, FALSE, 0, 0);
     GdipSetImageAttributesRemapTable(imageAttr, type, FALSE, 0, NULL);
     GdipSetImageAttributesGamma(imageAttr, type, FALSE, 0.0);
     GdipSetImageAttributesColorKeys(imageAttr, type, FALSE, 0, 0);
     GdipSetImageAttributesRemapTable(imageAttr, type, FALSE, 0, NULL);
     GdipSetImageAttributesGamma(imageAttr, type, FALSE, 0.0);
+    imageAttr->noop[type] = IMAGEATTR_NOOP_UNDEFINED;
 
     return Ok;
 }
 
     return Ok;
 }
index 6171352..165a55d 100644 (file)
 #include <assert.h>
 #include <ole2.h>
 
 #include <assert.h>
 #include <ole2.h>
 
-typedef struct EmfPlusARGB
-{
-    BYTE Blue;
-    BYTE Green;
-    BYTE Red;
-    BYTE Alpha;
-} EmfPlusARGB;
+HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
+
+typedef ARGB EmfPlusARGB;
 
 typedef struct EmfPlusRecordHeader
 {
 
 typedef struct EmfPlusRecordHeader
 {
@@ -175,6 +171,30 @@ enum LineStyle
     LineStyleCustom
 };
 
     LineStyleCustom
 };
 
+typedef struct EmfPlusDashedLineData
+{
+    DWORD DashedLineDataSize;
+    BYTE data[1];
+} EmfPlusDashedLineData;
+
+typedef struct EmfPlusCompoundLineData
+{
+    DWORD CompoundLineDataSize;
+    BYTE data[1];
+} EmfPlusCompoundLineData;
+
+typedef struct EmfPlusCustomStartCapData
+{
+    DWORD CustomStartCapSize;
+    BYTE data[1];
+} EmfPlusCustomStartCapData;
+
+typedef struct EmfPlusCustomEndCapData
+{
+    DWORD CustomEndCapSize;
+    BYTE data[1];
+} EmfPlusCustomEndCapData;
+
 typedef struct EmfPlusPenData
 {
     DWORD PenDataFlags;
 typedef struct EmfPlusPenData
 {
     DWORD PenDataFlags;
@@ -183,17 +203,66 @@ typedef struct EmfPlusPenData
     BYTE OptionalData[1];
 } EmfPlusPenData;
 
     BYTE OptionalData[1];
 } EmfPlusPenData;
 
+enum BrushDataFlags
+{
+    BrushDataPath             = 1 << 0,
+    BrushDataTransform        = 1 << 1,
+    BrushDataPresetColors     = 1 << 2,
+    BrushDataBlendFactorsH    = 1 << 3,
+    BrushDataBlendFactorsV    = 1 << 4,
+    BrushDataFocusScales      = 1 << 6,
+    BrushDataIsGammaCorrected = 1 << 7,
+    BrushDataDoNotTransform   = 1 << 8,
+};
+
 typedef struct EmfPlusSolidBrushData
 {
     EmfPlusARGB SolidColor;
 } EmfPlusSolidBrushData;
 
 typedef struct EmfPlusSolidBrushData
 {
     EmfPlusARGB SolidColor;
 } EmfPlusSolidBrushData;
 
+typedef struct EmfPlusHatchBrushData
+{
+    DWORD HatchStyle;
+    EmfPlusARGB ForeColor;
+    EmfPlusARGB BackColor;
+} EmfPlusHatchBrushData;
+
+typedef struct EmfPlusTextureBrushData
+{
+    DWORD BrushDataFlags;
+    INT WrapMode;
+    BYTE OptionalData[1];
+} EmfPlusTextureBrushData;
+
+typedef struct EmfPlusRectF
+{
+    float X;
+    float Y;
+    float Width;
+    float Height;
+} EmfPlusRectF;
+
+typedef struct EmfPlusLinearGradientBrushData
+{
+    DWORD BrushDataFlags;
+    INT WrapMode;
+    EmfPlusRectF RectF;
+    EmfPlusARGB StartColor;
+    EmfPlusARGB EndColor;
+    DWORD Reserved1;
+    DWORD Reserved2;
+    BYTE OptionalData[1];
+} EmfPlusLinearGradientBrushData;
+
 typedef struct EmfPlusBrush
 {
     DWORD Version;
     DWORD Type;
     union {
         EmfPlusSolidBrushData solid;
 typedef struct EmfPlusBrush
 {
     DWORD Version;
     DWORD Type;
     union {
         EmfPlusSolidBrushData solid;
+        EmfPlusHatchBrushData hatch;
+        EmfPlusTextureBrushData texture;
+        EmfPlusLinearGradientBrushData lineargradient;
     } BrushData;
 } EmfPlusBrush;
 
     } BrushData;
 } EmfPlusBrush;
 
@@ -217,6 +286,12 @@ typedef struct EmfPlusPath
     BYTE data[1];
 } EmfPlusPath;
 
     BYTE data[1];
 } EmfPlusPath;
 
+typedef struct EmfPlusRegionNodePath
+{
+    DWORD RegionNodePathLength;
+    EmfPlusPath RegionNodePath;
+} EmfPlusRegionNodePath;
+
 typedef struct EmfPlusRegion
 {
     DWORD Version;
 typedef struct EmfPlusRegion
 {
     DWORD Version;
@@ -224,6 +299,13 @@ typedef struct EmfPlusRegion
     BYTE RegionNode[1];
 } EmfPlusRegion;
 
     BYTE RegionNode[1];
 } EmfPlusRegion;
 
+typedef struct EmfPlusPalette
+{
+    DWORD PaletteStyleFlags;
+    DWORD PaletteCount;
+    BYTE PaletteEntries[1];
+} EmfPlusPalette;
+
 typedef enum
 {
     BitmapDataTypePixel,
 typedef enum
 {
     BitmapDataTypePixel,
@@ -275,20 +357,6 @@ typedef struct EmfPlusImageAttributes
     DWORD Reserved2;
 } EmfPlusImageAttributes;
 
     DWORD Reserved2;
 } EmfPlusImageAttributes;
 
-typedef enum ObjectType
-{
-    ObjectTypeInvalid,
-    ObjectTypeBrush,
-    ObjectTypePen,
-    ObjectTypePath,
-    ObjectTypeRegion,
-    ObjectTypeImage,
-    ObjectTypeFont,
-    ObjectTypeStringFormat,
-    ObjectTypeImageAttributes,
-    ObjectTypeCustomLineCap,
-} ObjectType;
-
 typedef struct EmfPlusObject
 {
     EmfPlusRecordHeader Header;
 typedef struct EmfPlusObject
 {
     EmfPlusRecordHeader Header;
@@ -303,13 +371,17 @@ typedef struct EmfPlusObject
     } ObjectData;
 } EmfPlusObject;
 
     } ObjectData;
 } EmfPlusObject;
 
-typedef struct EmfPlusRectF
+typedef struct EmfPlusPointR7
 {
 {
-    float X;
-    float Y;
-    float Width;
-    float Height;
-} EmfPlusRectF;
+    BYTE X;
+    BYTE Y;
+} EmfPlusPointR7;
+
+typedef struct EmfPlusPoint
+{
+    short X;
+    short Y;
+} EmfPlusPoint;
 
 typedef struct EmfPlusPointF
 {
 
 typedef struct EmfPlusPointF
 {
@@ -317,6 +389,19 @@ typedef struct EmfPlusPointF
     float Y;
 } EmfPlusPointF;
 
     float Y;
 } EmfPlusPointF;
 
+typedef struct EmfPlusDrawImage
+{
+    EmfPlusRecordHeader Header;
+    DWORD ImageAttributesID;
+    DWORD SrcUnit;
+    EmfPlusRectF SrcRect;
+    union
+    {
+        EmfPlusRect rect;
+        EmfPlusRectF rectF;
+    } RectData;
+} EmfPlusDrawImage;
+
 typedef struct EmfPlusDrawImagePoints
 {
     EmfPlusRecordHeader Header;
 typedef struct EmfPlusDrawImagePoints
 {
     EmfPlusRecordHeader Header;
@@ -326,10 +411,10 @@ typedef struct EmfPlusDrawImagePoints
     DWORD count;
     union
     {
     DWORD count;
     union
     {
-        /*EmfPlusPointR pointR;
-        EmfPlusPoint point;*/
-        EmfPlusPointF pointF;
-    } PointData[3];
+        EmfPlusPointR7 pointsR[3];
+        EmfPlusPoint points[3];
+        EmfPlusPointF pointsF[3];
+    } PointData;
 } EmfPlusDrawImagePoints;
 
 typedef struct EmfPlusDrawPath
 } EmfPlusDrawImagePoints;
 
 typedef struct EmfPlusDrawPath
@@ -338,6 +423,51 @@ typedef struct EmfPlusDrawPath
     DWORD PenId;
 } EmfPlusDrawPath;
 
     DWORD PenId;
 } EmfPlusDrawPath;
 
+typedef struct EmfPlusDrawArc
+{
+    EmfPlusRecordHeader Header;
+    float StartAngle;
+    float SweepAngle;
+    union
+    {
+        EmfPlusRect rect;
+        EmfPlusRectF rectF;
+    } RectData;
+} EmfPlusDrawArc;
+
+typedef struct EmfPlusDrawEllipse
+{
+    EmfPlusRecordHeader Header;
+    union
+    {
+        EmfPlusRect rect;
+        EmfPlusRectF rectF;
+    } RectData;
+} EmfPlusDrawEllipse;
+
+typedef struct EmfPlusDrawPie
+{
+    EmfPlusRecordHeader Header;
+    float StartAngle;
+    float SweepAngle;
+    union
+    {
+        EmfPlusRect rect;
+        EmfPlusRectF rectF;
+    } RectData;
+} EmfPlusDrawPie;
+
+typedef struct EmfPlusDrawRects
+{
+    EmfPlusRecordHeader Header;
+    DWORD Count;
+    union
+    {
+        EmfPlusRect rect[1];
+        EmfPlusRectF rectF[1];
+    } RectData;
+} EmfPlusDrawRects;
+
 typedef struct EmfPlusFillPath
 {
     EmfPlusRecordHeader Header;
 typedef struct EmfPlusFillPath
 {
     EmfPlusRecordHeader Header;
@@ -348,9 +478,119 @@ typedef struct EmfPlusFillPath
     } data;
 } EmfPlusFillPath;
 
     } data;
 } EmfPlusFillPath;
 
+typedef struct EmfPlusFillClosedCurve
+{
+    EmfPlusRecordHeader Header;
+    DWORD BrushId;
+    float Tension;
+    DWORD Count;
+    union
+    {
+        EmfPlusPointR7 pointsR[1];
+        EmfPlusPoint points[1];
+        EmfPlusPointF pointsF[1];
+    } PointData;
+} EmfPlusFillClosedCurve;
+
+typedef struct EmfPlusFillEllipse
+{
+    EmfPlusRecordHeader Header;
+    DWORD BrushId;
+    union
+    {
+        EmfPlusRect rect;
+        EmfPlusRectF rectF;
+    } RectData;
+} EmfPlusFillEllipse;
+
+typedef struct EmfPlusFillPie
+{
+    EmfPlusRecordHeader Header;
+    DWORD BrushId;
+    float StartAngle;
+    float SweepAngle;
+    union
+    {
+        EmfPlusRect rect;
+        EmfPlusRectF rectF;
+    } RectData;
+} EmfPlusFillPie;
+
+typedef struct EmfPlusFont
+{
+    DWORD Version;
+    float EmSize;
+    DWORD SizeUnit;
+    DWORD FontStyleFlags;
+    DWORD Reserved;
+    DWORD Length;
+    WCHAR FamilyName[1];
+} EmfPlusFont;
+
+static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
+{
+    struct emfplus_object *object = &metafile->objtable[id];
+
+    switch (object->type)
+    {
+    case ObjectTypeInvalid:
+        break;
+    case ObjectTypeBrush:
+        GdipDeleteBrush(object->u.brush);
+        break;
+    case ObjectTypePen:
+        GdipDeletePen(object->u.pen);
+        break;
+    case ObjectTypePath:
+        GdipDeletePath(object->u.path);
+        break;
+    case ObjectTypeRegion:
+        GdipDeleteRegion(object->u.region);
+        break;
+    case ObjectTypeImage:
+        GdipDisposeImage(object->u.image);
+        break;
+    case ObjectTypeFont:
+        GdipDeleteFont(object->u.font);
+        break;
+    case ObjectTypeImageAttributes:
+        GdipDisposeImageAttributes(object->u.image_attributes);
+        break;
+    default:
+        FIXME("not implemented for object type %u.\n", object->type);
+        return;
+    }
+
+    object->type = ObjectTypeInvalid;
+    object->u.object = NULL;
+}
+
+void METAFILE_Free(GpMetafile *metafile)
+{
+    unsigned int i;
+
+    heap_free(metafile->comment_data);
+    DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
+    if (!metafile->preserve_hemf)
+        DeleteEnhMetaFile(metafile->hemf);
+    if (metafile->record_graphics)
+    {
+        WARN("metafile closed while recording\n");
+        /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
+        metafile->record_graphics->image = NULL;
+        metafile->record_graphics->busy = TRUE;
+    }
+
+    if (metafile->record_stream)
+        IStream_Release(metafile->record_stream);
+
+    for (i = 0; i < sizeof(metafile->objtable)/sizeof(metafile->objtable[0]); i++)
+        metafile_free_object_table_entry(metafile, i);
+}
+
 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
 {
 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
 {
-    return (metafile->next_object_id++) % 64;
+    return (metafile->next_object_id++) % EmfPlusObjectTableSize;
 }
 
 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
 }
 
 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
@@ -718,6 +958,74 @@ static BOOL is_integer_rect(const GpRectF *rect)
     return TRUE;
 }
 
     return TRUE;
 }
 
+static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
+{
+    switch (brush->bt)
+    {
+    case BrushTypeSolidColor:
+        *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
+        break;
+    case BrushTypeHatchFill:
+        *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
+        break;
+    default:
+        FIXME("unsupported brush type: %d\n", brush->bt);
+        return NotImplemented;
+    }
+
+    return Ok;
+}
+
+static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
+{
+    data->Version = VERSION_MAGIC2;
+    data->Type = brush->bt;
+
+    switch (brush->bt)
+    {
+    case BrushTypeSolidColor:
+    {
+        GpSolidFill *solid = (GpSolidFill *)brush;
+        data->BrushData.solid.SolidColor = solid->color;
+        break;
+    }
+    case BrushTypeHatchFill:
+    {
+        GpHatch *hatch = (GpHatch *)brush;
+        data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
+        data->BrushData.hatch.ForeColor = hatch->forecol;
+        data->BrushData.hatch.BackColor = hatch->backcol;
+        break;
+    }
+    default:
+        FIXME("unsupported brush type: %d\n", brush->bt);
+    }
+}
+
+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_FillRectangles(GpMetafile* metafile, GpBrush* brush,
     GDIPCONST GpRectF* rects, INT count)
 {
 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
     GDIPCONST GpRectF* rects, INT count)
 {
@@ -737,8 +1045,9 @@ GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
         }
         else
         {
         }
         else
         {
-            FIXME("brush serialization not implemented\n");
-            return NotImplemented;
+            stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
+            if (stat != Ok)
+                return stat;
         }
 
         for (i=0; i<count; i++)
         }
 
         for (i=0; i<count; i++)
@@ -1385,99 +1694,841 @@ static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
     return stat;
 }
 
     return stat;
 }
 
-GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
-    EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
+static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
 {
 {
-    GpStatus stat;
-    GpMetafile *real_metafile = (GpMetafile*)metafile;
+    metafile_free_object_table_entry(metafile, id);
+    metafile->objtable[id].type = type;
+    metafile->objtable[id].u.object = object;
+}
 
 
-    TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
+static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
+{
+    EmfPlusImage *data = (EmfPlusImage *)record_data;
+    GpStatus status;
 
 
-    if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
+    *image = NULL;
+
+    if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
         return InvalidParameter;
         return InvalidParameter;
+    data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
 
 
-    if (recordType >= 1 && recordType <= 0x7a)
+    switch (data->Type)
     {
     {
-        /* regular EMF record */
-        if (metafile->playback_dc)
-        {
-            switch (recordType)
-            {
-            case EMR_SETMAPMODE:
-            case EMR_SAVEDC:
-            case EMR_RESTOREDC:
-            case EMR_SETWINDOWORGEX:
-            case EMR_SETWINDOWEXTEX:
-            case EMR_SETVIEWPORTORGEX:
-            case EMR_SETVIEWPORTEXTEX:
-            case EMR_SCALEVIEWPORTEXTEX:
-            case EMR_SCALEWINDOWEXTEX:
-            case EMR_MODIFYWORLDTRANSFORM:
-                FIXME("not implemented for record type %x\n", recordType);
-                break;
-            case EMR_SETWORLDTRANSFORM:
-            {
-                const XFORM* xform = (void*)data;
-                real_metafile->gdiworldtransform = *xform;
-                METAFILE_PlaybackUpdateGdiTransform(real_metafile);
-                break;
-            }
-            case EMR_EXTSELECTCLIPRGN:
-            {
-                DWORD rgndatasize = *(DWORD*)data;
-                DWORD mode = *(DWORD*)(data + 4);
-                const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
-                HRGN hrgn = NULL;
+    case ImageDataTypeBitmap:
+    {
+        EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
 
 
-                if (dataSize > 8)
-                {
-                    XFORM final;
+        if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
+            return InvalidParameter;
+        data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
 
 
-                    METAFILE_GetFinalGdiTransform(metafile, &final);
+        switch (bitmapdata->Type)
+        {
+        case BitmapDataTypePixel:
+        {
+            ColorPalette *palette;
+            BYTE *scan0;
 
 
-                    hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
-                }
+            if (bitmapdata->PixelFormat & PixelFormatIndexed)
+            {
+                EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
+                UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
 
 
-                ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
+                if (data_size <= palette_size)
+                    return InvalidParameter;
+                palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
 
 
-                DeleteObject(hrgn);
+                if (data_size < palette_size)
+                    return InvalidParameter;
+                data_size -= palette_size;
 
 
-                return Ok;
+                palette = (ColorPalette *)bitmapdata->BitmapData;
+                scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
             }
             }
-            default:
+            else
             {
             {
-                ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
-
-                if (record)
-                {
-                    record->iType = recordType;
-                    record->nSize = dataSize + 8;
-                    memcpy(record->dParm, data, dataSize);
+                palette = NULL;
+                scan0 = bitmapdata->BitmapData;
+            }
 
 
-                    if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
-                            record, metafile->handle_count) == 0)
-                        ERR("PlayEnhMetaFileRecord failed\n");
+            if (data_size < bitmapdata->Height * bitmapdata->Stride)
+                return InvalidParameter;
 
 
-                    heap_free(record);
+            status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
+                bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
+            if (status == Ok && palette)
+            {
+                status = GdipSetImagePalette(*image, palette);
+                if (status != Ok)
+                {
+                    GdipDisposeImage(*image);
+                    *image = NULL;
                 }
                 }
-                else
-                    return OutOfMemory;
-
-                break;
-            }
             }
             }
+            break;
         }
         }
-    }
-    else
-    {
-        EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
+        case BitmapDataTypeCompressed:
+        {
+            IWICImagingFactory *factory;
+            IWICStream *stream;
+            HRESULT hr;
 
 
-        METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
+            if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
+                return GenericError;
 
 
-        switch(recordType)
-        {
-        case EmfPlusRecordTypeHeader:
-        case EmfPlusRecordTypeEndOfFile:
+            hr = IWICImagingFactory_CreateStream(factory, &stream);
+            IWICImagingFactory_Release(factory);
+            if (hr != S_OK)
+                return GenericError;
+
+            if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
+                status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
+            else
+                status = GenericError;
+
+            IWICStream_Release(stream);
+            break;
+        }
+        default:
+            WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
+            return InvalidParameter;
+        }
+        break;
+    }
+    default:
+        FIXME("image type %d not supported.\n", data->Type);
+        return NotImplemented;
+    }
+
+    return status;
+}
+
+static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
+{
+    EmfPlusPath *data = (EmfPlusPath *)record_data;
+    GpStatus status;
+    BYTE *types;
+    UINT size;
+    DWORD i;
+
+    *path = NULL;
+
+    if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
+        return InvalidParameter;
+    data_size -= FIELD_OFFSET(EmfPlusPath, data);
+
+    if (data->PathPointFlags & 0x800) /* R */
+    {
+        FIXME("RLE encoded path data is not supported.\n");
+        return NotImplemented;
+    }
+    else
+    {
+        if (data->PathPointFlags & 0x4000) /* C */
+            size = sizeof(EmfPlusPoint);
+        else
+            size = sizeof(EmfPlusPointF);
+        size += sizeof(BYTE); /* EmfPlusPathPointType */
+        size *= data->PathPointCount;
+    }
+
+    if (data_size < size)
+        return InvalidParameter;
+
+    status = GdipCreatePath(FillModeAlternate, path);
+    if (status != Ok)
+        return status;
+
+    (*path)->pathdata.Count = data->PathPointCount;
+    (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
+    (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
+    (*path)->datalen = (*path)->pathdata.Count;
+
+    if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
+    {
+        GdipDeletePath(*path);
+        return OutOfMemory;
+    }
+
+    if (data->PathPointFlags & 0x4000) /* C */
+    {
+        EmfPlusPoint *points = (EmfPlusPoint *)data->data;
+        for (i = 0; i < data->PathPointCount; i++)
+        {
+            (*path)->pathdata.Points[i].X = points[i].X;
+            (*path)->pathdata.Points[i].Y = points[i].Y;
+        }
+        types = (BYTE *)(points + i);
+    }
+    else
+    {
+        EmfPlusPointF *points = (EmfPlusPointF *)data->data;
+        memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
+        types = (BYTE *)(points + data->PathPointCount);
+    }
+
+    memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
+
+    return Ok;
+}
+
+static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
+{
+    const DWORD *type;
+    GpStatus status;
+
+    type = buffer_read(mbuf, sizeof(*type));
+    if (!type) return Ok;
+
+    node->type = *type;
+
+    switch (node->type)
+    {
+    case CombineModeReplace:
+    case CombineModeIntersect:
+    case CombineModeUnion:
+    case CombineModeXor:
+    case CombineModeExclude:
+    case CombineModeComplement:
+    {
+        region_element *left, *right;
+
+        left = heap_alloc_zero(sizeof(*left));
+        if (!left)
+            return OutOfMemory;
+
+        right = heap_alloc_zero(sizeof(*right));
+        if (!right)
+        {
+            heap_free(left);
+            return OutOfMemory;
+        }
+
+        status = metafile_read_region_node(mbuf, region, left, count);
+        if (status == Ok)
+        {
+            status = metafile_read_region_node(mbuf, region, right, count);
+            if (status == Ok)
+            {
+                node->elementdata.combine.left = left;
+                node->elementdata.combine.right = right;
+                region->num_children += 2;
+                return Ok;
+            }
+        }
+
+        heap_free(left);
+        heap_free(right);
+        return status;
+    }
+    case RegionDataRect:
+    {
+        const EmfPlusRectF *rect;
+
+        rect = buffer_read(mbuf, sizeof(*rect));
+        if (!rect)
+            return InvalidParameter;
+
+        memcpy(&node->elementdata.rect, rect, sizeof(*rect));
+        *count += 1;
+        return Ok;
+    }
+    case RegionDataPath:
+    {
+        const BYTE *path_data;
+        const UINT *data_size;
+        GpPath *path;
+
+        data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
+        if (!data_size)
+            return InvalidParameter;
+
+        path_data = buffer_read(mbuf, *data_size);
+        if (!path_data)
+            return InvalidParameter;
+
+        status = metafile_deserialize_path(path_data, *data_size, &path);
+        if (status == Ok)
+        {
+            node->elementdata.path = path;
+            *count += 1;
+        }
+        return Ok;
+    }
+    case RegionDataEmptyRect:
+    case RegionDataInfiniteRect:
+        *count += 1;
+        return Ok;
+    default:
+        FIXME("element type %#x is not supported\n", *type);
+        break;
+    }
+
+    return InvalidParameter;
+}
+
+static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
+{
+    struct memory_buffer mbuf;
+    GpStatus status;
+    UINT count;
+
+    *region = NULL;
+
+    init_memory_buffer(&mbuf, record_data, data_size);
+
+    if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
+        return InvalidParameter;
+
+    status = GdipCreateRegion(region);
+    if (status != Ok)
+        return status;
+
+    count = 0;
+    status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
+    if (status == Ok && !count)
+        status = InvalidParameter;
+
+    if (status != Ok)
+    {
+        GdipDeleteRegion(*region);
+        *region = NULL;
+    }
+
+    return status;
+}
+
+static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
+{
+    static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
+    EmfPlusBrush *data = (EmfPlusBrush *)record_data;
+    EmfPlusTransformMatrix *transform = NULL;
+    DWORD brushflags;
+    GpStatus status;
+    UINT offset;
+
+    *brush = NULL;
+
+    if (data_size < header_size)
+        return InvalidParameter;
+
+    switch (data->Type)
+    {
+    case BrushTypeSolidColor:
+        if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
+            return InvalidParameter;
+
+        status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
+        break;
+    case BrushTypeHatchFill:
+        if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
+            return InvalidParameter;
+
+        status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
+            data->BrushData.hatch.BackColor, (GpHatch **)brush);
+        break;
+    case BrushTypeTextureFill:
+    {
+        GpImage *image;
+
+        offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
+        if (data_size <= offset)
+            return InvalidParameter;
+
+        brushflags = data->BrushData.texture.BrushDataFlags;
+        if (brushflags & BrushDataTransform)
+        {
+            if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
+                return InvalidParameter;
+            transform = (EmfPlusTransformMatrix *)(record_data + offset);
+            offset += sizeof(EmfPlusTransformMatrix);
+        }
+
+        status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
+        if (status != Ok)
+            return status;
+
+        status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
+        if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
+            GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
+
+        GdipDisposeImage(image);
+        break;
+    }
+    case BrushTypeLinearGradient:
+    {
+        GpLineGradient *gradient = NULL;
+        GpPointF startpoint, endpoint;
+        UINT position_count = 0;
+
+        offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
+        if (data_size <= offset)
+            return InvalidParameter;
+
+        brushflags = data->BrushData.lineargradient.BrushDataFlags;
+        if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
+            return InvalidParameter;
+
+        if (brushflags & BrushDataTransform)
+        {
+            if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
+                return InvalidParameter;
+            transform = (EmfPlusTransformMatrix *)(record_data + offset);
+            offset += sizeof(EmfPlusTransformMatrix);
+        }
+
+        if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
+        {
+            if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
+                return InvalidParameter;
+            position_count = *(DWORD *)(record_data + offset);
+            offset += sizeof(DWORD);
+        }
+
+        if (brushflags & BrushDataPresetColors)
+        {
+            if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
+                return InvalidParameter;
+        }
+        else if (brushflags & BrushDataBlendFactorsH)
+        {
+            if (data_size != offset + position_count * 2 * sizeof(float))
+                return InvalidParameter;
+        }
+
+        startpoint.X = data->BrushData.lineargradient.RectF.X;
+        startpoint.Y = data->BrushData.lineargradient.RectF.Y;
+        endpoint.X = startpoint.X + data->BrushData.lineargradient.RectF.Width;
+        endpoint.Y = startpoint.Y + data->BrushData.lineargradient.RectF.Height;
+
+        status = GdipCreateLineBrush(&startpoint, &endpoint, data->BrushData.lineargradient.StartColor,
+            data->BrushData.lineargradient.EndColor, data->BrushData.lineargradient.WrapMode, &gradient);
+        if (status == Ok)
+        {
+            if (transform)
+                status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
+
+            if (status == Ok)
+            {
+                if (brushflags & BrushDataPresetColors)
+                    status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
+                        position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
+                else if (brushflags & BrushDataBlendFactorsH)
+                    status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
+                        (REAL *)(record_data + offset), position_count);
+
+                if (brushflags & BrushDataIsGammaCorrected)
+                    FIXME("BrushDataIsGammaCorrected is not handled.\n");
+            }
+        }
+
+        if (status == Ok)
+            *brush = (GpBrush *)gradient;
+        else
+            GdipDeleteBrush((GpBrush *)gradient);
+
+        break;
+    }
+    default:
+        FIXME("brush type %u is not supported.\n", data->Type);
+        return NotImplemented;
+    }
+
+    return status;
+}
+
+static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
+{
+    EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
+    DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
+
+    if (data_size <= offset)
+        return InvalidParameter;
+
+    offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
+    if (data_size <= offset)
+        return InvalidParameter;
+
+    if (pendata->PenDataFlags & PenDataTransform)
+        offset += sizeof(EmfPlusTransformMatrix);
+
+    if (pendata->PenDataFlags & PenDataStartCap)
+        offset += sizeof(DWORD);
+
+    if (pendata->PenDataFlags & PenDataEndCap)
+        offset += sizeof(DWORD);
+
+    if (pendata->PenDataFlags & PenDataJoin)
+        offset += sizeof(DWORD);
+
+    if (pendata->PenDataFlags & PenDataMiterLimit)
+        offset += sizeof(REAL);
+
+    if (pendata->PenDataFlags & PenDataLineStyle)
+        offset += sizeof(DWORD);
+
+    if (pendata->PenDataFlags & PenDataDashedLineCap)
+        offset += sizeof(DWORD);
+
+    if (pendata->PenDataFlags & PenDataDashedLineOffset)
+        offset += sizeof(REAL);
+
+    if (pendata->PenDataFlags & PenDataDashedLine)
+    {
+        EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
+
+        offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
+        if (data_size <= offset)
+            return InvalidParameter;
+
+        offset += dashedline->DashedLineDataSize * sizeof(float);
+    }
+
+    if (pendata->PenDataFlags & PenDataNonCenter)
+        offset += sizeof(DWORD);
+
+    if (pendata->PenDataFlags & PenDataCompoundLine)
+    {
+        EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
+
+        offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
+        if (data_size <= offset)
+            return InvalidParameter;
+
+        offset += compoundline->CompoundLineDataSize * sizeof(float);
+    }
+
+    if (pendata->PenDataFlags & PenDataCustomStartCap)
+    {
+        EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
+
+        offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
+        if (data_size <= offset)
+            return InvalidParameter;
+
+        offset += startcap->CustomStartCapSize;
+    }
+
+    if (pendata->PenDataFlags & PenDataCustomEndCap)
+    {
+        EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
+
+        offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
+        if (data_size <= offset)
+            return InvalidParameter;
+
+        offset += endcap->CustomEndCapSize;
+    }
+
+    *ret = offset;
+    return Ok;
+}
+
+static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
+{
+    BYTE type = (flags >> 8) & 0xff;
+    BYTE id = flags & 0xff;
+    void *object = NULL;
+    GpStatus status;
+
+    if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
+        return InvalidParameter;
+
+    switch (type)
+    {
+    case ObjectTypeBrush:
+        status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
+        break;
+    case ObjectTypePen:
+    {
+        EmfPlusPen *data = (EmfPlusPen *)record_data;
+        EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
+        GpBrush *brush;
+        DWORD offset;
+        GpPen *pen;
+
+        status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
+        if (status != Ok)
+            return status;
+
+        status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
+        if (status != Ok)
+            return status;
+
+        status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
+        GdipDeleteBrush(brush);
+        if (status != Ok)
+            return status;
+
+        offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
+
+        if (pendata->PenDataFlags & PenDataTransform)
+        {
+            FIXME("PenDataTransform is not supported.\n");
+            offset += sizeof(EmfPlusTransformMatrix);
+        }
+
+        if (pendata->PenDataFlags & PenDataStartCap)
+        {
+            if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
+                goto penfailed;
+            offset += sizeof(DWORD);
+        }
+
+        if (pendata->PenDataFlags & PenDataEndCap)
+        {
+            if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
+                goto penfailed;
+            offset += sizeof(DWORD);
+        }
+
+        if (pendata->PenDataFlags & PenDataJoin)
+        {
+            if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
+                goto penfailed;
+            offset += sizeof(DWORD);
+        }
+
+        if (pendata->PenDataFlags & PenDataMiterLimit)
+        {
+            if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
+                goto penfailed;
+            offset += sizeof(REAL);
+        }
+
+        if (pendata->PenDataFlags & PenDataLineStyle)
+        {
+            if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
+                goto penfailed;
+            offset += sizeof(DWORD);
+        }
+
+        if (pendata->PenDataFlags & PenDataDashedLineCap)
+        {
+            FIXME("PenDataDashedLineCap is not supported.\n");
+            offset += sizeof(DWORD);
+        }
+
+        if (pendata->PenDataFlags & PenDataDashedLineOffset)
+        {
+            if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
+                goto penfailed;
+            offset += sizeof(REAL);
+        }
+
+        if (pendata->PenDataFlags & PenDataDashedLine)
+        {
+            EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
+            FIXME("PenDataDashedLine is not supported.\n");
+            offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
+        }
+
+        if (pendata->PenDataFlags & PenDataNonCenter)
+        {
+            FIXME("PenDataNonCenter is not supported.\n");
+            offset += sizeof(DWORD);
+        }
+
+        if (pendata->PenDataFlags & PenDataCompoundLine)
+        {
+            EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
+            FIXME("PenDataCompundLine is not supported.\n");
+            offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
+        }
+
+        if (pendata->PenDataFlags & PenDataCustomStartCap)
+        {
+            EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
+            FIXME("PenDataCustomStartCap is not supported.\n");
+            offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
+        }
+
+        if (pendata->PenDataFlags & PenDataCustomEndCap)
+        {
+            EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
+            FIXME("PenDataCustomEndCap is not supported.\n");
+            offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
+        }
+
+        object = pen;
+        break;
+
+    penfailed:
+        GdipDeletePen(pen);
+        return status;
+    }
+    case ObjectTypePath:
+        status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
+        break;
+    case ObjectTypeRegion:
+        status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
+        break;
+    case ObjectTypeImage:
+        status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
+        break;
+    case ObjectTypeFont:
+    {
+        EmfPlusFont *data = (EmfPlusFont *)record_data;
+        GpFontFamily *family;
+        WCHAR *familyname;
+
+        if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
+            return InvalidParameter;
+        data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
+
+        if (data_size < data->Length * sizeof(WCHAR))
+            return InvalidParameter;
+
+        if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
+            return OutOfMemory;
+
+        memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
+        familyname[data->Length] = 0;
+
+        status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
+        GdipFree(familyname);
+        if (status != Ok)
+            return InvalidParameter;
+
+        status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
+        GdipDeleteFontFamily(family);
+        break;
+    }
+    case ObjectTypeImageAttributes:
+    {
+        EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
+        GpImageAttributes *attributes = NULL;
+
+        if (data_size != sizeof(*data))
+            return InvalidParameter;
+
+        if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
+            return status;
+
+        status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
+                !!data->ObjectClamp);
+        if (status == Ok)
+            object = attributes;
+        else
+            GdipDisposeImageAttributes(attributes);
+        break;
+    }
+    default:
+        FIXME("not implemented for object type %d.\n", type);
+        return NotImplemented;
+    }
+
+    if (status == Ok)
+        metafile_set_object_table_entry(metafile, id, type, object);
+
+    return status;
+}
+
+static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
+{
+    GpMatrix world_to_device;
+
+    get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
+
+    GdipTransformRegion(region, &world_to_device);
+    GdipCombineRegionRegion(metafile->clip, region, mode);
+
+    return METAFILE_PlaybackUpdateClip(metafile);
+}
+
+GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
+    EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
+{
+    GpStatus stat;
+    GpMetafile *real_metafile = (GpMetafile*)metafile;
+
+    TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
+
+    if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
+        return InvalidParameter;
+
+    if (recordType >= 1 && recordType <= 0x7a)
+    {
+        /* regular EMF record */
+        if (metafile->playback_dc)
+        {
+            switch (recordType)
+            {
+            case EMR_SETMAPMODE:
+            case EMR_SAVEDC:
+            case EMR_RESTOREDC:
+            case EMR_SETWINDOWORGEX:
+            case EMR_SETWINDOWEXTEX:
+            case EMR_SETVIEWPORTORGEX:
+            case EMR_SETVIEWPORTEXTEX:
+            case EMR_SCALEVIEWPORTEXTEX:
+            case EMR_SCALEWINDOWEXTEX:
+            case EMR_MODIFYWORLDTRANSFORM:
+                FIXME("not implemented for record type %x\n", recordType);
+                break;
+            case EMR_SETWORLDTRANSFORM:
+            {
+                const XFORM* xform = (void*)data;
+                real_metafile->gdiworldtransform = *xform;
+                METAFILE_PlaybackUpdateGdiTransform(real_metafile);
+                break;
+            }
+            case EMR_EXTSELECTCLIPRGN:
+            {
+                DWORD rgndatasize = *(DWORD*)data;
+                DWORD mode = *(DWORD*)(data + 4);
+                const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
+                HRGN hrgn = NULL;
+
+                if (dataSize > 8)
+                {
+                    XFORM final;
+
+                    METAFILE_GetFinalGdiTransform(metafile, &final);
+
+                    hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
+                }
+
+                ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
+
+                DeleteObject(hrgn);
+
+                return Ok;
+            }
+            default:
+            {
+                ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
+
+                if (record)
+                {
+                    record->iType = recordType;
+                    record->nSize = dataSize + 8;
+                    memcpy(record->dParm, data, dataSize);
+
+                    if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
+                            record, metafile->handle_count) == 0)
+                        ERR("PlayEnhMetaFileRecord failed\n");
+
+                    heap_free(record);
+                }
+                else
+                    return OutOfMemory;
+
+                break;
+            }
+            }
+        }
+    }
+    else
+    {
+        EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
+
+        METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
+
+        switch(recordType)
+        {
+        case EmfPlusRecordTypeHeader:
+        case EmfPlusRecordTypeEndOfFile:
             break;
         case EmfPlusRecordTypeGetDC:
             METAFILE_PlaybackGetDC((GpMetafile*)metafile);
             break;
         case EmfPlusRecordTypeGetDC:
             METAFILE_PlaybackGetDC((GpMetafile*)metafile);
@@ -1486,6 +2537,9 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
         {
             EmfPlusClear *record = (EmfPlusClear*)header;
 
         {
             EmfPlusClear *record = (EmfPlusClear*)header;
 
+            if (dataSize != sizeof(record->Color))
+                return InvalidParameter;
+
             return GdipGraphicsClear(metafile->playback_graphics, record->Color);
         }
         case EmfPlusRecordTypeFillRects:
             return GdipGraphicsClear(metafile->playback_graphics, record->Color);
         }
         case EmfPlusRecordTypeFillRects:
@@ -1510,13 +2564,17 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
 
             if (flags & 0x8000)
             {
 
             if (flags & 0x8000)
             {
-                stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
+                stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
                 brush = temp_brush;
             }
             else
             {
                 brush = temp_brush;
             }
             else
             {
-                FIXME("brush deserialization not implemented\n");
-                return NotImplemented;
+                if (record->BrushID >= EmfPlusObjectTableSize ||
+                        real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
+                    return InvalidParameter;
+
+                brush = real_metafile->objtable[record->BrushID].u.brush;
+                stat = Ok;
             }
 
             if (stat == Ok)
             }
 
             if (stat == Ok)
@@ -1546,39 +2604,74 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
 
             if (stat == Ok)
             {
 
             if (stat == Ok)
             {
-                stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
+                stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
+            }
+
+            GdipDeleteBrush(temp_brush);
+            heap_free(temp_rects);
+
+            return stat;
+        }
+        case EmfPlusRecordTypeSetClipRect:
+        {
+            EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
+            CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
+            GpRegion *region;
+
+            if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
+                return InvalidParameter;
+
+            stat = GdipCreateRegionRect(&record->ClipRect, &region);
+
+            if (stat == Ok)
+            {
+                stat = metafile_set_clip_region(real_metafile, region, mode);
+                GdipDeleteRegion(region);
+            }
+
+            return stat;
+        }
+        case EmfPlusRecordTypeSetClipRegion:
+        {
+            CombineMode mode = (flags >> 8) & 0xf;
+            BYTE regionid = flags & 0xff;
+            GpRegion *region;
+
+            if (dataSize != 0)
+                return InvalidParameter;
+
+            if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
+                return InvalidParameter;
+
+            stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
+            if (stat == Ok)
+            {
+                stat = metafile_set_clip_region(real_metafile, region, mode);
+                GdipDeleteRegion(region);
             }
 
             }
 
-            GdipDeleteBrush(temp_brush);
-            heap_free(temp_rects);
-
             return stat;
         }
             return stat;
         }
-        case EmfPlusRecordTypeSetClipRect:
+        case EmfPlusRecordTypeSetClipPath:
         {
         {
-            EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
-            CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
+            CombineMode mode = (flags >> 8) & 0xf;
+            BYTE pathid = flags & 0xff;
             GpRegion *region;
             GpRegion *region;
-            GpMatrix world_to_device;
 
 
-            if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
+            if (dataSize != 0)
                 return InvalidParameter;
 
                 return InvalidParameter;
 
-            stat = GdipCreateRegionRect(&record->ClipRect, &region);
+            if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
+                return InvalidParameter;
 
 
+            stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
             if (stat == Ok)
             {
             if (stat == Ok)
             {
-                get_graphics_transform(real_metafile->playback_graphics,
-                    CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
-
-                GdipTransformRegion(region, &world_to_device);
-
-                GdipCombineRegionRegion(real_metafile->clip, region, mode);
-
+                stat = metafile_set_clip_region(real_metafile, region, mode);
                 GdipDeleteRegion(region);
             }
 
                 GdipDeleteRegion(region);
             }
 
-            return METAFILE_PlaybackUpdateClip(real_metafile);
+            return stat;
         }
         case EmfPlusRecordTypeSetPageTransform:
         {
         }
         case EmfPlusRecordTypeSetPageTransform:
         {
@@ -1810,6 +2903,447 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
 
             break;
         }
 
             break;
         }
+        case EmfPlusRecordTypeSetPixelOffsetMode:
+        {
+            return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
+        }
+        case EmfPlusRecordTypeSetCompositingQuality:
+        {
+            return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
+        }
+        case EmfPlusRecordTypeSetInterpolationMode:
+        {
+            return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
+        }
+        case EmfPlusRecordTypeSetTextRenderingHint:
+        {
+            return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
+        }
+        case EmfPlusRecordTypeSetAntiAliasMode:
+        {
+            return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
+        }
+        case EmfPlusRecordTypeSetCompositingMode:
+        {
+            return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
+        }
+        case EmfPlusRecordTypeObject:
+        {
+            return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
+        }
+        case EmfPlusRecordTypeDrawImage:
+        {
+            EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
+            BYTE image = flags & 0xff;
+            GpPointF points[3];
+
+            if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
+                return InvalidParameter;
+
+            if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
+                    (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
+                    real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
+                return InvalidParameter;
+
+            if (flags & 0x4000) /* C */
+            {
+                points[0].X = draw->RectData.rect.X;
+                points[0].Y = draw->RectData.rect.Y;
+                points[1].X = points[0].X + draw->RectData.rect.Width;
+                points[1].Y = points[0].Y;
+                points[2].X = points[1].X;
+                points[2].Y = points[1].Y + draw->RectData.rect.Height;
+            }
+            else
+            {
+                points[0].X = draw->RectData.rectF.X;
+                points[0].Y = draw->RectData.rectF.Y;
+                points[1].X = points[0].X + draw->RectData.rectF.Width;
+                points[1].Y = points[0].Y;
+                points[2].X = points[1].X;
+                points[2].Y = points[1].Y + draw->RectData.rectF.Height;
+            }
+
+            return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
+                points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
+                real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
+        }
+        case EmfPlusRecordTypeDrawImagePoints:
+        {
+            EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
+            static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
+                FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
+            BYTE image = flags & 0xff;
+            GpPointF points[3];
+            unsigned int i;
+            UINT size;
+
+            if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
+                return InvalidParameter;
+
+            if (dataSize <= fixed_part_size)
+                return InvalidParameter;
+            dataSize -= fixed_part_size;
+
+            if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
+                    real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
+                return InvalidParameter;
+
+            if (draw->count != 3)
+                return InvalidParameter;
+
+            if ((flags >> 13) & 1) /* E */
+                FIXME("image effects are not supported.\n");
+
+            if ((flags >> 11) & 1) /* P */
+                size = sizeof(EmfPlusPointR7) * draw->count;
+            else if ((flags >> 14) & 1) /* C */
+                size = sizeof(EmfPlusPoint) * draw->count;
+            else
+                size = sizeof(EmfPlusPointF) * draw->count;
+
+            if (dataSize != size)
+                return InvalidParameter;
+
+            if ((flags >> 11) & 1) /* P */
+            {
+                points[0].X = draw->PointData.pointsR[0].X;
+                points[0].Y = draw->PointData.pointsR[0].Y;
+                for (i = 1; i < 3; i++)
+                {
+                    points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
+                    points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
+                }
+            }
+            else if ((flags >> 14) & 1) /* C */
+            {
+                for (i = 0; i < 3; i++)
+                {
+                    points[i].X = draw->PointData.points[i].X;
+                    points[i].Y = draw->PointData.points[i].Y;
+                }
+            }
+            else
+                memcpy(points, draw->PointData.pointsF, sizeof(points));
+
+            return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
+                points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
+                real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
+        }
+        case EmfPlusRecordTypeFillPath:
+        {
+            EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
+            GpSolidFill *solidfill = NULL;
+            BYTE path = flags & 0xff;
+            GpBrush *brush;
+
+            if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
+                return InvalidParameter;
+
+            if (dataSize != sizeof(fill->data.BrushId))
+                return InvalidParameter;
+
+            if (flags & 0x8000)
+            {
+                stat = GdipCreateSolidFill(fill->data.Color, (GpSolidFill **)&solidfill);
+                if (stat != Ok)
+                    return stat;
+                brush = (GpBrush *)solidfill;
+            }
+            else
+            {
+                if (fill->data.BrushId >= EmfPlusObjectTableSize ||
+                        real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
+                    return InvalidParameter;
+
+                brush = real_metafile->objtable[fill->data.BrushId].u.brush;
+            }
+
+            stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
+            GdipDeleteBrush((GpBrush *)solidfill);
+            return stat;
+        }
+        case EmfPlusRecordTypeFillClosedCurve:
+        {
+            static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
+                sizeof(EmfPlusRecordHeader);
+            EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
+            GpSolidFill *solidfill = NULL;
+            GpFillMode mode;
+            GpBrush *brush;
+            UINT size, i;
+
+            if (dataSize <= fixed_part_size)
+                return InvalidParameter;
+
+            if (fill->Count == 0)
+                return InvalidParameter;
+
+            if (flags & 0x800) /* P */
+                size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
+            else if (flags & 0x4000) /* C */
+                size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
+            else
+                size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
+
+            if (dataSize != size)
+                return InvalidParameter;
+
+            mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
+
+            if (flags & 0x8000) /* S */
+            {
+                stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
+                if (stat != Ok)
+                    return stat;
+                brush = (GpBrush *)solidfill;
+            }
+            else
+            {
+                if (fill->BrushId >= EmfPlusObjectTableSize ||
+                        real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
+                    return InvalidParameter;
+
+                brush = real_metafile->objtable[fill->BrushId].u.brush;
+            }
+
+            if (flags & (0x800 | 0x4000))
+            {
+                GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
+                if (points)
+                {
+                    if (flags & 0x800) /* P */
+                    {
+                        for (i = 1; i < fill->Count; i++)
+                        {
+                            points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
+                            points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
+                        }
+                    }
+                    else
+                    {
+                        for (i = 0; i < fill->Count; i++)
+                        {
+                            points[i].X = fill->PointData.points[i].X;
+                            points[i].Y = fill->PointData.points[i].Y;
+                        }
+                    }
+
+                    stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
+                        points, fill->Count, fill->Tension, mode);
+                    GdipFree(points);
+                }
+                else
+                    stat = OutOfMemory;
+            }
+            else
+                stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
+                    (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
+
+            GdipDeleteBrush((GpBrush *)solidfill);
+            return stat;
+        }
+        case EmfPlusRecordTypeFillEllipse:
+        {
+            EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
+            GpSolidFill *solidfill = NULL;
+            GpBrush *brush;
+
+            if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
+                return InvalidParameter;
+            dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
+
+            if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (flags & 0x8000)
+            {
+                stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
+                if (stat != Ok)
+                    return stat;
+                brush = (GpBrush *)solidfill;
+            }
+            else
+            {
+                if (fill->BrushId >= EmfPlusObjectTableSize ||
+                        real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
+                    return InvalidParameter;
+
+                brush = real_metafile->objtable[fill->BrushId].u.brush;
+            }
+
+            if (flags & 0x4000)
+                stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
+                    fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
+            else
+                stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
+                    fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
+
+            GdipDeleteBrush((GpBrush *)solidfill);
+            return stat;
+        }
+        case EmfPlusRecordTypeFillPie:
+        {
+            EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
+            GpSolidFill *solidfill = NULL;
+            GpBrush *brush;
+
+            if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
+                return InvalidParameter;
+            dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
+
+            if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (flags & 0x8000) /* S */
+            {
+                stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
+                if (stat != Ok)
+                    return stat;
+                brush = (GpBrush *)solidfill;
+            }
+            else
+            {
+                if (fill->BrushId >= EmfPlusObjectTableSize ||
+                        real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
+                    return InvalidParameter;
+
+                brush = real_metafile->objtable[fill->BrushId].u.brush;
+            }
+
+            if (flags & 0x4000) /* C */
+                stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
+                    fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
+                    fill->StartAngle, fill->SweepAngle);
+            else
+                stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
+                    fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
+                    fill->StartAngle, fill->SweepAngle);
+
+            GdipDeleteBrush((GpBrush *)solidfill);
+            return stat;
+        }
+        case EmfPlusRecordTypeDrawPath:
+        {
+            EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
+            BYTE path = flags & 0xff;
+
+            if (dataSize != sizeof(draw->PenId))
+                return InvalidParameter;
+
+            if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
+                return InvalidParameter;
+
+            if (real_metafile->objtable[path].type != ObjectTypePath ||
+                    real_metafile->objtable[draw->PenId].type != ObjectTypePen)
+                return InvalidParameter;
+
+            return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
+                real_metafile->objtable[path].u.path);
+        }
+        case EmfPlusRecordTypeDrawArc:
+        {
+            EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
+            BYTE pen = flags & 0xff;
+
+            if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
+                return InvalidParameter;
+
+            if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
+                    (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (flags & 0x4000) /* C */
+                return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
+                    draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
+            else
+                return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
+                    draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
+        }
+        case EmfPlusRecordTypeDrawEllipse:
+        {
+            EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
+            BYTE pen = flags & 0xff;
+
+            if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
+                return InvalidParameter;
+
+            if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (flags & 0x4000) /* C */
+                return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
+                    draw->RectData.rect.Height);
+            else
+                return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
+                    draw->RectData.rectF.Height);
+        }
+        case EmfPlusRecordTypeDrawPie:
+        {
+            EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
+            BYTE pen = flags & 0xff;
+
+            if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
+                return InvalidParameter;
+
+            if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
+                    (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (flags & 0x4000) /* C */
+                return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
+                    draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
+            else
+                return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
+                    draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
+        }
+        case EmfPlusRecordTypeDrawRects:
+        {
+            EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
+            BYTE pen = flags & 0xff;
+            GpRectF *rects = NULL;
+
+            if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
+                return InvalidParameter;
+
+            if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
+                return InvalidParameter;
+            dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
+
+            if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
+                return InvalidParameter;
+
+            if (flags & 0x4000)
+            {
+                DWORD i;
+
+                rects = GdipAlloc(draw->Count * sizeof(*rects));
+                if (!rects)
+                    return OutOfMemory;
+
+                for (i = 0; i < draw->Count; i++)
+                {
+                    rects[i].X = draw->RectData.rect[i].X;
+                    rects[i].Y = draw->RectData.rect[i].Y;
+                    rects[i].Width = draw->RectData.rect[i].Width;
+                    rects[i].Height = draw->RectData.rect[i].Height;
+                }
+            }
+
+            stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
+                    rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
+            GdipFree(rects);
+            return stat;
+        }
         default:
             FIXME("Not implemented for record type %x\n", recordType);
             return NotImplemented;
         default:
             FIXME("Not implemented for record type %x\n", recordType);
             return NotImplemented;
@@ -2625,10 +4159,7 @@ static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const Gp
     attrs_record->Version = VERSION_MAGIC2;
     attrs_record->Reserved1 = 0;
     attrs_record->WrapMode = attrs->wrap;
     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->ClampColor = attrs->outside_color;
     attrs_record->ObjectClamp = attrs->clamp;
     attrs_record->Reserved2 = 0;
     return Ok;
     attrs_record->ObjectClamp = attrs->clamp;
     attrs_record->Reserved2 = 0;
     return Ok;
@@ -2708,12 +4239,7 @@ GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
     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->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;
+    memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
     METAFILE_WriteRecords(metafile);
     return Ok;
 }
     METAFILE_WriteRecords(metafile);
     return Ok;
 }
@@ -2759,33 +4285,6 @@ static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD
     return Ok;
 }
 
     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;
 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
 {
     DWORD i, data_flags, pen_data_size, brush_size;
@@ -2981,30 +4480,6 @@ GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
     return Ok;
 }
 
     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;
 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
 {
     EmfPlusFillPath *fill_path_record;
@@ -3035,10 +4510,7 @@ GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
     if (inline_color)
     {
         fill_path_record->Header.Flags = 0x8000 | path_id;
     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;
+        fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
     }
     else
     {
     }
     else
     {
index b5fa5bc..c40882c 100644 (file)
 
 #define FLAGS_INTPATH   0x4000
 
 
 #define FLAGS_INTPATH   0x4000
 
-struct memory_buffer
-{
-    const BYTE *buffer;
-    INT size, pos;
-};
-
 struct region_header
 {
     DWORD magic;
 struct region_header
 {
     DWORD magic;
@@ -766,24 +760,6 @@ GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
     return Ok;
 }
 
     return Ok;
 }
 
-static inline void init_memory_buffer(struct memory_buffer *mbuf, const BYTE *buffer, INT size)
-{
-    mbuf->buffer = buffer;
-    mbuf->size = size;
-    mbuf->pos = 0;
-}
-
-static inline const void *buffer_read(struct memory_buffer *mbuf, INT size)
-{
-    if (mbuf->size - mbuf->pos >= size)
-    {
-        const void *data = mbuf->buffer + mbuf->pos;
-        mbuf->pos += size;
-        return data;
-    }
-    return NULL;
-}
-
 static GpStatus read_element(struct memory_buffer *mbuf, GpRegion *region, region_element *node, INT *count)
 {
     GpStatus status;
 static GpStatus read_element(struct memory_buffer *mbuf, GpRegion *region, region_element *node, INT *count)
 {
     GpStatus status;
index 8b15f80..4cc97bd 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 Wine-3.0
 reactos/dll/win32/faultrep            # Synced to WineStaging-2.9
 reactos/dll/win32/fontsub             # Synced to WineStaging-2.9
 reactos/dll/win32/fusion              # Synced to Wine-3.0
-reactos/dll/win32/gdiplus             # Synced to WineStaging-2.16
+reactos/dll/win32/gdiplus             # Synced to Wine-3.0
 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
 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