From d436e51872ad9ff8810d6339f98a8e8bf507738c Mon Sep 17 00:00:00 2001 From: Vincent Povirk Date: Fri, 1 Apr 2011 17:09:23 -0500 Subject: [PATCH] gdiplus: Add basic metafile recording support. --- dlls/gdiplus/Makefile.in | 1 + dlls/gdiplus/gdiplus_private.h | 10 + dlls/gdiplus/graphics.c | 42 ++--- dlls/gdiplus/image.c | 9 +- dlls/gdiplus/metafile.c | 324 +++++++++++++++++++++++++++++++++ dlls/gdiplus/tests/metafile.c | 20 +- 6 files changed, 370 insertions(+), 36 deletions(-) create mode 100644 dlls/gdiplus/metafile.c diff --git a/dlls/gdiplus/Makefile.in b/dlls/gdiplus/Makefile.in index fb762b1b8f2..b057eafdcb0 100644 --- a/dlls/gdiplus/Makefile.in +++ b/dlls/gdiplus/Makefile.in @@ -13,6 +13,7 @@ C_SRCS = \ image.c \ imageattributes.c \ matrix.c \ + metafile.c \ pathiterator.c \ pen.c \ region.c \ diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 2953b45d5ed..9cae489af6a 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -51,6 +51,9 @@ extern REAL convert_unit(REAL logpixels, GpUnit unit) DECLSPEC_HIDDEN; extern GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN; + extern void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1, REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN; extern void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, @@ -268,6 +271,13 @@ struct GpMetafile{ GpImage image; GpRectF bounds; GpUnit unit; + MetafileType metafile_type; + HDC record_dc; + GpGraphics *record_graphics; + BYTE *comment_data; + DWORD comment_data_size; + DWORD comment_data_length; + HENHMETAFILE hemf; }; struct GpBitmap{ diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 336929fc9b3..ed024adeec4 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -206,6 +206,11 @@ static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, return Ok; } + else if (graphics->image && graphics->image->type == ImageTypeMetafile) + { + ERR("This should not be used for metafiles; fix caller\n"); + return NotImplemented; + } else { HDC hdc; @@ -1967,11 +1972,19 @@ GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename, GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics) { GraphicsContainerItem *cont, *next; + GpStatus stat; TRACE("(%p)\n", graphics); if(!graphics) return InvalidParameter; if(graphics->busy) return ObjectBusy; + if (graphics->image && graphics->image->type == ImageTypeMetafile) + { + stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image); + if (stat != Ok) + return stat; + } + if(graphics->owndc) ReleaseDC(graphics->hwnd, graphics->hdc); @@ -5873,23 +5886,6 @@ GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 return stat; } -GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect, - MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) -{ - FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile); - return NotImplemented; -} - -/***************************************************************************** - * GdipRecordMetafileI [GDIPLUS.@] - */ -GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect, - MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) -{ - FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile); - return NotImplemented; -} - GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect, MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) { @@ -5919,15 +5915,3 @@ cleanup: GdipDeleteRegion(rgn); return stat; } - -GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf) -{ - FIXME("(%p,%p): stub\n", metafile, hEmf); - - if (!metafile || !hEmf) - return InvalidParameter; - - *hEmf = NULL; - - return NotImplemented; -} diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c index 9af2519b1e4..10376f5b414 100644 --- a/dlls/gdiplus/image.c +++ b/dlls/gdiplus/image.c @@ -2143,12 +2143,7 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image, if(!image || !graphics) return InvalidParameter; - if(image->type != ImageTypeBitmap){ - FIXME("not implemented for image type %d\n", image->type); - return NotImplemented; - } - - if (((GpBitmap*)image)->hbitmap) + if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap) { hdc = ((GpBitmap*)image)->hdc; @@ -2163,6 +2158,8 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image, if (stat == Ok) (*graphics)->image = image; } + else if (image->type == ImageTypeMetafile) + stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics); else stat = graphics_from_image(image, graphics); diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c new file mode 100644 index 00000000000..99b16213a94 --- /dev/null +++ b/dlls/gdiplus/metafile.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2011 Vincent Povirk for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "wine/unicode.h" + +#define COBJMACROS +#include "objbase.h" +#include "ocidl.h" +#include "olectl.h" +#include "ole2.h" + +#include "winreg.h" +#include "shlwapi.h" + +#include "gdiplus.h" +#include "gdiplus_private.h" +#include "wine/debug.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); + +typedef struct EmfPlusRecordHeader +{ + WORD Type; + WORD Flags; + DWORD Size; + DWORD DataSize; +} EmfPlusRecordHeader; + +typedef struct EmfPlusHeader +{ + EmfPlusRecordHeader Header; + DWORD Version; + DWORD EmfPlusFlags; + DWORD LogicalDpiX; + DWORD LogicalDpiY; +} EmfPlusHeader; + +static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result) +{ + DWORD size_needed; + EmfPlusRecordHeader *record; + + if (!metafile->comment_data_size) + { + DWORD data_size = max(256, size * 2 + 4); + metafile->comment_data = GdipAlloc(data_size); + + if (!metafile->comment_data) + return OutOfMemory; + + memcpy(metafile->comment_data, "EMF+", 4); + + metafile->comment_data_size = data_size; + metafile->comment_data_length = 4; + } + + size_needed = size + metafile->comment_data_length; + + if (size_needed > metafile->comment_data_size) + { + DWORD data_size = size_needed * 2; + BYTE *new_data = GdipAlloc(data_size); + + if (!new_data) + return OutOfMemory; + + memcpy(new_data, metafile->comment_data, metafile->comment_data_length); + + metafile->comment_data_size = data_size; + GdipFree(metafile->comment_data); + metafile->comment_data = new_data; + } + + *result = metafile->comment_data + metafile->comment_data_length; + metafile->comment_data_length += size; + + record = (EmfPlusRecordHeader*)*result; + record->Size = size; + record->DataSize = size - sizeof(EmfPlusRecordHeader); + + return Ok; +} + +static void METAFILE_WriteRecords(GpMetafile *metafile) +{ + if (metafile->comment_data_length > 4) + { + GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data); + metafile->comment_data_length = 4; + } +} + +static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc) +{ + GpStatus stat; + + if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) + { + EmfPlusHeader *header; + + stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header); + if (stat != Ok) + return stat; + + header->Header.Type = EmfPlusRecordTypeHeader; + + if (metafile->metafile_type == MetafileTypeEmfPlusDual) + header->Header.Flags = 1; + else + header->Header.Flags = 0; + + header->Version = 0xDBC01002; + + if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY) + header->EmfPlusFlags = 1; + else + header->EmfPlusFlags = 0; + + header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX); + header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY); + + METAFILE_WriteRecords(metafile); + } + + return Ok; +} + +static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile) +{ + GpStatus stat; + + if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) + { + EmfPlusRecordHeader *record; + + stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record); + if (stat != Ok) + return stat; + + record->Type = EmfPlusRecordTypeEndOfFile; + record->Flags = 0; + + METAFILE_WriteRecords(metafile); + } + + return Ok; +} + +GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect, + MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) +{ + HDC record_dc; + REAL framerect_factor_x, framerect_factor_y; + RECT rc; + GpStatus stat; + + TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile); + + if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile) + return InvalidParameter; + + if (!frameRect) + { + FIXME("not implemented for NULL rect\n"); + return NotImplemented; + } + + switch (frameUnit) + { + case MetafileFrameUnitPixel: + framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX); + framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY); + break; + case MetafileFrameUnitPoint: + framerect_factor_x = framerect_factor_y = 2540.0 / 72.0; + break; + case MetafileFrameUnitInch: + framerect_factor_x = framerect_factor_y = 2540.0; + break; + case MetafileFrameUnitDocument: + framerect_factor_x = framerect_factor_y = 2540.0 / 300.0; + break; + case MetafileFrameUnitMillimeter: + framerect_factor_x = framerect_factor_y = 100.0; + break; + case MetafileFrameUnitGdi: + framerect_factor_x = framerect_factor_y = 1.0; + break; + default: + return InvalidParameter; + } + + rc.left = framerect_factor_x * frameRect->X; + rc.top = framerect_factor_y * frameRect->Y; + rc.right = rc.left + framerect_factor_x * frameRect->Width; + rc.bottom = rc.top + framerect_factor_y * frameRect->Height; + + record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc); + + if (!record_dc) + return GenericError; + + *metafile = GdipAlloc(sizeof(GpMetafile)); + if(!*metafile) + { + DeleteEnhMetaFile(CloseEnhMetaFile(record_dc)); + return OutOfMemory; + } + + (*metafile)->image.type = ImageTypeMetafile; + (*metafile)->image.picture = NULL; + (*metafile)->image.flags = ImageFlagsNone; + (*metafile)->image.palette_flags = 0; + (*metafile)->image.palette_count = 0; + (*metafile)->image.palette_size = 0; + (*metafile)->image.palette_entries = NULL; + (*metafile)->bounds = *frameRect; + (*metafile)->unit = frameUnit; + (*metafile)->metafile_type = type; + (*metafile)->record_dc = record_dc; + (*metafile)->comment_data = NULL; + (*metafile)->comment_data_size = 0; + (*metafile)->comment_data_length = 0; + (*metafile)->hemf = NULL; + + stat = METAFILE_WriteHeader(*metafile, hdc); + + if (stat != Ok) + { + DeleteEnhMetaFile(CloseEnhMetaFile(record_dc)); + GdipFree(*metafile); + *metafile = NULL; + return OutOfMemory; + } + + return stat; +} + +/***************************************************************************** + * GdipRecordMetafileI [GDIPLUS.@] + */ +GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect, + MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) +{ + GpRectF frameRectF, *pFrameRectF; + + TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile); + + if (frameRect) + { + frameRectF.X = frameRect->X; + frameRectF.Y = frameRect->Y; + frameRectF.Width = frameRect->Width; + frameRectF.Height = frameRect->Height; + pFrameRectF = &frameRectF; + } + else + pFrameRectF = NULL; + + return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile); +} + +GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result) +{ + GpStatus stat; + + if (!metafile->record_dc || metafile->record_graphics) + return InvalidParameter; + + stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics); + + if (stat == Ok) + *result = metafile->record_graphics; + + return stat; +} + +GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) +{ + GpStatus stat; + + stat = METAFILE_WriteEndOfFile(metafile); + metafile->record_graphics = NULL; + + metafile->hemf = CloseEnhMetaFile(metafile->record_dc); + metafile->record_dc = NULL; + + return stat; +} + +GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf) +{ + TRACE("(%p,%p)\n", metafile, hEmf); + + if (!metafile || !hEmf || !metafile->hemf) + return InvalidParameter; + + *hEmf = metafile->hemf; + metafile->hemf = NULL; + + return Ok; +} diff --git a/dlls/gdiplus/tests/metafile.c b/dlls/gdiplus/tests/metafile.c index b467ce3ecf3..8dcda256575 100644 --- a/dlls/gdiplus/tests/metafile.c +++ b/dlls/gdiplus/tests/metafile.c @@ -161,8 +161,26 @@ static void test_empty(void) hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(NULL, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(InvalidParameter, stat); + + stat = GdipRecordMetafile(hdc, MetafileTypeInvalid, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(InvalidParameter, stat); + + stat = GdipRecordMetafile(hdc, MetafileTypeWmf, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(InvalidParameter, stat); + + stat = GdipRecordMetafile(hdc, MetafileTypeWmfPlaceable, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(InvalidParameter, stat); + + stat = GdipRecordMetafile(hdc, MetafileTypeEmfPlusDual+1, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(InvalidParameter, stat); + + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, NULL); + expect(InvalidParameter, stat); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); - todo_wine expect(Ok, stat); + expect(Ok, stat); DeleteDC(hdc);