Sweden-Number/dlls/gdiplus/metafile.c

1470 lines
44 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 "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "wine/unicode.h"
#define COBJMACROS
#include "objbase.h"
#include "ocidl.h"
#include "olectl.h"
#include "ole2.h"
#include "winreg.h"
#include "shlwapi.h"
#include "gdiplus.h"
#include "gdiplus_private.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
typedef struct EmfPlusRecordHeader
{
WORD Type;
WORD Flags;
DWORD Size;
DWORD DataSize;
} EmfPlusRecordHeader;
typedef struct EmfPlusHeader
{
EmfPlusRecordHeader Header;
DWORD Version;
DWORD EmfPlusFlags;
DWORD LogicalDpiX;
DWORD LogicalDpiY;
} EmfPlusHeader;
typedef struct EmfPlusClear
{
EmfPlusRecordHeader Header;
DWORD Color;
} EmfPlusClear;
typedef struct EmfPlusFillRects
{
EmfPlusRecordHeader Header;
DWORD BrushID;
DWORD Count;
} EmfPlusFillRects;
typedef struct EmfPlusSetPageTransform
{
EmfPlusRecordHeader Header;
REAL PageScale;
} EmfPlusSetPageTransform;
typedef struct EmfPlusRect
{
SHORT X;
SHORT Y;
SHORT Width;
SHORT Height;
} EmfPlusRect;
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_WriteRecords(GpMetafile *metafile)
{
if (metafile->comment_data_length > 4)
{
GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
metafile->comment_data_length = 4;
}
}
static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
{
GpStatus stat;
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
{
EmfPlusHeader *header;
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
if (stat != Ok)
return stat;
header->Header.Type = EmfPlusRecordTypeHeader;
if (metafile->metafile_type == MetafileTypeEmfPlusDual)
header->Header.Flags = 1;
else
header->Header.Flags = 0;
header->Version = 0xDBC01002;
if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
header->EmfPlusFlags = 1;
else
header->EmfPlusFlags = 0;
header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
METAFILE_WriteRecords(metafile);
}
return Ok;
}
static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
{
GpStatus stat;
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
{
EmfPlusRecordHeader *record;
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
if (stat != Ok)
return stat;
record->Type = EmfPlusRecordTypeEndOfFile;
record->Flags = 0;
METAFILE_WriteRecords(metafile);
}
return Ok;
}
GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
{
HDC record_dc;
REAL 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.picture = NULL;
(*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;
if (!frameRect)
{
(*metafile)->auto_frame = TRUE;
(*metafile)->auto_frame_min.X = 0;
(*metafile)->auto_frame_min.Y = 0;
(*metafile)->auto_frame_max.X = -1;
(*metafile)->auto_frame_max.Y = -1;
}
stat = METAFILE_WriteHeader(*metafile, hdc);
if (stat != Ok)
{
DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
heap_free(*metafile);
*metafile = NULL;
return OutOfMemory;
}
return stat;
}
/*****************************************************************************
* GdipRecordMetafileI [GDIPLUS.@]
*/
GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
{
GpRectF frameRectF, *pFrameRectF;
TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
if (frameRect)
{
frameRectF.X = frameRect->X;
frameRectF.Y = frameRect->Y;
frameRectF.Width = frameRect->Width;
frameRectF.Height = frameRect->Height;
pFrameRectF = &frameRectF;
}
else
pFrameRectF = NULL;
return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
}
GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
{
GpStatus stat;
TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
if (!stream)
return InvalidParameter;
stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
if (stat == Ok)
{
(*metafile)->record_stream = stream;
IStream_AddRef(stream);
}
return stat;
}
static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
UINT num_points)
{
int i;
if (!metafile->auto_frame || !num_points)
return;
if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
metafile->auto_frame_max = metafile->auto_frame_min = points[0];
for (i=0; i<num_points; i++)
{
if (points[i].X < metafile->auto_frame_min.X)
metafile->auto_frame_min.X = points[i].X;
if (points[i].X > metafile->auto_frame_max.X)
metafile->auto_frame_max.X = points[i].X;
if (points[i].Y < metafile->auto_frame_min.Y)
metafile->auto_frame_min.Y = points[i].Y;
if (points[i].Y > metafile->auto_frame_max.Y)
metafile->auto_frame_max.Y = points[i].Y;
}
}
GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
{
GpStatus stat;
if (!metafile->record_dc || metafile->record_graphics)
return InvalidParameter;
stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
if (stat == Ok)
{
*result = metafile->record_graphics;
metafile->record_graphics->xres = 96.0;
metafile->record_graphics->yres = 96.0;
}
return stat;
}
GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
{
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
{
EmfPlusRecordHeader *record;
GpStatus stat;
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
if (stat != Ok)
return stat;
record->Type = EmfPlusRecordTypeGetDC;
record->Flags = 0;
METAFILE_WriteRecords(metafile);
}
*hdc = metafile->record_dc;
return Ok;
}
GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
{
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
{
EmfPlusClear *record;
GpStatus stat;
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
if (stat != Ok)
return stat;
record->Header.Type = EmfPlusRecordTypeClear;
record->Header.Flags = 0;
record->Color = color;
METAFILE_WriteRecords(metafile);
}
return Ok;
}
static BOOL is_integer_rect(const GpRectF *rect)
{
SHORT x, y, width, height;
x = rect->X;
y = rect->Y;
width = rect->Width;
height = rect->Height;
if (rect->X != (REAL)x || rect->Y != (REAL)y ||
rect->Width != (REAL)width || rect->Height != (REAL)height)
return FALSE;
return TRUE;
}
GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
GDIPCONST GpRectF* rects, INT count)
{
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
{
EmfPlusFillRects *record;
GpStatus stat;
BOOL integer_rects = TRUE;
int i;
DWORD brushid;
int flags = 0;
if (brush->bt == BrushTypeSolidColor)
{
flags |= 0x8000;
brushid = ((GpSolidFill*)brush)->color;
}
else
{
FIXME("brush serialization not implemented\n");
return NotImplemented;
}
for (i=0; i<count; i++)
{
if (!is_integer_rect(&rects[i]))
{
integer_rects = FALSE;
break;
}
}
if (integer_rects)
flags |= 0x4000;
stat = METAFILE_AllocateRecord(metafile,
sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
(void**)&record);
if (stat != Ok)
return stat;
record->Header.Type = EmfPlusRecordTypeFillRects;
record->Header.Flags = flags;
record->BrushID = brushid;
record->Count = count;
if (integer_rects)
{
EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
for (i=0; i<count; i++)
{
record_rects[i].X = (SHORT)rects[i].X;
record_rects[i].Y = (SHORT)rects[i].Y;
record_rects[i].Width = (SHORT)rects[i].Width;
record_rects[i].Height = (SHORT)rects[i].Height;
}
}
else
memcpy(record+1, rects, sizeof(GpRectF) * count);
METAFILE_WriteRecords(metafile);
}
if (metafile->auto_frame)
{
GpPointF corners[4];
int i;
for (i=0; i<count; i++)
{
corners[0].X = rects[i].X;
corners[0].Y = rects[i].Y;
corners[1].X = rects[i].X + rects[i].Width;
corners[1].Y = rects[i].Y;
corners[2].X = rects[i].X;
corners[2].Y = rects[i].Y + rects[i].Height;
corners[3].X = rects[i].X + rects[i].Width;
corners[3].Y = rects[i].Y + rects[i].Height;
GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
CoordinateSpaceWorld, corners, 4);
METAFILE_AdjustFrame(metafile, corners, 4);
}
}
return Ok;
}
GpStatus METAFILE_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_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.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 GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
{
GpStatus stat = Ok;
stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
if (stat == Ok)
{
/* The result of GdipGetDC always expects device co-ordinates, but the
* device co-ordinates of the source metafile do not correspond to
* device co-ordinates of the destination. Therefore, we set up the DC
* so that the metafile's bounds map to the destination points where we
* are drawing this metafile. */
SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
SetViewportExtEx(metafile->playback_dc,
metafile->playback_points[1].X - metafile->playback_points[0].X,
metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
}
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)
{
return GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
}
static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
{
GpMatrix *real_transform;
GpStatus stat;
stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
if (stat == Ok)
{
REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
if (metafile->page_unit != UnitDisplay)
scale *= metafile->page_scale;
stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
if (stat == Ok)
stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
if (stat == Ok)
stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
GdipDeleteMatrix(real_transform);
}
return stat;
}
GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
{
GpStatus stat;
GpMetafile *real_metafile = (GpMetafile*)metafile;
TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
return InvalidParameter;
if (recordType >= 1 && recordType <= 0x7a)
{
/* regular EMF record */
if (metafile->playback_dc)
{
ENHMETARECORD *record;
record = heap_alloc_zero(dataSize + 8);
if (record)
{
record->iType = recordType;
record->nSize = dataSize + 8;
memcpy(record->dParm, data, dataSize);
PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
record, metafile->handle_count);
heap_free(record);
}
else
return OutOfMemory;
}
}
else
{
EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
switch(recordType)
{
case EmfPlusRecordTypeHeader:
case EmfPlusRecordTypeEndOfFile:
break;
case EmfPlusRecordTypeGetDC:
METAFILE_PlaybackGetDC((GpMetafile*)metafile);
break;
case EmfPlusRecordTypeClear:
{
EmfPlusClear *record = (EmfPlusClear*)header;
return GdipGraphicsClear(metafile->playback_graphics, record->Color);
}
case EmfPlusRecordTypeFillRects:
{
EmfPlusFillRects *record = (EmfPlusFillRects*)header;
GpBrush *brush, *temp_brush=NULL;
GpRectF *rects, *temp_rects=NULL;
if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
return InvalidParameter;
if (flags & 0x4000)
{
if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
return InvalidParameter;
}
else
{
if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
return InvalidParameter;
}
if (flags & 0x8000)
{
stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
brush = temp_brush;
}
else
{
FIXME("brush deserialization not implemented\n");
return NotImplemented;
}
if (stat == Ok)
{
if (flags & 0x4000)
{
EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
int i;
rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
if (rects)
{
for (i=0; i<record->Count; i++)
{
rects[i].X = int_rects[i].X;
rects[i].Y = int_rects[i].Y;
rects[i].Width = int_rects[i].Width;
rects[i].Height = int_rects[i].Height;
}
}
else
stat = OutOfMemory;
}
else
rects = (GpRectF*)(record+1);
}
if (stat == Ok)
{
stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
}
GdipDeleteBrush(temp_brush);
heap_free(temp_rects);
return stat;
}
case 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);
}
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 = 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;
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)
{
static int calls;
TRACE("(%p, %p)\n", metafile, header);
if(!metafile || !header)
return InvalidParameter;
if(!(calls++))
FIXME("not implemented\n");
memset(header, 0, sizeof(MetafileHeader));
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->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 GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
MetafileHeader *header)
{
static int calls;
TRACE("(%s,%p)\n", debugstr_w(filename), header);
if(!filename || !header)
return InvalidParameter;
if(!(calls++))
FIXME("not implemented\n");
memset(header, 0, sizeof(MetafileHeader));
return Ok;
}
GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
MetafileHeader *header)
{
static int calls;
TRACE("(%p,%p)\n", stream, header);
if(!stream || !header)
return InvalidParameter;
if(!(calls++))
FIXME("not implemented\n");
memset(header, 0, sizeof(MetafileHeader));
return Ok;
}
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.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
(*metafile)->bounds.Y = (REAL)header.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
(*metafile)->bounds.Width = (REAL)(header.EmfHeader.rclFrame.right - header.EmfHeader.rclFrame.left)
/ 2540.0 * header.DpiX;
(*metafile)->bounds.Height = (REAL)(header.EmfHeader.rclFrame.bottom - header.EmfHeader.rclFrame.top)
/ 2540.0 * header.DpiY;
(*metafile)->unit = UnitPixel;
(*metafile)->metafile_type = header.Type;
(*metafile)->hemf = hemf;
(*metafile)->preserve_hemf = !delete;
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 = GetMetaFileW(file);
TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
if(!hmf) return InvalidParameter;
return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
}
GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
GpMetafile **metafile)
{
FIXME("(%p, %p): stub\n", file, metafile);
return NotImplemented;
}
GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
GpMetafile **metafile)
{
FIXME("(%p, %p): stub\n", stream, metafile);
return NotImplemented;
}
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;
}