TBitmap32.LoadFromStream() Auto-Recognize Image Fo

2019-06-28 09:19发布

问题:

I'm using Delphi XE2 (Update 3) and GR32. I am unable to use TBitmap32.LoadFromStream() to load the image data. It raises the following exception:

Project MyApp.exe raised exception class EInvalidGraphic with message 'Bitmap image is not valid'.

Code

uses GR32, GifImg, PngImage, Vcl.Graphics;

var
  pic: TBitmap32;
  bs: TBytesStream;
begin
  bs := TBytesStream.Create(TClientDataSet(cds).FieldByName('filedata').AsBytes);
  try
    pic := TBitmap32.Create;
    try
//      bs.SaveToFile('c:\delme.png');
//      pic.LoadFromFile('c:\delme.png');
      pic.LoadFromStream(bs); // <- EInvalidGraphic exception
      // do something with 'pic'
    finally
      FreeAndNil(pic);
    end;
  finally
    FreeAndNil(bs);
  end;
end;

Workaround(s)

If I comment the LoadFromStream() code and uncomment the prior two lines, it works -- so it is able to determine the image format when loading it from a file. In this example, bs contains a valid PNG image. However, sometimes it may be a GIF, JPG, BMP or another graphic format.

I also know that I can use an intermediate object (e.g., TPNGImage, TJPEGImage, etc.), load the image data using LoadFromStream() into the intermediate object, and then Assign() it to the TBitmap32. However, it would be better if TBitmap32 could handle any type of image without an intermediate object, which I thought it could...

Question

Does anyone know how to use TBitmap32.LoadFromStream() to load the image and have it auto-recognize the image format without saving the image to the HDD (even temporarily), or using an intermediate object?

回答1:

This is the code for LoadFromFile:

procedure TCustomBitmap32.LoadFromFile(const FileName: string);
var
  FileStream: TFileStream;
  Header: TBmpHeader;
  P: TPicture;
begin
  FileStream := TFileStream.Create(Filename, fmOpenRead);
  try
    FileStream.ReadBuffer(Header, SizeOf(TBmpHeader));

    // Check for Windows bitmap magic bytes...
    if Header.bfType = $4D42 then
    begin
      // if it is, use our stream read method...
      FileStream.Seek(-SizeOf(TBmpHeader), soFromCurrent);
      LoadFromStream(FileStream);
      Exit;
    end
  finally
    FileStream.Free;
  end;

  // if we got here, use the fallback approach via TPicture...
  P := TPicture.Create;
  try
    P.LoadFromFile(FileName);
    Assign(P);
  finally
    P.Free;
  end;
end;

As you can see, the code checks to see if the file is a Windows bitmap. If so then it loads it directly by calling LoadFromStream.

If not then it loads into a TPicture and then assigns to this instance.

The point is that LoadFromStream only understands Windows bitmaps. It does not understand any other file format. And you are trying to load a PNG. So, there's no way to do what you need without using an intermediate object.

The solution for you is to:

  1. Instantiate a TPNGImage object.
  2. Call LoadFromStream on that instance.
  3. Call Assign on the TBitmap32 instance passing the TPNGImage instance.


回答2:

Delphi XE2 includes a class in Vcl.Graphics called TWICImage that handles images supported by the Windows Imaging Component, including BMP, GIF, ICO, JPEG, PNG, TIF and Windows Media Photo. This means you can load any of the supported image types directly from a stream and it will automatically detect the image type and load it. Then the loaded image can be assigned to the TBitmap32 component (or a TImage or a TImage32, etc.).

uses GR32, Vcl.Graphics;

var
  pic: TBitmap32;
  wic: TWICImage;
  bs: TBytesStream;
begin
  bs := TBytesStream.Create(TClientDataSet(cds).FieldByName('filedata').AsBytes);
  try
    pic := TBitmap32.Create;
    try
      wic := TWICImage.Create;
      try
        bs.Position := 0;
        wic.LoadFromStream(bs);
        pic.Assign(wic);
      finally
        FreeAndNil(wic);
      end;
    finally
      FreeAndNil(pic);
    end;
  finally
    FreeAndNil(bs);
  end;
end;

It works without adding PNGImage, GIFImg, or JPEG to your uses statement.