[GDIPLUS] Sync with Wine Staging 1.9.23. CORE-12409
[reactos.git] / reactos / dll / win32 / gdiplus / image.c
index aa6ea9e..eab2f7f 100644 (file)
@@ -26,6 +26,7 @@
 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
 
 #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24)
+#define WMF_PLACEABLE_KEY 0x9ac6cdd7
 
 static const struct
 {
@@ -2138,6 +2139,10 @@ GpStatus WINGDIPAPI GdipGetImageBounds(GpImage *image, GpRectF *srcRect,
         srcRect->Height = (REAL) ((GpBitmap*)image)->height;
         *srcUnit = UnitPixel;
     }
+    else{
+        WARN("GpImage with no image data\n");
+        return InvalidParameter;
+    }
 
     TRACE("returning (%f, %f) (%f, %f) unit type %d\n", srcRect->X, srcRect->Y,
           srcRect->Width, srcRect->Height, *srcUnit);
@@ -2161,6 +2166,10 @@ GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width,
         *height = ((GpBitmap*)image)->height;
         *width = ((GpBitmap*)image)->width;
     }
+    else{
+        WARN("GpImage with no image data\n");
+        return InvalidParameter;
+    }
 
     TRACE("returning (%f, %f)\n", *height, *width);
     return Ok;
@@ -2215,6 +2224,11 @@ GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
         *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres);
     else if(image->type == ImageTypeBitmap)
         *height = ((GpBitmap*)image)->height;
+    else
+    {
+        WARN("GpImage with no image data\n");
+        return InvalidParameter;
+    }
 
     TRACE("returning %d\n", *height);
 
@@ -2313,6 +2327,11 @@ GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
         *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres);
     else if(image->type == ImageTypeBitmap)
         *width = ((GpBitmap*)image)->width;
+    else
+    {
+        WARN("GpImage with no image data\n");
+        return InvalidParameter;
+    }
 
     TRACE("returning %d\n", *width);
 
@@ -3981,29 +4000,25 @@ static GpStatus decode_image_tiff(IStream* stream, GpImage **image)
 
 static GpStatus load_wmf(IStream *stream, GpMetafile **metafile)
 {
-    GpStatus status = GenericError;
-    HRESULT hr;
-    UINT size;
-    LARGE_INTEGER pos;
     WmfPlaceableFileHeader pfh;
     BOOL is_placeable = FALSE;
+    LARGE_INTEGER seek;
+    GpStatus status;
     METAHEADER mh;
     HMETAFILE hmf;
+    HRESULT hr;
+    UINT size;
     void *buf;
 
-    pos.QuadPart = 0;
-    IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
-
     hr = IStream_Read(stream, &mh, sizeof(mh), &size);
     if (hr != S_OK || size != sizeof(mh))
         return GenericError;
 
-    if (mh.mtType == 0xcdd7 && mh.mtHeaderSize == 0x9ac6)
+    if (((WmfPlaceableFileHeader *)&mh)->Key == WMF_PLACEABLE_KEY)
     {
-        is_placeable = TRUE;
-
-        pos.QuadPart = 0;
-        IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
+        seek.QuadPart = 0;
+        hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
+        if (FAILED(hr)) return hresult_to_status(hr);
 
         hr = IStream_Read(stream, &pfh, sizeof(pfh), &size);
         if (hr != S_OK || size != sizeof(pfh))
@@ -4012,82 +4027,112 @@ static GpStatus load_wmf(IStream *stream, GpMetafile **metafile)
         hr = IStream_Read(stream, &mh, sizeof(mh), &size);
         if (hr != S_OK || size != sizeof(mh))
             return GenericError;
+
+        is_placeable = TRUE;
     }
 
-    pos.QuadPart = is_placeable ? sizeof(pfh) : 0;
-    IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
+    seek.QuadPart = is_placeable ? sizeof(pfh) : 0;
+    hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
+    if (FAILED(hr)) return hresult_to_status(hr);
 
     buf = heap_alloc(mh.mtSize * 2);
     if (!buf) return OutOfMemory;
 
     hr = IStream_Read(stream, buf, mh.mtSize * 2, &size);
-    if (hr == S_OK && size == mh.mtSize * 2)
+    if (hr != S_OK || size != mh.mtSize * 2)
     {
-        hmf = SetMetaFileBitsEx(mh.mtSize * 2, buf);
-        if (hmf)
-        {
-            status = GdipCreateMetafileFromWmf(hmf, TRUE, is_placeable ? &pfh : NULL, metafile);
-            if (status != Ok)
-                DeleteMetaFile(hmf);
-        }
+        heap_free(buf);
+        return GenericError;
     }
 
+    hmf = SetMetaFileBitsEx(mh.mtSize * 2, buf);
     heap_free(buf);
+    if (!hmf)
+        return GenericError;
+
+    status = GdipCreateMetafileFromWmf(hmf, TRUE, is_placeable ? &pfh : NULL, metafile);
+    if (status != Ok)
+        DeleteMetaFile(hmf);
     return status;
 }
 
+static GpStatus decode_image_wmf(IStream *stream, GpImage **image)
+{
+    GpMetafile *metafile;
+    GpStatus status;
+
+    TRACE("%p %p\n", stream, image);
+
+    if (!stream || !image)
+        return InvalidParameter;
+
+    status = load_wmf(stream, &metafile);
+    if (status != Ok)
+    {
+        TRACE("Could not load metafile\n");
+        return status;
+    }
+
+    *image = (GpImage *)metafile;
+    TRACE("<-- %p\n", *image);
+
+    return Ok;
+}
+
 static GpStatus load_emf(IStream *stream, GpMetafile **metafile)
 {
-    GpStatus status = GenericError;
-    HRESULT hr;
-    UINT size;
-    LARGE_INTEGER pos;
+    LARGE_INTEGER seek;
     ENHMETAHEADER emh;
     HENHMETAFILE hemf;
+    GpStatus status;
+    HRESULT hr;
+    UINT size;
     void *buf;
 
-    pos.QuadPart = 0;
-    IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
-
     hr = IStream_Read(stream, &emh, sizeof(emh), &size);
     if (hr != S_OK || size != sizeof(emh) || emh.dSignature != ENHMETA_SIGNATURE)
         return GenericError;
 
-    pos.QuadPart = 0;
-    IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
+    seek.QuadPart = 0;
+    hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
+    if (FAILED(hr)) return hresult_to_status(hr);
 
     buf = heap_alloc(emh.nBytes);
     if (!buf) return OutOfMemory;
 
     hr = IStream_Read(stream, buf, emh.nBytes, &size);
-    if (hr == S_OK && size == emh.nBytes)
+    if (hr != S_OK || size != emh.nBytes)
     {
-        hemf = SetEnhMetaFileBits(emh.nBytes, buf);
-        if (hemf)
-        {
-            status = GdipCreateMetafileFromEmf(hemf, FALSE, metafile);
-            if (status != Ok)
-                DeleteEnhMetaFile(hemf);
-        }
+        heap_free(buf);
+        return GenericError;
     }
 
+    hemf = SetEnhMetaFileBits(emh.nBytes, buf);
     heap_free(buf);
+    if (!hemf)
+        return GenericError;
+
+    status = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
+    if (status != Ok)
+        DeleteEnhMetaFile(hemf);
     return status;
 }
 
-static GpStatus decode_image_metafile(IStream *stream, GpImage **image)
+static GpStatus decode_image_emf(IStream *stream, GpImage **image)
 {
     GpMetafile *metafile;
+    GpStatus status;
 
     TRACE("%p %p\n", stream, image);
 
-    if(!stream || !image)
+    if (!stream || !image)
         return InvalidParameter;
 
-    if (load_emf(stream, &metafile) != Ok && load_wmf(stream, &metafile) != Ok)
+    status = load_emf(stream, &metafile);
+    if (status != Ok)
     {
         TRACE("Could not load metafile\n");
-        return GenericError;
+        return status;
     }
 
     *image = (GpImage *)metafile;
@@ -4487,7 +4532,7 @@ static GpStatus encode_image_jpeg(GpImage *image, IStream* stream,
 static GpStatus encode_image_gif(GpImage *image, IStream* stream,
     GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params)
 {
-    return encode_image_wic(image, stream, &CLSID_WICGifEncoder, params);
+    return encode_image_wic(image, stream, &GUID_ContainerFormatGif, params);
 }
 
 /*****************************************************************************
@@ -4500,7 +4545,7 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream,
     encode_image_func encode_image;
     int i;
 
-    TRACE("%p %p %p %p\n", image, stream, clsid, params);
+    TRACE("%p %p %s %p\n", image, stream, wine_dbgstr_guid(clsid), params);
 
     if(!image || !stream)
         return InvalidParameter;
@@ -4748,7 +4793,7 @@ static const struct image_codec codecs[NUM_CODECS] = {
             /* SigMask */            emf_sig_mask,
         },
         NULL,
-        decode_image_metafile,
+        decode_image_emf,
         NULL
     },
     {
@@ -4768,7 +4813,7 @@ static const struct image_codec codecs[NUM_CODECS] = {
             /* SigMask */            wmf_sig_mask,
         },
         NULL,
-        decode_image_metafile,
+        decode_image_wmf,
         NULL
     },
     {
@@ -5348,8 +5393,15 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
  */
 GpStatus WINGDIPAPI GdipImageSetAbort(GpImage *image, GdiplusAbort *pabort)
 {
-    FIXME("(%p, %p): stub\n", image, pabort);
-    return NotImplemented;
+    TRACE("(%p, %p)\n", image, pabort);
+
+    if (!image)
+        return InvalidParameter;
+
+    if (pabort)
+        FIXME("Abort callback is not supported.\n");
+
+    return Ok;
 }
 
 /*****************************************************************************
@@ -5361,3 +5413,248 @@ GpStatus WINGDIPAPI GdipBitmapConvertFormat(GpBitmap *bitmap, PixelFormat format
     FIXME("(%p, 0x%08x, %d, %d, %p, %f): stub\n", bitmap, format, dithertype, palettetype, palette, alphathreshold);
     return NotImplemented;
 }
+
+static void set_histogram_point_argb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[ color >> 24        ]++;
+    ch1[(color >> 16) & 0xff]++;
+    ch2[(color >>  8) & 0xff]++;
+    ch3[ color        & 0xff]++;
+}
+
+static void set_histogram_point_pargb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    BYTE alpha = color >> 24;
+
+    ch0[alpha]++;
+    ch1[(((color >> 16) & 0xff) * alpha) / 0xff]++;
+    ch2[(((color >>  8) & 0xff) * alpha) / 0xff]++;
+    ch3[(( color        & 0xff) * alpha) / 0xff]++;
+}
+
+static void set_histogram_point_rgb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[(color >> 16) & 0xff]++;
+    ch1[(color >>  8) & 0xff]++;
+    ch2[ color        & 0xff]++;
+}
+
+static void set_histogram_point_gray(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[(76 * ((color >> 16) & 0xff) + 150 * ((color >> 8) & 0xff) + 29 * (color & 0xff)) / 0xff]++;
+}
+
+static void set_histogram_point_b(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[color & 0xff]++;
+}
+
+static void set_histogram_point_g(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[(color >> 8) & 0xff]++;
+}
+
+static void set_histogram_point_r(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[(color >> 16) & 0xff]++;
+}
+
+static void set_histogram_point_a(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    ch0[(color >> 24) & 0xff]++;
+}
+
+/*****************************************************************************
+ * GdipBitmapGetHistogram [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap *bitmap, HistogramFormat format, UINT num_of_entries,
+    UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3)
+{
+    static void (* const set_histogram_point[])(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) =
+    {
+        set_histogram_point_argb,
+        set_histogram_point_pargb,
+        set_histogram_point_rgb,
+        set_histogram_point_gray,
+        set_histogram_point_b,
+        set_histogram_point_g,
+        set_histogram_point_r,
+        set_histogram_point_a,
+    };
+    UINT width, height, x, y;
+
+    TRACE("(%p, %d, %u, %p, %p, %p, %p)\n", bitmap, format, num_of_entries,
+        ch0, ch1, ch2, ch3);
+
+    if (!bitmap || num_of_entries != 256)
+        return InvalidParameter;
+
+    /* Make sure passed channel pointers match requested format */
+    switch (format)
+    {
+    case HistogramFormatARGB:
+    case HistogramFormatPARGB:
+        if (!ch0 || !ch1 || !ch2 || !ch3)
+            return InvalidParameter;
+        memset(ch0, 0, num_of_entries * sizeof(UINT));
+        memset(ch1, 0, num_of_entries * sizeof(UINT));
+        memset(ch2, 0, num_of_entries * sizeof(UINT));
+        memset(ch3, 0, num_of_entries * sizeof(UINT));
+        break;
+    case HistogramFormatRGB:
+        if (!ch0 || !ch1 || !ch2 || ch3)
+            return InvalidParameter;
+        memset(ch0, 0, num_of_entries * sizeof(UINT));
+        memset(ch1, 0, num_of_entries * sizeof(UINT));
+        memset(ch2, 0, num_of_entries * sizeof(UINT));
+        break;
+    case HistogramFormatGray:
+    case HistogramFormatB:
+    case HistogramFormatG:
+    case HistogramFormatR:
+    case HistogramFormatA:
+        if (!ch0 || ch1 || ch2 || ch3)
+            return InvalidParameter;
+        memset(ch0, 0, num_of_entries * sizeof(UINT));
+        break;
+    default:
+        WARN("Invalid histogram format requested, %d\n", format);
+        return InvalidParameter;
+    }
+
+    GdipGetImageWidth(&bitmap->image, &width);
+    GdipGetImageHeight(&bitmap->image, &height);
+
+    for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++)
+        {
+            ARGB color;
+
+            GdipBitmapGetPixel(bitmap, x, y, &color);
+            set_histogram_point[format](color, ch0, ch1, ch2, ch3);
+        }
+
+    return Ok;
+}
+
+/*****************************************************************************
+ * GdipBitmapGetHistogramSize [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat format, UINT *num_of_entries)
+{
+    TRACE("(%d, %p)\n", format, num_of_entries);
+
+    if (!num_of_entries)
+        return InvalidParameter;
+
+    *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;
+}