7

在将图像绘制到屏幕分辨率之外的坐标时,我们遇到了 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 操作。

这是当图像在屏幕坐标范围内时提取的 Metafile 操作:

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}

以下是当图像超出屏幕坐标范围时的 Metafile 操作的提取:

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)丢失了。

4

2 回答 2

6

您正在创建TMetaFileCanvas参数ReferenceDevice设置为 0,因此它将设置ReferenceDeviceHDCfrom GetDC(0),即屏幕。ReferenceDevice用于获取在 EMF 绘图期间使用的分辨率和功能。例如,当TMetaFile的维度为空时,TMetaFileCanvas使用 的维度ReferenceDeviceTMetaFileCanvas然后根据或HDC的尺寸为自己创建一个边界矩形,无论哪个是有效的。TMetaFileReferenceDevice

因此,要解决您的问题,请提供一个ReferenceDevice足够大来处理您的绘图。您可以在创建 之前将尺寸预先TMetaFile设置为所需的最大尺寸TMetaFileCanvas,但您可能必须创建TBitmap所需的最大尺寸并使用其Canvas.HandleasReferenceDevice而不是使用屏幕。

在内部,TCanvas.StretchDraw()只需调用TGraphic.Draw(). TMetaFile.Draw()将元文件“播放”到目标画布的HDC. 当它HDC是由 创造的时TMetaFileCanvas,您不能在分配给它的尺寸之外绘制TMetaFileCanvas

于 2015-05-22T01:21:11.680 回答
2
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.
于 2015-05-27T21:26:09.427 回答