From 0eaaa1514454d584eb6638e0c7a31bb64bbc773b Mon Sep 17 00:00:00 2001 From: Piotr Caban Date: Tue, 14 Feb 2017 10:24:36 +0100 Subject: [PATCH] gdiplus: Make GpBitmap->bitmapbits access thread safe. Signed-off-by: Piotr Caban Signed-off-by: Vincent Povirk Signed-off-by: Alexandre Julliard --- dlls/gdiplus/gdiplus_private.h | 14 ++++++++++ dlls/gdiplus/image.c | 51 ++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index b44340f27c8..2e98065ff3c 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -348,6 +348,7 @@ struct GpImage{ UINT frame_count, current_frame; ColorPalette *palette; REAL xres, yres; + LONG busy; }; struct GpMetafile{ @@ -515,4 +516,17 @@ GpStatus gdip_format_string(HDC hdc, void get_log_fontW(const GpFont *, GpGraphics *, LOGFONTW *) DECLSPEC_HIDDEN; +static inline BOOL image_lock(GpImage *image, BOOL *unlock) +{ + LONG tid = GetCurrentThreadId(), owner_tid; + owner_tid = InterlockedCompareExchange(&image->busy, tid, 0); + *unlock = !owner_tid; + return !owner_tid || owner_tid==tid; +} + +static inline void image_unlock(GpImage *image, BOOL unlock) +{ + if (unlock) image->busy = 0; +} + #endif diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c index 21d5a3ac8d0..4de8ecc643d 100644 --- a/dlls/gdiplus/image.c +++ b/dlls/gdiplus/image.c @@ -1027,16 +1027,22 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, INT bitspp = PIXELFORMATBPP(format); GpRect act_rect; /* actual rect to be used */ GpStatus stat; + BOOL unlock; TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata); if(!lockeddata || !bitmap) return InvalidParameter; + if(!image_lock(&bitmap->image, &unlock)) + return ObjectBusy; if(rect){ if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) || (rect->Y + rect->Height > bitmap->height) || !flags) + { + image_unlock(&bitmap->image, unlock); return InvalidParameter; + } act_rect = *rect; } @@ -1049,6 +1055,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, if(bitmap->lockmode) { WARN("bitmap is already locked and cannot be locked again\n"); + image_unlock(&bitmap->image, unlock); return WrongState; } @@ -1066,6 +1073,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, bitmap->lockmode = flags | ImageLockModeRead; bitmap->numlocks++; + image_unlock(&bitmap->image, unlock); return Ok; } @@ -1076,6 +1084,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, if (stat == NotImplemented) { FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format); + image_unlock(&bitmap->image, unlock); return NotImplemented; } } @@ -1088,6 +1097,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, if (stat == NotImplemented) { FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format); + image_unlock(&bitmap->image, unlock); return NotImplemented; } } @@ -1103,7 +1113,11 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, bitmap->bitmapbits = heap_alloc_zero(lockeddata->Stride * act_rect.Height); - if (!bitmap->bitmapbits) return OutOfMemory; + if (!bitmap->bitmapbits) + { + image_unlock(&bitmap->image, unlock); + return OutOfMemory; + } lockeddata->Scan0 = bitmap->bitmapbits; } @@ -1128,6 +1142,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, { heap_free(bitmap->bitmapbits); bitmap->bitmapbits = NULL; + image_unlock(&bitmap->image, unlock); return stat; } } @@ -1137,6 +1152,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, bitmap->lockx = act_rect.X; bitmap->locky = act_rect.Y; + image_unlock(&bitmap->image, unlock); return Ok; } @@ -1158,14 +1174,20 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap, { GpStatus stat; static BOOL fixme = FALSE; + BOOL unlock; TRACE("(%p,%p)\n", bitmap, lockeddata); if(!bitmap || !lockeddata) return InvalidParameter; + if(!image_lock(&bitmap->image, &unlock)) + return ObjectBusy; if(!bitmap->lockmode) + { + image_unlock(&bitmap->image, unlock); return WrongState; + } if(!(lockeddata->Reserved & ImageLockModeWrite)){ if(!(--bitmap->numlocks)) @@ -1173,6 +1195,7 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap, heap_free(bitmap->bitmapbits); bitmap->bitmapbits = NULL; + image_unlock(&bitmap->image, unlock); return Ok; } @@ -1181,6 +1204,7 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap, /* we passed a direct reference; no need to do anything */ bitmap->lockmode = 0; bitmap->numlocks = 0; + image_unlock(&bitmap->image, unlock); return Ok; } @@ -1206,6 +1230,7 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap, bitmap->lockmode = 0; bitmap->numlocks = 0; + image_unlock(&bitmap->image, unlock); return stat; } @@ -3746,6 +3771,7 @@ static GpStatus select_frame_wic(GpImage *image, UINT active_frame) if(status != Ok) return status; + new_image->busy = image->busy; memcpy(&new_image->format, &image->format, sizeof(GUID)); free_image_data(image); if (image->type == ImageTypeBitmap) @@ -4219,30 +4245,39 @@ GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *d { GpStatus stat; const struct image_codec *codec = NULL; + BOOL unlock; TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame); if (!image || !dimensionID) return InvalidParameter; + if(!image_lock(image, &unlock)) + return ObjectBusy; if (frame >= image->frame_count) { WARN("requested frame %u, but image has only %u\n", frame, image->frame_count); + image_unlock(image, unlock); return InvalidParameter; } if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile) { WARN("invalid image type %d\n", image->type); + image_unlock(image, unlock); return InvalidParameter; } if (image->current_frame == frame) + { + image_unlock(image, unlock); return Ok; + } if (!image->decoder) { TRACE("image doesn't have an associated decoder\n"); + image_unlock(image, unlock); return Ok; } @@ -4251,10 +4286,13 @@ GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *d if (stat != Ok) { WARN("can't find decoder info\n"); + image_unlock(image, unlock); return stat; } - return codec->select_func(image, frame); + stat = codec->select_func(image, frame); + image_unlock(image, unlock); + return stat; } GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image) @@ -5264,11 +5302,14 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) UINT x, y, width, height; BitmapData src_lock, dst_lock; GpStatus stat; + BOOL unlock; TRACE("(%p, %u)\n", image, type); if (!image) return InvalidParameter; + if (!image_lock(image, &unlock)) + return ObjectBusy; rotate_90 = type&1; flip_x = (type&6) == 2 || (type&6) == 4; @@ -5277,6 +5318,7 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) if (image->type != ImageTypeBitmap) { FIXME("Not implemented for type %i\n", image->type); + image_unlock(image, unlock); return NotImplemented; } @@ -5286,6 +5328,7 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) if (bpp < 8) { FIXME("Not implemented for %i bit images\n", bpp); + image_unlock(image, unlock); return NotImplemented; } @@ -5305,7 +5348,10 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap); if (stat != Ok) + { + image_unlock(image, unlock); return stat; + } stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format, &src_lock); @@ -5365,6 +5411,7 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type) else GdipDisposeImage(&new_bitmap->image); + image_unlock(image, unlock); return stat; }