Recent

Author Topic: FPImageException while loading Bitmap from resource stream  (Read 5216 times)

Alex.Machado

  • New Member
  • *
  • Posts: 35
FPImageException while loading Bitmap from resource stream
« on: September 30, 2023, 06:36:36 am »
Hi all,

I'm trying to load a bitmap from a resource stream under Windows and the code is failing without any apparent reason. The same code would work fine in Delphi. The image is valid, the resource is linked to the EXE (Can be checked with any resource editor, like Resourcehacker). However seems to me that LCL is not able to load the bitmap header correctly. The error I get is always "FPImageException Bitmap with unknwon compression".

Here is the test case that shows the problem.

Is there any workaround or something that can be done to avoid this?

Thanks in advance

Dzandaa

  • Sr. Member
  • ****
  • Posts: 404
  • From C# to Lazarus
Re: FPImageException while loading Bitmap from resource stream
« Reply #1 on: September 30, 2023, 12:09:32 pm »
Hi,

Try with BGRABitmap instead:

Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  3.   BGRABitmap, BGRABitmapTypes;  
  4.  
  5.  
  6. function GetBitmap(AName: string): TBGRABitmap;
  7. var
  8.   RStream: TResourceStream;
  9. begin
  10.   AName := UpperCase(AName);
  11.   RStream := TResourceStream.Create(HInstance, AName, RT_RCDATA);
  12.   try
  13.     Result := TBGRABitmap.Create;
  14.     Result.LoadFromStream(RStream);
  15.   finally
  16.     FreeAndNil(RStream);
  17.   end;
  18. end;  
  19.  

B->
Regards,
Dzandaa

wp

  • Hero Member
  • *****
  • Posts: 12513
Re: FPImageException while loading Bitmap from resource stream
« Reply #2 on: September 30, 2023, 01:18:01 pm »
I studied the resource file with my hex editor and found that it in fact contains the bitmap as type RT_RCDATA which contains a full bitmap file beginning with the typical 'BM' signature which is the start of the BitmapFileHeader.

When, on the other hand, I create a resourcefile with LazRes the bmp file is contained as resource type RT_BITMAP. Since here the image type is already known the first header of the bitmap structure (the BitmapFileHeader) is missing and the resource begins with the BitmapInfoHeader.

Lazarus has a problem now. The class to be read is a TBitmap and makes Lazarus assume that the resource is type RT_BITMAP although the real resource type RT_RCDATA is specified in the reading command. Therefore it reads the BitmapFileHeader at the place where it expects the BitmapInfoHeader and gets incorrect data from now on...

This is clearly a bug in the LCL resource access routines and should be reported (I see that you are new here, and I can report it for you).

TRon

  • Hero Member
  • *****
  • Posts: 3739
Re: FPImageException while loading Bitmap from resource stream
« Reply #3 on: September 30, 2023, 01:35:29 pm »
@wp:
Have you verified with fpcres if that exposes the same behaviour ? afaik that should be used instead of lazres.

nvm, it seems to do the same (wrong) thing.
« Last Edit: September 30, 2023, 01:51:11 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

wp

  • Hero Member
  • *****
  • Posts: 12513
Re: FPImageException while loading Bitmap from resource stream
« Reply #4 on: September 30, 2023, 02:42:58 pm »
No, it's not a matter of resource generation, but of resource reading.

A possible solution: At the place where the BitmapInfoHeader is expected, but the BitmapFileHeader is found, the reader could check whether the first 2 bytes read are the bitmap signature 'BM'. If this is true it very probably has detected the BitmapFileHeader, must advance the stream position by the size of the BitmapFileHeader to get to the BitmapInfoHeader, and repeat from where it started reading.

To be more specific:
  • Open unit IntfGraphics (from folder lcl of the Lazarus installation)
  • Find procedure TLazReaderDIB.InternalReadHead.
  • Before the "begin" add a variable "Magic: Array[0..1] of ansiChar absolute BIH"
  • After the "{$ENDIF}" closely after the "begin" insert the following highlighted lines
Code: Pascal  [Select][+][-]
  1. var
  2.   ...
  3.   Magic: array[0..1] of AnsiChar absolute BIH;
  4. begin
  5.   StreamStart := theStream.Position;
  6.   TheStream.Read(BIH.biSize,SizeOf(BIH.biSize));
  7.   {$IFDEF FPC_BIG_ENDIAN}
  8.   BIH.biSize := LEtoN(BIH.biSize);
  9.   {$ENDIF}
  10.  
  11. // ------------------ wp: insert this ...
  12.   // Workaround for bitmaps in RT_RCDATA resources not being read correctly.
  13.   // These resources have the BitmapFileHeader at the place where the stream is
  14.   // here atm. The stream expects a TBitmapInfoHeader or TBitmapCoreHeader, though.
  15.   if (Magic[0] = 'B') and (Magic[1] = 'M') then
  16.   begin
  17.     TheStream.Position := StreamStart + SizeOf(TBitmapFileHeader);
  18.     TheStream.Read(BIH.biSize,SizeOf(BIH.biSize));
  19.     {$IFDEF FPC_BIG_ENDIAN}
  20.     BIH.biSize := LEtoN(BIH.biSize);
  21.     {$ENDIF}
  22.   end;
  23. // ------------------- ... until here.
  24.  
  25.   if BIH.biSize = 12  

Kind of "brute force"... but it seems to work. I am aware that the assumption about having the complete file structure has been made also at another place, and this makes me think that this change introduces another issue somewhere else...

paweld

  • Hero Member
  • *****
  • Posts: 1278
Re: FPImageException while loading Bitmap from resource stream
« Reply #5 on: September 30, 2023, 03:22:42 pm »
you can get around this by using TMemoryStream:
Code: Pascal  [Select][+][-]
  1. function GetBitmap(AName: string): Graphics.TBitmap;
  2. var
  3.   RStream: TResourceStream;
  4.   ms: TMemoryStream;
  5. begin
  6.   AName := UpperCase(AName);
  7.   RStream := TResourceStream.Create(HInstance, AName, RT_RCDATA);
  8.   ms := TMemoryStream.Create;
  9.   ms.CopyFrom(RStream, 0);
  10.   ms.Position := 0;
  11.   try
  12.     Result := Graphics.TBitmap.Create;
  13.     Result.LoadFromStream(ms, ms.Size);
  14.   finally
  15.     FreeAndNil(RStream);
  16.     ms.Free;
  17.   end;
  18. end;  
  19.  
Best regards / Pozdrawiam
paweld

TRon

  • Hero Member
  • *****
  • Posts: 3739
Re: FPImageException while loading Bitmap from resource stream
« Reply #6 on: September 30, 2023, 03:24:17 pm »
No, it's not a matter of resource generation, but of resource reading.
Yeah, I can see now (took me a while to verify). Thank you for the correction.

Quote
A possible solution: At the place where the BitmapInfoHeader is expected, but the BitmapFileHeader is found, the reader could check whether the first 2 bytes read are the bitmap signature 'BM'. If this is true it very probably has detected the BitmapFileHeader, must advance the stream position by the size of the BitmapFileHeader to get to the BitmapInfoHeader, and repeat from where it started reading.
I see. imho LoadFromStream() should be no different than LoadFromFile() other than the one is reading from a file while the other reads the same data from a stream.

Quote
Kind of "brute force"... but it seems to work. I am aware that the assumption about having the complete file structure has been made also at another place, and this makes me think that this change introduces another issue somewhere else...
Yes, I seem to remember that the issue was mentioned before (not able to locate it yet) and technically adding the workaround in dibreader would (also imho) not be the correct location (the dib itself does/should not contain the TBitMapFileHeader). But perhaps I am oversimplifying things right now.
« Last Edit: September 30, 2023, 03:47:12 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

TRon

  • Hero Member
  • *****
  • Posts: 3739
Re: FPImageException while loading Bitmap from resource stream
« Reply #7 on: September 30, 2023, 03:25:45 pm »
you can get around this by using TMemoryStream:
There are multiple workarounds possible, including yours. Fact is that it is (according to TS) not Delphi compatible which is more of an issue I guess.

Though I do not understand why TS would want to store a bitmap as a DATA resource. Using a BITMAP resource would make thing much more convenient.
« Last Edit: September 30, 2023, 04:05:03 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

TRon

  • Hero Member
  • *****
  • Posts: 3739
Re: FPImageException while loading Bitmap from resource stream
« Reply #8 on: September 30, 2023, 04:24:21 pm »
hmz, it seems even worse.

Code: Pascal  [Select][+][-]
  1. procedure TBitmap.LoadFromStream(AStream: TStream; ASize: Cardinal);
  2. var
  3.   S: THeaderStream;
  4.   Header: TBitmapFileHeader;
  5. begin
  6.   if AStream is TResourceStream then
  7.   begin
  8.     FillChar(Header, SizeOf(Header), 0);
  9.  
  10. { Create a BMP header ordered as it would be on disc, noting that if the CPU
  11.   is big-endian this will be the "wrong way round" for numeric operations.      }
  12.  
  13.     {$IFNDEF ENDIAN_BIG}
  14.     Header.bfType := $4d42;
  15.     Header.bfSize := SizeOf(Header) + ASize;
  16.     {$ELSE}
  17.     Header.bfType := $424d;
  18.     Header.bfSize := swap(SizeOf(Header) + ASize);
  19.     {$ENDIF}
  20.     //Header.bfOffBits := 0; //data imediately follows
  21.  
  22.     S := THeaderStream.Create(AStream, @Header, SizeOf(Header));
  23.     try
  24.       inherited LoadFromStream(S, SizeOf(Header) + ASize);
  25.     finally
  26.       S.Free;
  27.     end;
  28.   end
  29.   else
  30.     inherited LoadFromStream(AStream, ASize);
  31. end;
  32.  
Why ? This makes it only more confusing and seems more like a hack to me. It is simply wrong to assume that every stream that is of type resource requires a BMP header.

fwiw: TBitmap.LoadFromResourceXX() functions do not even seem to search RT_DATA resources and as such return a neat: "resource <name of resource> not found".

« Last Edit: September 30, 2023, 04:38:19 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

wp

  • Hero Member
  • *****
  • Posts: 12513

wp

  • Hero Member
  • *****
  • Posts: 12513
Re: FPImageException while loading Bitmap from resource stream
« Reply #10 on: September 30, 2023, 04:51:41 pm »
I think the TBitmap.LoadFromStream looks like the place where the correct fix should be done:

In pseudo-code
Code: Pascal  [Select][+][-]
  1. procedure TBitmap.LoadFromStream(AStream: TStream; ASize: Cardinal);
  2. ...
  3. begin
  4.   if (AStream is TResourceStream) and (GetResourceType = RT_BITMAP) then
  5.   begin
  6.     ...
  7.   end else
  8.     inherited LoadFromStream(AStream, ASize);
  9. end;

But how to determine the ResourceType here?

[EDIT] Ah well... The ResourceStream does not begin at the bitmap itself but contains some additional header data. Therefore, above pseudo-code is too simple.   
« Last Edit: September 30, 2023, 10:43:26 pm by wp »

TRon

  • Hero Member
  • *****
  • Posts: 3739
Re: FPImageException while loading Bitmap from resource stream
« Reply #11 on: September 30, 2023, 04:53:12 pm »
Thank you for the bugreport wp, and I agree with your presented solution. The earlier fixes that you proposed is afaik only required because of the double header.

The only option I can see for now is verifying the presence of $4d42 in the memory pointer but it is still ugly.
« Last Edit: September 30, 2023, 04:57:05 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

Alex.Machado

  • New Member
  • *
  • Posts: 35
Re: FPImageException while loading Bitmap from resource stream
« Reply #12 on: September 30, 2023, 10:15:12 pm »
Thank you all for the responses.

I could possibly set the resource as a BITMAP but for now I'm more than happy to use a TMemoryStream as suggested by @paweld. I missed from the source code of the TBitmap class itself that loading directly from a TResourceStream would assume a specific type of resource...

Cheers,
« Last Edit: September 30, 2023, 10:16:54 pm by Alex.Machado »

TRon

  • Hero Member
  • *****
  • Posts: 3739
Re: FPImageException while loading Bitmap from resource stream
« Reply #13 on: September 30, 2023, 10:25:29 pm »
@wp:
The more time I have to think about this, the more I am inclined to say that it should read:
Code: Pascal  [Select][+][-]
  1. procedure TBitmap.LoadFromStream(AStream: TStream; ASize: Cardinal);
  2. begin
  3.   inherited LoadFromStream(AStream, ASize);
  4. end;
  5.  
Or to be more precise: no overload at all for LoadFromStream.

Perhaps at the time the workaround was introduced the resource related routines were not up to par (at least the resource compiler wasn't) so it would probably justify such a workaround back then.

It is imho more logical that the LoadFromResourceXX() methods are there for a particular reason (e.g. to load a bitmap from a resource). Having said that, I do not have Delphi to be able to verify (but the documentation on Delphi's LoadFromStream also only mentions a bitmap (embarcadeo website is down for me atm)).

I do not have to remember anything anymore thanks to total-recall.

wp

  • Hero Member
  • *****
  • Posts: 12513
Re: FPImageException while loading Bitmap from resource stream
« Reply #14 on: September 30, 2023, 10:41:34 pm »
@wp:
The more time I have to think about this, the more I am inclined to say that it should read:
[...]
Or to be more precise: no overload at all for LoadFromStream.
I don't think so. Because when the stream is a resource stream the stream does not start at the BitmapFileHeader ('BM') but is offset by a few bytes and, in case of the RT_BITMAP, there is not BitmapFileHeader at all.

 

TinyPortal © 2005-2018