4578 lines
140 KiB
C
4578 lines
140 KiB
C
/*
|
|
* 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"
|
|
|
|
#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);
|
|
|
|
HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
|
|
|
|
typedef ARGB 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 EmfPlusDashedLineData
|
|
{
|
|
DWORD DashedLineDataSize;
|
|
BYTE data[1];
|
|
} EmfPlusDashedLineData;
|
|
|
|
typedef struct EmfPlusCompoundLineData
|
|
{
|
|
DWORD CompoundLineDataSize;
|
|
BYTE data[1];
|
|
} EmfPlusCompoundLineData;
|
|
|
|
typedef struct EmfPlusCustomStartCapData
|
|
{
|
|
DWORD CustomStartCapSize;
|
|
BYTE data[1];
|
|
} EmfPlusCustomStartCapData;
|
|
|
|
typedef struct EmfPlusCustomEndCapData
|
|
{
|
|
DWORD CustomEndCapSize;
|
|
BYTE data[1];
|
|
} EmfPlusCustomEndCapData;
|
|
|
|
typedef struct EmfPlusPenData
|
|
{
|
|
DWORD PenDataFlags;
|
|
DWORD PenUnit;
|
|
REAL PenWidth;
|
|
BYTE OptionalData[1];
|
|
} EmfPlusPenData;
|
|
|
|
enum BrushDataFlags
|
|
{
|
|
BrushDataPath = 1 << 0,
|
|
BrushDataTransform = 1 << 1,
|
|
BrushDataPresetColors = 1 << 2,
|
|
BrushDataBlendFactorsH = 1 << 3,
|
|
BrushDataBlendFactorsV = 1 << 4,
|
|
BrushDataFocusScales = 1 << 6,
|
|
BrushDataIsGammaCorrected = 1 << 7,
|
|
BrushDataDoNotTransform = 1 << 8,
|
|
};
|
|
|
|
typedef struct EmfPlusSolidBrushData
|
|
{
|
|
EmfPlusARGB SolidColor;
|
|
} EmfPlusSolidBrushData;
|
|
|
|
typedef struct EmfPlusHatchBrushData
|
|
{
|
|
DWORD HatchStyle;
|
|
EmfPlusARGB ForeColor;
|
|
EmfPlusARGB BackColor;
|
|
} EmfPlusHatchBrushData;
|
|
|
|
typedef struct EmfPlusTextureBrushData
|
|
{
|
|
DWORD BrushDataFlags;
|
|
INT WrapMode;
|
|
BYTE OptionalData[1];
|
|
} EmfPlusTextureBrushData;
|
|
|
|
typedef struct EmfPlusRectF
|
|
{
|
|
float X;
|
|
float Y;
|
|
float Width;
|
|
float Height;
|
|
} EmfPlusRectF;
|
|
|
|
typedef struct EmfPlusLinearGradientBrushData
|
|
{
|
|
DWORD BrushDataFlags;
|
|
INT WrapMode;
|
|
EmfPlusRectF RectF;
|
|
EmfPlusARGB StartColor;
|
|
EmfPlusARGB EndColor;
|
|
DWORD Reserved1;
|
|
DWORD Reserved2;
|
|
BYTE OptionalData[1];
|
|
} EmfPlusLinearGradientBrushData;
|
|
|
|
typedef struct EmfPlusBrush
|
|
{
|
|
DWORD Version;
|
|
DWORD Type;
|
|
union {
|
|
EmfPlusSolidBrushData solid;
|
|
EmfPlusHatchBrushData hatch;
|
|
EmfPlusTextureBrushData texture;
|
|
EmfPlusLinearGradientBrushData lineargradient;
|
|
} 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 EmfPlusRegionNodePath
|
|
{
|
|
DWORD RegionNodePathLength;
|
|
EmfPlusPath RegionNodePath;
|
|
} EmfPlusRegionNodePath;
|
|
|
|
typedef struct EmfPlusRegion
|
|
{
|
|
DWORD Version;
|
|
DWORD RegionNodeCount;
|
|
BYTE RegionNode[1];
|
|
} EmfPlusRegion;
|
|
|
|
typedef struct EmfPlusPalette
|
|
{
|
|
DWORD PaletteStyleFlags;
|
|
DWORD PaletteCount;
|
|
BYTE PaletteEntries[1];
|
|
} EmfPlusPalette;
|
|
|
|
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 struct EmfPlusObject
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
union
|
|
{
|
|
EmfPlusBrush brush;
|
|
EmfPlusPen pen;
|
|
EmfPlusPath path;
|
|
EmfPlusRegion region;
|
|
EmfPlusImage image;
|
|
EmfPlusImageAttributes image_attributes;
|
|
} ObjectData;
|
|
} EmfPlusObject;
|
|
|
|
typedef struct EmfPlusPointR7
|
|
{
|
|
BYTE X;
|
|
BYTE Y;
|
|
} EmfPlusPointR7;
|
|
|
|
typedef struct EmfPlusPoint
|
|
{
|
|
short X;
|
|
short Y;
|
|
} EmfPlusPoint;
|
|
|
|
typedef struct EmfPlusPointF
|
|
{
|
|
float X;
|
|
float Y;
|
|
} EmfPlusPointF;
|
|
|
|
typedef struct EmfPlusDrawImage
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD ImageAttributesID;
|
|
DWORD SrcUnit;
|
|
EmfPlusRectF SrcRect;
|
|
union
|
|
{
|
|
EmfPlusRect rect;
|
|
EmfPlusRectF rectF;
|
|
} RectData;
|
|
} EmfPlusDrawImage;
|
|
|
|
typedef struct EmfPlusDrawImagePoints
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD ImageAttributesID;
|
|
DWORD SrcUnit;
|
|
EmfPlusRectF SrcRect;
|
|
DWORD count;
|
|
union
|
|
{
|
|
EmfPlusPointR7 pointsR[3];
|
|
EmfPlusPoint points[3];
|
|
EmfPlusPointF pointsF[3];
|
|
} PointData;
|
|
} EmfPlusDrawImagePoints;
|
|
|
|
typedef struct EmfPlusDrawPath
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD PenId;
|
|
} EmfPlusDrawPath;
|
|
|
|
typedef struct EmfPlusDrawArc
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
float StartAngle;
|
|
float SweepAngle;
|
|
union
|
|
{
|
|
EmfPlusRect rect;
|
|
EmfPlusRectF rectF;
|
|
} RectData;
|
|
} EmfPlusDrawArc;
|
|
|
|
typedef struct EmfPlusDrawEllipse
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
union
|
|
{
|
|
EmfPlusRect rect;
|
|
EmfPlusRectF rectF;
|
|
} RectData;
|
|
} EmfPlusDrawEllipse;
|
|
|
|
typedef struct EmfPlusDrawPie
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
float StartAngle;
|
|
float SweepAngle;
|
|
union
|
|
{
|
|
EmfPlusRect rect;
|
|
EmfPlusRectF rectF;
|
|
} RectData;
|
|
} EmfPlusDrawPie;
|
|
|
|
typedef struct EmfPlusDrawRects
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD Count;
|
|
union
|
|
{
|
|
EmfPlusRect rect[1];
|
|
EmfPlusRectF rectF[1];
|
|
} RectData;
|
|
} EmfPlusDrawRects;
|
|
|
|
typedef struct EmfPlusFillPath
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
union
|
|
{
|
|
DWORD BrushId;
|
|
EmfPlusARGB Color;
|
|
} data;
|
|
} EmfPlusFillPath;
|
|
|
|
typedef struct EmfPlusFillClosedCurve
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD BrushId;
|
|
float Tension;
|
|
DWORD Count;
|
|
union
|
|
{
|
|
EmfPlusPointR7 pointsR[1];
|
|
EmfPlusPoint points[1];
|
|
EmfPlusPointF pointsF[1];
|
|
} PointData;
|
|
} EmfPlusFillClosedCurve;
|
|
|
|
typedef struct EmfPlusFillEllipse
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD BrushId;
|
|
union
|
|
{
|
|
EmfPlusRect rect;
|
|
EmfPlusRectF rectF;
|
|
} RectData;
|
|
} EmfPlusFillEllipse;
|
|
|
|
typedef struct EmfPlusFillPie
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD BrushId;
|
|
float StartAngle;
|
|
float SweepAngle;
|
|
union
|
|
{
|
|
EmfPlusRect rect;
|
|
EmfPlusRectF rectF;
|
|
} RectData;
|
|
} EmfPlusFillPie;
|
|
|
|
typedef struct EmfPlusFont
|
|
{
|
|
DWORD Version;
|
|
float EmSize;
|
|
DWORD SizeUnit;
|
|
DWORD FontStyleFlags;
|
|
DWORD Reserved;
|
|
DWORD Length;
|
|
WCHAR FamilyName[1];
|
|
} EmfPlusFont;
|
|
|
|
static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
|
|
{
|
|
struct emfplus_object *object = &metafile->objtable[id];
|
|
|
|
switch (object->type)
|
|
{
|
|
case ObjectTypeInvalid:
|
|
break;
|
|
case ObjectTypeBrush:
|
|
GdipDeleteBrush(object->u.brush);
|
|
break;
|
|
case ObjectTypePen:
|
|
GdipDeletePen(object->u.pen);
|
|
break;
|
|
case ObjectTypePath:
|
|
GdipDeletePath(object->u.path);
|
|
break;
|
|
case ObjectTypeRegion:
|
|
GdipDeleteRegion(object->u.region);
|
|
break;
|
|
case ObjectTypeImage:
|
|
GdipDisposeImage(object->u.image);
|
|
break;
|
|
case ObjectTypeFont:
|
|
GdipDeleteFont(object->u.font);
|
|
break;
|
|
case ObjectTypeImageAttributes:
|
|
GdipDisposeImageAttributes(object->u.image_attributes);
|
|
break;
|
|
default:
|
|
FIXME("not implemented for object type %u.\n", object->type);
|
|
return;
|
|
}
|
|
|
|
object->type = ObjectTypeInvalid;
|
|
object->u.object = NULL;
|
|
}
|
|
|
|
void METAFILE_Free(GpMetafile *metafile)
|
|
{
|
|
unsigned int i;
|
|
|
|
heap_free(metafile->comment_data);
|
|
DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
|
|
if (!metafile->preserve_hemf)
|
|
DeleteEnhMetaFile(metafile->hemf);
|
|
if (metafile->record_graphics)
|
|
{
|
|
WARN("metafile closed while recording\n");
|
|
/* not sure what to do here; for now just prevent the graphics from functioning or using this object */
|
|
metafile->record_graphics->image = NULL;
|
|
metafile->record_graphics->busy = TRUE;
|
|
}
|
|
|
|
if (metafile->record_stream)
|
|
IStream_Release(metafile->record_stream);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
|
|
metafile_free_object_table_entry(metafile, i);
|
|
}
|
|
|
|
static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
|
|
{
|
|
return (metafile->next_object_id++) % EmfPlusObjectTableSize;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
|
|
{
|
|
switch (brush->bt)
|
|
{
|
|
case BrushTypeSolidColor:
|
|
*size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
|
|
break;
|
|
case BrushTypeHatchFill:
|
|
*size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
|
|
break;
|
|
default:
|
|
FIXME("unsupported brush type: %d\n", brush->bt);
|
|
return NotImplemented;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
|
|
{
|
|
data->Version = VERSION_MAGIC2;
|
|
data->Type = brush->bt;
|
|
|
|
switch (brush->bt)
|
|
{
|
|
case BrushTypeSolidColor:
|
|
{
|
|
GpSolidFill *solid = (GpSolidFill *)brush;
|
|
data->BrushData.solid.SolidColor = solid->color;
|
|
break;
|
|
}
|
|
case BrushTypeHatchFill:
|
|
{
|
|
GpHatch *hatch = (GpHatch *)brush;
|
|
data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
|
|
data->BrushData.hatch.ForeColor = hatch->forecol;
|
|
data->BrushData.hatch.BackColor = hatch->backcol;
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("unsupported brush type: %d\n", brush->bt);
|
|
}
|
|
}
|
|
|
|
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_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
|
|
{
|
|
stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
|
|
if (stat != Ok)
|
|
return stat;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
|
|
{
|
|
metafile_free_object_table_entry(metafile, id);
|
|
metafile->objtable[id].type = type;
|
|
metafile->objtable[id].u.object = object;
|
|
}
|
|
|
|
static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
|
|
{
|
|
EmfPlusImage *data = (EmfPlusImage *)record_data;
|
|
GpStatus status;
|
|
|
|
*image = NULL;
|
|
|
|
if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
|
|
return InvalidParameter;
|
|
data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
|
|
|
|
switch (data->Type)
|
|
{
|
|
case ImageDataTypeBitmap:
|
|
{
|
|
EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
|
|
|
|
if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
|
|
return InvalidParameter;
|
|
data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
|
|
|
|
switch (bitmapdata->Type)
|
|
{
|
|
case BitmapDataTypePixel:
|
|
{
|
|
ColorPalette *palette;
|
|
BYTE *scan0;
|
|
|
|
if (bitmapdata->PixelFormat & PixelFormatIndexed)
|
|
{
|
|
EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
|
|
UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
|
|
|
|
if (data_size <= palette_size)
|
|
return InvalidParameter;
|
|
palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
|
|
|
|
if (data_size < palette_size)
|
|
return InvalidParameter;
|
|
data_size -= palette_size;
|
|
|
|
palette = (ColorPalette *)bitmapdata->BitmapData;
|
|
scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
|
|
}
|
|
else
|
|
{
|
|
palette = NULL;
|
|
scan0 = bitmapdata->BitmapData;
|
|
}
|
|
|
|
if (data_size < bitmapdata->Height * bitmapdata->Stride)
|
|
return InvalidParameter;
|
|
|
|
status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
|
|
bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
|
|
if (status == Ok && palette)
|
|
{
|
|
status = GdipSetImagePalette(*image, palette);
|
|
if (status != Ok)
|
|
{
|
|
GdipDisposeImage(*image);
|
|
*image = NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case BitmapDataTypeCompressed:
|
|
{
|
|
IWICImagingFactory *factory;
|
|
IWICStream *stream;
|
|
HRESULT hr;
|
|
|
|
if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
|
|
return GenericError;
|
|
|
|
hr = IWICImagingFactory_CreateStream(factory, &stream);
|
|
IWICImagingFactory_Release(factory);
|
|
if (hr != S_OK)
|
|
return GenericError;
|
|
|
|
if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
|
|
status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
|
|
else
|
|
status = GenericError;
|
|
|
|
IWICStream_Release(stream);
|
|
break;
|
|
}
|
|
default:
|
|
WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
|
|
return InvalidParameter;
|
|
}
|
|
break;
|
|
}
|
|
case ImageDataTypeMetafile:
|
|
{
|
|
EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
|
|
|
|
if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
|
|
return InvalidParameter;
|
|
data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
|
|
|
|
switch (metafiledata->Type) {
|
|
case MetafileTypeEmf:
|
|
case MetafileTypeEmfPlusOnly:
|
|
case MetafileTypeEmfPlusDual:
|
|
{
|
|
HENHMETAFILE hemf;
|
|
|
|
hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
|
|
|
|
if (!hemf)
|
|
return GenericError;
|
|
|
|
status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
|
|
|
|
if (status != Ok)
|
|
DeleteEnhMetaFile(hemf);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("metafile type %d not supported.\n", metafiledata->Type);
|
|
return NotImplemented;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("image type %d not supported.\n", data->Type);
|
|
return NotImplemented;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
|
|
{
|
|
EmfPlusPath *data = (EmfPlusPath *)record_data;
|
|
GpStatus status;
|
|
BYTE *types;
|
|
UINT size;
|
|
DWORD i;
|
|
|
|
*path = NULL;
|
|
|
|
if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
|
|
return InvalidParameter;
|
|
data_size -= FIELD_OFFSET(EmfPlusPath, data);
|
|
|
|
if (data->PathPointFlags & 0x800) /* R */
|
|
{
|
|
FIXME("RLE encoded path data is not supported.\n");
|
|
return NotImplemented;
|
|
}
|
|
else
|
|
{
|
|
if (data->PathPointFlags & 0x4000) /* C */
|
|
size = sizeof(EmfPlusPoint);
|
|
else
|
|
size = sizeof(EmfPlusPointF);
|
|
size += sizeof(BYTE); /* EmfPlusPathPointType */
|
|
size *= data->PathPointCount;
|
|
}
|
|
|
|
if (data_size < size)
|
|
return InvalidParameter;
|
|
|
|
status = GdipCreatePath(FillModeAlternate, path);
|
|
if (status != Ok)
|
|
return status;
|
|
|
|
(*path)->pathdata.Count = data->PathPointCount;
|
|
(*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
|
|
(*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
|
|
(*path)->datalen = (*path)->pathdata.Count;
|
|
|
|
if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
|
|
{
|
|
GdipDeletePath(*path);
|
|
return OutOfMemory;
|
|
}
|
|
|
|
if (data->PathPointFlags & 0x4000) /* C */
|
|
{
|
|
EmfPlusPoint *points = (EmfPlusPoint *)data->data;
|
|
for (i = 0; i < data->PathPointCount; i++)
|
|
{
|
|
(*path)->pathdata.Points[i].X = points[i].X;
|
|
(*path)->pathdata.Points[i].Y = points[i].Y;
|
|
}
|
|
types = (BYTE *)(points + i);
|
|
}
|
|
else
|
|
{
|
|
EmfPlusPointF *points = (EmfPlusPointF *)data->data;
|
|
memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
|
|
types = (BYTE *)(points + data->PathPointCount);
|
|
}
|
|
|
|
memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
|
|
|
|
return Ok;
|
|
}
|
|
|
|
static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
|
|
{
|
|
const DWORD *type;
|
|
GpStatus status;
|
|
|
|
type = buffer_read(mbuf, sizeof(*type));
|
|
if (!type) return Ok;
|
|
|
|
node->type = *type;
|
|
|
|
switch (node->type)
|
|
{
|
|
case CombineModeReplace:
|
|
case CombineModeIntersect:
|
|
case CombineModeUnion:
|
|
case CombineModeXor:
|
|
case CombineModeExclude:
|
|
case CombineModeComplement:
|
|
{
|
|
region_element *left, *right;
|
|
|
|
left = heap_alloc_zero(sizeof(*left));
|
|
if (!left)
|
|
return OutOfMemory;
|
|
|
|
right = heap_alloc_zero(sizeof(*right));
|
|
if (!right)
|
|
{
|
|
heap_free(left);
|
|
return OutOfMemory;
|
|
}
|
|
|
|
status = metafile_read_region_node(mbuf, region, left, count);
|
|
if (status == Ok)
|
|
{
|
|
status = metafile_read_region_node(mbuf, region, right, count);
|
|
if (status == Ok)
|
|
{
|
|
node->elementdata.combine.left = left;
|
|
node->elementdata.combine.right = right;
|
|
region->num_children += 2;
|
|
return Ok;
|
|
}
|
|
}
|
|
|
|
heap_free(left);
|
|
heap_free(right);
|
|
return status;
|
|
}
|
|
case RegionDataRect:
|
|
{
|
|
const EmfPlusRectF *rect;
|
|
|
|
rect = buffer_read(mbuf, sizeof(*rect));
|
|
if (!rect)
|
|
return InvalidParameter;
|
|
|
|
memcpy(&node->elementdata.rect, rect, sizeof(*rect));
|
|
*count += 1;
|
|
return Ok;
|
|
}
|
|
case RegionDataPath:
|
|
{
|
|
const BYTE *path_data;
|
|
const UINT *data_size;
|
|
GpPath *path;
|
|
|
|
data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
|
|
if (!data_size)
|
|
return InvalidParameter;
|
|
|
|
path_data = buffer_read(mbuf, *data_size);
|
|
if (!path_data)
|
|
return InvalidParameter;
|
|
|
|
status = metafile_deserialize_path(path_data, *data_size, &path);
|
|
if (status == Ok)
|
|
{
|
|
node->elementdata.path = path;
|
|
*count += 1;
|
|
}
|
|
return Ok;
|
|
}
|
|
case RegionDataEmptyRect:
|
|
case RegionDataInfiniteRect:
|
|
*count += 1;
|
|
return Ok;
|
|
default:
|
|
FIXME("element type %#x is not supported\n", *type);
|
|
break;
|
|
}
|
|
|
|
return InvalidParameter;
|
|
}
|
|
|
|
static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
|
|
{
|
|
struct memory_buffer mbuf;
|
|
GpStatus status;
|
|
UINT count;
|
|
|
|
*region = NULL;
|
|
|
|
init_memory_buffer(&mbuf, record_data, data_size);
|
|
|
|
if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
|
|
return InvalidParameter;
|
|
|
|
status = GdipCreateRegion(region);
|
|
if (status != Ok)
|
|
return status;
|
|
|
|
count = 0;
|
|
status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
|
|
if (status == Ok && !count)
|
|
status = InvalidParameter;
|
|
|
|
if (status != Ok)
|
|
{
|
|
GdipDeleteRegion(*region);
|
|
*region = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
|
|
{
|
|
static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
|
|
EmfPlusBrush *data = (EmfPlusBrush *)record_data;
|
|
EmfPlusTransformMatrix *transform = NULL;
|
|
DWORD brushflags;
|
|
GpStatus status;
|
|
UINT offset;
|
|
|
|
*brush = NULL;
|
|
|
|
if (data_size < header_size)
|
|
return InvalidParameter;
|
|
|
|
switch (data->Type)
|
|
{
|
|
case BrushTypeSolidColor:
|
|
if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
|
|
return InvalidParameter;
|
|
|
|
status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
|
|
break;
|
|
case BrushTypeHatchFill:
|
|
if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
|
|
return InvalidParameter;
|
|
|
|
status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
|
|
data->BrushData.hatch.BackColor, (GpHatch **)brush);
|
|
break;
|
|
case BrushTypeTextureFill:
|
|
{
|
|
GpImage *image;
|
|
|
|
offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
brushflags = data->BrushData.texture.BrushDataFlags;
|
|
if (brushflags & BrushDataTransform)
|
|
{
|
|
if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
|
|
return InvalidParameter;
|
|
transform = (EmfPlusTransformMatrix *)(record_data + offset);
|
|
offset += sizeof(EmfPlusTransformMatrix);
|
|
}
|
|
|
|
status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
|
|
if (status != Ok)
|
|
return status;
|
|
|
|
status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
|
|
if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
|
|
GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
|
|
|
|
GdipDisposeImage(image);
|
|
break;
|
|
}
|
|
case BrushTypeLinearGradient:
|
|
{
|
|
GpLineGradient *gradient = NULL;
|
|
GpPointF startpoint, endpoint;
|
|
UINT position_count = 0;
|
|
|
|
offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
brushflags = data->BrushData.lineargradient.BrushDataFlags;
|
|
if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
|
|
return InvalidParameter;
|
|
|
|
if (brushflags & BrushDataTransform)
|
|
{
|
|
if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
|
|
return InvalidParameter;
|
|
transform = (EmfPlusTransformMatrix *)(record_data + offset);
|
|
offset += sizeof(EmfPlusTransformMatrix);
|
|
}
|
|
|
|
if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
|
|
{
|
|
if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
|
|
return InvalidParameter;
|
|
position_count = *(DWORD *)(record_data + offset);
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (brushflags & BrushDataPresetColors)
|
|
{
|
|
if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
|
|
return InvalidParameter;
|
|
}
|
|
else if (brushflags & BrushDataBlendFactorsH)
|
|
{
|
|
if (data_size != offset + position_count * 2 * sizeof(float))
|
|
return InvalidParameter;
|
|
}
|
|
|
|
startpoint.X = data->BrushData.lineargradient.RectF.X;
|
|
startpoint.Y = data->BrushData.lineargradient.RectF.Y;
|
|
endpoint.X = startpoint.X + data->BrushData.lineargradient.RectF.Width;
|
|
endpoint.Y = startpoint.Y + data->BrushData.lineargradient.RectF.Height;
|
|
|
|
status = GdipCreateLineBrush(&startpoint, &endpoint, data->BrushData.lineargradient.StartColor,
|
|
data->BrushData.lineargradient.EndColor, data->BrushData.lineargradient.WrapMode, &gradient);
|
|
if (status == Ok)
|
|
{
|
|
if (transform)
|
|
status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
|
|
|
|
if (status == Ok)
|
|
{
|
|
if (brushflags & BrushDataPresetColors)
|
|
status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
|
|
position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
|
|
else if (brushflags & BrushDataBlendFactorsH)
|
|
status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
|
|
(REAL *)(record_data + offset), position_count);
|
|
|
|
if (brushflags & BrushDataIsGammaCorrected)
|
|
FIXME("BrushDataIsGammaCorrected is not handled.\n");
|
|
}
|
|
}
|
|
|
|
if (status == Ok)
|
|
*brush = (GpBrush *)gradient;
|
|
else
|
|
GdipDeleteBrush((GpBrush *)gradient);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("brush type %u is not supported.\n", data->Type);
|
|
return NotImplemented;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
|
|
{
|
|
EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
|
|
DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
|
|
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
if (pendata->PenDataFlags & PenDataTransform)
|
|
offset += sizeof(EmfPlusTransformMatrix);
|
|
|
|
if (pendata->PenDataFlags & PenDataStartCap)
|
|
offset += sizeof(DWORD);
|
|
|
|
if (pendata->PenDataFlags & PenDataEndCap)
|
|
offset += sizeof(DWORD);
|
|
|
|
if (pendata->PenDataFlags & PenDataJoin)
|
|
offset += sizeof(DWORD);
|
|
|
|
if (pendata->PenDataFlags & PenDataMiterLimit)
|
|
offset += sizeof(REAL);
|
|
|
|
if (pendata->PenDataFlags & PenDataLineStyle)
|
|
offset += sizeof(DWORD);
|
|
|
|
if (pendata->PenDataFlags & PenDataDashedLineCap)
|
|
offset += sizeof(DWORD);
|
|
|
|
if (pendata->PenDataFlags & PenDataDashedLineOffset)
|
|
offset += sizeof(REAL);
|
|
|
|
if (pendata->PenDataFlags & PenDataDashedLine)
|
|
{
|
|
EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
|
|
|
|
offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
offset += dashedline->DashedLineDataSize * sizeof(float);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataNonCenter)
|
|
offset += sizeof(DWORD);
|
|
|
|
if (pendata->PenDataFlags & PenDataCompoundLine)
|
|
{
|
|
EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
|
|
|
|
offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
offset += compoundline->CompoundLineDataSize * sizeof(float);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataCustomStartCap)
|
|
{
|
|
EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
|
|
|
|
offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
offset += startcap->CustomStartCapSize;
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataCustomEndCap)
|
|
{
|
|
EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
|
|
|
|
offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
|
|
if (data_size <= offset)
|
|
return InvalidParameter;
|
|
|
|
offset += endcap->CustomEndCapSize;
|
|
}
|
|
|
|
*ret = offset;
|
|
return Ok;
|
|
}
|
|
|
|
static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
|
|
{
|
|
BYTE type = (flags >> 8) & 0xff;
|
|
BYTE id = flags & 0xff;
|
|
void *object = NULL;
|
|
GpStatus status;
|
|
|
|
if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
|
|
return InvalidParameter;
|
|
|
|
switch (type)
|
|
{
|
|
case ObjectTypeBrush:
|
|
status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
|
|
break;
|
|
case ObjectTypePen:
|
|
{
|
|
EmfPlusPen *data = (EmfPlusPen *)record_data;
|
|
EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
|
|
GpBrush *brush;
|
|
DWORD offset;
|
|
GpPen *pen;
|
|
|
|
status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
|
|
if (status != Ok)
|
|
return status;
|
|
|
|
status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
|
|
if (status != Ok)
|
|
return status;
|
|
|
|
status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
|
|
GdipDeleteBrush(brush);
|
|
if (status != Ok)
|
|
return status;
|
|
|
|
offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
|
|
|
|
if (pendata->PenDataFlags & PenDataTransform)
|
|
{
|
|
FIXME("PenDataTransform is not supported.\n");
|
|
offset += sizeof(EmfPlusTransformMatrix);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataStartCap)
|
|
{
|
|
if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
|
|
goto penfailed;
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataEndCap)
|
|
{
|
|
if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
|
|
goto penfailed;
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataJoin)
|
|
{
|
|
if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
|
|
goto penfailed;
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataMiterLimit)
|
|
{
|
|
if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
|
|
goto penfailed;
|
|
offset += sizeof(REAL);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataLineStyle)
|
|
{
|
|
if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
|
|
goto penfailed;
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataDashedLineCap)
|
|
{
|
|
FIXME("PenDataDashedLineCap is not supported.\n");
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataDashedLineOffset)
|
|
{
|
|
if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
|
|
goto penfailed;
|
|
offset += sizeof(REAL);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataDashedLine)
|
|
{
|
|
EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
|
|
FIXME("PenDataDashedLine is not supported.\n");
|
|
offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataNonCenter)
|
|
{
|
|
FIXME("PenDataNonCenter is not supported.\n");
|
|
offset += sizeof(DWORD);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataCompoundLine)
|
|
{
|
|
EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
|
|
FIXME("PenDataCompundLine is not supported.\n");
|
|
offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataCustomStartCap)
|
|
{
|
|
EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
|
|
FIXME("PenDataCustomStartCap is not supported.\n");
|
|
offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
|
|
}
|
|
|
|
if (pendata->PenDataFlags & PenDataCustomEndCap)
|
|
{
|
|
EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
|
|
FIXME("PenDataCustomEndCap is not supported.\n");
|
|
offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
|
|
}
|
|
|
|
object = pen;
|
|
break;
|
|
|
|
penfailed:
|
|
GdipDeletePen(pen);
|
|
return status;
|
|
}
|
|
case ObjectTypePath:
|
|
status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
|
|
break;
|
|
case ObjectTypeRegion:
|
|
status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
|
|
break;
|
|
case ObjectTypeImage:
|
|
status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
|
|
break;
|
|
case ObjectTypeFont:
|
|
{
|
|
EmfPlusFont *data = (EmfPlusFont *)record_data;
|
|
GpFontFamily *family;
|
|
WCHAR *familyname;
|
|
|
|
if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
|
|
return InvalidParameter;
|
|
data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
|
|
|
|
if (data_size < data->Length * sizeof(WCHAR))
|
|
return InvalidParameter;
|
|
|
|
if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
|
|
return OutOfMemory;
|
|
|
|
memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
|
|
familyname[data->Length] = 0;
|
|
|
|
status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
|
|
GdipFree(familyname);
|
|
if (status != Ok)
|
|
return InvalidParameter;
|
|
|
|
status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
|
|
GdipDeleteFontFamily(family);
|
|
break;
|
|
}
|
|
case ObjectTypeImageAttributes:
|
|
{
|
|
EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
|
|
GpImageAttributes *attributes = NULL;
|
|
|
|
if (data_size != sizeof(*data))
|
|
return InvalidParameter;
|
|
|
|
if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
|
|
return status;
|
|
|
|
status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
|
|
!!data->ObjectClamp);
|
|
if (status == Ok)
|
|
object = attributes;
|
|
else
|
|
GdipDisposeImageAttributes(attributes);
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("not implemented for object type %d.\n", type);
|
|
return NotImplemented;
|
|
}
|
|
|
|
if (status == Ok)
|
|
metafile_set_object_table_entry(metafile, id, type, object);
|
|
|
|
return status;
|
|
}
|
|
|
|
static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
|
|
{
|
|
GpMatrix world_to_device;
|
|
|
|
get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
|
|
|
|
GdipTransformRegion(region, &world_to_device);
|
|
GdipCombineRegionRegion(metafile->clip, region, mode);
|
|
|
|
return METAFILE_PlaybackUpdateClip(metafile);
|
|
}
|
|
|
|
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;
|
|
|
|
if (dataSize != sizeof(record->Color))
|
|
return InvalidParameter;
|
|
|
|
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(record->BrushID, (GpSolidFill **)&temp_brush);
|
|
brush = temp_brush;
|
|
}
|
|
else
|
|
{
|
|
if (record->BrushID >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
|
|
return InvalidParameter;
|
|
|
|
brush = real_metafile->objtable[record->BrushID].u.brush;
|
|
stat = Ok;
|
|
}
|
|
|
|
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;
|
|
|
|
if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
|
|
return InvalidParameter;
|
|
|
|
stat = GdipCreateRegionRect(&record->ClipRect, ®ion);
|
|
|
|
if (stat == Ok)
|
|
{
|
|
stat = metafile_set_clip_region(real_metafile, region, mode);
|
|
GdipDeleteRegion(region);
|
|
}
|
|
|
|
return stat;
|
|
}
|
|
case EmfPlusRecordTypeSetClipRegion:
|
|
{
|
|
CombineMode mode = (flags >> 8) & 0xf;
|
|
BYTE regionid = flags & 0xff;
|
|
GpRegion *region;
|
|
|
|
if (dataSize != 0)
|
|
return InvalidParameter;
|
|
|
|
if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
|
|
return InvalidParameter;
|
|
|
|
stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, ®ion);
|
|
if (stat == Ok)
|
|
{
|
|
stat = metafile_set_clip_region(real_metafile, region, mode);
|
|
GdipDeleteRegion(region);
|
|
}
|
|
|
|
return stat;
|
|
}
|
|
case EmfPlusRecordTypeSetClipPath:
|
|
{
|
|
CombineMode mode = (flags >> 8) & 0xf;
|
|
BYTE pathid = flags & 0xff;
|
|
GpRegion *region;
|
|
|
|
if (dataSize != 0)
|
|
return InvalidParameter;
|
|
|
|
if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
|
|
return InvalidParameter;
|
|
|
|
stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, ®ion);
|
|
if (stat == Ok)
|
|
{
|
|
stat = metafile_set_clip_region(real_metafile, region, mode);
|
|
GdipDeleteRegion(region);
|
|
}
|
|
|
|
return stat;
|
|
}
|
|
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 & 0xff);
|
|
}
|
|
case EmfPlusRecordTypeSetCompositingQuality:
|
|
{
|
|
return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
|
|
}
|
|
case EmfPlusRecordTypeSetInterpolationMode:
|
|
{
|
|
return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
|
|
}
|
|
case EmfPlusRecordTypeSetTextRenderingHint:
|
|
{
|
|
return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
|
|
}
|
|
case EmfPlusRecordTypeSetAntiAliasMode:
|
|
{
|
|
return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
|
|
}
|
|
case EmfPlusRecordTypeSetCompositingMode:
|
|
{
|
|
return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
|
|
}
|
|
case EmfPlusRecordTypeObject:
|
|
{
|
|
return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
|
|
}
|
|
case EmfPlusRecordTypeDrawImage:
|
|
{
|
|
EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
|
|
BYTE image = flags & 0xff;
|
|
GpPointF points[3];
|
|
|
|
if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
|
|
(flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x4000) /* C */
|
|
{
|
|
points[0].X = draw->RectData.rect.X;
|
|
points[0].Y = draw->RectData.rect.Y;
|
|
points[1].X = points[0].X + draw->RectData.rect.Width;
|
|
points[1].Y = points[0].Y;
|
|
points[2].X = points[1].X;
|
|
points[2].Y = points[1].Y + draw->RectData.rect.Height;
|
|
}
|
|
else
|
|
{
|
|
points[0].X = draw->RectData.rectF.X;
|
|
points[0].Y = draw->RectData.rectF.Y;
|
|
points[1].X = points[0].X + draw->RectData.rectF.Width;
|
|
points[1].Y = points[0].Y;
|
|
points[2].X = points[1].X;
|
|
points[2].Y = points[1].Y + draw->RectData.rectF.Height;
|
|
}
|
|
|
|
return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
|
|
points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
|
|
real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
|
|
}
|
|
case EmfPlusRecordTypeDrawImagePoints:
|
|
{
|
|
EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
|
|
static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
|
|
FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
|
|
BYTE image = flags & 0xff;
|
|
GpPointF points[3];
|
|
unsigned int i;
|
|
UINT size;
|
|
|
|
if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize <= fixed_part_size)
|
|
return InvalidParameter;
|
|
dataSize -= fixed_part_size;
|
|
|
|
if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
|
|
return InvalidParameter;
|
|
|
|
if (draw->count != 3)
|
|
return InvalidParameter;
|
|
|
|
if ((flags >> 13) & 1) /* E */
|
|
FIXME("image effects are not supported.\n");
|
|
|
|
if ((flags >> 11) & 1) /* P */
|
|
size = sizeof(EmfPlusPointR7) * draw->count;
|
|
else if ((flags >> 14) & 1) /* C */
|
|
size = sizeof(EmfPlusPoint) * draw->count;
|
|
else
|
|
size = sizeof(EmfPlusPointF) * draw->count;
|
|
|
|
if (dataSize != size)
|
|
return InvalidParameter;
|
|
|
|
if ((flags >> 11) & 1) /* P */
|
|
{
|
|
points[0].X = draw->PointData.pointsR[0].X;
|
|
points[0].Y = draw->PointData.pointsR[0].Y;
|
|
for (i = 1; i < 3; i++)
|
|
{
|
|
points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
|
|
points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
|
|
}
|
|
}
|
|
else if ((flags >> 14) & 1) /* C */
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
points[i].X = draw->PointData.points[i].X;
|
|
points[i].Y = draw->PointData.points[i].Y;
|
|
}
|
|
}
|
|
else
|
|
memcpy(points, draw->PointData.pointsF, sizeof(points));
|
|
|
|
return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
|
|
points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
|
|
real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
|
|
}
|
|
case EmfPlusRecordTypeFillPath:
|
|
{
|
|
EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
|
|
GpSolidFill *solidfill = NULL;
|
|
BYTE path = flags & 0xff;
|
|
GpBrush *brush;
|
|
|
|
if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize != sizeof(fill->data.BrushId))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x8000)
|
|
{
|
|
stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
|
|
if (stat != Ok)
|
|
return stat;
|
|
brush = (GpBrush *)solidfill;
|
|
}
|
|
else
|
|
{
|
|
if (fill->data.BrushId >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
|
|
return InvalidParameter;
|
|
|
|
brush = real_metafile->objtable[fill->data.BrushId].u.brush;
|
|
}
|
|
|
|
stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
|
|
GdipDeleteBrush((GpBrush *)solidfill);
|
|
return stat;
|
|
}
|
|
case EmfPlusRecordTypeFillClosedCurve:
|
|
{
|
|
static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
|
|
sizeof(EmfPlusRecordHeader);
|
|
EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
|
|
GpSolidFill *solidfill = NULL;
|
|
GpFillMode mode;
|
|
GpBrush *brush;
|
|
UINT size, i;
|
|
|
|
if (dataSize <= fixed_part_size)
|
|
return InvalidParameter;
|
|
|
|
if (fill->Count == 0)
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x800) /* P */
|
|
size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
|
|
else if (flags & 0x4000) /* C */
|
|
size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
|
|
else
|
|
size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
|
|
|
|
if (dataSize != size)
|
|
return InvalidParameter;
|
|
|
|
mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
|
|
|
|
if (flags & 0x8000) /* S */
|
|
{
|
|
stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
|
|
if (stat != Ok)
|
|
return stat;
|
|
brush = (GpBrush *)solidfill;
|
|
}
|
|
else
|
|
{
|
|
if (fill->BrushId >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
|
|
return InvalidParameter;
|
|
|
|
brush = real_metafile->objtable[fill->BrushId].u.brush;
|
|
}
|
|
|
|
if (flags & (0x800 | 0x4000))
|
|
{
|
|
GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
|
|
if (points)
|
|
{
|
|
if (flags & 0x800) /* P */
|
|
{
|
|
for (i = 1; i < fill->Count; i++)
|
|
{
|
|
points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
|
|
points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < fill->Count; i++)
|
|
{
|
|
points[i].X = fill->PointData.points[i].X;
|
|
points[i].Y = fill->PointData.points[i].Y;
|
|
}
|
|
}
|
|
|
|
stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
|
|
points, fill->Count, fill->Tension, mode);
|
|
GdipFree(points);
|
|
}
|
|
else
|
|
stat = OutOfMemory;
|
|
}
|
|
else
|
|
stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
|
|
(const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
|
|
|
|
GdipDeleteBrush((GpBrush *)solidfill);
|
|
return stat;
|
|
}
|
|
case EmfPlusRecordTypeFillEllipse:
|
|
{
|
|
EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
|
|
GpSolidFill *solidfill = NULL;
|
|
GpBrush *brush;
|
|
|
|
if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
|
|
return InvalidParameter;
|
|
dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
|
|
|
|
if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x8000)
|
|
{
|
|
stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
|
|
if (stat != Ok)
|
|
return stat;
|
|
brush = (GpBrush *)solidfill;
|
|
}
|
|
else
|
|
{
|
|
if (fill->BrushId >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
|
|
return InvalidParameter;
|
|
|
|
brush = real_metafile->objtable[fill->BrushId].u.brush;
|
|
}
|
|
|
|
if (flags & 0x4000)
|
|
stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
|
|
fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
|
|
else
|
|
stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
|
|
fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
|
|
|
|
GdipDeleteBrush((GpBrush *)solidfill);
|
|
return stat;
|
|
}
|
|
case EmfPlusRecordTypeFillPie:
|
|
{
|
|
EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
|
|
GpSolidFill *solidfill = NULL;
|
|
GpBrush *brush;
|
|
|
|
if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
|
|
return InvalidParameter;
|
|
dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
|
|
|
|
if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x8000) /* S */
|
|
{
|
|
stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
|
|
if (stat != Ok)
|
|
return stat;
|
|
brush = (GpBrush *)solidfill;
|
|
}
|
|
else
|
|
{
|
|
if (fill->BrushId >= EmfPlusObjectTableSize ||
|
|
real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
|
|
return InvalidParameter;
|
|
|
|
brush = real_metafile->objtable[fill->BrushId].u.brush;
|
|
}
|
|
|
|
if (flags & 0x4000) /* C */
|
|
stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
|
|
fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
|
|
fill->StartAngle, fill->SweepAngle);
|
|
else
|
|
stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
|
|
fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
|
|
fill->StartAngle, fill->SweepAngle);
|
|
|
|
GdipDeleteBrush((GpBrush *)solidfill);
|
|
return stat;
|
|
}
|
|
case EmfPlusRecordTypeDrawPath:
|
|
{
|
|
EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
|
|
BYTE path = flags & 0xff;
|
|
|
|
if (dataSize != sizeof(draw->PenId))
|
|
return InvalidParameter;
|
|
|
|
if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
|
|
return InvalidParameter;
|
|
|
|
if (real_metafile->objtable[path].type != ObjectTypePath ||
|
|
real_metafile->objtable[draw->PenId].type != ObjectTypePen)
|
|
return InvalidParameter;
|
|
|
|
return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
|
|
real_metafile->objtable[path].u.path);
|
|
}
|
|
case EmfPlusRecordTypeDrawArc:
|
|
{
|
|
EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
|
|
BYTE pen = flags & 0xff;
|
|
|
|
if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
|
|
(flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x4000) /* C */
|
|
return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
|
|
draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
|
|
else
|
|
return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
|
|
draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
|
|
}
|
|
case EmfPlusRecordTypeDrawEllipse:
|
|
{
|
|
EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
|
|
BYTE pen = flags & 0xff;
|
|
|
|
if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x4000) /* C */
|
|
return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
|
|
draw->RectData.rect.Height);
|
|
else
|
|
return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
|
|
draw->RectData.rectF.Height);
|
|
}
|
|
case EmfPlusRecordTypeDrawPie:
|
|
{
|
|
EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
|
|
BYTE pen = flags & 0xff;
|
|
|
|
if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
|
|
(flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x4000) /* C */
|
|
return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
|
|
draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
|
|
else
|
|
return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
|
|
draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
|
|
}
|
|
case EmfPlusRecordTypeDrawRects:
|
|
{
|
|
EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
|
|
BYTE pen = flags & 0xff;
|
|
GpRectF *rects = NULL;
|
|
|
|
if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
|
|
return InvalidParameter;
|
|
|
|
if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
|
|
return InvalidParameter;
|
|
dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
|
|
|
|
if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
|
|
return InvalidParameter;
|
|
|
|
if (flags & 0x4000)
|
|
{
|
|
DWORD i;
|
|
|
|
rects = GdipAlloc(draw->Count * sizeof(*rects));
|
|
if (!rects)
|
|
return OutOfMemory;
|
|
|
|
for (i = 0; i < draw->Count; i++)
|
|
{
|
|
rects[i].X = draw->RectData.rect[i].X;
|
|
rects[i].Y = draw->RectData.rect[i].Y;
|
|
rects[i].Width = draw->RectData.rect[i].Width;
|
|
rects[i].Height = draw->RectData.rect[i].Height;
|
|
}
|
|
}
|
|
|
|
stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
|
|
rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
|
|
GdipFree(rects);
|
|
return stat;
|
|
}
|
|
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 = attrs->outside_color;
|
|
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;
|
|
memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
|
|
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_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;
|
|
}
|
|
|
|
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 = ((GpSolidFill *)brush)->color;
|
|
}
|
|
else
|
|
{
|
|
fill_path_record->Header.Flags = path_id;
|
|
fill_path_record->data.BrushId = brush_id;
|
|
}
|
|
|
|
METAFILE_WriteRecords(metafile);
|
|
return Ok;
|
|
}
|