gdiplus: Fix animated gif frames composition.
This commit is contained in:
parent
799362a0b7
commit
821932dab9
|
@ -42,6 +42,11 @@
|
||||||
#define VERSION_MAGIC 0xdbc01001
|
#define VERSION_MAGIC 0xdbc01001
|
||||||
#define TENSION_CONST (0.3)
|
#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;
|
COLORREF ARGB2COLORREF(ARGB color) DECLSPEC_HIDDEN;
|
||||||
HBITMAP ARGB2BMP(ARGB color) DECLSPEC_HIDDEN;
|
HBITMAP ARGB2BMP(ARGB color) DECLSPEC_HIDDEN;
|
||||||
extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2,
|
extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2,
|
||||||
|
|
|
@ -3238,14 +3238,13 @@ static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader)
|
||||||
return index;
|
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;
|
HRESULT hr;
|
||||||
IWICMetadataBlockReader *block_reader;
|
IWICMetadataBlockReader *block_reader;
|
||||||
IWICMetadataReader *reader;
|
IWICMetadataReader *reader;
|
||||||
UINT block_count, i;
|
UINT block_count, i;
|
||||||
PropertyItem *delay;
|
PropertyItem *prop;
|
||||||
LONG value = 0;
|
LONG value = 0;
|
||||||
|
|
||||||
hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader);
|
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);
|
hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader);
|
||||||
if (hr == S_OK)
|
if (hr == S_OK)
|
||||||
{
|
{
|
||||||
delay = get_property(reader, &GUID_MetadataFormatGCE, delayW);
|
prop = get_property(reader, format, property);
|
||||||
if (delay)
|
if (prop)
|
||||||
{
|
{
|
||||||
if (delay->type == PropertyTagTypeShort && delay->length == 2)
|
if (prop->type == PropertyTagTypeByte && prop->length == 1)
|
||||||
value = *(SHORT *)delay->value;
|
value = *(BYTE *)prop->value;
|
||||||
|
else if (prop->type == PropertyTagTypeShort && prop->length == 2)
|
||||||
|
value = *(SHORT *)prop->value;
|
||||||
|
|
||||||
GdipFree(delay);
|
GdipFree(prop);
|
||||||
}
|
}
|
||||||
IWICMetadataReader_Release(reader);
|
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 void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame)
|
||||||
{
|
{
|
||||||
|
static const WCHAR delayW[] = { 'D','e','l','a','y',0 };
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
IWICBitmapFrameDecode *frame;
|
IWICBitmapFrameDecode *frame;
|
||||||
IWICMetadataBlockReader *block_reader;
|
IWICMetadataBlockReader *block_reader;
|
||||||
|
@ -3307,7 +3309,7 @@ static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UI
|
||||||
hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
|
hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame);
|
||||||
if (hr == S_OK)
|
if (hr == S_OK)
|
||||||
{
|
{
|
||||||
value[i] = get_gif_frame_delay(frame);
|
value[i] = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, delayW);
|
||||||
IWICBitmapFrameDecode_Release(frame);
|
IWICBitmapFrameDecode_Release(frame);
|
||||||
}
|
}
|
||||||
else value[i] = 0;
|
else value[i] = 0;
|
||||||
|
@ -3603,23 +3605,159 @@ static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
|
||||||
return Ok;
|
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; 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 GpStatus select_frame_gif(GpImage* image, UINT active_frame)
|
||||||
{
|
{
|
||||||
GpImage *new_image;
|
static const WCHAR disposalW[] = {'D','i','s','p','o','s','a','l',0};
|
||||||
GpStatus status;
|
|
||||||
|
|
||||||
status = decode_frame_wic(image->decoder, TRUE, active_frame, gif_metadata_reader, &new_image);
|
GpBitmap *bitmap = (GpBitmap*)image;
|
||||||
if(status != Ok)
|
IWICBitmapFrameDecode *frame;
|
||||||
return status;
|
int cur_frame=0, disposal;
|
||||||
|
BOOL bgcolor_set = FALSE;
|
||||||
|
DWORD bgcolor = 0;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
memcpy(&new_image->format, &image->format, sizeof(GUID));
|
if(active_frame > image->current_frame) {
|
||||||
free_image_data(image);
|
hr = IWICBitmapDecoder_GetFrame(bitmap->image.decoder, image->current_frame, &frame);
|
||||||
if (image->type == ImageTypeBitmap)
|
if(FAILED(hr))
|
||||||
*(GpBitmap *)image = *(GpBitmap *)new_image;
|
return hresult_to_status(hr);
|
||||||
else if (image->type == ImageTypeMetafile)
|
disposal = get_gif_frame_property(frame, &GUID_MetadataFormatGCE, disposalW);
|
||||||
*(GpMetafile *)image = *(GpMetafile *)new_image;
|
IWICBitmapFrameDecode_Release(frame);
|
||||||
new_image->type = ~0;
|
|
||||||
GdipFree(new_image);
|
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;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue