Signed-off-by: Florian Will florian.will@gmail.com --- dlls/gdiplus/gdiplus_private.h | 1 + dlls/gdiplus/image.c | 67 +++++++++++++++++++++++++++++++--- dlls/gdiplus/tests/image.c | 24 ++++++------ 3 files changed, 75 insertions(+), 17 deletions(-)
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 36e79e29eb..12d7e32dda 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -347,6 +347,7 @@ struct GpAdjustableArrowCap{
struct GpImage{ IWICBitmapDecoder *decoder; + IWICBitmapEncoder *encoder; /* set during multi-frame save (GdipSaveAdd functions) */ ImageType type; GUID format; UINT flags; diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c index 94d9344082..263b796b18 100644 --- a/dlls/gdiplus/image.c +++ b/dlls/gdiplus/image.c @@ -1827,6 +1827,7 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride, (*bitmap)->height = height; (*bitmap)->format = format; (*bitmap)->image.decoder = NULL; + (*bitmap)->image.encoder = NULL; (*bitmap)->hbitmap = hbitmap; (*bitmap)->hdc = NULL; (*bitmap)->bits = bits; @@ -2046,6 +2047,9 @@ static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette) if (dst->image.decoder) IWICBitmapDecoder_Release(dst->image.decoder); dst->image.decoder = src->image.decoder; + if (dst->image.encoder) + terminate_encoder_wic(dst->image.encoder); + dst->image.encoder = src->image.encoder; dst->image.frame_count = src->image.frame_count; dst->image.current_frame = src->image.current_frame; dst->image.format = src->image.format; @@ -2078,6 +2082,8 @@ static GpStatus free_image_data(GpImage *image) } if (image->decoder) IWICBitmapDecoder_Release(image->decoder); + if (image->encoder) + terminate_encoder_wic(image->encoder); heap_free(image->palette);
return Ok; @@ -3744,6 +3750,8 @@ static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
new_image->busy = image->busy; memcpy(&new_image->format, &image->format, sizeof(GUID)); + new_image->encoder = image->encoder; + image->encoder = NULL; free_image_data(image); if (image->type == ImageTypeBitmap) *(GpBitmap *)image = *(GpBitmap *)new_image; @@ -4420,6 +4428,13 @@ GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filenam if (!image || !filename|| !clsidEncoder) return InvalidParameter;
+ if (image->encoder) + { + /* this might release an old file stream held by the encoder so we can re-create it below */ + terminate_encoder_wic(image->encoder); + image->encoder = NULL; + } + stat = GdipCreateStreamOnFile(filename, GENERIC_WRITE, &stream); if (stat != Ok) return GenericError; @@ -4581,21 +4596,48 @@ static GpStatus encode_frame_wic(IWICBitmapEncoder *encoder, GpImage *image) return hresult_to_status(hr); }
+static BOOL has_encoder_param_long(GDIPCONST EncoderParameters *params, GUID param_guid, ULONG val) +{ + if (!params) + return FALSE; + + for (int param_idx = 0; param_idx < params->Count; param_idx++) + { + EncoderParameter param = params->Parameter[param_idx]; + if (param.Type == EncoderParameterValueTypeLong && IsEqualCLSID(¶m.Guid, ¶m_guid)) + { + ULONG *value_array = (ULONG*) param.Value; + for (int value_idx = 0; value_idx < param.NumberOfValues; value_idx++) + { + if (value_array[value_idx] == val) + return TRUE; + } + } + } + return FALSE; +} + static GpStatus encode_image_wic(GpImage *image, IStream *stream, REFGUID container, GDIPCONST EncoderParameters *params) { IWICBitmapEncoder *encoder = NULL; GpStatus status; + BOOL is_multi_frame = has_encoder_param_long(params, EncoderSaveFlag, EncoderValueMultiFrame);
if (image->type != ImageTypeBitmap) return GenericError;
+ if (is_multi_frame && image->encoder != NULL) + terminate_encoder_wic(image->encoder); + status = initialize_encoder_wic(stream, container, &encoder);
if (status == Ok) status = encode_frame_wic(encoder, image);
- if (encoder) + if (is_multi_frame) + image->encoder = encoder; + else if (encoder) { GpStatus terminate_status = terminate_encoder_wic(encoder); if (status == Ok) @@ -4673,8 +4715,7 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream, */ GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params) { - FIXME("(%p,%p): stub\n", image, params); - return NotImplemented; + return GdipSaveAddImage(image, image, params); }
/***************************************************************************** @@ -4690,8 +4731,24 @@ GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *par GpStatus WINGDIPAPI GdipSaveAddImage(GpImage *image, GpImage *additional_image, GDIPCONST EncoderParameters *params) { - FIXME("(%p,%p,%p): stub\n", image, additional_image, params); - return NotImplemented; + TRACE("%p, %p, %p\n", image, additional_image, params); + + if (!image || !additional_image || !params) + return InvalidParameter; + + if (!image->encoder) + return Win32Error; + + if (has_encoder_param_long(params, EncoderSaveFlag, EncoderValueFlush)) + { + GpStatus status = terminate_encoder_wic(image->encoder); + image->encoder = NULL; + return status; + } + else if (has_encoder_param_long(params, EncoderSaveFlag, EncoderValueFrameDimensionPage)) + return encode_frame_wic(image->encoder, additional_image); + else + return InvalidParameter; }
/***************************************************************************** diff --git a/dlls/gdiplus/tests/image.c b/dlls/gdiplus/tests/image.c index 01d79f08c4..bbb9365edb 100644 --- a/dlls/gdiplus/tests/image.c +++ b/dlls/gdiplus/tests/image.c @@ -552,41 +552,41 @@ static void test_SavingMultiPageTiff(void)
/* invalid params: NULL */ stat = GdipSaveAdd(0, ¶ms); - todo_wine expect(InvalidParameter, stat); + expect(InvalidParameter, stat); stat = GdipSaveAdd((GpImage*)bm1, 0); - todo_wine expect(InvalidParameter, stat); + expect(InvalidParameter, stat);
stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, 0); - todo_wine expect(InvalidParameter, stat); + expect(InvalidParameter, stat); stat = GdipSaveAddImage((GpImage*)bm1, 0, ¶ms); - todo_wine expect(InvalidParameter, stat); + expect(InvalidParameter, stat); stat = GdipSaveAddImage(0, (GpImage*)bm2, ¶ms); - todo_wine expect(InvalidParameter, stat); + expect(InvalidParameter, stat);
/* win32 error: SaveAdd() can only be called after Save() with the MultiFrame param */ stat = GdipSaveAdd((GpImage*)bm1, ¶ms); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat); stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat);
stat = GdipSaveImageToFile((GpImage*)bm1, filename1, &tiff_clsid, 0); /* param not set! */ expect(Ok, stat); if (stat != Ok) goto cleanup;
stat = GdipSaveAdd((GpImage*)bm1, ¶ms); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat); stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat);
/* win32 error: can't flush before starting the encoding process */ paramValue = EncoderValueFlush; stat = GdipSaveAdd((GpImage*)bm1, ¶ms); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat);
/* win32 error: can't start encoding process through SaveAdd(), only Save() */ paramValue = EncoderValueMultiFrame; stat = GdipSaveAdd((GpImage*)bm1, ¶ms); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat);
/* start encoding process: add first frame (bm1) */ paramValue = EncoderValueMultiFrame; @@ -600,7 +600,7 @@ static void test_SavingMultiPageTiff(void) /* add second frame (bm2) */ paramValue = EncoderValueFrameDimensionPage; stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); - todo_wine expect(Ok, stat); + expect(Ok, stat); if (stat != Ok) goto cleanup;
/* finish encoding process */