Piotr Caban : gdiplus: Fix animated gif frames composition.
Alexandre Julliard
julliard at wine.codeweavers.com
Fri Mar 13 08:44:24 CDT 2015
Module: wine
Branch: master
Commit: 821932dab90969c1c09a6b5021f9e43f7be1b26b
URL: http://source.winehq.org/git/wine.git/?a=commit;h=821932dab90969c1c09a6b5021f9e43f7be1b26b
Author: Piotr Caban <piotr at codeweavers.com>
Date: Thu Mar 12 10:17:40 2015 +0100
gdiplus: Fix animated gif frames composition.
---
dlls/gdiplus/gdiplus_private.h | 5 ++
dlls/gdiplus/image.c | 184 +++++++++++++++++++++++++++++++++++------
2 files changed, 166 insertions(+), 23 deletions(-)
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index 0d62eef..065f5ed 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -42,6 +42,11 @@
#define VERSION_MAGIC 0xdbc01001
#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,
diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c
index 409a3c6..4bce334 100644
--- a/dlls/gdiplus/image.c
+++ b/dlls/gdiplus/image.c
@@ -3238,14 +3238,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);
@@ -3259,13 +3258,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);
}
@@ -3279,6 +3280,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;
@@ -3307,7 +3309,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;
@@ -3603,23 +3605,159 @@ static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
return Ok;
}
-static GpStatus select_frame_gif(GpImage *image, UINT active_frame)
+static HRESULT get_gif_frame_rect(IWICBitmapFrameDecode *frame,
+ UINT *left, UINT *top, UINT *width, UINT *height)
{
- GpImage *new_image;
- GpStatus status;
+ static const WCHAR leftW[] = {'L','e','f','t',0};
+ static const WCHAR topW[] = {'T','o','p',0};
- status = decode_frame_wic(image->decoder, TRUE, active_frame, gif_metadata_reader, &new_image);
- if(status != Ok)
- return status;
+ *left = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, leftW);
+ *top = get_gif_frame_property(frame, &GUID_MetadataFormatIMD, topW);
- 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 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; i<height && i+top<bitmap->height; i++) {
+ for(j=0; j<width && j+left<bitmap->width; 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; i<bitmap->prop_count; i++) {
+ if(bitmap->prop_item[i].id == PropertyTagIndexBackground) {
+ bgcolor_idx = *(BYTE*)bitmap->prop_item[i].value;
+ break;
+ }
+ }
+
+ for(i=0; i<bitmap->prop_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; i<bitmap->prop_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; i<top+height && i<bitmap->height; i++) {
+ DWORD *bits = (DWORD*)(bitmap->bits+i*bitmap->stride);
+ for(j=left; j<left+width && j<bitmap->width; 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;
}
More information about the wine-cvs
mailing list