diff --git a/src/GdiPlusFlat.h b/src/GdiPlusFlat.h index e967f71db..23cbc7843 100644 --- a/src/GdiPlusFlat.h +++ b/src/GdiPlusFlat.h @@ -52,6 +52,7 @@ extern "C" typedef void GpAdjustableArrowCap; typedef void GpBitmap; typedef void GpBrush; +typedef void GpCachedBitmap; typedef void GpCustomLineCap; typedef void GpFont; typedef void GpFontCollection; @@ -87,6 +88,7 @@ typedef void GpTexture; #include "adjustablearrowcap.h" #include "bitmap.h" #include "brush.h" +#include "cachedbitmap.h" #include "customlinecap.h" #include "font.h" #include "fontcollection.h" diff --git a/src/Makefile.am b/src/Makefile.am index 3d3de32f8..80f10e15a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,9 @@ libgdiplus_la_SOURCES = \ brush.c \ brush.h \ brush-private.h \ + cachedbitmap.c \ + cachedbitmap.h \ + cachedbitmap-private.h \ carbon-private.c \ carbon-private.h \ codecs.h \ diff --git a/src/cachedbitmap-private.h b/src/cachedbitmap-private.h new file mode 100644 index 000000000..5db06d4a8 --- /dev/null +++ b/src/cachedbitmap-private.h @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __CACHEDBITMAP_PRIVATE_H__ +#define __CACHEDBITMAP_PRIVATE_H__ + +typedef struct _CachedBitmap { + cairo_surface_t *surface; +} CachedBitmap; + +#include "cachedbitmap.h" + +#endif diff --git a/src/cachedbitmap.c b/src/cachedbitmap.c new file mode 100644 index 000000000..452a0c2af --- /dev/null +++ b/src/cachedbitmap.c @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "general-private.h" +#include "bitmap-private.h" +#include "graphics-private.h" +#include "cachedbitmap-private.h" + +GpStatus WINGDIPAPI +GdipCreateCachedBitmap (GpBitmap *bitmap, GpGraphics *graphics, GpCachedBitmap **cachedBitmap) +{ + cairo_t *ct; + cairo_surface_t *surface; + GpCachedBitmap *newCachedBitmap; + cairo_status_t status; + + if (!bitmap || !graphics || !cachedBitmap) + return InvalidParameter; + if (bitmap->type != ImageTypeBitmap) + return InvalidParameter; + + gdip_bitmap_ensure_surface (bitmap); + + surface = cairo_surface_create_similar (bitmap->surface, CAIRO_CONTENT_COLOR_ALPHA, bitmap->active_bitmap->width, bitmap->active_bitmap->height); + + ct = cairo_create (surface); + + cairo_set_source_surface (ct, bitmap->surface, 0, 0); + cairo_paint (ct); + + cairo_destroy (ct); + + status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy (surface); + return gdip_get_status (status); + } + + newCachedBitmap = GdipAlloc (sizeof (GpCachedBitmap)); + if (!newCachedBitmap) + { + cairo_surface_destroy(surface); + return OutOfMemory; + } + + newCachedBitmap->surface = surface; + *cachedBitmap = newCachedBitmap; + + return Ok; +} + +GpStatus WINGDIPAPI +GdipDeleteCachedBitmap (GpCachedBitmap *cachedBitmap) +{ + if (!cachedBitmap) + return InvalidParameter; + + cairo_surface_destroy (cachedBitmap->surface); + GdipFree (cachedBitmap); + + return Ok; +} diff --git a/src/cachedbitmap.h b/src/cachedbitmap.h new file mode 100644 index 000000000..259b8029f --- /dev/null +++ b/src/cachedbitmap.h @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __CACHEDBITMAP_H__ +#define __CACHEDBITMAP_H__ + +GpStatus WINGDIPAPI GdipCreateCachedBitmap (GpBitmap *bitmap, GpGraphics *graphics, GpCachedBitmap **cachedBitmap); + +GpStatus WINGDIPAPI GdipDeleteCachedBitmap (GpCachedBitmap *cachedBitmap); + +#endif diff --git a/src/gdiplus-private.h b/src/gdiplus-private.h index 00c2cc1ce..f6a39cc81 100644 --- a/src/gdiplus-private.h +++ b/src/gdiplus-private.h @@ -80,6 +80,7 @@ typedef struct _AdjustableArrowCap GpAdjustableArrowCap; typedef struct _Brush GpBrush; +typedef struct _CachedBitmap GpCachedBitmap; typedef struct _CustomLineCap GpCustomLineCap; typedef struct _Font GpFont; typedef struct _FontCollection GpFontCollection; diff --git a/src/graphics.c b/src/graphics.c index 340629099..f8645195d 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -35,6 +35,7 @@ #include "brush-private.h" #include "matrix-private.h" #include "bitmap-private.h" +#include "cachedbitmap-private.h" #include "metafile-private.h" #include @@ -1361,6 +1362,30 @@ GdipDrawCurve3I (GpGraphics *graphics, GpPen* pen, GDIPCONST GpPoint *points, IN return status; } +GpStatus WINGDIPAPI +GdipDrawCachedBitmap (GpGraphics *graphics, GpCachedBitmap *cachedBitmap, INT x, INT y) +{ + if (!graphics || !cachedBitmap) + return InvalidParameter; + if (graphics->state == GraphicsStateBusy) + return ObjectBusy; + + // For compat with Windows, only support translation matrices. + // Return WrongState otherwise. + + cairo_matrix_t matrix; + cairo_get_matrix (graphics->ct, &matrix); + if (matrix.xx != 1 || matrix.yx != 0 || matrix.xy != 0 || matrix.yy != 1) + { + return WrongState; + } + + cairo_set_source_surface (graphics->ct, cachedBitmap->surface, x, y); + cairo_paint (graphics->ct); + + return Ok; +} + /* * Fills */ diff --git a/src/graphics.h b/src/graphics.h index 716fa0189..4e06f4286 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -115,6 +115,8 @@ GpStatus WINGDIPAPI GdipFillPieI( GpGraphics *graphics, GpBrush *brush, INT x, I GpStatus WINGDIPAPI GdipFillRegion (GpGraphics *graphics, GpBrush *brush, GpRegion *region); GpStatus WINGDIPAPI GdipGraphicsClear (GpGraphics *graphics, ARGB color); +GpStatus WINGDIPAPI GdipDrawCachedBitmap (GpGraphics *graphics, GpCachedBitmap *cachedBitmap, INT x, INT y); + GpStatus WINGDIPAPI GdipGetDpiX( GpGraphics *graphics, REAL *dpi); GpStatus WINGDIPAPI GdipGetDpiY (GpGraphics *graphics, REAL *dpi); GpStatus WINGDIPAPI GdipGetNearestColor (GpGraphics *graphics, ARGB *argb); diff --git a/tests/Makefile.am b/tests/Makefile.am index baa00c658..e1b69972d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,7 @@ noinst_PROGRAMS = \ testbits \ testbmpcodec \ testbrush \ + testcachedbitmap \ testclip \ testcodecs \ testcustomlinecap \ @@ -91,6 +92,13 @@ testbrush_SOURCES = \ testbrush_DEPENDENCIES = $(TEST_DEPS) testbrush_LDADD = $(LDADDS) + +testcachedbitmap_SOURCES = \ + testcachedbitmap.c + +testcachedbitmap_DEPENDENCIES = $(TEST_DEPS) +testcachedbitmap_LDADD = $(LDADDS) + testclip_SOURCES = \ testclip.c @@ -283,6 +291,7 @@ EXTRA_DIST = \ $(testbits_SOURCES) \ $(testbmpcodec_SOURCES) \ $(testbrush_SOURCES) \ + $(testcachedbitmap_SOURCES) \ $(testclip_SOURCES) \ $(test_codecs_SOURCES) \ $(testcustomlinecap_SOURCES) \ @@ -339,6 +348,7 @@ TESTS = \ testbits \ testbmpcodec \ testbrush \ + testcachedbitmap \ testclip \ testcodecs \ testcustomlinecap \ diff --git a/tests/test-clipped.png b/tests/test-clipped.png new file mode 100644 index 000000000..729e67be6 Binary files /dev/null and b/tests/test-clipped.png differ diff --git a/tests/test-translated.png b/tests/test-translated.png new file mode 100644 index 000000000..b551ecf07 Binary files /dev/null and b/tests/test-translated.png differ diff --git a/tests/testcachedbitmap.c b/tests/testcachedbitmap.c new file mode 100644 index 000000000..18778ef5f --- /dev/null +++ b/tests/testcachedbitmap.c @@ -0,0 +1,198 @@ +#ifdef WIN32 +#ifndef __cplusplus +#error Please compile with a C++ compiler. +#endif +#endif + +#if defined(USE_WINDOWS_GDIPLUS) +#include +#include + +#pragma comment(lib, "gdiplus.lib") +#else +#include +#endif + +#if defined(USE_WINDOWS_GDIPLUS) +using namespace Gdiplus; +using namespace DllExports; +#endif + +#include +#include +#include +#include "testhelpers.h" + +#define C(func) assertEqualInt (func, Ok) + +static GpGraphics *getImageGraphics (GpImage **image) +{ + WCHAR *filePath; + GpGraphics *graphics; + + filePath = createWchar ("test.bmp"); + C (GdipLoadImageFromFile (filePath, image)); + + freeWchar (filePath); + + C (GdipGetImageGraphicsContext (*image, &graphics)); + + return graphics; +} + +static GpImage* getImage (const char* fileName) +{ + GpStatus status; + WCHAR *wFileName = wcharFromChar (fileName); + GpImage *image; + + status = GdipLoadImageFromFile (wFileName, &image); + assertEqualInt (status, Ok); + + freeWchar (wFileName); + + return image; +} + +static void createBitmaps (GpBitmap **originalBitmap, GpBitmap **blankBitmap, GpGraphics **blankGraphics, UINT *width, UINT *height) +{ + *originalBitmap = getImage ("test.bmp"); + C (GdipGetImageWidth (*originalBitmap, width)); + C (GdipGetImageHeight (*originalBitmap, height)); + + C (GdipCreateBitmapFromScan0 (*width, *height, 0, PixelFormat32bppARGB, 0, blankBitmap)); + C (GdipGetImageGraphicsContext (*blankBitmap, blankGraphics)); +} + +static void compareEqual (GpBitmap *expected, GpBitmap *actual, UINT width, UINT height) +{ + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + ARGB expectedColor, actualColor; + + C (GdipBitmapGetPixel (expected, x, y, &expectedColor)); + C (GdipBitmapGetPixel (actual, x, y, &actualColor)); + + assertEqualInt (expectedColor, actualColor); + } + } +} + +CLSID png_clsid = { 0x557cf406, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } }; + +static void test_clip () +{ + UINT width; + UINT height; + GpBitmap *originalBitmap; + GpBitmap *surface; + GpGraphics *graphics; + GpCachedBitmap *cached; + + createBitmaps(&originalBitmap, &surface, &graphics, &width, &height); + + C (GdipSetClipRect (graphics, 20, 20, 20, 20, CombineModeReplace)); + + C (GdipCreateCachedBitmap (originalBitmap, graphics, &cached)); + C (GdipDrawCachedBitmap (graphics, cached, 0, 0)); + + GpImage *expected = getImage("test-clipped.png"); + compareEqual(expected, surface, width, height); +} + +static void test_matrix () +{ + UINT width; + UINT height; + GpBitmap *originalBitmap; + GpBitmap *surface; + GpGraphics *graphics; + GpCachedBitmap *cached; + + createBitmaps(&originalBitmap, &surface, &graphics, &width, &height); + + C (GdipTranslateWorldTransform(graphics, 30, 30, MatrixOrderAppend)); + + C (GdipCreateCachedBitmap (originalBitmap, graphics, &cached)); + C (GdipDrawCachedBitmap (graphics, cached, 0, 0)); + + GpImage *expected = getImage("test-translated.png"); + compareEqual(expected, surface, width, height); + + // Negative tests. + C (GdipScaleWorldTransform(graphics, 30, 30, MatrixOrderAppend)); + assertEqualInt (GdipDrawCachedBitmap(graphics, cached, 0, 0), WrongState); + C (GdipRotateWorldTransform(graphics, 30, MatrixOrderAppend)); + assertEqualInt (GdipDrawCachedBitmap(graphics, cached, 0, 0), WrongState); +} + + +static void test_roundtrip () +{ + UINT width; + UINT height; + GpBitmap *originalBitmap; + GpBitmap *surface; + GpGraphics *graphics; + GpCachedBitmap *cached; + + createBitmaps(&originalBitmap, &surface, &graphics, &width, &height); + + C (GdipCreateCachedBitmap (originalBitmap, graphics, &cached)); + C (GdipDrawCachedBitmap (graphics, cached, 0, 0)); + + compareEqual(originalBitmap, surface, width, height); +} + +static void test_create () +{ + GpImage *image; + GpGraphics *graphics; + GpCachedBitmap* cachedBitmap; + + graphics = getImageGraphics (&image); + + C (GdipCreateCachedBitmap (image, graphics, &cachedBitmap)); + + // Negative tests. + image = getImage ("test.wmf"); + assertEqualInt (GdipCreateCachedBitmap (image, graphics, &cachedBitmap), InvalidParameter); + + assertEqualInt (GdipCreateCachedBitmap (image, graphics, NULL), InvalidParameter); + assertEqualInt (GdipCreateCachedBitmap (image, NULL, &cachedBitmap), InvalidParameter); + assertEqualInt (GdipCreateCachedBitmap (NULL, graphics, &cachedBitmap), InvalidParameter); + + C (GdipDeleteGraphics(graphics)); + C (GdipDisposeImage(image)); +} + +static void test_delete () +{ + GpImage *image; + GpGraphics *graphics; + GpCachedBitmap* cachedBitmap; + + graphics = getImageGraphics (&image); + + C (GdipCreateCachedBitmap(image, graphics, &cachedBitmap)); + C (GdipDeleteCachedBitmap(cachedBitmap)); + + // Negative tests. + assertEqualInt (GdipDeleteCachedBitmap (NULL), InvalidParameter); +} + +int main (int argc, char**argv) +{ + STARTUP; + + test_create (); + test_roundtrip (); + test_clip (); + test_matrix (); + test_delete (); + + SHUTDOWN; + return 0; +}