| /* |
| * 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 <stdarg.h> |
| #include <math.h> |
| #include <assert.h> |
| |
| #define NONAMELESSUNION |
| |
| #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 EmfPlusARGB |
| { |
| BYTE Blue; |
| BYTE Green; |
| BYTE Red; |
| BYTE Alpha; |
| } EmfPlusARGB; |
| |
| 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; |
| |
| typedef struct EmfPlusClear |
| { |
| EmfPlusRecordHeader Header; |
| DWORD Color; |
| } EmfPlusClear; |
| |
| typedef struct EmfPlusFillRects |
| { |
| EmfPlusRecordHeader Header; |
| DWORD BrushID; |
| DWORD Count; |
| } EmfPlusFillRects; |
| |
| typedef struct EmfPlusSetClipRect |
| { |
| EmfPlusRecordHeader Header; |
| GpRectF ClipRect; |
| } EmfPlusSetClipRect; |
| |
| typedef struct EmfPlusSetPageTransform |
| { |
| EmfPlusRecordHeader Header; |
| REAL PageScale; |
| } EmfPlusSetPageTransform; |
| |
| typedef struct EmfPlusRect |
| { |
| SHORT X; |
| SHORT Y; |
| SHORT Width; |
| SHORT Height; |
| } EmfPlusRect; |
| |
| typedef struct EmfPlusSetWorldTransform |
| { |
| EmfPlusRecordHeader Header; |
| REAL MatrixData[6]; |
| } EmfPlusSetWorldTransform; |
| |
| typedef struct EmfPlusScaleWorldTransform |
| { |
| EmfPlusRecordHeader Header; |
| REAL Sx; |
| REAL Sy; |
| } EmfPlusScaleWorldTransform; |
| |
| typedef struct EmfPlusMultiplyWorldTransform |
| { |
| EmfPlusRecordHeader Header; |
| REAL MatrixData[6]; |
| } EmfPlusMultiplyWorldTransform; |
| |
| typedef struct EmfPlusRotateWorldTransform |
| { |
| EmfPlusRecordHeader Header; |
| REAL Angle; |
| } EmfPlusRotateWorldTransform; |
| |
| typedef struct EmfPlusTranslateWorldTransform |
| { |
| EmfPlusRecordHeader Header; |
| REAL dx; |
| REAL dy; |
| } EmfPlusTranslateWorldTransform; |
| |
| typedef struct EmfPlusBeginContainer |
| { |
| EmfPlusRecordHeader Header; |
| GpRectF DestRect; |
| GpRectF SrcRect; |
| DWORD StackIndex; |
| } EmfPlusBeginContainer; |
| |
| typedef struct EmfPlusContainerRecord |
| { |
| EmfPlusRecordHeader Header; |
| DWORD StackIndex; |
| } EmfPlusContainerRecord; |
| |
| enum container_type |
| { |
| BEGIN_CONTAINER, |
| SAVE_GRAPHICS |
| }; |
| |
| typedef struct container |
| { |
| struct list entry; |
| DWORD id; |
| enum container_type type; |
| GraphicsContainer state; |
| GpMatrix world_transform; |
| GpUnit page_unit; |
| REAL page_scale; |
| GpRegion *clip; |
| } container; |
| |
| enum PenDataFlags |
| { |
| PenDataTransform = 0x0001, |
| PenDataStartCap = 0x0002, |
| PenDataEndCap = 0x0004, |
| PenDataJoin = 0x0008, |
| PenDataMiterLimit = 0x0010, |
| PenDataLineStyle = 0x0020, |
| PenDataDashedLineCap = 0x0040, |
| PenDataDashedLineOffset = 0x0080, |
| PenDataDashedLine = 0x0100, |
| PenDataNonCenter = 0x0200, |
| PenDataCompoundLine = 0x0400, |
| PenDataCustomStartCap = 0x0800, |
| PenDataCustomEndCap = 0x1000 |
| }; |
| |
| typedef struct EmfPlusTransformMatrix |
| { |
| REAL TransformMatrix[6]; |
| } EmfPlusTransformMatrix; |
| |
| enum LineStyle |
| { |
| LineStyleSolid, |
| LineStyleDash, |
| LineStyleDot, |
| LineStyleDashDot, |
| LineStyleDashDotDot, |
| LineStyleCustom |
| }; |
| |
| typedef struct EmfPlusPenData |
| { |
| DWORD PenDataFlags; |
| DWORD PenUnit; |
| REAL PenWidth; |
| BYTE OptionalData[1]; |
| } EmfPlusPenData; |
| |
| typedef struct EmfPlusSolidBrushData |
| { |
| EmfPlusARGB SolidColor; |
| } EmfPlusSolidBrushData; |
| |
| typedef struct EmfPlusBrush |
| { |
| DWORD Version; |
| DWORD Type; |
| union { |
| EmfPlusSolidBrushData solid; |
| } BrushData; |
| } EmfPlusBrush; |
| |
| typedef struct EmfPlusPen |
| { |
| DWORD Version; |
| DWORD Type; |
| /* EmfPlusPenData */ |
| /* EmfPlusBrush */ |
| BYTE data[1]; |
| } EmfPlusPen; |
| |
| typedef struct EmfPlusPath |
| { |
| DWORD Version; |
| DWORD PathPointCount; |
| DWORD PathPointFlags; |
| /* PathPoints[] */ |
| /* PathPointTypes[] */ |
| /* AlignmentPadding */ |
| BYTE data[1]; |
| } EmfPlusPath; |
| |
| typedef struct EmfPlusRegion |
| { |
| DWORD Version; |
| DWORD RegionNodeCount; |
| BYTE RegionNode[1]; |
| } EmfPlusRegion; |
| |
| typedef enum |
| { |
| BitmapDataTypePixel, |
| BitmapDataTypeCompressed, |
| } BitmapDataType; |
| |
| typedef struct EmfPlusBitmap |
| { |
| DWORD Width; |
| DWORD Height; |
| DWORD Stride; |
| DWORD PixelFormat; |
| DWORD Type; |
| BYTE BitmapData[1]; |
| } EmfPlusBitmap; |
| |
| typedef struct EmfPlusMetafile |
| { |
| DWORD Type; |
| DWORD MetafileDataSize; |
| BYTE MetafileData[1]; |
| } EmfPlusMetafile; |
| |
| typedef enum ImageDataType |
| { |
| ImageDataTypeUnknown, |
| ImageDataTypeBitmap, |
| ImageDataTypeMetafile, |
| } ImageDataType; |
| |
| typedef struct EmfPlusImage |
| { |
| DWORD Version; |
| ImageDataType Type; |
| union |
| { |
| EmfPlusBitmap bitmap; |
| EmfPlusMetafile metafile; |
| } ImageData; |
| } EmfPlusImage; |
| |
| typedef struct EmfPlusImageAttributes |
| { |
| DWORD Version; |
| DWORD Reserved1; |
| DWORD WrapMode; |
| EmfPlusARGB ClampColor; |
| DWORD ObjectClamp; |
| DWORD Reserved2; |
| } EmfPlusImageAttributes; |
| |
| typedef enum ObjectType |
| { |
| ObjectTypeInvalid, |
| ObjectTypeBrush, |
| ObjectTypePen, |
| ObjectTypePath, |
| ObjectTypeRegion, |
| ObjectTypeImage, |
| ObjectTypeFont, |
| ObjectTypeStringFormat, |
| ObjectTypeImageAttributes, |
| ObjectTypeCustomLineCap, |
| } ObjectType; |
| |
| typedef struct EmfPlusObject |
| { |
| EmfPlusRecordHeader Header; |
| union |
| { |
| EmfPlusBrush brush; |
| EmfPlusPen pen; |
| EmfPlusPath path; |
| EmfPlusRegion region; |
| EmfPlusImage image; |
| EmfPlusImageAttributes image_attributes; |
| } ObjectData; |
| } EmfPlusObject; |
| |
| typedef struct EmfPlusRectF |
| { |
| float X; |
| float Y; |
| float Width; |
| float Height; |
| } EmfPlusRectF; |
| |
| typedef struct EmfPlusPointF |
| { |
| float X; |
| float Y; |
| } EmfPlusPointF; |
| |
| typedef struct EmfPlusDrawImagePoints |
| { |
| EmfPlusRecordHeader Header; |
| DWORD ImageAttributesID; |
| DWORD SrcUnit; |
| EmfPlusRectF SrcRect; |
| DWORD count; |
| union |
| { |
| /*EmfPlusPointR pointR; |
| EmfPlusPoint point;*/ |
| EmfPlusPointF pointF; |
| } PointData[3]; |
| } EmfPlusDrawImagePoints; |
| |
| typedef struct EmfPlusDrawPath |
| { |
| EmfPlusRecordHeader Header; |
| DWORD PenId; |
| } EmfPlusDrawPath; |
| |
| typedef struct EmfPlusFillPath |
| { |
| EmfPlusRecordHeader Header; |
| union |
| { |
| DWORD BrushId; |
| EmfPlusARGB Color; |
| } data; |
| } EmfPlusFillPath; |
| |
| static DWORD METAFILE_AddObjectId(GpMetafile *metafile) |
| { |
| return (metafile->next_object_id++) % 64; |
| } |
| |
| 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 = heap_alloc_zero(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 = heap_alloc_zero(data_size); |
| |
| if (!new_data) |
| return OutOfMemory; |
| |
| memcpy(new_data, metafile->comment_data, metafile->comment_data_length); |
| |
| metafile->comment_data_size = data_size; |
| heap_free(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_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record) |
| { |
| assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size); |
| metafile->comment_data_length -= record->Size; |
| } |
| |
| 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 = VERSION_MAGIC2; |
| |
| 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 dpix, dpiy; |
| REAL framerect_factor_x, framerect_factor_y; |
| RECT rc, *lprc; |
| 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; |
| |
| dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4; |
| dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4; |
| |
| if (frameRect) |
| { |
| switch (frameUnit) |
| { |
| case MetafileFrameUnitPixel: |
| framerect_factor_x = 2540.0 / dpix; |
| framerect_factor_y = 2540.0 / dpiy; |
| 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; |
| |
| lprc = &rc; |
| } |
| else |
| lprc = NULL; |
| |
| record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc); |
| |
| if (!record_dc) |
| return GenericError; |
| |
| *metafile = heap_alloc_zero(sizeof(GpMetafile)); |
| if(!*metafile) |
| { |
| DeleteEnhMetaFile(CloseEnhMetaFile(record_dc)); |
| return OutOfMemory; |
| } |
| |
| (*metafile)->image.type = ImageTypeMetafile; |
| (*metafile)->image.flags = ImageFlagsNone; |
| (*metafile)->image.palette = NULL; |
| (*metafile)->image.xres = dpix; |
| (*metafile)->image.yres = dpiy; |
| (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0; |
| (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0; |
| (*metafile)->unit = UnitPixel; |
| (*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; |
| list_init(&(*metafile)->containers); |
| |
| if (!frameRect) |
| { |
| (*metafile)->auto_frame = TRUE; |
| (*metafile)->auto_frame_min.X = 0; |
| (*metafile)->auto_frame_min.Y = 0; |
| (*metafile)->auto_frame_max.X = -1; |
| (*metafile)->auto_frame_max.Y = -1; |
| } |
| |
| stat = METAFILE_WriteHeader(*metafile, hdc); |
| |
| if (stat != Ok) |
| { |
| DeleteEnhMetaFile(CloseEnhMetaFile(record_dc)); |
| heap_free(*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 WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect, |
| MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) |
| { |
| GpStatus stat; |
| |
| TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile); |
| |
| if (!stream) |
| return InvalidParameter; |
| |
| stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile); |
| |
| if (stat == Ok) |
| { |
| (*metafile)->record_stream = stream; |
| IStream_AddRef(stream); |
| } |
| |
| return stat; |
| } |
| |
| static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points, |
| UINT num_points) |
| { |
| int i; |
| |
| if (!metafile->auto_frame || !num_points) |
| return; |
| |
| if (metafile->auto_frame_max.X < metafile->auto_frame_min.X) |
| metafile->auto_frame_max = metafile->auto_frame_min = points[0]; |
| |
| for (i=0; i<num_points; i++) |
| { |
| if (points[i].X < metafile->auto_frame_min.X) |
| metafile->auto_frame_min.X = points[i].X; |
| if (points[i].X > metafile->auto_frame_max.X) |
| metafile->auto_frame_max.X = points[i].X; |
| if (points[i].Y < metafile->auto_frame_min.Y) |
| metafile->auto_frame_min.Y = points[i].Y; |
| if (points[i].Y > metafile->auto_frame_max.Y) |
| metafile->auto_frame_max.Y = points[i].Y; |
| } |
| } |
| |
| 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; |
| metafile->record_graphics->xres = 96.0; |
| metafile->record_graphics->yres = 96.0; |
| } |
| |
| return stat; |
| } |
| |
| GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusRecordHeader *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Type = EmfPlusRecordTypeGetDC; |
| record->Flags = 0; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| *hdc = metafile->record_dc; |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusClear *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeClear; |
| record->Header.Flags = 0; |
| record->Color = color; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| static BOOL is_integer_rect(const GpRectF *rect) |
| { |
| SHORT x, y, width, height; |
| x = rect->X; |
| y = rect->Y; |
| width = rect->Width; |
| height = rect->Height; |
| if (rect->X != (REAL)x || rect->Y != (REAL)y || |
| rect->Width != (REAL)width || rect->Height != (REAL)height) |
| return FALSE; |
| return TRUE; |
| } |
| |
| GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush, |
| GDIPCONST GpRectF* rects, INT count) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusFillRects *record; |
| GpStatus stat; |
| BOOL integer_rects = TRUE; |
| int i; |
| DWORD brushid; |
| int flags = 0; |
| |
| if (brush->bt == BrushTypeSolidColor) |
| { |
| flags |= 0x8000; |
| brushid = ((GpSolidFill*)brush)->color; |
| } |
| else |
| { |
| FIXME("brush serialization not implemented\n"); |
| return NotImplemented; |
| } |
| |
| for (i=0; i<count; i++) |
| { |
| if (!is_integer_rect(&rects[i])) |
| { |
| integer_rects = FALSE; |
| break; |
| } |
| } |
| |
| if (integer_rects) |
| flags |= 0x4000; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeFillRects; |
| record->Header.Flags = flags; |
| record->BrushID = brushid; |
| record->Count = count; |
| |
| if (integer_rects) |
| { |
| EmfPlusRect *record_rects = (EmfPlusRect*)(record+1); |
| for (i=0; i<count; i++) |
| { |
| record_rects[i].X = (SHORT)rects[i].X; |
| record_rects[i].Y = (SHORT)rects[i].Y; |
| record_rects[i].Width = (SHORT)rects[i].Width; |
| record_rects[i].Height = (SHORT)rects[i].Height; |
| } |
| } |
| else |
| memcpy(record+1, rects, sizeof(GpRectF) * count); |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| if (metafile->auto_frame) |
| { |
| GpPointF corners[4]; |
| int i; |
| |
| for (i=0; i<count; i++) |
| { |
| corners[0].X = rects[i].X; |
| corners[0].Y = rects[i].Y; |
| corners[1].X = rects[i].X + rects[i].Width; |
| corners[1].Y = rects[i].Y; |
| corners[2].X = rects[i].X; |
| corners[2].Y = rects[i].Y + rects[i].Height; |
| corners[3].X = rects[i].X + rects[i].Width; |
| corners[3].Y = rects[i].Y + rects[i].Height; |
| |
| GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice, |
| CoordinateSpaceWorld, corners, 4); |
| |
| METAFILE_AdjustFrame(metafile, corners, 4); |
| } |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusSetClipRect *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusSetClipRect), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeSetClipRect; |
| record->Header.Flags = (mode & 0xf) << 8; |
| record->ClipRect.X = x; |
| record->ClipRect.Y = y; |
| record->ClipRect.Width = width; |
| record->ClipRect.Height = height; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id) |
| { |
| EmfPlusObject *object_record; |
| DWORD size; |
| GpStatus stat; |
| |
| *id = -1; |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| size = write_region_data(region, NULL); |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record); |
| if (stat != Ok) return stat; |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | ObjectTypeRegion << 8; |
| write_region_data(region, &object_record->ObjectData.region); |
| return Ok; |
| } |
| |
| GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode) |
| { |
| EmfPlusRecordHeader *record; |
| DWORD region_id; |
| GpStatus stat; |
| |
| if (metafile->metafile_type == MetafileTypeEmf) |
| { |
| FIXME("stub!\n"); |
| return NotImplemented; |
| } |
| |
| stat = METAFILE_AddRegionObject(metafile, region, ®ion_id); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record); |
| if (stat != Ok) return stat; |
| |
| record->Type = EmfPlusRecordTypeSetClipRegion; |
| record->Flags = region_id | mode << 8; |
| |
| METAFILE_WriteRecords(metafile); |
| return Ok; |
| } |
| |
| GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusSetPageTransform *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusSetPageTransform), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeSetPageTransform; |
| record->Header.Flags = unit; |
| record->PageScale = scale; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusSetWorldTransform *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusSetWorldTransform), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeSetWorldTransform; |
| record->Header.Flags = 0; |
| memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData)); |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusScaleWorldTransform *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusScaleWorldTransform), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeScaleWorldTransform; |
| record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0); |
| record->Sx = sx; |
| record->Sy = sy; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusMultiplyWorldTransform *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusMultiplyWorldTransform), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform; |
| record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0); |
| memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData)); |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusRotateWorldTransform *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusRotateWorldTransform), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeRotateWorldTransform; |
| record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0); |
| record->Angle = angle; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusTranslateWorldTransform *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusTranslateWorldTransform), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform; |
| record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0); |
| record->dx = dx; |
| record->dy = dy; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusRecordHeader *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusRecordHeader), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Type = EmfPlusRecordTypeResetWorldTransform; |
| record->Flags = 0; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect, |
| GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusBeginContainer *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeBeginContainer; |
| record->Header.Flags = unit & 0xff; |
| record->DestRect = *dstrect; |
| record->SrcRect = *srcrect; |
| record->StackIndex = StackIndex; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusContainerRecord *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusContainerRecord), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams; |
| record->Header.Flags = 0; |
| record->StackIndex = StackIndex; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusContainerRecord *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusContainerRecord), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeEndContainer; |
| record->Header.Flags = 0; |
| record->StackIndex = StackIndex; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusContainerRecord *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusContainerRecord), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeSave; |
| record->Header.Flags = 0; |
| record->StackIndex = StackIndex; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex) |
| { |
| if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) |
| { |
| EmfPlusContainerRecord *record; |
| GpStatus stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusContainerRecord), |
| (void**)&record); |
| if (stat != Ok) |
| return stat; |
| |
| record->Header.Type = EmfPlusRecordTypeRestore; |
| record->Header.Flags = 0; |
| record->StackIndex = StackIndex; |
| |
| METAFILE_WriteRecords(metafile); |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc) |
| { |
| if (hdc != metafile->record_dc) |
| return InvalidParameter; |
| |
| return Ok; |
| } |
| |
| 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; |
| |
| heap_free(metafile->comment_data); |
| metafile->comment_data = NULL; |
| metafile->comment_data_size = 0; |
| |
| if (stat == Ok) |
| { |
| MetafileHeader header; |
| |
| stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header); |
| if (stat == Ok && metafile->auto_frame && |
| metafile->auto_frame_max.X >= metafile->auto_frame_min.X) |
| { |
| RECTL bounds_rc, gdi_bounds_rc; |
| REAL x_scale = 2540.0 / header.DpiX; |
| REAL y_scale = 2540.0 / header.DpiY; |
| BYTE* buffer; |
| UINT buffer_size; |
| |
| bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale); |
| bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale); |
| bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale); |
| bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale); |
| |
| gdi_bounds_rc = header.u.EmfHeader.rclBounds; |
| if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top) |
| { |
| bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left); |
| bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top); |
| bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right); |
| bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom); |
| } |
| |
| buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL); |
| buffer = heap_alloc(buffer_size); |
| if (buffer) |
| { |
| HENHMETAFILE new_hemf; |
| |
| GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer); |
| |
| ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc; |
| |
| new_hemf = SetEnhMetaFileBits(buffer_size, buffer); |
| |
| if (new_hemf) |
| { |
| DeleteEnhMetaFile(metafile->hemf); |
| metafile->hemf = new_hemf; |
| } |
| else |
| stat = OutOfMemory; |
| |
| heap_free(buffer); |
| } |
| else |
| stat = OutOfMemory; |
| |
| if (stat == Ok) |
| stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header); |
| } |
| if (stat == Ok) |
| { |
| metafile->bounds.X = header.X; |
| metafile->bounds.Y = header.Y; |
| metafile->bounds.Width = header.Width; |
| metafile->bounds.Height = header.Height; |
| } |
| } |
| |
| if (stat == Ok && metafile->record_stream) |
| { |
| BYTE *buffer; |
| UINT buffer_size; |
| |
| buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL); |
| |
| buffer = heap_alloc(buffer_size); |
| if (buffer) |
| { |
| HRESULT hr; |
| |
| GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer); |
| |
| hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL); |
| |
| if (FAILED(hr)) |
| stat = hresult_to_status(hr); |
| |
| heap_free(buffer); |
| } |
| else |
| stat = OutOfMemory; |
| } |
| |
| if (metafile->record_stream) |
| { |
| IStream_Release(metafile->record_stream); |
| metafile->record_stream = 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; |
| } |
| |
| static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result) |
| { |
| const GpRectF *rect; |
| const GpPointF *pt; |
| |
| /* This transforms metafile device space to output points. */ |
| rect = &metafile->src_rect; |
| pt = metafile->playback_points; |
| result->eM11 = (pt[1].X - pt[0].X) / rect->Width; |
| result->eM21 = (pt[2].X - pt[0].X) / rect->Height; |
| result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y; |
| result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width; |
| result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height; |
| result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y; |
| } |
| |
| static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile) |
| { |
| XFORM combined, final; |
| |
| METAFILE_GetFinalGdiTransform(metafile, &final); |
| |
| CombineTransform(&combined, &metafile->gdiworldtransform, &final); |
| |
| SetGraphicsMode(metafile->playback_dc, GM_ADVANCED); |
| SetWorldTransform(metafile->playback_dc, &combined); |
| |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile) |
| { |
| GpStatus stat = Ok; |
| |
| stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc); |
| |
| if (stat == Ok) |
| { |
| static const XFORM identity = {1, 0, 0, 1, 0, 0}; |
| |
| metafile->gdiworldtransform = identity; |
| METAFILE_PlaybackUpdateGdiTransform(metafile); |
| } |
| |
| return stat; |
| } |
| |
| static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile) |
| { |
| if (metafile->playback_dc) |
| { |
| GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc); |
| metafile->playback_dc = NULL; |
| } |
| } |
| |
| static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile) |
| { |
| GpStatus stat; |
| stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace); |
| if (stat == Ok) |
| stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect); |
| return stat; |
| } |
| |
| static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile) |
| { |
| GpMatrix *real_transform; |
| GpStatus stat; |
| |
| stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform); |
| |
| if (stat == Ok) |
| { |
| REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0); |
| |
| if (metafile->page_unit != UnitDisplay) |
| scale *= metafile->page_scale; |
| |
| stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend); |
| |
| if (stat == Ok) |
| stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend); |
| |
| if (stat == Ok) |
| stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform); |
| |
| GdipDeleteMatrix(real_transform); |
| } |
| |
| return stat; |
| } |
| |
| GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile, |
| EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data) |
| { |
| GpStatus stat; |
| GpMetafile *real_metafile = (GpMetafile*)metafile; |
| |
| TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data); |
| |
| if (!metafile || (dataSize && !data) || !metafile->playback_graphics) |
| return InvalidParameter; |
| |
| if (recordType >= 1 && recordType <= 0x7a) |
| { |
| /* regular EMF record */ |
| if (metafile->playback_dc) |
| { |
| switch (recordType) |
| { |
| case EMR_SETMAPMODE: |
| case EMR_SAVEDC: |
| case EMR_RESTOREDC: |
| case EMR_SETWINDOWORGEX: |
| case EMR_SETWINDOWEXTEX: |
| case EMR_SETVIEWPORTORGEX: |
| case EMR_SETVIEWPORTEXTEX: |
| case EMR_SCALEVIEWPORTEXTEX: |
| case EMR_SCALEWINDOWEXTEX: |
| case EMR_MODIFYWORLDTRANSFORM: |
| FIXME("not implemented for record type %x\n", recordType); |
| break; |
| case EMR_SETWORLDTRANSFORM: |
| { |
| const XFORM* xform = (void*)data; |
| real_metafile->gdiworldtransform = *xform; |
| METAFILE_PlaybackUpdateGdiTransform(real_metafile); |
| break; |
| } |
| case EMR_EXTSELECTCLIPRGN: |
| { |
| DWORD rgndatasize = *(DWORD*)data; |
| DWORD mode = *(DWORD*)(data + 4); |
| const RGNDATA *rgndata = (const RGNDATA*)(data + 8); |
| HRGN hrgn = NULL; |
| |
| if (dataSize > 8) |
| { |
| XFORM final; |
| |
| METAFILE_GetFinalGdiTransform(metafile, &final); |
| |
| hrgn = ExtCreateRegion(&final, rgndatasize, rgndata); |
| } |
| |
| ExtSelectClipRgn(metafile->playback_dc, hrgn, mode); |
| |
| DeleteObject(hrgn); |
| |
| return Ok; |
| } |
| default: |
| { |
| ENHMETARECORD *record = heap_alloc_zero(dataSize + 8); |
| |
| if (record) |
| { |
| record->iType = recordType; |
| record->nSize = dataSize + 8; |
| memcpy(record->dParm, data, dataSize); |
| |
| if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table, |
| record, metafile->handle_count) == 0) |
| ERR("PlayEnhMetaFileRecord failed\n"); |
| |
| heap_free(record); |
| } |
| else |
| return OutOfMemory; |
| |
| break; |
| } |
| } |
| } |
| } |
| else |
| { |
| EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1; |
| |
| METAFILE_PlaybackReleaseDC((GpMetafile*)metafile); |
| |
| switch(recordType) |
| { |
| case EmfPlusRecordTypeHeader: |
| case EmfPlusRecordTypeEndOfFile: |
| break; |
| case EmfPlusRecordTypeGetDC: |
| METAFILE_PlaybackGetDC((GpMetafile*)metafile); |
| break; |
| case EmfPlusRecordTypeClear: |
| { |
| EmfPlusClear *record = (EmfPlusClear*)header; |
| |
| return GdipGraphicsClear(metafile->playback_graphics, record->Color); |
| } |
| case EmfPlusRecordTypeFillRects: |
| { |
| EmfPlusFillRects *record = (EmfPlusFillRects*)header; |
| GpBrush *brush, *temp_brush=NULL; |
| GpRectF *rects, *temp_rects=NULL; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects)) |
| return InvalidParameter; |
| |
| if (flags & 0x4000) |
| { |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count) |
| return InvalidParameter; |
| } |
| else |
| { |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count) |
| return InvalidParameter; |
| } |
| |
| if (flags & 0x8000) |
| { |
| stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush); |
| brush = temp_brush; |
| } |
| else |
| { |
| FIXME("brush deserialization not implemented\n"); |
| return NotImplemented; |
| } |
| |
| if (stat == Ok) |
| { |
| if (flags & 0x4000) |
| { |
| EmfPlusRect *int_rects = (EmfPlusRect*)(record+1); |
| int i; |
| |
| rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count); |
| if (rects) |
| { |
| for (i=0; i<record->Count; i++) |
| { |
| rects[i].X = int_rects[i].X; |
| rects[i].Y = int_rects[i].Y; |
| rects[i].Width = int_rects[i].Width; |
| rects[i].Height = int_rects[i].Height; |
| } |
| } |
| else |
| stat = OutOfMemory; |
| } |
| else |
| rects = (GpRectF*)(record+1); |
| } |
| |
| if (stat == Ok) |
| { |
| stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count); |
| } |
| |
| GdipDeleteBrush(temp_brush); |
| heap_free(temp_rects); |
| |
| return stat; |
| } |
| case EmfPlusRecordTypeSetClipRect: |
| { |
| EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header; |
| CombineMode mode = (CombineMode)((flags >> 8) & 0xf); |
| GpRegion *region; |
| GpMatrix world_to_device; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record)) |
| return InvalidParameter; |
| |
| stat = GdipCreateRegionRect(&record->ClipRect, ®ion); |
| |
| if (stat == Ok) |
| { |
| get_graphics_transform(real_metafile->playback_graphics, |
| CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device); |
| |
| GdipTransformRegion(region, &world_to_device); |
| |
| GdipCombineRegionRegion(real_metafile->clip, region, mode); |
| |
| GdipDeleteRegion(region); |
| } |
| |
| return METAFILE_PlaybackUpdateClip(real_metafile); |
| } |
| case EmfPlusRecordTypeSetPageTransform: |
| { |
| EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header; |
| GpUnit unit = (GpUnit)flags; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform)) |
| return InvalidParameter; |
| |
| real_metafile->page_unit = unit; |
| real_metafile->page_scale = record->PageScale; |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeSetWorldTransform: |
| { |
| EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform)) |
| return InvalidParameter; |
| |
| memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData)); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeScaleWorldTransform: |
| { |
| EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header; |
| MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform)) |
| return InvalidParameter; |
| |
| GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeMultiplyWorldTransform: |
| { |
| EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header; |
| MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend; |
| GpMatrix matrix; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform)) |
| return InvalidParameter; |
| |
| memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix)); |
| |
| GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeRotateWorldTransform: |
| { |
| EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header; |
| MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform)) |
| return InvalidParameter; |
| |
| GdipRotateMatrix(real_metafile->world_transform, record->Angle, order); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeTranslateWorldTransform: |
| { |
| EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header; |
| MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend; |
| |
| if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform)) |
| return InvalidParameter; |
| |
| GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeResetWorldTransform: |
| { |
| GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeBeginContainer: |
| { |
| EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header; |
| container* cont; |
| GpUnit unit; |
| REAL scale_x, scale_y; |
| GpRectF scaled_srcrect; |
| GpMatrix transform; |
| |
| cont = heap_alloc_zero(sizeof(*cont)); |
| if (!cont) |
| return OutOfMemory; |
| |
| stat = GdipCloneRegion(metafile->clip, &cont->clip); |
| if (stat != Ok) |
| { |
| heap_free(cont); |
| return stat; |
| } |
| |
| stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state); |
| |
| if (stat != Ok) |
| { |
| GdipDeleteRegion(cont->clip); |
| heap_free(cont); |
| return stat; |
| } |
| |
| cont->id = record->StackIndex; |
| cont->type = BEGIN_CONTAINER; |
| cont->world_transform = *metafile->world_transform; |
| cont->page_unit = metafile->page_unit; |
| cont->page_scale = metafile->page_scale; |
| list_add_head(&real_metafile->containers, &cont->entry); |
| |
| unit = record->Header.Flags & 0xff; |
| |
| scale_x = units_to_pixels(1.0, unit, metafile->image.xres); |
| scale_y = units_to_pixels(1.0, unit, metafile->image.yres); |
| |
| scaled_srcrect.X = scale_x * record->SrcRect.X; |
| scaled_srcrect.Y = scale_y * record->SrcRect.Y; |
| scaled_srcrect.Width = scale_x * record->SrcRect.Width; |
| scaled_srcrect.Height = scale_y * record->SrcRect.Height; |
| |
| transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width; |
| transform.matrix[1] = 0.0; |
| transform.matrix[2] = 0.0; |
| transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height; |
| transform.matrix[4] = record->DestRect.X - scaled_srcrect.X; |
| transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y; |
| |
| GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend); |
| |
| return METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| case EmfPlusRecordTypeBeginContainerNoParams: |
| case EmfPlusRecordTypeSave: |
| { |
| EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header; |
| container* cont; |
| |
| cont = heap_alloc_zero(sizeof(*cont)); |
| if (!cont) |
| return OutOfMemory; |
| |
| stat = GdipCloneRegion(metafile->clip, &cont->clip); |
| if (stat != Ok) |
| { |
| heap_free(cont); |
| return stat; |
| } |
| |
| if (recordType == EmfPlusRecordTypeBeginContainerNoParams) |
| stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state); |
| else |
| stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state); |
| |
| if (stat != Ok) |
| { |
| GdipDeleteRegion(cont->clip); |
| heap_free(cont); |
| return stat; |
| } |
| |
| cont->id = record->StackIndex; |
| if (recordType == EmfPlusRecordTypeBeginContainerNoParams) |
| cont->type = BEGIN_CONTAINER; |
| else |
| cont->type = SAVE_GRAPHICS; |
| cont->world_transform = *metafile->world_transform; |
| cont->page_unit = metafile->page_unit; |
| cont->page_scale = metafile->page_scale; |
| list_add_head(&real_metafile->containers, &cont->entry); |
| |
| break; |
| } |
| case EmfPlusRecordTypeEndContainer: |
| case EmfPlusRecordTypeRestore: |
| { |
| EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header; |
| container* cont; |
| enum container_type type; |
| BOOL found=FALSE; |
| |
| if (recordType == EmfPlusRecordTypeEndContainer) |
| type = BEGIN_CONTAINER; |
| else |
| type = SAVE_GRAPHICS; |
| |
| LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry) |
| { |
| if (cont->id == record->StackIndex && cont->type == type) |
| { |
| found = TRUE; |
| break; |
| } |
| } |
| |
| if (found) |
| { |
| container* cont2; |
| |
| /* pop any newer items on the stack */ |
| while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont) |
| { |
| list_remove(&cont2->entry); |
| GdipDeleteRegion(cont2->clip); |
| heap_free(cont2); |
| } |
| |
| if (type == BEGIN_CONTAINER) |
| GdipEndContainer(real_metafile->playback_graphics, cont->state); |
| else |
| GdipRestoreGraphics(real_metafile->playback_graphics, cont->state); |
| |
| *real_metafile->world_transform = cont->world_transform; |
| real_metafile->page_unit = cont->page_unit; |
| real_metafile->page_scale = cont->page_scale; |
| GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace); |
| |
| list_remove(&cont->entry); |
| GdipDeleteRegion(cont->clip); |
| heap_free(cont); |
| } |
| |
| break; |
| } |
| case EmfPlusRecordTypeSetPixelOffsetMode: |
| { |
| return GdipSetPixelOffsetMode(real_metafile->playback_graphics, (flags >> 8) & 0xf); |
| } |
| case EmfPlusRecordTypeSetCompositingQuality: |
| { |
| return GdipSetCompositingQuality(real_metafile->playback_graphics, (flags >> 8) & 0xf); |
| } |
| case EmfPlusRecordTypeSetInterpolationMode: |
| { |
| return GdipSetInterpolationMode(real_metafile->playback_graphics, (flags >> 8) & 0xf); |
| } |
| default: |
| FIXME("Not implemented for record type %x\n", recordType); |
| return NotImplemented; |
| } |
| } |
| |
| return Ok; |
| } |
| |
| struct enum_metafile_data |
| { |
| EnumerateMetafileProc callback; |
| void *callback_data; |
| GpMetafile *metafile; |
| }; |
| |
| static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, |
| int nObj, LPARAM lpData) |
| { |
| BOOL ret; |
| struct enum_metafile_data *data = (struct enum_metafile_data*)lpData; |
| const BYTE* pStr; |
| |
| data->metafile->handle_table = lpHTable; |
| data->metafile->handle_count = nObj; |
| |
| /* First check for an EMF+ record. */ |
| if (lpEMFR->iType == EMR_GDICOMMENT) |
| { |
| const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR; |
| |
| if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0) |
| { |
| int offset = 4; |
| |
| while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData) |
| { |
| const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset]; |
| |
| if (record->DataSize) |
| pStr = (const BYTE*)(record+1); |
| else |
| pStr = NULL; |
| |
| ret = data->callback(record->Type, record->Flags, record->DataSize, |
| pStr, data->callback_data); |
| |
| if (!ret) |
| return 0; |
| |
| offset += record->Size; |
| } |
| |
| return 1; |
| } |
| } |
| |
| if (lpEMFR->nSize != 8) |
| pStr = (const BYTE*)lpEMFR->dParm; |
| else |
| pStr = NULL; |
| |
| return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8, |
| pStr, data->callback_data); |
| } |
| |
| GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, |
| GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count, |
| GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback, |
| VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes) |
| { |
| struct enum_metafile_data data; |
| GpStatus stat; |
| GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */ |
| GraphicsContainer state; |
| GpPath *dst_path; |
| |
| TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile, |
| destPoints, count, srcRect, srcUnit, callback, callbackData, |
| imageAttributes); |
| |
| if (!graphics || !metafile || !destPoints || count != 3 || !srcRect) |
| return InvalidParameter; |
| |
| if (!metafile->hemf) |
| return InvalidParameter; |
| |
| if (metafile->playback_graphics) |
| return ObjectBusy; |
| |
| TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit, |
| debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]), |
| debugstr_pointf(&destPoints[2])); |
| |
| data.callback = callback; |
| data.callback_data = callbackData; |
| data.metafile = real_metafile; |
| |
| real_metafile->playback_graphics = graphics; |
| real_metafile->playback_dc = NULL; |
| real_metafile->src_rect = *srcRect; |
| |
| memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3); |
| stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3); |
| |
| if (stat == Ok) |
| stat = GdipBeginContainer2(graphics, &state); |
| |
| if (stat == Ok) |
| { |
| stat = GdipSetPageScale(graphics, 1.0); |
| |
| if (stat == Ok) |
| stat = GdipSetPageUnit(graphics, UnitPixel); |
| |
| if (stat == Ok) |
| stat = GdipResetWorldTransform(graphics); |
| |
| if (stat == Ok) |
| stat = GdipCreateRegion(&real_metafile->base_clip); |
| |
| if (stat == Ok) |
| stat = GdipGetClip(graphics, real_metafile->base_clip); |
| |
| if (stat == Ok) |
| stat = GdipCreateRegion(&real_metafile->clip); |
| |
| if (stat == Ok) |
| stat = GdipCreatePath(FillModeAlternate, &dst_path); |
| |
| if (stat == Ok) |
| { |
| GpPointF clip_points[4]; |
| |
| clip_points[0] = real_metafile->playback_points[0]; |
| clip_points[1] = real_metafile->playback_points[1]; |
| clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X |
| - real_metafile->playback_points[0].X; |
| clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y |
| - real_metafile->playback_points[0].Y; |
| clip_points[3] = real_metafile->playback_points[2]; |
| |
| stat = GdipAddPathPolygon(dst_path, clip_points, 4); |
| |
| if (stat == Ok) |
| stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect); |
| |
| GdipDeletePath(dst_path); |
| } |
| |
| if (stat == Ok) |
| stat = GdipCreateMatrix(&real_metafile->world_transform); |
| |
| if (stat == Ok) |
| { |
| real_metafile->page_unit = UnitDisplay; |
| real_metafile->page_scale = 1.0; |
| stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile); |
| } |
| |
| if (stat == Ok) |
| { |
| stat = METAFILE_PlaybackUpdateClip(real_metafile); |
| } |
| |
| if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf || |
| metafile->metafile_type == MetafileTypeWmfPlaceable || |
| metafile->metafile_type == MetafileTypeWmf)) |
| stat = METAFILE_PlaybackGetDC(real_metafile); |
| |
| if (stat == Ok) |
| EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL); |
| |
| METAFILE_PlaybackReleaseDC(real_metafile); |
| |
| GdipDeleteMatrix(real_metafile->world_transform); |
| real_metafile->world_transform = NULL; |
| |
| GdipDeleteRegion(real_metafile->base_clip); |
| real_metafile->base_clip = NULL; |
| |
| GdipDeleteRegion(real_metafile->clip); |
| real_metafile->clip = NULL; |
| |
| while (list_head(&real_metafile->containers)) |
| { |
| container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry); |
| list_remove(&cont->entry); |
| GdipDeleteRegion(cont->clip); |
| heap_free(cont); |
| } |
| |
| GdipEndContainer(graphics, state); |
| } |
| |
| real_metafile->playback_graphics = NULL; |
| |
| return stat; |
| } |
| |
| GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics, |
| GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest, |
| EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) |
| { |
| GpPointF points[3]; |
| |
| if (!graphics || !metafile || !dest) return InvalidParameter; |
| |
| points[0].X = points[2].X = dest->X; |
| points[0].Y = points[1].Y = dest->Y; |
| points[1].X = dest->X + dest->Width; |
| points[2].Y = dest->Y + dest->Height; |
| |
| return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3, |
| &metafile->bounds, metafile->unit, callback, cb_data, attrs); |
| } |
| |
| GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics, |
| GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest, |
| EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) |
| { |
| GpRectF destf; |
| |
| if (!graphics || !metafile || !dest) return InvalidParameter; |
| |
| destf.X = dest->X; |
| destf.Y = dest->Y; |
| destf.Width = dest->Width; |
| destf.Height = dest->Height; |
| |
| return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs); |
| } |
| |
| GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics, |
| GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest, |
| EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) |
| { |
| GpRectF destf; |
| |
| if (!graphics || !metafile || !dest) return InvalidParameter; |
| |
| destf.X = dest->X; |
| destf.Y = dest->Y; |
| destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres); |
| destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres); |
| |
| return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs); |
| } |
| |
| GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics, |
| GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest, |
| EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) |
| { |
| GpPointF ptf; |
| |
| if (!graphics || !metafile || !dest) return InvalidParameter; |
| |
| ptf.X = dest->X; |
| ptf.Y = dest->Y; |
| |
| return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs); |
| } |
| |
| GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile, |
| MetafileHeader * header) |
| { |
| GpStatus status; |
| |
| TRACE("(%p, %p)\n", metafile, header); |
| |
| if(!metafile || !header) |
| return InvalidParameter; |
| |
| if (metafile->hemf) |
| { |
| status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header); |
| if (status != Ok) return status; |
| } |
| else |
| { |
| memset(header, 0, sizeof(*header)); |
| header->Version = VERSION_MAGIC2; |
| } |
| |
| header->Type = metafile->metafile_type; |
| header->DpiX = metafile->image.xres; |
| header->DpiY = metafile->image.yres; |
| header->Width = gdip_round(metafile->bounds.Width); |
| header->Height = gdip_round(metafile->bounds.Height); |
| |
| return Ok; |
| } |
| |
| static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, |
| int nObj, LPARAM lpData) |
| { |
| EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData; |
| |
| if (lpEMFR->iType == EMR_GDICOMMENT) |
| { |
| const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR; |
| |
| if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0) |
| { |
| const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4]; |
| |
| if (4 + sizeof(EmfPlusHeader) <= comment->cbData && |
| header->Type == EmfPlusRecordTypeHeader) |
| { |
| memcpy(dst_header, header, sizeof(*dst_header)); |
| } |
| } |
| } |
| else if (lpEMFR->iType == EMR_HEADER) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf, |
| MetafileHeader *header) |
| { |
| ENHMETAHEADER3 emfheader; |
| EmfPlusHeader emfplusheader; |
| MetafileType metafile_type; |
| |
| TRACE("(%p,%p)\n", hemf, header); |
| |
| if(!hemf || !header) |
| return InvalidParameter; |
| |
| if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0) |
| return GenericError; |
| |
| emfplusheader.Header.Type = 0; |
| |
| EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL); |
| |
| if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader) |
| { |
| if ((emfplusheader.Header.Flags & 1) == 1) |
| metafile_type = MetafileTypeEmfPlusDual; |
| else |
| metafile_type = MetafileTypeEmfPlusOnly; |
| } |
| else |
| metafile_type = MetafileTypeEmf; |
| |
| header->Type = metafile_type; |
| header->Size = emfheader.nBytes; |
| header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx; |
| header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy; |
| header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX); |
| header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY); |
| header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX); |
| header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY); |
| header->u.EmfHeader = emfheader; |
| |
| if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly) |
| { |
| header->Version = emfplusheader.Version; |
| header->EmfPlusFlags = emfplusheader.EmfPlusFlags; |
| header->EmfPlusHeaderSize = emfplusheader.Header.Size; |
| header->LogicalDpiX = emfplusheader.LogicalDpiX; |
| header->LogicalDpiY = emfplusheader.LogicalDpiY; |
| } |
| else |
| { |
| header->Version = emfheader.nVersion; |
| header->EmfPlusFlags = 0; |
| header->EmfPlusHeaderSize = 0; |
| header->LogicalDpiX = 0; |
| header->LogicalDpiY = 0; |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf, |
| GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header) |
| { |
| GpStatus status; |
| GpMetafile *metafile; |
| |
| TRACE("(%p,%p,%p)\n", hwmf, placeable, header); |
| |
| status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile); |
| if (status == Ok) |
| { |
| status = GdipGetMetafileHeaderFromMetafile(metafile, header); |
| GdipDisposeImage(&metafile->image); |
| } |
| return status; |
| } |
| |
| GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename, |
| MetafileHeader *header) |
| { |
| GpStatus status; |
| GpMetafile *metafile; |
| |
| TRACE("(%s,%p)\n", debugstr_w(filename), header); |
| |
| if (!filename || !header) |
| return InvalidParameter; |
| |
| status = GdipCreateMetafileFromFile(filename, &metafile); |
| if (status == Ok) |
| { |
| status = GdipGetMetafileHeaderFromMetafile(metafile, header); |
| GdipDisposeImage(&metafile->image); |
| } |
| return status; |
| } |
| |
| GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream, |
| MetafileHeader *header) |
| { |
| GpStatus status; |
| GpMetafile *metafile; |
| |
| TRACE("(%p,%p)\n", stream, header); |
| |
| if (!stream || !header) |
| return InvalidParameter; |
| |
| status = GdipCreateMetafileFromStream(stream, &metafile); |
| if (status == Ok) |
| { |
| status = GdipGetMetafileHeaderFromMetafile(metafile, header); |
| GdipDisposeImage(&metafile->image); |
| } |
| return status; |
| } |
| |
| GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete, |
| GpMetafile **metafile) |
| { |
| GpStatus stat; |
| MetafileHeader header; |
| |
| TRACE("(%p,%i,%p)\n", hemf, delete, metafile); |
| |
| if(!hemf || !metafile) |
| return InvalidParameter; |
| |
| stat = GdipGetMetafileHeaderFromEmf(hemf, &header); |
| if (stat != Ok) |
| return stat; |
| |
| *metafile = heap_alloc_zero(sizeof(GpMetafile)); |
| if (!*metafile) |
| return OutOfMemory; |
| |
| (*metafile)->image.type = ImageTypeMetafile; |
| (*metafile)->image.format = ImageFormatEMF; |
| (*metafile)->image.frame_count = 1; |
| (*metafile)->image.xres = header.DpiX; |
| (*metafile)->image.yres = header.DpiY; |
| (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX; |
| (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY; |
| (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left) |
| / 2540.0 * header.DpiX; |
| (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top) |
| / 2540.0 * header.DpiY; |
| (*metafile)->unit = UnitPixel; |
| (*metafile)->metafile_type = header.Type; |
| (*metafile)->hemf = hemf; |
| (*metafile)->preserve_hemf = !delete; |
| list_init(&(*metafile)->containers); |
| |
| TRACE("<-- %p\n", *metafile); |
| |
| return Ok; |
| } |
| |
| GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, |
| GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile) |
| { |
| UINT read; |
| BYTE *copy; |
| HENHMETAFILE hemf; |
| GpStatus retval = Ok; |
| |
| TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile); |
| |
| if(!hwmf || !metafile) |
| return InvalidParameter; |
| |
| *metafile = NULL; |
| read = GetMetaFileBitsEx(hwmf, 0, NULL); |
| if(!read) |
| return GenericError; |
| copy = heap_alloc_zero(read); |
| GetMetaFileBitsEx(hwmf, read, copy); |
| |
| hemf = SetWinMetaFileBits(read, copy, NULL, NULL); |
| heap_free(copy); |
| |
| /* FIXME: We should store and use hwmf instead of converting to hemf */ |
| retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile); |
| |
| if (retval == Ok) |
| { |
| if (placeable) |
| { |
| (*metafile)->image.xres = (REAL)placeable->Inch; |
| (*metafile)->image.yres = (REAL)placeable->Inch; |
| (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch); |
| (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch); |
| (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right - |
| placeable->BoundingBox.Left); |
| (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom - |
| placeable->BoundingBox.Top); |
| (*metafile)->metafile_type = MetafileTypeWmfPlaceable; |
| } |
| else |
| (*metafile)->metafile_type = MetafileTypeWmf; |
| (*metafile)->image.format = ImageFormatWMF; |
| |
| if (delete) DeleteMetaFile(hwmf); |
| } |
| else |
| DeleteEnhMetaFile(hemf); |
| return retval; |
| } |
| |
| GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file, |
| GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile) |
| { |
| HMETAFILE hmf; |
| HENHMETAFILE emf; |
| |
| TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile); |
| |
| hmf = GetMetaFileW(file); |
| if(hmf) |
| return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile); |
| |
| emf = GetEnhMetaFileW(file); |
| if(emf) |
| return GdipCreateMetafileFromEmf(emf, TRUE, metafile); |
| |
| return GenericError; |
| } |
| |
| GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file, |
| GpMetafile **metafile) |
| { |
| GpStatus status; |
| IStream *stream; |
| |
| TRACE("(%p, %p)\n", file, metafile); |
| |
| if (!file || !metafile) return InvalidParameter; |
| |
| *metafile = NULL; |
| |
| status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream); |
| if (status == Ok) |
| { |
| status = GdipCreateMetafileFromStream(stream, metafile); |
| IStream_Release(stream); |
| } |
| return status; |
| } |
| |
| GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream, |
| GpMetafile **metafile) |
| { |
| GpStatus stat; |
| |
| TRACE("%p %p\n", stream, metafile); |
| |
| stat = GdipLoadImageFromStream(stream, (GpImage **)metafile); |
| if (stat != Ok) return stat; |
| |
| if ((*metafile)->image.type != ImageTypeMetafile) |
| { |
| GdipDisposeImage(&(*metafile)->image); |
| *metafile = NULL; |
| return GenericError; |
| } |
| |
| return Ok; |
| } |
| |
| GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile, |
| UINT limitDpi) |
| { |
| TRACE("(%p,%u)\n", metafile, limitDpi); |
| |
| return Ok; |
| } |
| |
| GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref, |
| GpMetafile* metafile, BOOL* succ, EmfType emfType, |
| const WCHAR* description, GpMetafile** out_metafile) |
| { |
| static int calls; |
| |
| TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType, |
| debugstr_w(description), out_metafile); |
| |
| if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual) |
| return InvalidParameter; |
| |
| if(succ) |
| *succ = FALSE; |
| *out_metafile = NULL; |
| |
| if(!(calls++)) |
| FIXME("not implemented\n"); |
| |
| return NotImplemented; |
| } |
| |
| GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16, |
| LPBYTE pData16, INT iMapMode, INT eFlags) |
| { |
| FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags); |
| return NotImplemented; |
| } |
| |
| GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName, |
| HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect, |
| MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, |
| GpMetafile **metafile) |
| { |
| FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect, |
| frameUnit, debugstr_w(desc), metafile); |
| |
| return NotImplemented; |
| } |
| |
| GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type, |
| GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit, |
| GDIPCONST WCHAR *desc, GpMetafile **metafile) |
| { |
| FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect, |
| frameUnit, debugstr_w(desc), metafile); |
| |
| return NotImplemented; |
| } |
| |
| /***************************************************************************** |
| * GdipConvertToEmfPlusToFile [GDIPLUS.@] |
| */ |
| |
| GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics, |
| GpMetafile* metafile, BOOL* conversionSuccess, |
| const WCHAR* filename, EmfType emfType, |
| const WCHAR* description, GpMetafile** out_metafile) |
| { |
| FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile); |
| return NotImplemented; |
| } |
| |
| static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size) |
| { |
| LARGE_INTEGER zero; |
| STATSTG statstg; |
| GpStatus stat; |
| HRESULT hr; |
| |
| *size = 0; |
| |
| hr = CreateStreamOnHGlobal(NULL, TRUE, stream); |
| if (FAILED(hr)) return hresult_to_status(hr); |
| |
| stat = encode_image_png(image, *stream, NULL); |
| if (stat != Ok) |
| { |
| IStream_Release(*stream); |
| return stat; |
| } |
| |
| hr = IStream_Stat(*stream, &statstg, 1); |
| if (FAILED(hr)) |
| { |
| IStream_Release(*stream); |
| return hresult_to_status(hr); |
| } |
| *size = statstg.cbSize.u.LowPart; |
| |
| zero.QuadPart = 0; |
| hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL); |
| if (FAILED(hr)) |
| { |
| IStream_Release(*stream); |
| return hresult_to_status(hr); |
| } |
| |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size) |
| { |
| HRESULT hr; |
| |
| record->Width = 0; |
| record->Height = 0; |
| record->Stride = 0; |
| record->PixelFormat = 0; |
| record->Type = BitmapDataTypeCompressed; |
| |
| hr = IStream_Read(stream, record->BitmapData, size, NULL); |
| if (FAILED(hr)) return hresult_to_status(hr); |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id) |
| { |
| EmfPlusObject *object_record; |
| GpStatus stat; |
| DWORD size; |
| |
| *id = -1; |
| |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| if (image->type == ImageTypeBitmap) |
| { |
| IStream *stream; |
| DWORD aligned_size; |
| |
| stat = METAFILE_CreateCompressedImageStream(image, &stream, &size); |
| if (stat != Ok) return stat; |
| aligned_size = (size + 3) & ~3; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]), |
| (void**)&object_record); |
| if (stat != Ok) |
| { |
| IStream_Release(stream); |
| return stat; |
| } |
| memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size); |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | ObjectTypeImage << 8; |
| object_record->ObjectData.image.Version = VERSION_MAGIC2; |
| object_record->ObjectData.image.Type = ImageDataTypeBitmap; |
| |
| stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size); |
| IStream_Release(stream); |
| if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header); |
| return stat; |
| } |
| else if (image->type == ImageTypeMetafile) |
| { |
| HENHMETAFILE hemf = ((GpMetafile*)image)->hemf; |
| EmfPlusMetafile *metafile_record; |
| |
| if (!hemf) return InvalidParameter; |
| |
| size = GetEnhMetaFileBits(hemf, 0, NULL); |
| if (!size) return GenericError; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]), |
| (void**)&object_record); |
| if (stat != Ok) return stat; |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | ObjectTypeImage << 8; |
| object_record->ObjectData.image.Version = VERSION_MAGIC2; |
| object_record->ObjectData.image.Type = ImageDataTypeMetafile; |
| metafile_record = &object_record->ObjectData.image.ImageData.metafile; |
| metafile_record->Type = ((GpMetafile*)image)->metafile_type; |
| metafile_record->MetafileDataSize = size; |
| if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size) |
| { |
| METAFILE_RemoveLastRecord(metafile, &object_record->Header); |
| return GenericError; |
| } |
| return Ok; |
| } |
| else |
| { |
| FIXME("not supported image type (%d)\n", image->type); |
| return NotImplemented; |
| } |
| } |
| |
| static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id) |
| { |
| EmfPlusObject *object_record; |
| EmfPlusImageAttributes *attrs_record; |
| GpStatus stat; |
| |
| *id = -1; |
| |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| if (!attrs) |
| return Ok; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes), |
| (void**)&object_record); |
| if (stat != Ok) return stat; |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8); |
| attrs_record = &object_record->ObjectData.image_attributes; |
| attrs_record->Version = VERSION_MAGIC2; |
| attrs_record->Reserved1 = 0; |
| attrs_record->WrapMode = attrs->wrap; |
| attrs_record->ClampColor.Blue = attrs->outside_color & 0xff; |
| attrs_record->ClampColor.Green = (attrs->outside_color >> 8) & 0xff; |
| attrs_record->ClampColor.Red = (attrs->outside_color >> 16) & 0xff; |
| attrs_record->ClampColor.Alpha = attrs->outside_color >> 24; |
| attrs_record->ObjectClamp = attrs->clamp; |
| attrs_record->Reserved2 = 0; |
| return Ok; |
| } |
| |
| GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image, |
| GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth, |
| REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes, |
| DrawImageAbort callback, VOID *callbackData) |
| { |
| EmfPlusDrawImagePoints *draw_image_record; |
| DWORD image_id, attributes_id; |
| GpStatus stat; |
| |
| if (count != 3) return InvalidParameter; |
| |
| if (metafile->metafile_type == MetafileTypeEmf) |
| { |
| FIXME("MetafileTypeEmf metafiles not supported\n"); |
| return NotImplemented; |
| } |
| else |
| FIXME("semi-stub\n"); |
| |
| if (!imageAttributes) |
| { |
| stat = METAFILE_AddImageObject(metafile, image, &image_id); |
| } |
| else if (image->type == ImageTypeBitmap) |
| { |
| INT width = ((GpBitmap*)image)->width; |
| INT height = ((GpBitmap*)image)->height; |
| GpGraphics *graphics; |
| GpBitmap *bitmap; |
| |
| stat = GdipCreateBitmapFromScan0(width, height, |
| 0, PixelFormat32bppARGB, NULL, &bitmap); |
| if (stat != Ok) return stat; |
| |
| stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); |
| if (stat != Ok) |
| { |
| GdipDisposeImage((GpImage*)bitmap); |
| return stat; |
| } |
| |
| stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height, |
| 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL); |
| GdipDeleteGraphics(graphics); |
| if (stat != Ok) |
| { |
| GdipDisposeImage((GpImage*)bitmap); |
| return stat; |
| } |
| |
| stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id); |
| GdipDisposeImage((GpImage*)bitmap); |
| } |
| else |
| { |
| FIXME("imageAttributes not supported (image type %d)\n", image->type); |
| return NotImplemented; |
| } |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record); |
| if (stat != Ok) return stat; |
| draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints; |
| draw_image_record->Header.Flags = image_id; |
| draw_image_record->ImageAttributesID = attributes_id; |
| draw_image_record->SrcUnit = UnitPixel; |
| draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres); |
| draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres); |
| draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres); |
| draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres); |
| draw_image_record->count = 3; |
| draw_image_record->PointData[0].pointF.X = points[0].X; |
| draw_image_record->PointData[0].pointF.Y = points[0].Y; |
| draw_image_record->PointData[1].pointF.X = points[1].X; |
| draw_image_record->PointData[1].pointF.Y = points[1].Y; |
| draw_image_record->PointData[2].pointF.X = points[2].X; |
| draw_image_record->PointData[2].pointF.Y = points[2].Y; |
| METAFILE_WriteRecords(metafile); |
| return Ok; |
| } |
| |
| GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val) |
| { |
| EmfPlusRecordHeader *record; |
| GpStatus stat; |
| |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record); |
| if (stat != Ok) return stat; |
| |
| record->Type = prop; |
| record->Flags = val; |
| |
| METAFILE_WriteRecords(metafile); |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id) |
| { |
| EmfPlusObject *object_record; |
| GpStatus stat; |
| DWORD size; |
| |
| *id = -1; |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| size = write_path_data(path, NULL); |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size, |
| (void**)&object_record); |
| if (stat != Ok) return stat; |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | ObjectTypePath << 8; |
| write_path_data(path, &object_record->ObjectData.path); |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size) |
| { |
| if (brush->bt == BrushTypeSolidColor) |
| { |
| *size = FIELD_OFFSET(EmfPlusBrush, BrushData.solid) + sizeof(EmfPlusSolidBrushData); |
| return Ok; |
| } |
| |
| FIXME("unsupported brush type: %d\n", brush->bt); |
| return NotImplemented; |
| } |
| |
| static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data) |
| { |
| if (brush->bt == BrushTypeSolidColor) |
| { |
| GpSolidFill *solid = (GpSolidFill*)brush; |
| |
| data->Version = VERSION_MAGIC2; |
| data->Type = solid->brush.bt; |
| data->BrushData.solid.SolidColor.Blue = solid->color & 0xff; |
| data->BrushData.solid.SolidColor.Green = (solid->color >> 8) & 0xff; |
| data->BrushData.solid.SolidColor.Red = (solid->color >> 16) & 0xff; |
| data->BrushData.solid.SolidColor.Alpha = solid->color >> 24; |
| } |
| } |
| |
| static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id) |
| { |
| DWORD i, data_flags, pen_data_size, brush_size; |
| EmfPlusObject *object_record; |
| EmfPlusPenData *pen_data; |
| GpStatus stat; |
| BOOL result; |
| |
| *id = -1; |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| data_flags = 0; |
| pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData); |
| |
| GdipIsMatrixIdentity(&pen->transform, &result); |
| if (!result) |
| { |
| data_flags |= PenDataTransform; |
| pen_data_size += sizeof(EmfPlusTransformMatrix); |
| } |
| if (pen->startcap != LineCapFlat) |
| { |
| data_flags |= PenDataStartCap; |
| pen_data_size += sizeof(DWORD); |
| } |
| if (pen->endcap != LineCapFlat) |
| { |
| data_flags |= PenDataEndCap; |
| pen_data_size += sizeof(DWORD); |
| } |
| if (pen->join != LineJoinMiter) |
| { |
| data_flags |= PenDataJoin; |
| pen_data_size += sizeof(DWORD); |
| } |
| if (pen->miterlimit != 10.0) |
| { |
| data_flags |= PenDataMiterLimit; |
| pen_data_size += sizeof(REAL); |
| } |
| if (pen->style != GP_DEFAULT_PENSTYLE) |
| { |
| data_flags |= PenDataLineStyle; |
| pen_data_size += sizeof(DWORD); |
| } |
| if (pen->dashcap != DashCapFlat) |
| { |
| data_flags |= PenDataDashedLineCap; |
| pen_data_size += sizeof(DWORD); |
| } |
| data_flags |= PenDataDashedLineOffset; |
| pen_data_size += sizeof(REAL); |
| if (pen->numdashes) |
| { |
| data_flags |= PenDataDashedLine; |
| pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL); |
| } |
| if (pen->align != PenAlignmentCenter) |
| { |
| data_flags |= PenDataNonCenter; |
| pen_data_size += sizeof(DWORD); |
| } |
| /* TODO: Add support for PenDataCompoundLine */ |
| if (pen->customstart) |
| { |
| FIXME("ignoring custom start cup\n"); |
| } |
| if (pen->customend) |
| { |
| FIXME("ignoring custom end cup\n"); |
| } |
| |
| stat = METAFILE_PrepareBrushData(pen->brush, &brush_size); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size, |
| (void**)&object_record); |
| if (stat != Ok) return stat; |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | ObjectTypePen << 8; |
| object_record->ObjectData.pen.Version = VERSION_MAGIC2; |
| object_record->ObjectData.pen.Type = 0; |
| |
| pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data; |
| pen_data->PenDataFlags = data_flags; |
| pen_data->PenUnit = pen->unit; |
| pen_data->PenWidth = pen->width; |
| |
| i = 0; |
| if (data_flags & PenDataTransform) |
| { |
| EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i); |
| memcpy(m, &pen->transform, sizeof(*m)); |
| i += sizeof(EmfPlusTransformMatrix); |
| } |
| if (data_flags & PenDataStartCap) |
| { |
| *(DWORD*)(pen_data->OptionalData + i) = pen->startcap; |
| i += sizeof(DWORD); |
| } |
| if (data_flags & PenDataEndCap) |
| { |
| *(DWORD*)(pen_data->OptionalData + i) = pen->endcap; |
| i += sizeof(DWORD); |
| } |
| if (data_flags & PenDataJoin) |
| { |
| *(DWORD*)(pen_data->OptionalData + i) = pen->join; |
| i += sizeof(DWORD); |
| } |
| if (data_flags & PenDataMiterLimit) |
| { |
| *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit; |
| i += sizeof(REAL); |
| } |
| if (data_flags & PenDataLineStyle) |
| { |
| switch (pen->style & PS_STYLE_MASK) |
| { |
| case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break; |
| case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break; |
| case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break; |
| case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break; |
| case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break; |
| default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break; |
| } |
| i += sizeof(DWORD); |
| } |
| if (data_flags & PenDataDashedLineCap) |
| { |
| *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap; |
| i += sizeof(DWORD); |
| } |
| if (data_flags & PenDataDashedLineOffset) |
| { |
| *(REAL*)(pen_data->OptionalData + i) = pen->offset; |
| i += sizeof(REAL); |
| } |
| if (data_flags & PenDataDashedLine) |
| { |
| int j; |
| |
| *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes; |
| i += sizeof(DWORD); |
| |
| for (j=0; j<pen->numdashes; j++) |
| { |
| *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j]; |
| i += sizeof(REAL); |
| } |
| } |
| if (data_flags & PenDataNonCenter) |
| { |
| *(REAL*)(pen_data->OptionalData + i) = pen->align; |
| i += sizeof(DWORD); |
| } |
| |
| METAFILE_FillBrushData(pen->brush, |
| (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size)); |
| return Ok; |
| } |
| |
| GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path) |
| { |
| EmfPlusDrawPath *draw_path_record; |
| DWORD path_id; |
| DWORD pen_id; |
| GpStatus stat; |
| |
| if (metafile->metafile_type == MetafileTypeEmf) |
| { |
| FIXME("stub!\n"); |
| return NotImplemented; |
| } |
| |
| stat = METAFILE_AddPenObject(metafile, pen, &pen_id); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AddPathObject(metafile, path, &path_id); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record); |
| if (stat != Ok) return stat; |
| draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath; |
| draw_path_record->Header.Flags = path_id; |
| draw_path_record->PenId = pen_id; |
| |
| METAFILE_WriteRecords(metafile); |
| return Ok; |
| } |
| |
| static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id) |
| { |
| EmfPlusObject *object_record; |
| GpStatus stat; |
| DWORD size; |
| |
| *id = -1; |
| if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual) |
| return Ok; |
| |
| stat = METAFILE_PrepareBrushData(brush, &size); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record); |
| if (stat != Ok) return stat; |
| |
| *id = METAFILE_AddObjectId(metafile); |
| object_record->Header.Type = EmfPlusRecordTypeObject; |
| object_record->Header.Flags = *id | ObjectTypeBrush << 8; |
| METAFILE_FillBrushData(brush, &object_record->ObjectData.brush); |
| return Ok; |
| } |
| |
| GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path) |
| { |
| EmfPlusFillPath *fill_path_record; |
| DWORD brush_id = -1, path_id; |
| BOOL inline_color; |
| GpStatus stat; |
| |
| if (metafile->metafile_type == MetafileTypeEmf) |
| { |
| FIXME("stub!\n"); |
| return NotImplemented; |
| } |
| |
| inline_color = brush->bt == BrushTypeSolidColor; |
| if (!inline_color) |
| { |
| stat = METAFILE_AddBrushObject(metafile, brush, &brush_id); |
| if (stat != Ok) return stat; |
| } |
| |
| stat = METAFILE_AddPathObject(metafile, path, &path_id); |
| if (stat != Ok) return stat; |
| |
| stat = METAFILE_AllocateRecord(metafile, |
| sizeof(EmfPlusFillPath), (void**)&fill_path_record); |
| if (stat != Ok) return stat; |
| fill_path_record->Header.Type = EmfPlusRecordTypeFillPath; |
| if (inline_color) |
| { |
| fill_path_record->Header.Flags = 0x8000 | path_id; |
| fill_path_record->data.Color.Blue = ((GpSolidFill*)brush)->color & 0xff; |
| fill_path_record->data.Color.Green = (((GpSolidFill*)brush)->color >> 8) & 0xff; |
| fill_path_record->data.Color.Red = (((GpSolidFill*)brush)->color >> 16) & 0xff; |
| fill_path_record->data.Color.Alpha = ((GpSolidFill*)brush)->color >> 24; |
| } |
| else |
| { |
| fill_path_record->Header.Flags = path_id; |
| fill_path_record->data.BrushId = brush_id; |
| } |
| |
| METAFILE_WriteRecords(metafile); |
| return Ok; |
| } |