加载Jpg/Gif/Bitmap并转换为Bitmap

20

我需要从一个XML文件中加载一张图片,但是在XML文件中没有关于这个图片是JPG/GIF/BMP的信息。加载完图片后,我需要将其转换为位图。

有人知道如何在不知道实际文件格式的情况下将图片转换为位图吗?我正在使用Delphi 2007/2009。

谢谢。


请见:https://dev59.com/P6_la4cB1Zd3GeqP0ews#53382894 - Gabriel
4个回答

41

Delphi 2009内置了对JPEG、BMP、GIF和PNG的支持。

如果你使用的是早期版本的Delphi,可能需要寻找第三方实现的PNG和GIF,但在Delphi 2009中,你只需要将JpegpngimageGIFImg单元添加到你的uses子句中即可。

如果文件有扩展名,你可以使用以下代码,如其他人所指出的,TPicture.LoadFromFile会查看由继承类注册的扩展来确定要加载哪个图像。

uses
  Graphics, Jpeg, pngimage, GIFImg;

procedure TForm1.Button1Click(Sender: TObject);
var
  Picture: TPicture;
  Bitmap: TBitmap;
begin
  Picture := TPicture.Create;
  try
    Picture.LoadFromFile('C:\imagedata.dat');
    Bitmap := TBitmap.Create;
    try
      Bitmap.Width := Picture.Width;
      Bitmap.Height := Picture.Height;
      Bitmap.Canvas.Draw(0, 0, Picture.Graphic);
      Bitmap.SaveToFile('C:\test.bmp');
    finally
      Bitmap.Free;
    end;
  finally
    Picture.Free;
  end;
end;

如果文件扩展名未知,一种方法是查看前几个字节以确定图像类型。

procedure DetectImage(const InputFileName: string; BM: TBitmap);
var
  FS: TFileStream;
  FirstBytes: AnsiString;
  Graphic: TGraphic;
begin
  Graphic := nil;
  FS := TFileStream.Create(InputFileName, fmOpenRead);
  try
    SetLength(FirstBytes, 8);
    FS.Read(FirstBytes[1], 8);
    if Copy(FirstBytes, 1, 2) = 'BM' then
    begin
      Graphic := TBitmap.Create;
    end else
    if FirstBytes = #137'PNG'#13#10#26#10 then
    begin
      Graphic := TPngImage.Create;
    end else
    if Copy(FirstBytes, 1, 3) =  'GIF' then
    begin
      Graphic := TGIFImage.Create;
    end else
    if Copy(FirstBytes, 1, 2) = #$FF#$D8 then
    begin
      Graphic := TJPEGImage.Create;
    end;
    if Assigned(Graphic) then
    begin
      try
        FS.Seek(0, soFromBeginning);
        Graphic.LoadFromStream(FS);
        BM.Assign(Graphic);
      except
      end;
      Graphic.Free;
    end;
  finally
    FS.Free;
  end;
end;

这么简单吗?非常感谢!是的,图像数据在xml文件的CDATA部分中。顺便问一下,Delphi 2007不支持Gif吗?我不需要png支持。 - J K Kunil
1
JPEG 已经存在一段时间了。不确定 GIF。我过去总是使用 Jedi 来支持 GIF。 - Jim McKeeth
使用:fmOpenRead 或 fmShareDenyNone {这样可以避免文件被锁定而导致失败} - Gabriel

13

我发现了一个更简单的方法!它会自动加载JPG/GIF/BMP等文件,甚至不用知道/检查文件格式,并相应地进行转换。 对我来说完全奏效。

在这里分享一下 :)

Uses
Classes, ExtCtrls, Graphics, axCtrls;

Procedure TForm1.Button1Click(Sender: TObject);
Var
     OleGraphic               : TOleGraphic;
     fs                       : TFileStream;
     Source                   : TImage;
     BMP                      : TBitmap;
Begin
     Try
          OleGraphic := TOleGraphic.Create; {The magic class!}

          fs := TFileStream.Create('c:\testjpg.dat', fmOpenRead Or fmSharedenyNone);
          OleGraphic.LoadFromStream(fs);

          Source := Timage.Create(Nil);
          Source.Picture.Assign(OleGraphic);

          BMP := TBitmap.Create; {Converting to Bitmap}
          bmp.Width := Source.Picture.Width;
          bmp.Height := source.Picture.Height;
          bmp.Canvas.Draw(0, 0, source.Picture.Graphic);

          image1.Picture.Bitmap := bmp; {Show the bitmap on form}
     Finally
          fs.Free;
          OleGraphic.Free;
          Source.Free;
          bmp.Free;
     End;
End;

是的。伤心。没有PNG = 没有乐趣! - Gabriel
请注意,此答案存在严重的内存管理错误。如果 TOleGraphic 构造函数失败,则会在随机指针上尝试 fs.FreeOleGraphic.FreeSource.Freebmp.Free,这非常糟糕!同样,如果 TFileStream 构造函数失败,则会在随机指针上尝试 fs.FreeSource.Freebmp.Free。等等,直到 bmp.Width := ...。这一行是第一行正确的代码。始终使用习语 X := TX.Create; try {use X} finally X.Free; end,或者至少在 try 之前将指针初始化为 nil。在此问题得到解决之前,我会扣除1分。 - Andreas Rejbrand

9
如果您不知道图形的格式,就不能使用TPicture.LoadFromFile,因为该方法使用文件扩展名来确定需要加载哪个注册图形格式。这就是为什么没有匹配的TPicture.LoadFromStream方法的原因。
最好的解决方案是使用一个外部库,在运行时检查数据并确定图形格式。您可以使用efg页面作为您研究的起点。
一个快速而肮脏的解决方案是尝试处理您需要处理的几种格式,直到成功为止:
function TryLoadPicture(const AFileName: string; APicture: TPicture): boolean;
const
  GraphicClasses: array[0..3] of TGraphicClass = (
    TBitmap, TJPEGImage, TGIFImage, TPngImage);
var
  FileStr, MemStr: TStream;
  ClassIndex: integer;
  Graphic: TGraphic;
begin
  Assert(APicture <> nil);
  FileStr := TFileStream.Create('D:\Temp\img.dat', fmOpenRead);
  try
    MemStr := TMemoryStream.Create;
    try
      MemStr.CopyFrom(FileStr, FileStr.Size);
      // try various
      for ClassIndex := Low(GraphicClasses) to High(GraphicClasses) do begin
        Graphic := GraphicClasses[ClassIndex].Create;
        try
          try
            MemStr.Seek(0, soFromBeginning);
            Graphic.LoadFromStream(MemStr);
            APicture.Assign(Graphic);
            Result := TRUE;
            exit;
          except
          end;
        finally
          Graphic.Free;
        end;
      end;
    finally
      MemStr.Free;
    end;
  finally
    FileStr.Free;
  end;
  Result := FALSE;
end;

编辑:

GraphicEx库有一个示例转换,使用以下内容:

GraphicClass := FileFormatList.GraphicFromContent(...);

确定图形格式。这似乎与您提到的VB6方式非常相似。也许您可以使用此库来实现您的目的。


谢谢您的解决方案和澄清上面的回复。 您的代码看起来很有趣,但正如您所提到的,它实际上是一种快速而粗糙的方法。我正在寻找类似于VB 6 LoadPicture函数的东西,它适用于支持的图像,无论文件名扩展名如何。 - J K Kunil
来自GraphicEx网站的引用:“最后更改日期:2007年2月3日” - Gabriel

7

我没有Delphi 2007或2009版本,无法确定该方法是否适用于这两个版本。然而,在XE2中,Vcl.Graphics中有另一个类TWICImage,它处理由Microsoft Imaging Component支持的图像格式,包括BMP,GIF,ICO,JPEG,PNG,TIF和Windows Media Photo 。它能够从数据流中检测图像类型。假设你在窗体上有一个名为的TImage

procedure LoadImageFromStream(Stream: TStream; Image: TImage);
var
  wic: TWICImage;
begin
  Stream.Position := 0;
  wic := TWICImage.Create;
  try
    wic.LoadFromStream(Stream);
    Image.Picture.Assign(wic);
  finally
    wic.Free;
  end;
end;

procedure RenderImage(const Filename: string);
var
  fs: TFileStream;
begin
  fs := TFileStream.Create(Filename, fmOpenRead);
  try
    LoadImageFromStream(fs, Image1);
  finally
    fs.Free;
  end;
end;

无需将 PNGImage, GIFImg, 或 JPEG 添加到你的 uses 语句中,就可以正常工作。

其他答案展示了如何将 TImage 转换为 BMP,因此我在这里不包括它。 我只是展示了另一种方法,可以在不预先知道图像类型或文件扩展名的情况下,将各种图形类型加载到 TImage 中...


1
在C++Builder XE5中完美运行。 - M.M
TPicture::Assign会将图像数据复制到Picture中吗?(我担心在wic.Free之后会出现悬空指针) - M.M
1
@MattMcNabb - 是的,它会复制它(http://docwiki.embarcadero.com/Libraries/XE5/en/Vcl.Graphics.TPicture.Assign),这已经通过跟踪 VCL 代码进行验证。 - James L.
通过WIC加载比通过TImage从文件加载要快得多。 - Gabriel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接