Копирование графики в TMetaFileCanvas вне размеров экрана
У нас есть проблема с выводом TMetaFileCanvas при рисовании изображения по координатам за пределами разрешения экрана. Векторные операции, кажется, не имеют проблем, но операции с изображениями просто "игнорируются". Если мы рисуем одно и то же изображение с координатой в пределах экрана, проблем не возникает.
Например. Этот SSCCE будет производить 4 выходных файла. Вариант растрового изображения не имеет проблем и будет отображаться, как и ожидалось, с красным квадратом в верхнем левом углу для inscreen.bmp
и красный квадрат в правом нижнем углу для outsidescreen.bmp
, inscreen.emf
метафайл работает как положено с красным квадратом, нарисованным в верхнем левом углу. outsidescreen.emf
не работает, и только линия рисуется.
program Project6;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Types,
Windows,
Vcl.Graphics;
const
SIZECONST = 3000; // should be larger than your screen resolution
OFFSET = 1500;
function GetMyMetafile(const aHDC: HDC): TMetafile;
var
metcnv: TMetafileCanvas;
begin
Result := TMetafile.Create;
Result.SetSize(500, 500);
metcnv := TMetafileCanvas.Create(Result, aHDC);
metcnv.Brush.Color := clRed;
metcnv.FillRect(Rect(0, 0, 500, 500));
metcnv.Free;
end;
procedure OutputToMetaFile(const aFilename: string; const aStartOffset,
aEndOffset, aMaxSize: Integer; aGraphic: TGraphic; aHDC: HDC);
var
metafile: TMetafile;
metcnv: TMetafileCanvas;
begin
metafile := TMetafile.Create;
try
metafile.SetSize(aMaxSize, aMaxSize);
metcnv := TMetafileCanvas.Create(metafile, aHDC);
try
// draw it somewhere offscreen
metcnv.StretchDraw(Rect(aStartOffset, aStartOffset, aEndOffset, aEndOffset), aGraphic);
metcnv.MoveTo(aStartOffset, aStartOffset);
metcnv.LineTo(aEndOffset, aEndOffset);
finally
metcnv.Free;
end;
metafile.SaveToFile(aFilename);
finally
metafile.Free;
end;
end;
procedure OutputToBitmap(const aFilename: string; const aStartOffset,
aEndOffset, aMaxSize: Integer; aGraphic: TGraphic);
var
bmp: TBitmap;
begin
bmp := TBitmap.Create;
try
bmp.SetSize(aMaxSize, aMaxSize);
bmp.Canvas.StretchDraw(Rect(aStartOffset, aStartOffset, aEndOffset, aEndOffset), aGraphic);
bmp.Canvas.MoveTo(aStartOffset, aStartOffset);
bmp.Canvas.LineTo(aEndOffset, aEndOffset);
bmp.SaveToFile(aFilename);
finally
bmp.Free;
end;
end;
var
mygraph: TMetafile;
bigBitmap: TBitmap;
begin
bigBitmap := TBitmap.Create;
try
bigBitmap.PixelFormat := pf24bit;
Assert(bigBitmap.HandleType = bmDIB, 'Handle Type should be a DIB');
bigBitmap.Width := SIZECONST;
bigBitmap.Height := SIZECONST;
mygraph := GetMyMetafile(bigBitmap.Canvas.Handle);
OutputToMetaFile('inscreen.emf', 0, 1000, SIZECONST, mygraph, bigBitmap.Canvas.Handle);
OutputToMetaFile('outsidescreen.emf', OFFSET, SIZECONST-1, SIZECONST, mygraph, bigBitmap.Canvas.Handle);
// do the same using bitmap
OutputToBitmap('inscreen.bmp', 0, 1000, SIZECONST, mygraph);
OutputToBitmap('outsidescreen.bmp', OFFSET, SIZECONST-1, SIZECONST, mygraph);
finally
bigBitmap.Free;
mygraph.Free;
end;
end.
Кто-нибудь может увидеть, в чем проблема, или вы знаете, как обойти это?
Обновить
Я должен был включить это, когда я первоначально задал вопрос. Мы провели тестирование с использованием HDC для большого растрового изображения, и это показало ту же проблему. Я обновил пример кода, чтобы продемонстрировать это.
Обновление 2
К сожалению, решение остается труднодостижимым даже после щедрости. Любая операция BitBlt за пределами размера экрана не рисуется.
Вот извлечение операций метафайла, когда изображение находится в границах экранных координат:
R0001: [001] EMR_HEADER (s=108) {{ Bounds(500,500,18138,18129), Frame(0,0,105000,105000), ver(0x10000), size(688), recs(33), handles(2), pals(0), dev_pix(1080,1920), dev_mil(381,677), pixf_size(0), pixf_ofs(0), openGL(0) }}
R0002: [033] EMR_SAVEDC (s=8)
R0003: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)}
R0004: [028] EMR_SETMETARGN (s=8)
R0005: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)}
R0006: [037] EMR_SELECTOBJECT (s=12) {Stock object: 7=OBJ_PEN.(PS_SOLID | COSMETIC)}
R0007: [037] EMR_SELECTOBJECT (s=12) {Stock object: 14=OBJ_FONT}
R0008: [025] EMR_SETBKCOLOR (s=12) {0x00FFFFFF}
R0009: [024] EMR_SETTEXTCOLOR (s=12) {0x00000000}
R0010: [018] EMR_SETBKMODE (s=12) {iMode(2=OPAQUE)}
R0011: [019] EMR_SETPOLYFILLMODE (s=12) {iMode(1=ALTERNATE)}
R0012: [020] EMR_SETROP2 (s=12) {iMode(13=R2_COPYPEN)}
R0013: [021] EMR_SETSTRETCHBLTMODE (s=12) {iMode(1=BLACKONWHITE)}
R0014: [022] EMR_SETTEXTALIGN (s=12) {iMode(0= TA_LEFT TA_TOP)}
R0015: [013] EMR_SETBRUSHORGEX (s=16) {ptlOrigin(0,0)}
R0016: [058] EMR_SETMITERLIMIT (s=12) {Limit:0.000}
R0017: [027] EMR_MOVETOEX (s=16) { ptl(0,0)}
R0018: [035] EMR_SETWORLDTRANSFORM (s=32) {xform(eDx:500.000000, eDy:500.000000, eM11:5.039683, eM12:0.000000, eM21:0.000000, eM22:5.037203)}
R0019: [036] EMR_MODIFYWORLDTRANSFORM (s=36) {iMode(4=MWT_??), xform(eDx:500.000000, eDy:500.000000, eM11:5.039683, eM12:0.000000, eM21:0.000000, eM22:5.037203)}
R0020: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)}
R0021: [070] EMR_GDICOMMENT (s=40) {GDI.Begin Group}
R0022: [039] EMR_CREATEBRUSHINDIRECT (s=24) {ihBrush(1), style(0=BS_SOLID, color:0x000000FF)}
R0023: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)}
R0024: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)}
R0025: [076] EMR_BITBLT (s=100) {rclBounds(500,500,18138,18129), Dest[x:0, y:0, cx:3500, cy:3500)], dwRop(0x00F00021), Src[x:0, y:0, xform(eDx:0.000000, eDy:0.000000, eM11:1.000000, eM12:0.000000, eM21:0.000000, eM22:1.000000), BkColor:0x00000000, iUsage:0, offBmi:0, Bmi:0, offBits:0, Bits:0]}
R0026: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)}
R0027: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)}
R0028: [040] EMR_DELETEOBJECT (s=12) {ihObject(1)}
R0029: [070] EMR_GDICOMMENT (s=20) {GDI.End Group}
R0030: [034] EMR_RESTOREDC (s=12) {iRelative(-1)}
R0031: [027] EMR_MOVETOEX (s=16) { ptl(500,500)}
R0032: [054] EMR_LINETO (s=16) { ptl(1000,1000)}
R0033: [014] EMR_EOF (s=20) {nPalEntries:0, offPalEntries:16, nSizeLast:20}
Вот извлечение операций метафайла, когда изображение выходит за границы координат экрана:
R0001: [001] EMR_HEADER (s=108) {{ Bounds(1500,1500,2999,2999), Frame(0,0,105000,105000), ver(0x10000), size(588), recs(32), handles(2), pals(0), dev_pix(1080,1920), dev_mil(381,677), pixf_size(0), pixf_ofs(0), openGL(0) }}
R0002: [033] EMR_SAVEDC (s=8)
R0003: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)}
R0004: [028] EMR_SETMETARGN (s=8)
R0005: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)}
R0006: [037] EMR_SELECTOBJECT (s=12) {Stock object: 7=OBJ_PEN.(PS_SOLID | COSMETIC)}
R0007: [037] EMR_SELECTOBJECT (s=12) {Stock object: 14=OBJ_FONT}
R0008: [025] EMR_SETBKCOLOR (s=12) {0x00FFFFFF}
R0009: [024] EMR_SETTEXTCOLOR (s=12) {0x00000000}
R0010: [018] EMR_SETBKMODE (s=12) {iMode(2=OPAQUE)}
R0011: [019] EMR_SETPOLYFILLMODE (s=12) {iMode(1=ALTERNATE)}
R0012: [020] EMR_SETROP2 (s=12) {iMode(13=R2_COPYPEN)}
R0013: [021] EMR_SETSTRETCHBLTMODE (s=12) {iMode(1=BLACKONWHITE)}
R0014: [022] EMR_SETTEXTALIGN (s=12) {iMode(0= TA_LEFT TA_TOP)}
R0015: [013] EMR_SETBRUSHORGEX (s=16) {ptlOrigin(0,0)}
R0016: [058] EMR_SETMITERLIMIT (s=12) {Limit:0.000}
R0017: [027] EMR_MOVETOEX (s=16) { ptl(0,0)}
R0018: [035] EMR_SETWORLDTRANSFORM (s=32) {xform(eDx:1500.000000, eDy:1500.000000, eM11:15.108969, eM12:0.000000, eM21:0.000000, eM22:15.101533)}
R0019: [036] EMR_MODIFYWORLDTRANSFORM (s=36) {iMode(4=MWT_??), xform(eDx:1500.000000, eDy:1500.000000, eM11:15.108969, eM12:0.000000, eM21:0.000000, eM22:15.101533)}
R0020: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)}
R0021: [070] EMR_GDICOMMENT (s=40) {GDI.Begin Group}
R0022: [039] EMR_CREATEBRUSHINDIRECT (s=24) {ihBrush(1), style(0=BS_SOLID, color:0x000000FF)}
R0023: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)}
R0024: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)}
R0025: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)}
R0026: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)}
R0027: [040] EMR_DELETEOBJECT (s=12) {ihObject(1)}
R0028: [070] EMR_GDICOMMENT (s=20) {GDI.End Group}
R0029: [034] EMR_RESTOREDC (s=12) {iRelative(-1)}
R0030: [027] EMR_MOVETOEX (s=16) { ptl(1500,1500)}
R0031: [054] EMR_LINETO (s=16) { ptl(2999,2999)}
R0032: [014] EMR_EOF (s=20) {nPalEntries:0, offPalEntries:16, nSizeLast:20}
Вы можете очень ясно увидеть, что операция BilBlt (R0025 в первом) отсутствует.
2 ответа
Вы создаете TMetaFileCanvas
с ReferenceDevice
параметр установлен в 0, поэтому он будет устанавливать ReferenceDevice
к HDC
от GetDC(0)
т.е. экран. ReferenceDevice
используется для получения разрешения и возможностей, которые используются во время рисования ЭДС. Например, когда TMetaFile
размеры пустые, TMetaFileCanvas
использует размеры ReferenceDevice
, TMetaFileCanvas
затем создает HDC
для себя, который имеет ограничивающий прямоугольник, основанный на размерах либо TMetaFile
или же ReferenceDevice
какой бы ни был действителен.
Итак, чтобы обойти вашу проблему, предоставьте ReferenceDevice
это достаточно большой, чтобы справиться с вашим рисунком. Вы можете предварительно определить размер TMetaFile
размеры до желаемого максимального размера до создания TMetaFileCanvas
, но вам, вероятно, придется создать TBitmap
желаемого максимального размера и использовать его Canvas.Handle
как ReferenceDevice
вместо использования экрана.
Внутренне TCanvas.StretchDraw()
просто звонки TGraphic.Draw()
, TMetaFile.Draw()
"воспроизводит" метафайл на целевом холсте HDC
, Когда это HDC
это тот, который создан TMetaFileCanvas
вы не можете рисовать вне размеров, назначенных этому TMetaFileCanvas
,
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Types, Windows, Graphics;
const
SIZECONST = 3000; // should be larger than your screen resolution
OFFSET = 1500;
var
//holds millimeter per pixel ratios
MMPerPixelHorz,
MMPerPixelVer: Integer;
procedure CreateMyMetafile(var HmyGraphic: HENHMETAFILE; aHDC: HDC);
var
R: Trect;
TheBrush: HBRUSH;
OldBrush: HBRUSH;
MetafileDC: HDC;
begin
R:= Rect(0, 0, 100*MMPerPixelHorz, 100*MMPerPixelVer);
MetafileDC:= CreateEnhMetaFile(aHDC, 'myGraphic.emf', @R, nil);
TheBrush:=CreateSolidBrush(RGB(255, 0, 0));
OldBrush:=SelectObject(MetafileDC, TheBrush);
Rectangle(MetafileDC, r.Left, r.Top, r.Right, r.Bottom);
SelectObject(MetafileDC, OldBrush);
DeleteObject(TheBrush);
HmyGraphic:=CloseEnhMetaFile(MetafileDC);
end;
procedure OutputToMetaFile(const aFilename: string; const aStartOffset,
aEndOffset, aMaxSize: Integer; aHDC: HDC);
var
r: Trect;
ReferenceRect: TRect;
MetafileDC: HDC;
HMetaFile, HMetaMyGraphic: HENHMETAFILE; {EMF file handle}
begin
//create our reference rectangle for the metafile
ReferenceRect:= Rect(0, 0, aMaxSize * MMPerPixelHorz, aMaxSize * MMPerPixelVer);
//Create First EnhMetaFile
CreateMyMetafile(HMetaMyGraphic, aHDC);
MetafileDC:=CreateEnhMetaFile(aHDC, pchar(aFilename),@ReferenceRect, nil);
//SetMapMode(MetafileDC, MM_ANISOTROPIC);
try
r:= Rect(aStartOffset, aStartOffset, aEndOffset, aEndOffset);
PlayEnhMetaFile (MetaFileDC, HMetaMyGraphic, r);
MoveToEx(MetafileDC, aStartOffset, aStartOffset, nil);
LineTo(MetafileDC, aEndOffset, aEndOffset);
HMetaFile:=CloseEnhMetaFile(MetafileDC);
finally
DeleteEnhMetaFile (HMetaFile);
DeleteEnhMetaFile (HMetaMyGraphic);
end;
end;
var
WidthInMM,
HeightInMM,
WidthInPixels,
HeightInPixels: Integer;
bigBitmap: TBitmap;
mHDC: HDC;
begin
bigBitmap := TBitmap.Create;
try
bigBitmap.PixelFormat := pf24bit;
Assert(bigBitmap.HandleType = bmDIB, 'Handle Type should be a DIB');
bigBitmap.Width := SIZECONST;
bigBitmap.Height := SIZECONST;
mHDC:= bigBitmap.Canvas.Handle;
//retrieve the size of the screen in millimeters
WidthInMM:=GetDeviceCaps(mHDC, HORZSIZE);
HeightInMM:=GetDeviceCaps(mHDC, VERTSIZE);
//retrieve the size of the screen in pixels
WidthInPixels:=GetDeviceCaps(mHDC, HORZRES);
HeightInPixels:=GetDeviceCaps(mHDC, VERTRES);
MMPerPixelHorz:=(WidthInMM * 100) div WidthInPixels;
MMPerPixelVer:=(HeightInMM * 100) div HeightInPixels;
OutputToMetaFile('inscreen.emf', 500, 1000, SIZECONST, mHDC);
OutputToMetaFile('outsidescreen.emf', OFFSET, SIZECONST-1, SIZECONST, mHDC);
finally
bigBitmap.Free;
end;
end.