From 1af55cdea880c3fcddf70edf5cef2857316d3e7a Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 2 Oct 2020 13:31:47 -0400 Subject: [PATCH] Allow GpGraphics to take ownership of GpMetafile on dispose The Windows GDI+ allows the metafile instance to be disposed before the graphics instance. To support that, keep track in the GpGraphics of whether it is responsible for freeing the GpMetafile. If the graphics instance is deleted first, own_metafile is FALSE and nothing happens. If the metafile instance is disposed first, set own_metafile to TRUE and don't dispose of the metafile. It will be disposed when the graphics instance is deleted. --- Also on Windows GDI+ creating more than one graphics instance for a metafile instance throws an OutOfMemoryException, so do the same in GdipGetImageGraphicsContext --- src/graphics-private.h | 1 + src/graphics.c | 14 +++++++++++++- src/image.c | 3 +++ src/metafile-private.h | 1 + src/metafile.c | 11 +++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/graphics-private.h b/src/graphics-private.h index e91a2c2fa..99f2df88f 100644 --- a/src/graphics-private.h +++ b/src/graphics-private.h @@ -126,6 +126,7 @@ typedef struct _Graphics { float aa_offset_y; /* metafile-specific stuff */ EmfType emf_type; + BOOL own_metafile; /* true when metafile is owned by the graphics instance */ GpMetafile *metafile; cairo_surface_t *metasurface; /* bogus surface to satisfy some API calls */ /* common-stuff */ diff --git a/src/graphics.c b/src/graphics.c index 340629099..db0d2adc6 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -201,6 +201,11 @@ gdip_graphics_metafile_init (GpGraphics *graphics, GpMetafile *metafile) graphics->metasurface = cairo_image_surface_create (CAIRO_FORMAT_A1, 1, 1); graphics->ct = cairo_create (graphics->metasurface); graphics->metafile = metafile; + graphics->own_metafile = FALSE; + + g_assert (metafile->graphics == NULL); + /* Track the graphics instance in the metafile */ + metafile->graphics = graphics; gdip_graphics_common_init (graphics); } @@ -455,8 +460,15 @@ GdipDeleteGraphics (GpGraphics *graphics) if (graphics->backend == GraphicsBackEndMetafile) { /* if recording this is where we save the metafile (stream or file) */ - if (graphics->metafile->recording) + if (graphics->metafile && graphics->metafile->recording) gdip_metafile_stop_recording (graphics->metafile); + /* break the link to the metafile */ + if (graphics->metafile) + graphics->metafile->graphics = NULL; + if (graphics->own_metafile) + gdip_metafile_dispose (graphics->metafile); + graphics->metafile = NULL; + graphics->own_metafile = FALSE; cairo_surface_destroy (graphics->metasurface); graphics->metasurface = NULL; } diff --git a/src/image.c b/src/image.c index a89e463a1..7d911cdc9 100644 --- a/src/image.c +++ b/src/image.c @@ -295,6 +295,9 @@ GdipGetImageGraphicsContext (GpImage *image, GpGraphics **graphics) GpMetafile *mf = (GpMetafile*)image; if (!mf->recording) return OutOfMemory; + /* creating more than one graphics instance for a metafile is not supported */ + if (mf->graphics) + return OutOfMemory; *graphics = gdip_metafile_graphics_new (mf); return *graphics ? Ok : OutOfMemory; } diff --git a/src/metafile-private.h b/src/metafile-private.h index 133690da0..1dcbc98ba 100644 --- a/src/metafile-private.h +++ b/src/metafile-private.h @@ -68,6 +68,7 @@ struct _Metafile { BOOL recording; /* recording into memory (data), file (fp) or user stream (stream) */ FILE *fp; void *stream; + GpGraphics *graphics; /* set if GdipGetImageGraphicsContext was called */ }; typedef struct { diff --git a/src/metafile.c b/src/metafile.c index 7422b4173..eec32417b 100644 --- a/src/metafile.c +++ b/src/metafile.c @@ -918,6 +918,7 @@ gdip_metafile_create () mf->recording = FALSE; mf->fp = NULL; mf->stream = NULL; + mf->graphics = NULL; } return mf; } @@ -970,6 +971,13 @@ gdip_metafile_dispose (GpMetafile *metafile) if (!metafile) return InvalidParameter; + if (metafile->graphics) { + g_assert (!metafile->graphics->own_metafile); + g_assert (metafile->graphics->metafile == metafile); + /* Transfer responsiblity to the graphics instance that outlives this metafile instance */ + metafile->graphics->own_metafile = TRUE; + return Ok; + } /* TODO deal with "delete" flag */ metafile->length = 0; if (metafile->data) { @@ -980,6 +988,9 @@ gdip_metafile_dispose (GpMetafile *metafile) if (metafile->recording) gdip_metafile_stop_recording (metafile); + /* if there was a graphics instance, it should already be cleaned up */ + g_assert (metafile->graphics == NULL); + GdipFree (metafile); return Ok; }