We have a problem with the TMetaFileCanvas output when drawing an image to a coordinate outside of the screen resolution. Vector operations seem to have no issues but image operations are just "ignored". If we draw the same image to a coordinate within the screen bounds then there are no issues.
For example. This SSCCE will produce 4 output files. The bitmap variant has no problems and will output as expected with the red square in the top left hand corner for inscreen.bmp
and the red square in the bottom right hand corner for outsidescreen.bmp
. The inscreen.emf
meta file works as expected with the red square drawn in the top left corner. outsidescreen.emf
doesn't work and only the line is drawn.
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.
Can anyone see what the problem is or do you know of a work around for this?
Update
I should have included this when I originally asked the question. We did test using the HDC for a large bitmap and that exhibited the same problem. I have updated the example code to demonstrate this.
Update 2
Unfortunately the solution is still elusive even after the bounty. Any BitBlt operation outside the screen size is not drawn.
Here is a extraction of the Metafile operations when the image is in the bounds of the screen coordinates:
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}
Here is a extraction of the Metafile operations when the image is outside the bounds of the screen coordinates:
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}
You can see very clearly that the BilBlt operation (R0025 in the first one) is missing.