From 373482cc7de8600c582655854bac6a3ea656e518 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sun, 19 Jul 2015 13:31:15 +0000 Subject: [PATCH] [GDIPLUS] Sync with Wine Staging 1.7.47. CORE-9924 svn path=/trunk/; revision=68437 --- reactos/dll/win32/gdiplus/font.c | 2 +- reactos/dll/win32/gdiplus/gdiplus.rc | 3 + reactos/dll/win32/gdiplus/gdiplus.spec | 4 +- reactos/dll/win32/gdiplus/gdiplus11.manifest | 5 + reactos/dll/win32/gdiplus/gdiplus_private.h | 30 +- reactos/dll/win32/gdiplus/graphics.c | 110 +-- reactos/dll/win32/gdiplus/image.c | 674 +++++++++++++++---- reactos/dll/win32/gdiplus/metafile.c | 5 +- reactos/dll/win32/gdiplus/region.c | 478 +++++++------ reactos/media/doc/README.WINE | 2 +- 10 files changed, 877 insertions(+), 436 deletions(-) create mode 100644 reactos/dll/win32/gdiplus/gdiplus11.manifest diff --git a/reactos/dll/win32/gdiplus/font.c b/reactos/dll/win32/gdiplus/font.c index 97df0d57375..5630972aa06 100644 --- a/reactos/dll/win32/gdiplus/font.c +++ b/reactos/dll/win32/gdiplus/font.c @@ -450,7 +450,7 @@ GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW matrix = graphics->worldtrans; - if (font->unit == UnitPixel) + if (font->unit == UnitPixel || font->unit == UnitWorld) { height = units_to_pixels(font->emSize, graphics->unit, graphics->yres); if (graphics->unit != UnitDisplay) diff --git a/reactos/dll/win32/gdiplus/gdiplus.rc b/reactos/dll/win32/gdiplus/gdiplus.rc index 56f6c229990..5c7e2cfe972 100644 --- a/reactos/dll/win32/gdiplus/gdiplus.rc +++ b/reactos/dll/win32/gdiplus/gdiplus.rc @@ -20,3 +20,6 @@ /* @makedep: gdiplus.manifest */ WINE_MANIFEST 24 gdiplus.manifest + +/* @makedep: gdiplus11.manifest */ +WINE_MANIFEST11 24 gdiplus11.manifest diff --git a/reactos/dll/win32/gdiplus/gdiplus.spec b/reactos/dll/win32/gdiplus/gdiplus.spec index cee3ea6f4fc..64c9b9587a0 100644 --- a/reactos/dll/win32/gdiplus/gdiplus.spec +++ b/reactos/dll/win32/gdiplus/gdiplus.spec @@ -610,7 +610,7 @@ 610 stdcall GdipFindFirstImageItem(ptr ptr) 611 stub GdipFindNextImageItem 612 stdcall GdipGetImageItemData(ptr ptr) -613 stub GdipCreateEffect +613 stdcall -stub GdipCreateEffect(ptr ptr) 614 stdcall GdipDeleteEffect(ptr) 615 stub GdipGetEffectParameterSize 616 stub GdipGetEffectParameters @@ -621,7 +621,7 @@ 621 stub GdipBitmapGetHistogram 622 stub GdipBitmapGetHistogramSize 623 stub GdipBitmapConvertFormat -624 stub GdipImageSetAbort +624 stdcall GdipImageSetAbort(ptr ptr) 625 stub GdipGraphicsSetAbort 626 stub GdipDrawImageFX 627 stdcall GdipConvertToEmfPlus(ptr ptr ptr long ptr ptr) diff --git a/reactos/dll/win32/gdiplus/gdiplus11.manifest b/reactos/dll/win32/gdiplus/gdiplus11.manifest new file mode 100644 index 00000000000..c39804bca61 --- /dev/null +++ b/reactos/dll/win32/gdiplus/gdiplus11.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/reactos/dll/win32/gdiplus/gdiplus_private.h b/reactos/dll/win32/gdiplus/gdiplus_private.h index b98349257a5..54e7664f560 100644 --- a/reactos/dll/win32/gdiplus/gdiplus_private.h +++ b/reactos/dll/win32/gdiplus/gdiplus_private.h @@ -47,9 +47,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); #define MAX_DASHLEN (16) /* this is a limitation of gdi */ #define INCH_HIMETRIC (2540) -#define VERSION_MAGIC 0xdbc01001 +#define VERSION_MAGIC 0xdbc01001 +#define VERSION_MAGIC2 0xdbc01002 #define TENSION_CONST (0.3) +#define GIF_DISPOSE_UNSPECIFIED 0 +#define GIF_DISPOSE_DO_NOT_DISPOSE 1 +#define GIF_DISPOSE_RESTORE_TO_BKGND 2 +#define GIF_DISPOSE_RESTORE_TO_PREV 3 + COLORREF ARGB2COLORREF(ARGB color) DECLSPEC_HIDDEN; HBITMAP ARGB2BMP(ARGB color) DECLSPEC_HIDDEN; extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2, @@ -124,6 +130,26 @@ static inline ARGB color_over(ARGB bg, ARGB fg) return (a<<24)|(r<<16)|(g<<8)|b; } +/* fg is premult, bg and return value are not */ +static inline ARGB color_over_fgpremult(ARGB bg, ARGB fg) +{ + BYTE b, g, r, a; + BYTE bg_alpha, fg_alpha; + + fg_alpha = (fg>>24)&0xff; + + if (fg_alpha == 0) return bg; + + bg_alpha = (((bg>>24)&0xff) * (0xff-fg_alpha)) / 0xff; + + a = bg_alpha + fg_alpha; + b = ((bg&0xff)*bg_alpha + (fg&0xff)*0xff)/a; + g = (((bg>>8)&0xff)*bg_alpha + ((fg>>8)&0xff)*0xff)/a; + r = (((bg>>16)&0xff)*bg_alpha + ((fg>>16)&0xff)*0xff)/a; + + return (a<<24)|(r<<16)|(g<<8)|b; +} + extern const char *debugstr_rectf(const RectF* rc) DECLSPEC_HIDDEN; extern const char *debugstr_pointf(const PointF* pt) DECLSPEC_HIDDEN; @@ -278,7 +304,7 @@ struct GpAdustableArrowCap{ struct GpImage{ IPicture *picture; - IStream *stream; /* source stream */ + IWICBitmapDecoder *decoder; ImageType type; GUID format; UINT flags; diff --git a/reactos/dll/win32/gdiplus/graphics.c b/reactos/dll/win32/gdiplus/graphics.c index 0e796abcbe5..cdb5541ce09 100644 --- a/reactos/dll/win32/gdiplus/graphics.c +++ b/reactos/dll/win32/gdiplus/graphics.c @@ -247,6 +247,7 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen) (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0); width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres); + width *= graphics->scale; } if(pen->dash == DashStyleCustom){ @@ -349,21 +350,28 @@ static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn) return GdipGetRegionHRgn(graphics->clip, NULL, hrgn); } -/* Draw non-premultiplied ARGB data to the given graphics object */ +/* Draw ARGB data to the given graphics object */ static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, - const BYTE *src, INT src_width, INT src_height, INT src_stride) + const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt) { GpBitmap *dst_bitmap = (GpBitmap*)graphics->image; INT x, y; - for (x=0; xhdc, SHADEBLENDCAPS) == SB_NONE) + if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE || + fmt & PixelFormatPAlpha) memcpy(temp_bits, src, src_width * src_height * 4); else convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits, @@ -411,7 +420,7 @@ static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_ } static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y, - const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion) + const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt) { GpStatus stat=Ok; @@ -461,7 +470,7 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top, &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride], rects[i].right - rects[i].left, rects[i].bottom - rects[i].top, - src_stride); + src_stride, fmt); } GdipFree(rgndata); @@ -494,7 +503,7 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND); stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width, - src_height, src_stride); + src_height, src_stride, fmt); RestoreDC(graphics->hdc, save); @@ -505,27 +514,29 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst } static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, - const BYTE *src, INT src_width, INT src_height, INT src_stride) + const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt) { - return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL); + return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt); } static ARGB blend_colors(ARGB start, ARGB end, REAL position) { - ARGB result=0; - ARGB i; - INT a1, a2, a3; + INT start_a, end_a, final_a; + INT pos; - a1 = (start >> 24) & 0xff; - a2 = (end >> 24) & 0xff; + pos = gdip_round(position * 0xff); - a3 = (int)(a1*(1.0f - position)+a2*(position)); + start_a = ((start >> 24) & 0xff) * (pos ^ 0xff); + end_a = ((end >> 24) & 0xff) * pos; - result |= a3 << 24; + final_a = start_a + end_a; - for (i=0xff; i<=0xff0000; i = i << 8) - result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i; - return result; + if (final_a < 0xff) return 0; + + return (final_a / 0xff) << 24 | + ((((start >> 16) & 0xff) * start_a + (((end >> 16) & 0xff) * end_a)) / final_a) << 16 | + ((((start >> 8) & 0xff) * start_a + (((end >> 8) & 0xff) * end_a)) / final_a) << 8 | + (((start & 0xff) * start_a + ((end & 0xff) * end_a)) / final_a); } static ARGB blend_line_gradient(GpLineGradient* brush, REAL position) @@ -645,8 +656,9 @@ static BOOL color_is_gray(ARGB color) return (r == g) && (g == b); } -static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data, - UINT width, UINT height, INT stride, ColorAdjustType type) +/* returns preferred pixel format for the applied attributes */ +static PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data, + UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt) { UINT x, y; INT i; @@ -658,6 +670,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d BYTE min_blue, min_green, min_red; BYTE max_blue, max_green, max_red; + if (!data || fmt != PixelFormat32bppARGB) + return PixelFormat32bppARGB; + if (attributes->colorkeys[type].enabled) key = &attributes->colorkeys[type]; else @@ -691,6 +706,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d { const struct color_remap_table *table; + if (!data || fmt != PixelFormat32bppARGB) + return PixelFormat32bppARGB; + if (attributes->colorremaptables[type].enabled) table = &attributes->colorremaptables[type]; else @@ -720,6 +738,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d int gray_matrix[5][5]; BOOL identity; + if (!data || fmt != PixelFormat32bppARGB) + return PixelFormat32bppARGB; + if (attributes->colormatrices[type].enabled) colormatrices = &attributes->colormatrices[type]; else @@ -758,6 +779,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d { REAL gamma; + if (!data || fmt != PixelFormat32bppARGB) + return PixelFormat32bppARGB; + if (attributes->gamma_enabled[type]) gamma = attributes->gamma[type]; else @@ -782,6 +806,8 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue; } } + + return fmt; } /* Given a bitmap and its source rectangle, find the smallest rectangle in the @@ -1226,7 +1252,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, if (stat == Ok) apply_image_attributes(fill->imageattributes, fill->bitmap_bits, bitmap->width, bitmap->height, - src_stride, ColorAdjustTypeBitmap); + src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat); if (stat != Ok) { @@ -2115,7 +2141,7 @@ static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT unscaled_font; TEXTMETRICW textmet; - if (font->unit == UnitPixel) + if (font->unit == UnitPixel || font->unit == UnitWorld) font_height = font->emSize; else { @@ -2138,8 +2164,8 @@ static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, GpMatrix xform = *matrix; GdipTransformMatrixPoints(&xform, pt, 3); } - if (graphics) - GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3); + + GdipTransformPoints(graphics, CoordinateSpaceDevice, 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)); @@ -2979,15 +3005,18 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image return OutOfMemory; src_stride = sizeof(ARGB) * src_area.Width; - /* Read the bits we need from the source bitmap into an ARGB buffer. */ + /* Read the bits we need from the source bitmap into a compatible buffer. */ lockeddata.Width = src_area.Width; lockeddata.Height = src_area.Height; lockeddata.Stride = src_stride; - lockeddata.PixelFormat = PixelFormat32bppARGB; lockeddata.Scan0 = src_data; + if (!do_resampling && bitmap->format == PixelFormat32bppPARGB) + lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format); + else + lockeddata.PixelFormat = PixelFormat32bppARGB; stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf, - PixelFormat32bppARGB, &lockeddata); + lockeddata.PixelFormat, &lockeddata); if (stat == Ok) stat = GdipBitmapUnlockBits(bitmap, &lockeddata); @@ -3000,7 +3029,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image apply_image_attributes(imageAttributes, src_data, src_area.Width, src_area.Height, - src_stride, ColorAdjustTypeBitmap); + src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat); if (do_resampling) { @@ -3048,7 +3077,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image } stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top, - dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride); + dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride, + lockeddata.PixelFormat); GdipFree(src_data); @@ -3998,7 +4028,8 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush, if (stat == Ok) stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X, gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width, - gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion); + gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion, + PixelFormat32bppARGB); GdipFree(pixel_data); } @@ -4730,6 +4761,9 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, scaled_rect.Width = layoutRect->Width * args.rel_width; scaled_rect.Height = layoutRect->Height * args.rel_height; + if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23; + if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23; + get_font_hfont(graphics, font, stringFormat, &gdifont, NULL); oldfont = SelectObject(hdc, gdifont); @@ -5705,7 +5739,7 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc) { stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc); } - else if (!graphics->hdc || graphics->alpha_hdc || + else if (!graphics->hdc || (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha)) { /* Create a fake HDC and fill it with a constant color. */ @@ -5797,7 +5831,7 @@ GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc) /* Write the changed pixels to the real target. */ alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits, graphics->temp_hbitmap_width, graphics->temp_hbitmap_height, - graphics->temp_hbitmap_width * 4); + graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB); /* Clean up. */ DeleteDC(graphics->temp_hdc); @@ -6352,7 +6386,7 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI /* draw the result */ stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width, - pixel_area.Height, pixel_data_stride); + pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB); GdipFree(pixel_data); diff --git a/reactos/dll/win32/gdiplus/image.c b/reactos/dll/win32/gdiplus/image.c index 795bdf088cb..c2ee585e88d 100644 --- a/reactos/dll/win32/gdiplus/image.c +++ b/reactos/dll/win32/gdiplus/image.c @@ -23,6 +23,8 @@ #include #include +HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**); + #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24) static const struct @@ -52,8 +54,7 @@ static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteT IWICPalette *wic_palette; ColorPalette *palette = NULL; - hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, - &IID_IWICImagingFactory, (void **)&factory); + hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); if (hr != S_OK) return NULL; hr = IWICImagingFactory_CreatePalette(factory, &wic_palette); @@ -69,22 +70,36 @@ static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteT } if (hr == S_OK) { + WICBitmapPaletteType type; + BOOL alpha; UINT count; - BOOL mono, gray; - - IWICPalette_IsBlackWhite(wic_palette, &mono); - IWICPalette_IsGrayscale(wic_palette, &gray); IWICPalette_GetColorCount(wic_palette, &count); palette = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(UINT) + count * sizeof(ARGB)); IWICPalette_GetColors(wic_palette, count, palette->Entries, &palette->Count); - if (mono) - palette->Flags = 0; - else if (gray) - palette->Flags = PaletteFlagsGrayScale; - else - palette->Flags = PaletteFlagsHalftone; + IWICPalette_GetType(wic_palette, &type); + switch(type) { + case WICBitmapPaletteTypeFixedGray4: + case WICBitmapPaletteTypeFixedGray16: + case WICBitmapPaletteTypeFixedGray256: + palette->Flags = PaletteFlagsGrayScale; + break; + case WICBitmapPaletteTypeFixedHalftone8: + case WICBitmapPaletteTypeFixedHalftone27: + case WICBitmapPaletteTypeFixedHalftone64: + case WICBitmapPaletteTypeFixedHalftone125: + case WICBitmapPaletteTypeFixedHalftone216: + case WICBitmapPaletteTypeFixedHalftone252: + case WICBitmapPaletteTypeFixedHalftone256: + palette->Flags = PaletteFlagsHalftone; + break; + default: + palette->Flags = 0; + } + IWICPalette_HasAlpha(wic_palette, &alpha); + if(alpha) + palette->Flags |= PaletteFlagsHasAlpha; } IWICPalette_Release(wic_palette); } @@ -120,6 +135,15 @@ static INT ipicture_pixel_width(IPicture *pic) return x; } +#ifndef __REACTOS__ +GpStatus WINGDIPAPI GdipCreateEffect(const GUID guid, CGpEffect **effect) +{ + FIXME("(%s, %p): stub\n", debugstr_guid(&guid), effect); + *effect = NULL; + return NotImplemented; +} +#endif + GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect* effect, RECT* roi, BOOL useAuxData, VOID** auxData, INT* auxDataSize) { @@ -1853,7 +1877,7 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride, (*bitmap)->height = height; (*bitmap)->format = format; (*bitmap)->image.picture = NULL; - (*bitmap)->image.stream = NULL; + (*bitmap)->image.decoder = NULL; (*bitmap)->hbitmap = hbitmap; (*bitmap)->hdc = NULL; (*bitmap)->bits = bits; @@ -2070,9 +2094,9 @@ static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette) GdipFree(dst->prop_item); dst->prop_item = src->prop_item; dst->prop_count = src->prop_count; - if (dst->image.stream) - IStream_Release(dst->image.stream); - dst->image.stream = src->image.stream; + if (dst->image.decoder) + IWICBitmapDecoder_Release(dst->image.decoder); + dst->image.decoder = src->image.decoder; dst->image.frame_count = src->image.frame_count; dst->image.current_frame = src->image.current_frame; dst->image.format = src->image.format; @@ -2118,8 +2142,8 @@ static GpStatus free_image_data(GpImage *image) } if (image->picture) IPicture_Release(image->picture); - if (image->stream) - IStream_Release(image->stream); + if (image->decoder) + IWICBitmapDecoder_Release(image->decoder); GdipFree(image->palette); return Ok; @@ -3026,8 +3050,8 @@ static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, cons PROPVARIANT id, value; BOOL ret = FALSE; - IWICMetadataReader_GetMetadataFormat(reader, &format); - if (!IsEqualGUID(&format, guid)) return FALSE; + hr = IWICMetadataReader_GetMetadataFormat(reader, &format); + if (FAILED(hr) || !IsEqualGUID(&format, guid)) return FALSE; PropVariantInit(&id); PropVariantInit(&value); @@ -3053,8 +3077,8 @@ static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, PROPVARIANT id, value; PropertyItem *item = NULL; - IWICMetadataReader_GetMetadataFormat(reader, &format); - if (!IsEqualGUID(&format, guid)) return NULL; + hr = IWICMetadataReader_GetMetadataFormat(reader, &format); + if (FAILED(hr) || !IsEqualGUID(&format, guid)) return NULL; PropVariantInit(&id); PropVariantInit(&value); @@ -3162,8 +3186,7 @@ static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataRea if (!get_bool_property(reader, &GUID_MetadataFormatLSD, global_flagW)) return NULL; - hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, - &IID_IWICImagingFactory, (void **)&factory); + hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); if (hr != S_OK) return NULL; hr = IWICImagingFactory_CreatePalette(factory, &palette); @@ -3221,14 +3244,13 @@ static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader) return index; } -static LONG get_gif_frame_delay(IWICBitmapFrameDecode *frame) +static LONG get_gif_frame_property(IWICBitmapFrameDecode *frame, const GUID *format, const WCHAR *property) { - static const WCHAR delayW[] = { 'D','e','l','a','y',0 }; HRESULT hr; IWICMetadataBlockReader *block_reader; IWICMetadataReader *reader; UINT block_count, i; - PropertyItem *delay; + PropertyItem *prop; LONG value = 0; hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader); @@ -3242,13 +3264,15 @@ static LONG get_gif_frame_delay(IWICBitmapFrameDecode *frame) hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader); if (hr == S_OK) { - delay = get_property(reader, &GUID_MetadataFormatGCE, delayW); - if (delay) + prop = get_property(reader, format, property); + if (prop) { - if (delay->type == PropertyTagTypeShort && delay->length == 2) - value = *(SHORT *)delay->value; + if (prop->type == PropertyTagTypeByte && prop->length == 1) + value = *(BYTE *)prop->value; + else if (prop->type == PropertyTagTypeShort && prop->length == 2) + value = *(SHORT *)prop->value; - GdipFree(delay); + GdipFree(prop); } IWICMetadataReader_Release(reader); } @@ -3262,6 +3286,7 @@ static LONG get_gif_frame_delay(IWICBitmapFrameDecode *frame) static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame) { + static const WCHAR delayW[] = { 'D','e','l','a','y',0 }; HRESULT hr; IWICBitmapFrameDecode *frame; IWICMetadataBlockReader *block_reader; @@ -3290,7 +3315,7 @@ static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UI hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame); if (hr == S_OK) { - value[i] = get_gif_frame_delay(frame); + value[i] = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, delayW); IWICBitmapFrameDecode_Release(frame); } else value[i] = 0; @@ -3384,15 +3409,174 @@ static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UI IWICBitmapFrameDecode_Release(frame); } +static PropertyItem* create_prop(PROPID propid, PROPVARIANT* value) +{ + PropertyItem *item = NULL; + UINT item_size = propvariant_size(value); + + if (item_size) + { + item_size += sizeof(*item); + item = GdipAlloc(item_size); + if (propvariant_to_item(value, item, item_size, propid) != Ok) + { + GdipFree(item); + item = NULL; + } + } + + return item; +} + +static ULONG get_ulong_by_index(IWICMetadataReader* reader, ULONG index) +{ + PROPVARIANT value; + HRESULT hr; + ULONG result=0; + + hr = IWICMetadataReader_GetValueByIndex(reader, index, NULL, NULL, &value); + if (SUCCEEDED(hr)) + { + switch (value.vt) + { + case VT_UI4: + result = value.u.ulVal; + break; + default: + ERR("unhandled case %u\n", value.vt); + break; + } + PropVariantClear(&value); + } + return result; +} + +static void png_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame) +{ + HRESULT hr; + IWICBitmapFrameDecode *frame; + IWICMetadataBlockReader *block_reader; + IWICMetadataReader *reader; + UINT block_count, i, j; + struct keyword_info { + const char* name; + PROPID propid; + BOOL seen; + } keywords[] = { + { "Title", PropertyTagImageTitle }, + { "Author", PropertyTagArtist }, + { "Description", PropertyTagImageDescription }, + { "Copyright", PropertyTagCopyright }, + { "Software", PropertyTagSoftwareUsed }, + { "Source", PropertyTagEquipModel }, + { "Comment", PropertyTagExifUserComment }, + }; + BOOL seen_gamma=FALSE; + + hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame); + if (hr != S_OK) return; + + hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader); + if (hr == S_OK) + { + hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count); + if (hr == S_OK) + { + for (i = 0; i < block_count; i++) + { + hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader); + if (hr == S_OK) + { + GUID format; + + hr = IWICMetadataReader_GetMetadataFormat(reader, &format); + if (SUCCEEDED(hr) && IsEqualGUID(&GUID_MetadataFormatChunktEXt, &format)) + { + PROPVARIANT name, value; + PropertyItem* item; + + hr = IWICMetadataReader_GetValueByIndex(reader, 0, NULL, &name, &value); + + if (SUCCEEDED(hr)) + { + if (name.vt == VT_LPSTR) + { + for (j=0; jlength = sizeof(ULONG) * 2; + item->type = PropertyTagTypeRational; + item->id = PropertyTagGamma; + rational = item->value = item + 1; + rational[0] = 100000; + rational[1] = get_ulong_by_index(reader, 0); + add_property(bitmap, item); + seen_gamma = TRUE; + GdipFree(item); + } + } + } + + IWICMetadataReader_Release(reader); + } + } + } + IWICMetadataBlockReader_Release(block_reader); + } + + IWICBitmapFrameDecode_Release(frame); +} + +static GpStatus initialize_decoder_wic(IStream *stream, REFGUID container, IWICBitmapDecoder **decoder) +{ + IWICImagingFactory *factory; + HRESULT hr; + + TRACE("%p,%s\n", stream, wine_dbgstr_guid(container)); + + hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); + if (FAILED(hr)) return hresult_to_status(hr); + hr = IWICImagingFactory_CreateDecoder(factory, container, NULL, decoder); + IWICImagingFactory_Release(factory); + if (FAILED(hr)) return hresult_to_status(hr); + + hr = IWICBitmapDecoder_Initialize(*decoder, stream, WICDecodeMetadataCacheOnLoad); + if (FAILED(hr)) return hresult_to_status(hr); + return Ok; +} + typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame); -static GpStatus decode_image_wic(IStream *stream, GDIPCONST CLSID *clsid, +static GpStatus decode_frame_wic(IWICBitmapDecoder *decoder, BOOL force_conversion, UINT active_frame, metadata_reader_func metadata_reader, GpImage **image) { GpStatus status=Ok; GpBitmap *bitmap; HRESULT hr; - IWICBitmapDecoder *decoder; IWICBitmapFrameDecode *frame; IWICBitmapSource *source=NULL; IWICMetadataBlockReader *block_reader; @@ -3404,48 +3588,36 @@ static GpStatus decode_image_wic(IStream *stream, GDIPCONST CLSID *clsid, UINT width, height, frame_count; BitmapData lockeddata; WICRect wrc; - HRESULT initresult; - TRACE("%p,%s,%u,%p\n", stream, wine_dbgstr_guid(clsid), active_frame, image); - - initresult = CoInitialize(NULL); - - hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, - &IID_IWICBitmapDecoder, (void**)&decoder); - if (FAILED(hr)) goto end; - - hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad); - if (SUCCEEDED(hr)) - { - IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); - hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame); - } + TRACE("%p,%u,%p\n", decoder, active_frame, image); + IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); + hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame); if (SUCCEEDED(hr)) /* got frame */ { hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &wic_format); if (SUCCEEDED(hr)) { - IWICBitmapSource *bmp_source; - IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICBitmapSource, (void **)&bmp_source); - - for (i=0; pixel_formats[i].wic_format; i++) + if (!force_conversion) { - if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format)) + for (i=0; pixel_formats[i].wic_format; i++) { - source = bmp_source; - gdip_format = pixel_formats[i].gdip_format; - palette_type = pixel_formats[i].palette_type; - break; + if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format)) + { + source = (IWICBitmapSource*)frame; + IWICBitmapSource_AddRef(source); + gdip_format = pixel_formats[i].gdip_format; + palette_type = pixel_formats[i].palette_type; + break; + } } } if (!source) { /* unknown format; fall back on 32bppARGB */ - hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bmp_source, &source); + hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source); gdip_format = PixelFormat32bppARGB; - IWICBitmapSource_Release(bmp_source); } TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format); } @@ -3520,11 +3692,6 @@ static GpStatus decode_image_wic(IStream *stream, GDIPCONST CLSID *clsid, } } - IWICBitmapDecoder_Release(decoder); - -end: - if (SUCCEEDED(initresult)) CoUninitialize(); - if (FAILED(hr) && status == Ok) status = hresult_to_status(hr); if (status == Ok) @@ -3533,7 +3700,8 @@ end: bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI|ImageFlagsColorSpaceRGB; bitmap->image.frame_count = frame_count; bitmap->image.current_frame = active_frame; - bitmap->image.stream = stream; + bitmap->image.decoder = decoder; + IWICBitmapDecoder_AddRef(decoder); if (palette) { GdipFree(bitmap->image.palette); @@ -3544,25 +3712,214 @@ end: if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite)) bitmap->image.palette->Flags = 0; } - /* Pin the source stream */ - IStream_AddRef(stream); TRACE("=> %p\n", *image); } return status; } -static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus decode_image_wic(IStream *stream, REFGUID container, + metadata_reader_func metadata_reader, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICIcoDecoder, active_frame, NULL, image); + IWICBitmapDecoder *decoder; + GpStatus status; + + status = initialize_decoder_wic(stream, container, &decoder); + if(status != Ok) + return status; + + status = decode_frame_wic(decoder, FALSE, 0, metadata_reader, image); + IWICBitmapDecoder_Release(decoder); + return status; } -static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus select_frame_wic(GpImage *image, UINT active_frame) +{ + GpImage *new_image; + GpStatus status; + + status = decode_frame_wic(image->decoder, FALSE, active_frame, NULL, &new_image); + if(status != Ok) + return status; + + memcpy(&new_image->format, &image->format, sizeof(GUID)); + free_image_data(image); + if (image->type == ImageTypeBitmap) + *(GpBitmap *)image = *(GpBitmap *)new_image; + else if (image->type == ImageTypeMetafile) + *(GpMetafile *)image = *(GpMetafile *)new_image; + new_image->type = ~0; + GdipFree(new_image); + return Ok; +} + +static HRESULT get_gif_frame_rect(IWICBitmapFrameDecode *frame, + UINT *left, UINT *top, UINT *width, UINT *height) +{ + static const WCHAR leftW[] = {'L','e','f','t',0}; + static const WCHAR topW[] = {'T','o','p',0}; + + *left = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, leftW); + *top = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, topW); + + return IWICBitmapFrameDecode_GetSize(frame, width, height); +} + +static HRESULT blit_gif_frame(GpBitmap *bitmap, IWICBitmapFrameDecode *frame, BOOL first_frame) +{ + UINT i, j, left, top, width, height; + IWICBitmapSource *source; + BYTE *new_bits; + HRESULT hr; + + hr = get_gif_frame_rect(frame, &left, &top, &width, &height); + if(FAILED(hr)) + return hr; + + hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source); + if(FAILED(hr)) + return hr; + + new_bits = GdipAlloc(width*height*4); + if(!new_bits) + return E_OUTOFMEMORY; + + hr = IWICBitmapSource_CopyPixels(source, NULL, width*4, width*height*4, new_bits); + IWICBitmapSource_Release(source); + if(FAILED(hr)) { + GdipFree(new_bits); + return hr; + } + + for(i=0; iheight; i++) { + for(j=0; jwidth; j++) { + DWORD *src = (DWORD*)(new_bits+i*width*4+j*4); + DWORD *dst = (DWORD*)(bitmap->bits+(i+top)*bitmap->stride+(j+left)*4); + + if(first_frame || *src>>24 != 0) + *dst = *src; + } + } + GdipFree(new_bits); + return hr; +} + +static DWORD get_gif_background_color(GpBitmap *bitmap) +{ + BYTE bgcolor_idx = 0; + UINT i; + + for(i=0; iprop_count; i++) { + if(bitmap->prop_item[i].id == PropertyTagIndexBackground) { + bgcolor_idx = *(BYTE*)bitmap->prop_item[i].value; + break; + } + } + + for(i=0; iprop_count; i++) { + if(bitmap->prop_item[i].id == PropertyTagIndexTransparent) { + BYTE transparent_idx; + transparent_idx = *(BYTE*)bitmap->prop_item[i].value; + + if(transparent_idx == bgcolor_idx) + return 0; + } + } + + for(i=0; iprop_count; i++) { + if(bitmap->prop_item[i].id == PropertyTagGlobalPalette) { + if(bitmap->prop_item[i].length/3 > bgcolor_idx) { + BYTE *color = ((BYTE*)bitmap->prop_item[i].value)+bgcolor_idx*3; + return color[2] + (color[1]<<8) + (color[0]<<16) + (0xff<<24); + } + break; + } + } + + FIXME("can't get gif background color\n"); + return 0xffffffff; +} + +static GpStatus select_frame_gif(GpImage* image, UINT active_frame) +{ + static const WCHAR disposalW[] = {'D','i','s','p','o','s','a','l',0}; + + GpBitmap *bitmap = (GpBitmap*)image; + IWICBitmapFrameDecode *frame; + int cur_frame=0, disposal; + BOOL bgcolor_set = FALSE; + DWORD bgcolor = 0; + HRESULT hr; + + if(active_frame > image->current_frame) { + hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, image->current_frame, &frame); + if(FAILED(hr)) + return hresult_to_status(hr); + disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW); + IWICBitmapFrameDecode_Release(frame); + + if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND) + cur_frame = image->current_frame; + else if(disposal != GIF_DISPOSE_RESTORE_TO_PREV) + cur_frame = image->current_frame+1; + } + + while(cur_frame != active_frame) { + hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, cur_frame, &frame); + if(FAILED(hr)) + return hresult_to_status(hr); + disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW); + + if(disposal==GIF_DISPOSE_UNSPECIFIED || disposal==GIF_DISPOSE_DO_NOT_DISPOSE) { + hr = blit_gif_frame(bitmap, frame, cur_frame==0); + if(FAILED(hr)) + return hresult_to_status(hr); + }else if(disposal == GIF_DISPOSE_RESTORE_TO_BKGND) { + UINT left, top, width, height, i, j; + + if(!bgcolor_set) { + bgcolor = get_gif_background_color(bitmap); + bgcolor_set = TRUE; + } + + hr = get_gif_frame_rect(frame, &left, &top, &width, &height); + if(FAILED(hr)) + return hresult_to_status(hr); + for(i=top; iheight; i++) { + DWORD *bits = (DWORD*)(bitmap->bits+i*bitmap->stride); + for(j=left; jwidth; j++) + bits[j] = bgcolor; + } + } + + IWICBitmapFrameDecode_Release(frame); + cur_frame++; + } + + hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, active_frame, &frame); + if(FAILED(hr)) + return hresult_to_status(hr); + + hr = blit_gif_frame(bitmap, frame, cur_frame==0); + IWICBitmapFrameDecode_Release(frame); + if(FAILED(hr)) + return hresult_to_status(hr); + + image->current_frame = active_frame; + return Ok; +} + +static GpStatus decode_image_icon(IStream* stream, GpImage **image) +{ + return decode_image_wic(stream, &GUID_ContainerFormatIco, NULL, image); +} + +static GpStatus decode_image_bmp(IStream* stream, GpImage **image) { GpStatus status; GpBitmap* bitmap; - status = decode_image_wic(stream, &CLSID_WICBmpDecoder, active_frame, NULL, image); + status = decode_image_wic(stream, &GUID_ContainerFormatBmp, NULL, image); bitmap = (GpBitmap*)*image; @@ -3575,27 +3932,49 @@ static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, UINT active_fr return status; } -static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus decode_image_jpeg(IStream* stream, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICJpegDecoder, active_frame, NULL, image); + return decode_image_wic(stream, &GUID_ContainerFormatJpeg, NULL, image); } -static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus decode_image_png(IStream* stream, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICPngDecoder, active_frame, NULL, image); + return decode_image_wic(stream, &GUID_ContainerFormatPng, png_metadata_reader, image); } -static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus decode_image_gif(IStream* stream, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICGifDecoder, active_frame, gif_metadata_reader, image); + IWICBitmapDecoder *decoder; + UINT frame_count; + GpStatus status; + HRESULT hr; + + status = initialize_decoder_wic(stream, &GUID_ContainerFormatGif, &decoder); + if(status != Ok) + return status; + + hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); + if(FAILED(hr)) + return hresult_to_status(hr); + + status = decode_frame_wic(decoder, frame_count > 1, 0, gif_metadata_reader, image); + IWICBitmapDecoder_Release(decoder); + if(status != Ok) + return status; + + if(frame_count > 1) { + GdipFree((*image)->palette); + (*image)->palette = NULL; + } + return Ok; } -static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus decode_image_tiff(IStream* stream, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICTiffDecoder, active_frame, NULL, image); + return decode_image_wic(stream, &GUID_ContainerFormatTiff, NULL, image); } -static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) +static GpStatus decode_image_olepicture_metafile(IStream* stream, GpImage **image) { IPicture *pic; @@ -3614,7 +3993,7 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid *image = GdipAlloc(sizeof(GpMetafile)); if(!*image) return OutOfMemory; (*image)->type = ImageTypeMetafile; - (*image)->stream = NULL; + (*image)->decoder = NULL; (*image)->picture = pic; (*image)->flags = ImageFlagsNone; (*image)->frame_count = 1; @@ -3629,12 +4008,15 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream, GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params); -typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, UINT active_frame, GpImage **image); +typedef GpStatus (*decode_image_func)(IStream *stream, GpImage **image); + +typedef GpStatus (*select_image_func)(GpImage *image, UINT active_frame); typedef struct image_codec { ImageCodecInfo info; encode_image_func encode_func; decode_image_func decode_func; + select_image_func select_func; } image_codec; typedef enum { @@ -3699,14 +4081,28 @@ static GpStatus get_decoder_info(IStream* stream, const struct image_codec **res return GenericError; } +static GpStatus get_decoder_info_from_image(GpImage *image, const struct image_codec **result) +{ + int i; + + for (i = 0; i < NUM_CODECS; i++) { + if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) && + IsEqualIID(&codecs[i].info.FormatID, &image->format)) + { + *result = &codecs[i]; + return Ok; + } + } + + TRACE("no match for format: %s\n", wine_dbgstr_guid(&image->format)); + return GenericError; +} + GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID, UINT frame) { GpStatus stat; - LARGE_INTEGER seek; - HRESULT hr; const struct image_codec *codec = NULL; - GpImage *new_image; TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame); @@ -3728,43 +4124,21 @@ GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *d if (image->current_frame == frame) return Ok; - if (!image->stream) + if (!image->decoder) { - TRACE("image doesn't have an associated stream\n"); + TRACE("image doesn't have an associated decoder\n"); return Ok; } /* choose an appropriate image decoder */ - stat = get_decoder_info(image->stream, &codec); + stat = get_decoder_info_from_image(image, &codec); if (stat != Ok) { WARN("can't find decoder info\n"); return stat; } - /* seek to the start of the stream */ - seek.QuadPart = 0; - hr = IStream_Seek(image->stream, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - return hresult_to_status(hr); - - /* call on the image decoder to do the real work */ - stat = codec->decode_func(image->stream, &codec->info.Clsid, frame, &new_image); - - if (stat == Ok) - { - memcpy(&new_image->format, &codec->info.FormatID, sizeof(GUID)); - free_image_data(image); - if (image->type == ImageTypeBitmap) - *(GpBitmap *)image = *(GpBitmap *)new_image; - else if (image->type == ImageTypeMetafile) - *(GpMetafile *)image = *(GpMetafile *)new_image; - new_image->type = ~0; - GdipFree(new_image); - return Ok; - } - - return stat; + return codec->select_func(image, frame); } GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image) @@ -3784,7 +4158,7 @@ GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image) if (FAILED(hr)) return hresult_to_status(hr); /* call on the image decoder to do the real work */ - stat = codec->decode_func(stream, &codec->info.Clsid, 0, image); + stat = codec->decode_func(stream, image); /* take note of the original data format */ if (stat == Ok) @@ -3860,11 +4234,12 @@ GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filenam * These functions encode an image in different image file formats. */ -static GpStatus encode_image_WIC(GpImage *image, IStream* stream, - GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) +static GpStatus encode_image_wic(GpImage *image, IStream* stream, + REFGUID container, GDIPCONST EncoderParameters* params) { GpStatus stat; GpBitmap *bitmap; + IWICImagingFactory *factory; IWICBitmapEncoder *encoder; IWICBitmapFrameEncode *frameencode; IPropertyBag2 *encoderoptions; @@ -3875,7 +4250,6 @@ static GpStatus encode_image_WIC(GpImage *image, IStream* stream, WICPixelFormatGUID wicformat; GpRect rc; BitmapData lockeddata; - HRESULT initresult; UINT i; if (image->type != ImageTypeBitmap) @@ -3891,15 +4265,13 @@ static GpStatus encode_image_WIC(GpImage *image, IStream* stream, rc.Width = width; rc.Height = height; - initresult = CoInitialize(NULL); - - hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, - &IID_IWICBitmapEncoder, (void**)&encoder); + hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory); + if (FAILED(hr)) + return hresult_to_status(hr); + hr = IWICImagingFactory_CreateEncoder(factory, container, NULL, &encoder); + IWICImagingFactory_Release(factory); if (FAILED(hr)) - { - if (SUCCEEDED(initresult)) CoUninitialize(); return hresult_to_status(hr); - } hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache); @@ -3994,34 +4366,37 @@ static GpStatus encode_image_WIC(GpImage *image, IStream* stream, hr = IWICBitmapEncoder_Commit(encoder); IWICBitmapEncoder_Release(encoder); - - if (SUCCEEDED(initresult)) CoUninitialize(); - return hresult_to_status(hr); } static GpStatus encode_image_BMP(GpImage *image, IStream* stream, GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) { - return encode_image_WIC(image, stream, &CLSID_WICBmpEncoder, params); + return encode_image_wic(image, stream, &GUID_ContainerFormatBmp, params); } static GpStatus encode_image_tiff(GpImage *image, IStream* stream, GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) { - return encode_image_WIC(image, stream, &CLSID_WICTiffEncoder, params); + return encode_image_wic(image, stream, &GUID_ContainerFormatTiff, params); } static GpStatus encode_image_png(GpImage *image, IStream* stream, GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) { - return encode_image_WIC(image, stream, &CLSID_WICPngEncoder, params); + return encode_image_wic(image, stream, &GUID_ContainerFormatPng, params); } static GpStatus encode_image_jpeg(GpImage *image, IStream* stream, GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) { - return encode_image_WIC(image, stream, &CLSID_WICJpegEncoder, params); + return encode_image_wic(image, stream, &GUID_ContainerFormatJpeg, params); +} + +static GpStatus encode_image_gif(GpImage *image, IStream* stream, + GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params) +{ + return encode_image_wic(image, stream, &CLSID_WICGifEncoder, params); } /***************************************************************************** @@ -4202,7 +4577,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ bmp_sig_mask, }, encode_image_BMP, - decode_image_bmp + decode_image_bmp, + select_frame_wic }, { { /* JPEG */ @@ -4221,7 +4597,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ jpeg_sig_mask, }, encode_image_jpeg, - decode_image_jpeg + decode_image_jpeg, + select_frame_wic }, { { /* GIF */ @@ -4232,15 +4609,16 @@ static const struct image_codec codecs[NUM_CODECS] = { /* FormatDescription */ gif_format, /* FilenameExtension */ gif_extension, /* MimeType */ gif_mimetype, - /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, + /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsEncoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, /* Version */ 1, /* SigCount */ 2, /* SigSize */ 6, /* SigPattern */ gif_sig_pattern, /* SigMask */ gif_sig_mask, }, - NULL, - decode_image_gif + encode_image_gif, + decode_image_gif, + select_frame_gif }, { { /* TIFF */ @@ -4259,7 +4637,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ tiff_sig_mask, }, encode_image_tiff, - decode_image_tiff + decode_image_tiff, + select_frame_wic }, { { /* EMF */ @@ -4278,7 +4657,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ emf_sig_mask, }, NULL, - decode_image_olepicture_metafile + decode_image_olepicture_metafile, + NULL }, { { /* WMF */ @@ -4297,7 +4677,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ wmf_sig_mask, }, NULL, - decode_image_olepicture_metafile + decode_image_olepicture_metafile, + NULL }, { { /* PNG */ @@ -4316,7 +4697,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ png_sig_mask, }, encode_image_png, - decode_image_png + decode_image_png, + select_frame_wic }, { { /* ICO */ @@ -4335,7 +4717,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* SigMask */ ico_sig_mask, }, NULL, - decode_image_icon + decode_image_icon, + select_frame_wic }, }; @@ -4850,3 +5233,12 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) return stat; } + +/***************************************************************************** + * GdipImageSetAbort [GDIPLUS.@] + */ +GpStatus WINGDIPAPI GdipImageSetAbort(GpImage *image, GdiplusAbort *pabort) +{ + FIXME("(%p, %p): stub\n", image, pabort); + return NotImplemented; +} diff --git a/reactos/dll/win32/gdiplus/metafile.c b/reactos/dll/win32/gdiplus/metafile.c index 5d2cf49e3a7..4f82341fd62 100644 --- a/reactos/dll/win32/gdiplus/metafile.c +++ b/reactos/dll/win32/gdiplus/metafile.c @@ -1105,10 +1105,11 @@ GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref, TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType, debugstr_w(description), out_metafile); - if(!ref || !metafile || !out_metafile) + if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual) return InvalidParameter; - *succ = FALSE; + if(succ) + *succ = FALSE; *out_metafile = NULL; if(!(calls++)) diff --git a/reactos/dll/win32/gdiplus/region.c b/reactos/dll/win32/gdiplus/region.c index 98e0a6da067..01c4f6f566e 100644 --- a/reactos/dll/win32/gdiplus/region.c +++ b/reactos/dll/win32/gdiplus/region.c @@ -533,224 +533,6 @@ GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect, return GdipCreateRegionRect(&rectf, region); } -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; - const DWORD *type; - - type = buffer_read(mbuf, sizeof(DWORD)); - if (!type) return Ok; - - TRACE("type %#x\n", *type); - - node->type = *type; - - switch (node->type) - { - case CombineModeReplace: - case CombineModeIntersect: - case CombineModeUnion: - case CombineModeXor: - case CombineModeExclude: - case CombineModeComplement: - { - region_element *left, *right; - - left = GdipAlloc(sizeof(region_element)); - if (!left) return OutOfMemory; - right = GdipAlloc(sizeof(region_element)); - if (!right) - { - GdipFree(left); - return OutOfMemory; - } - - status = read_element(mbuf, region, left, count); - if (status == Ok) - { - status = read_element(mbuf, region, right, count); - if (status == Ok) - { - node->elementdata.combine.left = left; - node->elementdata.combine.right = right; - region->num_children += 2; - return Ok; - } - } - - GdipFree(left); - GdipFree(right); - return status; - } - - case RegionDataRect: - { - const GpRectF *rc; - - rc = buffer_read(mbuf, sizeof(GpRectF)); - if (!rc) - { - ERR("failed to read rect data\n"); - return InvalidParameter; - } - - node->elementdata.rect = *rc; - *count += 1; - return Ok; - } - - case RegionDataPath: - { - GpPath *path; - const struct path_header *path_header; - const BYTE *types; - - path_header = buffer_read(mbuf, sizeof(struct path_header)); - if (!path_header) - { - ERR("failed to read path header\n"); - return InvalidParameter; - } - if (path_header->magic != VERSION_MAGIC) - { - ERR("invalid path header magic %#x\n", path_header->magic); - return InvalidParameter; - } - - /* Windows always fails to create an empty path in a region */ - if (!path_header->count) - { - TRACE("refusing to create an empty path in a region\n"); - return GenericError; - } - - status = GdipCreatePath(FillModeAlternate, &path); - if (status) return status; - - node->elementdata.path = path; - - if (!lengthen_path(path, path_header->count)) - return OutOfMemory; - - path->pathdata.Count = path_header->count; - - if (path_header->flags & ~FLAGS_INTPATH) - FIXME("unhandled path flags %#x\n", path_header->flags); - - if (path_header->flags & FLAGS_INTPATH) - { - const packed_point *pt; - DWORD i; - - pt = buffer_read(mbuf, sizeof(packed_point) * path_header->count); - if (!pt) - { - ERR("failed to read packed %u path points\n", path_header->count); - return InvalidParameter; - } - - for (i = 0; i < path_header->count; i++) - { - path->pathdata.Points[i].X = (REAL)pt[i].X; - path->pathdata.Points[i].Y = (REAL)pt[i].Y; - } - } - else - { - const GpPointF *ptf; - - ptf = buffer_read(mbuf, sizeof(GpPointF) * path_header->count); - if (!ptf) - { - ERR("failed to read %u path points\n", path_header->count); - return InvalidParameter; - } - memcpy(path->pathdata.Points, ptf, sizeof(GpPointF) * path_header->count); - } - - types = buffer_read(mbuf, path_header->count); - if (!types) - { - ERR("failed to read %u path types\n", path_header->count); - return InvalidParameter; - } - memcpy(path->pathdata.Types, types, path_header->count); - if (path_header->count & 3) - { - if (!buffer_read(mbuf, 4 - (path_header->count & 3))) - { - ERR("failed to read rounding %u bytes\n", 4 - (path_header->count & 3)); - return InvalidParameter; - } - } - - *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; -} - -GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region) -{ - GpStatus status; - struct memory_buffer mbuf; - const struct region_header *region_header; - INT count; - - if (!data || !size) return InvalidParameter; - - TRACE("%p, %d, %p\n", data, size, region); - - init_memory_buffer(&mbuf, data, size); - - region_header = buffer_read(&mbuf, sizeof(struct region_header)); - if (!region_header || region_header->magic != VERSION_MAGIC) - return InvalidParameter; - - status = GdipCreateRegion(region); - if (status != Ok) return status; - - count = 0; - status = read_element(&mbuf, *region, &(*region)->node, &count); - if (status == Ok && !count) - status = InvalidParameter; - - if (status != Ok) - GdipDeleteRegion(*region); - - return status; -} - - /****************************************************************************** * GdipCreateRegionHrgn [GDIPLUS.@] */ @@ -1071,6 +853,232 @@ GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, 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; + const DWORD *type; + + type = buffer_read(mbuf, sizeof(*type)); + if (!type) return Ok; + + TRACE("type %#x\n", *type); + + node->type = *type; + + switch (node->type) + { + case CombineModeReplace: + case CombineModeIntersect: + case CombineModeUnion: + case CombineModeXor: + case CombineModeExclude: + case CombineModeComplement: + { + region_element *left, *right; + + left = GdipAlloc(sizeof(region_element)); + if (!left) return OutOfMemory; + right = GdipAlloc(sizeof(region_element)); + if (!right) + { + GdipFree(left); + return OutOfMemory; + } + + status = read_element(mbuf, region, left, count); + if (status == Ok) + { + status = read_element(mbuf, region, right, count); + if (status == Ok) + { + node->elementdata.combine.left = left; + node->elementdata.combine.right = right; + region->num_children += 2; + return Ok; + } + } + + GdipFree(left); + GdipFree(right); + return status; + } + + case RegionDataRect: + { + const GpRectF *rc; + + rc = buffer_read(mbuf, sizeof(*rc)); + if (!rc) + { + ERR("failed to read rect data\n"); + return InvalidParameter; + } + + node->elementdata.rect = *rc; + *count += 1; + return Ok; + } + + case RegionDataPath: + { + GpPath *path; + const struct path_header *path_header; + const BYTE *types; + + path_header = buffer_read(mbuf, sizeof(*path_header)); + if (!path_header) + { + ERR("failed to read path header\n"); + return InvalidParameter; + } + if (path_header->magic != VERSION_MAGIC) + { + ERR("invalid path header magic %#x\n", path_header->magic); + return InvalidParameter; + } + + /* Windows always fails to create an empty path in a region */ + if (!path_header->count) + { + TRACE("refusing to create an empty path in a region\n"); + return GenericError; + } + + status = GdipCreatePath(FillModeAlternate, &path); + if (status) return status; + + node->elementdata.path = path; + + if (!lengthen_path(path, path_header->count)) + return OutOfMemory; + + path->pathdata.Count = path_header->count; + + if (path_header->flags & ~FLAGS_INTPATH) + FIXME("unhandled path flags %#x\n", path_header->flags); + + if (path_header->flags & FLAGS_INTPATH) + { + const packed_point *pt; + DWORD i; + + pt = buffer_read(mbuf, sizeof(*pt) * path_header->count); + if (!pt) + { + ERR("failed to read packed %u path points\n", path_header->count); + return InvalidParameter; + } + + for (i = 0; i < path_header->count; i++) + { + path->pathdata.Points[i].X = (REAL)pt[i].X; + path->pathdata.Points[i].Y = (REAL)pt[i].Y; + } + } + else + { + const GpPointF *ptf; + + ptf = buffer_read(mbuf, sizeof(*ptf) * path_header->count); + if (!ptf) + { + ERR("failed to read %u path points\n", path_header->count); + return InvalidParameter; + } + memcpy(path->pathdata.Points, ptf, sizeof(*ptf) * path_header->count); + } + + types = buffer_read(mbuf, path_header->count); + if (!types) + { + ERR("failed to read %u path types\n", path_header->count); + return InvalidParameter; + } + memcpy(path->pathdata.Types, types, path_header->count); + if (path_header->count & 3) + { + if (!buffer_read(mbuf, 4 - (path_header->count & 3))) + { + ERR("failed to read rounding %u bytes\n", 4 - (path_header->count & 3)); + return InvalidParameter; + } + } + + *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; +} + +/***************************************************************************** + * GdipCreateRegionRgnData [GDIPLUS.@] + */ +GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region) +{ + const struct region_header *region_header; + struct memory_buffer mbuf; + GpStatus status; + INT count; + + TRACE("(%p, %d, %p)\n", data, size, region); + + if (!data || !size) + return InvalidParameter; + + init_memory_buffer(&mbuf, data, size); + + region_header = buffer_read(&mbuf, sizeof(*region_header)); + if (!region_header || (region_header->magic != VERSION_MAGIC && + region_header->magic != VERSION_MAGIC2)) + return InvalidParameter; + + status = GdipCreateRegion(region); + if (status != Ok) + return status; + + count = 0; + status = read_element(&mbuf, *region, &(*region)->node, &count); + if (status == Ok && !count) + status = InvalidParameter; + + if (status != Ok) + { + GdipDeleteRegion(*region); + *region = NULL; + } + + return status; +} + /***************************************************************************** * GdipGetRegionDataSize [GDIPLUS.@] */ @@ -1098,16 +1106,12 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) { new_hdc = CreateCompatibleDC(0); if (!new_hdc) - { - ERR("CreateCompatibleDC failed\n"); return OutOfMemory; - } stat = GdipCreateFromHDC(new_hdc, &new_graphics); graphics = new_graphics; if (stat != Ok) { - ERR("GdipCreateFromHDC failed: 0x%x\n", stat); DeleteDC(new_hdc); return stat; } @@ -1116,10 +1120,7 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) { graphics->hdc = new_hdc = CreateCompatibleDC(0); if (!new_hdc) - { - ERR("CreateCompatibleDC failed\n"); return OutOfMemory; - } } save_state = SaveDC(graphics->hdc); @@ -1132,16 +1133,8 @@ static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) if (stat == Ok) { *hrgn = PathToRegion(graphics->hdc); - if (*hrgn == NULL) - { - ERR("PathToRegion failed\n"); - } stat = *hrgn ? Ok : OutOfMemory; } - else - { - ERR("trace_path failed: 0x%x\n", stat); - } RestoreDC(graphics->hdc, save_state); if (new_hdc) @@ -1176,18 +1169,11 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap stat = GdipCreatePath(FillModeAlternate, &path); if (stat != Ok) - { - ERR("GdipCreatePath failed: 0x%x\n", stat); return stat; - } stat = GdipAddPathRectangle(path, rc->X, rc->Y, rc->Width, rc->Height); if (stat == Ok) stat = get_path_hrgn(path, graphics, hrgn); - else - { - ERR("GdipAddPathRectangle failed: 0x%x\n", stat); - } GdipDeletePath(path); @@ -1218,7 +1204,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap case CombineModeIntersect: return get_region_hrgn(element->elementdata.combine.right, graphics, hrgn); case CombineModeXor: case CombineModeExclude: - left = CreateRectRgn(-4194304, -4194304, 4194304, 4194304); + left = CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22); break; case CombineModeUnion: case CombineModeComplement: *hrgn = NULL; @@ -1243,7 +1229,7 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap *hrgn = left; return Ok; case CombineModeXor: case CombineModeComplement: - right = CreateRectRgn(-4194304, -4194304, 4194304, 4194304); + right = CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22); break; case CombineModeUnion: case CombineModeExclude: DeleteObject(left); @@ -1297,18 +1283,12 @@ static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *grap */ GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HRGN *hrgn) { - GpStatus status; TRACE("(%p, %p, %p)\n", region, graphics, hrgn); if (!region || !hrgn) return InvalidParameter; - status = get_region_hrgn(®ion->node, graphics, hrgn); - if (status != Ok) - { - ERR("get_region_hrgn() failed. region->node.type = 0x%x\n", region->node.type); - } - return status; + return get_region_hrgn(®ion->node, graphics, hrgn); } GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res) diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 4319b4edf38..e44d09316b4 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -70,7 +70,7 @@ reactos/dll/win32/dwmapi # Synced to WineStaging-1.7.37 reactos/dll/win32/faultrep # Synced to WineStaging-1.7.37 reactos/dll/win32/fltlib # Synced to WineStaging-1.7.47 reactos/dll/win32/fusion # Synced to WineStaging-1.7.47 -reactos/dll/win32/gdiplus # Synced to WineStaging-1.7.37 +reactos/dll/win32/gdiplus # Synced to WineStaging-1.7.47 reactos/dll/win32/hhctrl.ocx # Synced to WineStaging-1.7.47 reactos/dll/win32/hlink # Synced to WineStaging-1.7.37 reactos/dll/win32/hnetcfg # Synced to WineStaging-1.7.37