Board index » delphi » TBitmap **BUG** Read if you use TBitmap!!!

TBitmap **BUG** Read if you use TBitmap!!!

I'd love to be proved wrong, but it seems in rare cases
TBitmap.LoadFromFile (or stream, etc.) mis-aligns bitmap bytes.  This
results in, for example, red becoming green, green -> blue, and blue ->
red.

*** IF YOU ARE USING TBITMAP THEN YOUR PROGRAM COULD PRODUCE EMBARRASSING
RESULTS!! ***

I found the problem when loading bitmaps saved by my Kodak DC120 camera.
If anyone wants to confirm the bug, I'll send them a sample bitmap.  The
bitmaps load fine in commerical graphics apps.

THE CAUSE:  In 24-bit bitmaps, I think TBitmap.LoadFromFile expects the
bitmap data to follow immediately after the headers (BitmapFileHeader and
BitmapInfoHeader) - that is, 54 bytes in.  BUT, the BitmapFileHeader
'bfOffBits' field can specify a different starting point.  bfOffBits is
usually 54, but doesn't have to be.  The DC120 bitmaps have a 58 byte
offset.  Because Delphi starts reading at 54 and the bytes are in RGB
triples, the triples are misaligned and RGB becomes GBR!

A SOLUTION: I've implemented a 'TOKBitmap' type that descends from TBitmap
and overrides 'LoadFromFile'.  The code is below.  I'd like to know from
anyone if the code looks 'solid'.  Does it risk creating problems of its
own?

I'd love to hear from Borland on this!  I think it is a major bug!  It's in
Delphi2 and Delphi3 but I don't have Delphi4.

Hope this helps someone out there!

Regards
Julian Brewer

THE CODE:  *** MUST USE THE {$A-} DIRECTIVE ***

unit OKBitmap;

interface

{$A-}

uses
    Windows, Graphics, Classes, SysUtils;

type

  tBitmapfileheader = record
                            bfType : word;
                            bfSize : integer;
                            bfReserved1 : word;
                            bfReserved2 : word;
                            bfOffBits : integer;
                      end;
  tBitmapinfoheader = record
                            biSize : integer;
                            biWidth : integer;
                            biHeight : integer;
                            biPlanes : word;
                            biBitCount : word;
                            biCompression : integer;
                            biSizeImage : integer;
                            biXPel{*word*237}eter : integer;
                            biYPel{*word*237}eter : integer;
                            biClrUsed : integer;
                            biClrImportant : integer;
                      end;

  TOKBitmap = class(TBitmap)
  private
  public
    procedure LoadFromFile(const FileName: string); override;
  end;

implementation

procedure TOKBitmap.LoadFromFile(const FileName: string);
var
   FS,FSout : TFileStream;
   BMFH : tBitmapfileheader;
   BMIH : tBitmapinfoheader;
   NewFileName : string;
   CopySize : integer;
   StandardHeader : array[0..53] of byte;
   WasError : boolean;
begin
     if not FileExists(FileName) then exit;
     try
        WasError := true;
        FS := TFileStream.Create(FileName,fmOpenRead);
        FS.ReadBuffer(BMFH,sizeof(tBitmapfileheader));
        FS.ReadBuffer(BMIH,sizeof(tBitmapinfoheader));
        WasError := false;
     finally
            FS.Free;
     end;
     if WasError then exit;
     // If this is a 24-bit bitmap then Delphi appears to ignore the
     // bfOffbits field and starts reading the bits from an offset of
     // 54.  This offset is most common as the standard size of the
     // two bitmap headers is 54 bytes.  So, if the offset if greater
     // than 54, we need to construct a new file with an offset of 54.

     if ( (BMIH.biBitCount = 24) and (BMFH.bfOffBits <> 54) and
          ((BMIH.biSize < 36) or (BMIH.biClrUsed = 0)) )
     then begin
          // Create temporary files to house standard bitmap
          NewFileName := FileName;
          while FileExists(NewFileName) do NewFileName := NewFileName+'x';
          // Now write headers clipped or extended to standard size
          try
             WasError := true;
             FSout := TFileStream.Create(NewFileName,fmCreate);
             PreBytes := BMFH.bfOffBits;
             BMFH.bfOffBits := 54;
             FSOut.WriteBuffer(BMFH,SizeOf(BMFH));
             FSOut.WriteBuffer(BMIH,SizeOf(BMIH));
             // Now copy over the actual bitmap and anything following
             CopySize := BMFH.bfSize - PreBytes;
             FS := TFileStream.Create(FileName,fmOpenRead);
             FS.Seek(PreBytes,soFromBeginning);
             FSout.CopyFrom(FS,CopySize);
             WasError := false;
          finally
                 FS.Free;
                 FSout.Free;
          end;
          if WasError then exit;
          // Now load this standardized bitmap before deleting its file
          inherited LoadFromFile(NewFileName);
          DeleteFile(NewFileName);
     end else inherited LoadFromFile(FileName);
end;

end.

 

Re:TBitmap **BUG** Read if you use TBitmap!!!


Quote
Julian Brewer wrote in message <01bdcdc4$49d01660$3a18d3d4@mesh>...
>I'd love to be proved wrong, but it seems in rare cases
>TBitmap.LoadFromFile (or stream, etc.) mis-aligns bitmap bytes.  This
>results in, for example, red becoming green, green -> blue, and blue ->
>red.
...
> Because Delphi starts reading at 54 and the bytes are in RGB
>triples, the triples are misaligned and RGB becomes BGR!

Could you send me a sample BMP showing the problem?

I try to stay away from API calls whenever possible, but I thought the order
(RGB or BGR) was set somewhere in the BMP header.

In processing a Scanline in D3 or D4 from a 24-bits/pixel bitmap, each
Scanline is an ARRAY OF TRGBTriple, where each TRGBTriple is predefined as
in the BGR.   I guess you're saying you have an array of RGB instead of BGR.

efg
_________________________________________
efg's Computer Lab:  http://infomaster.net/external/efg

Earl F. Glynn                 E-Mail:  EarlGl...@att.net
MedTech Research Corporation, Lenexa, KS  USA

Re:TBitmap **BUG** Read if you use TBitmap!!!


Quote
> Could you send me a sample BMP showing the problem?

OK...

Quote
> I try to stay away from API calls whenever possible, but I thought the
order
> (RGB or BGR) was set somewhere in the BMP header.

I think it's always RGB in the actual file, although I seem to remember it
gets reversed
in some windows structures.  

Quote
> In processing a Scanline in D3 or D4 from a 24-bits/pixel bitmap, each
> Scanline is an ARRAY OF TRGBTriple, where each TRGBTriple is predefined
as
> in the BGR.   I guess you're saying you have an array of RGB instead of

BGR.

The array in the file is RGB but Delphi starts reading it in the wrong
place (if i'm right!).

Re:TBitmap **BUG** Read if you use TBitmap!!!


Quote
Julian Brewer wrote in message <01bdcde9$ec0eaa20$9019d3d4@mesh>...
>The array in the file is RGB but Delphi starts reading it in the wrong
>place (if i'm right!).

Julian:

The BMP you sent me DOES have a problem in at least two D3/D4 programs that
I've written, but DOES NOT have any problem with MS Image Composer, MS Photo
Editor or MS Paint.  So, yes there does seem to be some type of BMP that
Delphi does not recognize correctly.

I've got to go to a football game now (Go Chiefs!) but I'll check into this
tomorrow.

efg
_________________________________________
efg's Computer Lab:  http://infomaster.net/external/efg

Earl F. Glynn                 E-Mail:  EarlGl...@att.net
MedTech Research Corporation, Lenexa, KS  USA

Re:TBitmap **BUG** Read if you use TBitmap!!!


Quote
Earl F. Glynn wrote in message <6rnb95$...@bgtnsc02.worldnet.att.net>...
>Julian Brewer wrote in message <01bdcde9$ec0eaa20$9019d3d4@mesh>...
>The BMP you sent me DOES have a problem in at least two D3/D4 programs that
>I've written, but DOES NOT have any problem with MS Image Composer, MS
Photo
>Editor or MS Paint.  So, yes there does seem to be some type of BMP that
>Delphi does not recognize correctly.

I tried using Lead Tools to read your BMP since Lead Tools has an ORDER_BGR
and ORDER_RGB parameter.  The usual ORDER_BGR worked just fine with your
bitmap with Lead Tools.  Using ORDER_RGB gave different colors than what
Delphi does, however.

The color changes are unusual.  At pixel (227,333) in your red "dot" Lead
Tools said the RGB = (192, 92, 96).  With Delphi 3 I get (96, 96, 192).  In
your green dot at (375, 350), Lead Tools said RGB = (104, 116, 104), while
Delphi says (115, 99, 102).  Likewise in your blue dot at (535, 345), Lead
said RGB = (112, 88, 168) while Delphi said (83, 161, 104).  My conclusion
is that this isn't a BGR vs RGB order difference.

However, if you make any small change to your BMP in MS Paint and the
re-save it (I made a very short red line), the "new" BMP then works fine
with Delphi.  BUT, at loaction (227, 333) in the red dot, RGB =
(184,91,92) -- which isn't what Lead Tools said with the original BMP. So
why are the colors changing?  I don't know.

I'm not sure I would say this is a Delphi bug.  It's almost like there's
some "non-standard" forms of BMPs that the MS programs interpret correctly.

The spatial position of all the pixels is correct, or the image would have
shifted.  But the interpretation of the color information is not correct.
This is an enigma (for me).

efg
_________________________________________
efg's Computer Lab:  http://infomaster.net/external/efg

Earl F. Glynn                 E-Mail:  EarlGl...@att.net
MedTech Research Corporation, Lenexa, KS  USA

Re:TBitmap **BUG** Read if you use TBitmap!!!


Quote
> The color changes are unusual.  At pixel (227,333) in your red "dot" Lead
> Tools said the RGB = (192, 92, 96).  With Delphi 3 I get (96, 96, 192).
In
> your green dot at (375, 350), Lead Tools said RGB = (104, 116, 104),
while
> Delphi says (115, 99, 102).  Likewise in your blue dot at (535, 345),
Lead
> said RGB = (112, 88, 168) while Delphi said (83, 161, 104).  My
conclusion
> is that this isn't a BGR vs RGB order difference.

This is because delphi starts reading the bitmap 4 bytes too early (as it
ignores
the offset parameter).  Evidence for this is a spurrious black pixel on the
bottom-
left of the picture loaded in Delphi.  It is black because the four bytes
preceeding
the real bitmap location are all zero.  Therefore, if you are going to
compare
pixels, you should take a pixel (x,y) from the correct image and find its
component
values spread across pixels (x+1,y) and (x+2,y) in the Delphi image.

Quote
> However, if you make any small change to your BMP in MS Paint and the
> re-save it (I made a very short red line), the "new" BMP then works fine
> with Delphi.  

Right, because MS Paint saves with the common 54-byte offset.

BUT, at loaction (227, 333) in the red dot, RGB =

Quote
> (184,91,92) -- which isn't what Lead Tools said with the original BMP. So
> why are the colors changing?  I don't know.

Hmm... not sure what's happening there.

Quote
> I'm not sure I would say this is a Delphi bug.  It's almost like there's
> some "non-standard" forms of BMPs that the MS programs interpret

correctly.

My view is that these Bitmaps do follow the standard as set out in Petzold
and
so Delphi does have a bug.  My fix (see original note) seems to work well.
I'm
just worried that it too will fall over with other bitmap types.  I'd love
to hear
from Borland on this!

Quote
> The spatial position of all the pixels is correct, or the image would
have
> shifted.  

It is shifted (see above).

Quote
> This is an enigma (for me).

And now?

Hey: THANKS FOR YOUR HELP!  I hope your team won (being British, I couldn't
say!)

Other Threads