868 lines
25 KiB
C
868 lines
25 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 EmfPlusFillRects
|
|
{
|
|
EmfPlusRecordHeader Header;
|
|
DWORD BrushID;
|
|
DWORD Count;
|
|
} EmfPlusFillRects;
|
|
|
|
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 = GdipAlloc(data_size);
|
|
|
|
if (!metafile->comment_data)
|
|
return OutOfMemory;
|
|
|
|
memcpy(metafile->comment_data, "EMF+", 4);
|
|
|
|
metafile->comment_data_size = data_size;
|
|
metafile->comment_data_length = 4;
|
|
}
|
|
|
|
size_needed = size + metafile->comment_data_length;
|
|
|
|
if (size_needed > metafile->comment_data_size)
|
|
{
|
|
DWORD data_size = size_needed * 2;
|
|
BYTE *new_data = GdipAlloc(data_size);
|
|
|
|
if (!new_data)
|
|
return OutOfMemory;
|
|
|
|
memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
|
|
|
|
metafile->comment_data_size = data_size;
|
|
GdipFree(metafile->comment_data);
|
|
metafile->comment_data = new_data;
|
|
}
|
|
|
|
*result = metafile->comment_data + metafile->comment_data_length;
|
|
metafile->comment_data_length += size;
|
|
|
|
record = (EmfPlusRecordHeader*)*result;
|
|
record->Size = size;
|
|
record->DataSize = size - sizeof(EmfPlusRecordHeader);
|
|
|
|
return Ok;
|
|
}
|
|
|
|
static void METAFILE_WriteRecords(GpMetafile *metafile)
|
|
{
|
|
if (metafile->comment_data_length > 4)
|
|
{
|
|
GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
|
|
metafile->comment_data_length = 4;
|
|
}
|
|
}
|
|
|
|
static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
|
|
{
|
|
GpStatus stat;
|
|
|
|
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
|
|
{
|
|
EmfPlusHeader *header;
|
|
|
|
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
|
|
if (stat != Ok)
|
|
return stat;
|
|
|
|
header->Header.Type = EmfPlusRecordTypeHeader;
|
|
|
|
if (metafile->metafile_type == MetafileTypeEmfPlusDual)
|
|
header->Header.Flags = 1;
|
|
else
|
|
header->Header.Flags = 0;
|
|
|
|
header->Version = 0xDBC01002;
|
|
|
|
if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
|
|
header->EmfPlusFlags = 1;
|
|
else
|
|
header->EmfPlusFlags = 0;
|
|
|
|
header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
|
|
METAFILE_WriteRecords(metafile);
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
|
|
{
|
|
GpStatus stat;
|
|
|
|
if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
|
|
{
|
|
EmfPlusRecordHeader *record;
|
|
|
|
stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
|
|
if (stat != Ok)
|
|
return stat;
|
|
|
|
record->Type = EmfPlusRecordTypeEndOfFile;
|
|
record->Flags = 0;
|
|
|
|
METAFILE_WriteRecords(metafile);
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
|
|
MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
|
|
{
|
|
HDC record_dc;
|
|
REAL framerect_factor_x, framerect_factor_y;
|
|
RECT rc;
|
|
GpStatus stat;
|
|
|
|
TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
|
|
|
|
if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
|
|
return InvalidParameter;
|
|
|
|
if (!frameRect)
|
|
{
|
|
FIXME("not implemented for NULL rect\n");
|
|
return NotImplemented;
|
|
}
|
|
|
|
switch (frameUnit)
|
|
{
|
|
case MetafileFrameUnitPixel:
|
|
framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX);
|
|
framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY);
|
|
break;
|
|
case MetafileFrameUnitPoint:
|
|
framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
|
|
break;
|
|
case MetafileFrameUnitInch:
|
|
framerect_factor_x = framerect_factor_y = 2540.0;
|
|
break;
|
|
case MetafileFrameUnitDocument:
|
|
framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
|
|
break;
|
|
case MetafileFrameUnitMillimeter:
|
|
framerect_factor_x = framerect_factor_y = 100.0;
|
|
break;
|
|
case MetafileFrameUnitGdi:
|
|
framerect_factor_x = framerect_factor_y = 1.0;
|
|
break;
|
|
default:
|
|
return InvalidParameter;
|
|
}
|
|
|
|
rc.left = framerect_factor_x * frameRect->X;
|
|
rc.top = framerect_factor_y * frameRect->Y;
|
|
rc.right = rc.left + framerect_factor_x * frameRect->Width;
|
|
rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
|
|
|
|
record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
|
|
|
|
if (!record_dc)
|
|
return GenericError;
|
|
|
|
*metafile = GdipAlloc(sizeof(GpMetafile));
|
|
if(!*metafile)
|
|
{
|
|
DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
|
|
return OutOfMemory;
|
|
}
|
|
|
|
(*metafile)->image.type = ImageTypeMetafile;
|
|
(*metafile)->image.picture = NULL;
|
|
(*metafile)->image.flags = ImageFlagsNone;
|
|
(*metafile)->image.palette = NULL;
|
|
(*metafile)->bounds = *frameRect;
|
|
(*metafile)->unit = frameUnit;
|
|
(*metafile)->metafile_type = type;
|
|
(*metafile)->record_dc = record_dc;
|
|
(*metafile)->comment_data = NULL;
|
|
(*metafile)->comment_data_size = 0;
|
|
(*metafile)->comment_data_length = 0;
|
|
(*metafile)->hemf = NULL;
|
|
|
|
stat = METAFILE_WriteHeader(*metafile, hdc);
|
|
|
|
if (stat != Ok)
|
|
{
|
|
DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
|
|
GdipFree(*metafile);
|
|
*metafile = NULL;
|
|
return OutOfMemory;
|
|
}
|
|
|
|
return stat;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* GdipRecordMetafileI [GDIPLUS.@]
|
|
*/
|
|
GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
|
|
MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
|
|
{
|
|
GpRectF frameRectF, *pFrameRectF;
|
|
|
|
TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
|
|
|
|
if (frameRect)
|
|
{
|
|
frameRectF.X = frameRect->X;
|
|
frameRectF.Y = frameRect->Y;
|
|
frameRectF.Width = frameRect->Width;
|
|
frameRectF.Height = frameRect->Height;
|
|
pFrameRectF = &frameRectF;
|
|
}
|
|
else
|
|
pFrameRectF = NULL;
|
|
|
|
return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
|
|
}
|
|
|
|
GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
|
|
{
|
|
GpStatus stat;
|
|
|
|
if (!metafile->record_dc || metafile->record_graphics)
|
|
return InvalidParameter;
|
|
|
|
stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
|
|
|
|
if (stat == Ok)
|
|
*result = metafile->record_graphics;
|
|
|
|
return stat;
|
|
}
|
|
|
|
GpStatus METAFILE_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;
|
|
}
|
|
|
|
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=1;
|
|
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 = 0;
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
GdipFree(metafile->comment_data);
|
|
metafile->comment_data = NULL;
|
|
metafile->comment_data_size = 0;
|
|
|
|
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_PlaybackUpdateWorldTransform(GpMetafile *metafile)
|
|
{
|
|
GpMatrix *real_transform;
|
|
GpStatus stat;
|
|
|
|
stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
|
|
|
|
if (stat == Ok)
|
|
{
|
|
/* FIXME: Prepend page transform. */
|
|
|
|
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;
|
|
|
|
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 = GdipAlloc(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);
|
|
|
|
GdipFree(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 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 = GdipAlloc(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);
|
|
GdipFree(temp_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;
|
|
|
|
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 = GdipCreateMatrix(&real_metafile->world_transform);
|
|
|
|
if (stat == Ok)
|
|
{
|
|
real_metafile->page_unit = UnitPixel; /* FIXME: Use frame unit here? */
|
|
real_metafile->page_scale = 1.0;
|
|
stat = METAFILE_PlaybackUpdateWorldTransform(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;
|
|
|
|
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);
|
|
}
|
|
|
|
static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
|
|
int nObj, LPARAM lpData)
|
|
{
|
|
MetafileType *result = (MetafileType*)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(EmfPlusRecordHeader) <= comment->cbData &&
|
|
header->Type == EmfPlusRecordTypeHeader)
|
|
{
|
|
if ((header->Flags & 1) == 1)
|
|
*result = MetafileTypeEmfPlusDual;
|
|
else
|
|
*result = MetafileTypeEmfPlusOnly;
|
|
}
|
|
}
|
|
else
|
|
*result = MetafileTypeEmf;
|
|
}
|
|
else
|
|
*result = MetafileTypeEmf;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf)
|
|
{
|
|
MetafileType result = MetafileTypeInvalid;
|
|
EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL);
|
|
return result;
|
|
}
|