加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – Windows图元文件的维度是否有限制?

发布时间:2020-12-15 10:07:56 所属栏目:大数据 来源:网络整理
导读:我正在创建一些.wmf文件,但其中一些似乎已损坏,无法显示在任何图元文件查看器中.经过一番试错,我发现问题是由他们的尺寸造成的.如果我通过一个因素缩放相同的图形来减小尺寸,那么它将被显示出来. 现在,我想知道绘图的大小是否有限制,或者问题是其他的.我知道
我正在创建一些.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,Dim,Dim),1.0,'Original.wmf');
  DrawWMF(Rect(0,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.Enhanced为True并将其保存为.emf文件将解决问题,但生成文件的目标应用程序不支持增强型图元文件.

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

主要问题是关于文件本身,它对每个维度都有一个2 ^ 15的限制.如果绘图的宽度或高度超过此值,则delphi会写入损坏的文件.您可以在Sertac’s answer找到更多的细节.

第二个问题是将文件加载到TImage中.当您想在delphi VCL应用程序中显示图像时,还有另一个限制.这一个是系统依赖的,并且与图形将被绘制的DC的dpi相关. Tom’s answer详细描述.传递0.7作为缩放DrawWMF(上面的代码示例)在我的电脑上再现这种情况.生成的文件可以使用其他图元文件查看器(我使用MS Office Picture Manager)查看,但VCL无法显示,但在加载文件时不会出现异常.

解决方法

你的上限是32767.

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

即使尺寸小于32767,“可放置的标题”可能有错误的值(从Tom’s answer读取详细的原因和含义,并给出了答案),但更多的是在以后…

我使用下面的代码找到限制.请注意,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,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限制的唯一官方文档是this(其主要内容是“仅在16位可执行文件中使用wmf”)).

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

var
  Header: TEnhMetaHeader;
begin
  DrawWMF(Rect(0,'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;

  ...

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读