当前位置:  开发笔记 > 编程语言 > 正文

Windows图元文件的尺寸是否有限制?

如何解决《Windows图元文件的尺寸是否有限制?》经验,为你挑选了1个好方法。

我正在创建一些.wmf文件,但其中一些似乎已损坏,无法在任何元文件查看器中显示.经过一些反复试验,我发现问题是由它们的尺寸引起的.如果我按比例缩放相同的图形以减小尺寸,则会显示.

现在,我想知道绘图的大小是否存在限制,或者问题是否是其他问题.我知道这些文件有16位数据结构,所以我猜每个维度的限制是2 ^ 16个单位,(如果签名则为2 ^ 15).但在我的测试中它大约是25,000.所以我不能依赖这个值,因为限制可以是任何东西(宽度*高度可能,或者图纸的分辨率可能会影响它).我找不到关于描述这个的.wmf文件的可靠资源.

以下是显示问题的示例代码:

procedure DrawWMF(const Rect: TRect; const Scale: Double; FileName: string);
var
  Metafile: TMetafile;
  Canvas: TMetafileCanvas;
  W, H: Integer;
begin
  W := Round(Rect.Width * Scale);
  H := Round(Rect.Height * Scale);

  Metafile := TMetafile.Create;
  Metafile.SetSize(W, H);

  Canvas := TMetafileCanvas.Create(Metafile, 0);
  Canvas.LineTo(W, H);
  Canvas.Free;

  Metafile.SaveToFile(FileName);
  Metafile.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Dim = 40000;
begin
  DrawWMF(Rect(0, 0, Dim, Dim), 1.0, 'Original.wmf');
  DrawWMF(Rect(0, 0, Dim, Dim), 0.5, 'Scaled.wmf');

  try
    Image1.Picture.LoadFromFile('Original.wmf');
  except
    Image1.Picture.Assign(nil);
  end;

  try
    Image2.Picture.LoadFromFile('Scaled.wmf');
  except
    Image2.Picture.Assign(nil);
  end;
end;

PS:我知道,设置Metafile.EnhancedTrue并将其保存为一个文件的.emf就能解决问题,但我生成文件目标应用程序不支持增强型图元文件.

编辑: 如下面的答案所述,这里有两个不同的问题:

主要问题是关于文件本身,它对每个维度有2 ^ 15的限制.如果图形的宽度或高度超过此值,delphi将写入损坏的文件.您可以在Sertac的答案中找到更多详细信息.

第二个问题是关于加载文件TImage.当您想要在delphi VCL应用程序中显示图像时,还有另一个限制.这个是系统相关的,并且与将要绘制绘图的DC的dpi相关.汤姆的回答详细描述了这一点.传递0.7如ScaleDrawWMF(上面的代码示例)抄录我的电脑上这种情况.生成的文件正常,可以与其他Metafile查看器(我使用MS Office Picture Manager)查看,但VCL无法显示它,但是,加载文件时不会引发异常.



1> Sertac Akyuz..:

你的限制是32767.

跟踪VCL代码,输出文件被破坏TMetafile.WriteWMFStream.VCL写入WmfPlaceableFileHeader(TMetafileHeader在VCL中)记录,然后调用GetWinMetaFileBits将'emf'记录转换为'wmf'记录.如果边界矩形的任何维度(在调用时使用CreateEnhMetaFile)大于32767,则此函数将失败.不检查返回值,VCL不会引发任何异常并仅使用22个字节关闭文件 - 仅具有"可放置标头" .

即使对于小于32767的维度,"可放置的标题"也可能有错误的值(阅读有关汤姆答案的原因和含义的详细信息以及对答案的评论),但稍后会详细介绍......

我使用下面的代码来查找限制.请注意,GetWinMetaFileBits在VCL代码中不会使用增强的图元文件进行调用.

function IsDimOverLimit(W, H: Integer): Boolean;
var
  Metafile: TMetafile;
  RefDC: HDC;
begin
  Metafile := TMetafile.Create;
  Metafile.SetSize(W, H);
  RefDC := GetDC(0);
  TMetafileCanvas.Create(Metafile, RefDC).Free;
  Result := GetWinMetaFileBits(MetaFile.Handle, 0, nil, MM_ANISOTROPIC, RefDC) > 0;
  ReleaseDC(0, RefDC);
  Metafile.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i := 20000 to 40000 do
    if not IsDimOverLimit(100, i) then begin
      ShowMessage(SysErrorMessage(GetLastError)); // ReleaseDc and freeing meta file does not set any last error
      Break;
    end;
end;

错误是534("算术结果超过32位").显然有一些有符号整数溢出.某些"mf3216.dll"("32位到16位图元文件转换DLL")在调用GetWinMetaFileBits其导出ConvertEmfToWmf函数期间设置错误,但这不会导致有关溢出的任何文档.关于我可以找到的wmf限制的唯一官方文档就是这个(它的主要观点是"仅在16位可执行文件中使用wmf" :)).


如前所述,伪造的"可放置标题"结构可能具有"伪造"值,这可能会阻止VCL正确播放元文件.具体来说,元文件的维度,如VCL所知,可能会溢出.加载图像后,您可以执行简单的健全性检查,以便正确显示:

var
  Header: TEnhMetaHeader;
begin
  DrawWMF(Rect(0, 0, Dim, Dim), 1.0, 'Original.wmf');
  DrawWMF(Rect(0, 0, Dim, Dim), 0.5, 'Scaled.wmf');

  try
    Image1.Picture.LoadFromFile('Original.wmf');
    if (TMetafile(Image1.Picture.Graphic).Width < 0) or
        (TMetafile(Image1.Picture.Graphic).Height < 0) then begin
      GetEnhMetaFileHeader(TMetafile(Image1.Picture.Graphic).Handle,
          SizeOf(Header), @Header);
      TMetafile(Image1.Picture.Graphic).Width := MulDiv(Header.rclFrame.Right,
          Header.szlDevice.cx, Header.szlMillimeters.cx * 100);
      TMetafile(Image1.Picture.Graphic).Height := MulDiv(Header.rclFrame.Bottom,
          Header.szlDevice.cy, Header.szlMillimeters.cy * 100);
  end;

  ...

推荐阅读
围脖上的博博_771
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有