Delphi & C++ Builder have a TBitmap class with a Scanline property which returns the memory of pixels of the bitmap. This seems to be different when I look in a hex editor of the BMP file.
I'm trying to port a C++ Builder app to Java, and would like to understand the algorithm in Scanline. If I have the file, how do I generate the memory array like Scanline does? What is the exact spec behind Scanline?
Clarifcation: The BMP is a Windows 24bit DIB. I don't provide any other info in the code; C++ Builder seems to load it into some type of memory structure, but it's not byte-for-byte. Would like to know what the spec of that structture is.
A bitmap file starts with a BITMAPFILEHEADER
, the bfOffBits
member specifies the starting address of image data. This is a DWORD at Dh (11-14th bytes). Delphi VCL has the structure defined as TBitmapFileHeader
in 'windows.pas'.
The last row of the ScanLine
points to this image data (bottom-up). The VCL has this value in bmBits
member of the dsBm
(a BITMAP
) member or the DIBSECTION
of the image. When a scan line is requested, the VCL calculates an offset depending on the requested row, number of pixels in a row (width of the image) and how many bits make up a pixel, and returns a pointer to an address adding this offset to bmBits
. It's really byte-by-byte image data.
The below Delphi sample code reads a 24bit bitmap to a file stream and compares each read pixel with the pixel data of the Bitmap.ScanLine
counterpart:
procedure TForm1.Button1Click(Sender: TObject);
var
BmpFile: string;
Bmp: TBitmap;
fs: TFileStream;
FileHeader: TBitmapFileHeader;
InfoHeader: TBitmapInfoHeader;
iHeight, iWidth, Padding: Longint;
ScanLine: Pointer;
RGBFile, RGBBitmap: TRGBTriple;
begin
BmpFile := ExtractFilePath(Application.ExeName) + 'Attention_128_24.bmp';
// laod bitmap to TBitmap
Bmp := TBitmap.Create;
Bmp.LoadFromFile(BmpFile);
Assert(Bmp.PixelFormat = pf24bit);
// read bitmap file with stream
fs := TFileStream.Create(BmpFile, fmOpenRead or fmShareDenyWrite);
// need to get the start of pixel array
fs.Read(FileHeader, SizeOf(FileHeader));
// need to get width and height of bitmap
fs.Read(InfoHeader, SizeOf(InfoHeader));
// just a general demo - no top-down image allowed
Assert(InfoHeader.biHeight > 0);
// size of each row is a multiple of the size of a DWORD
Padding := SizeOf(DWORD) -
(InfoHeader.biWidth * 3) mod SizeOf(DWORD); // pf24bit -> 3 bytes
// start of pixel array
fs.Seek(FileHeader.bfOffBits, soFromBeginning);
// compare reading from file stream with the value from scanline
for iHeight := InfoHeader.biHeight - 1 downto 0 do begin
// get the scanline, bottom first
ScanLine := Bmp.ScanLine[iHeight];
for iWidth := 0 to InfoHeader.biWidth - 1 do begin
// read RGB from file stream
fs.Read(RGBFile, SizeOf(RGBFile));
// read RGB from scan line
RGBBitmap := TRGBTriple(Pointer(
Longint(ScanLine) + (iWidth * SizeOf(TRGBTriple)))^);
// assert the two values are the same
Assert((RGBBitmap.rgbtBlue = RGBFile.rgbtBlue) and
(RGBBitmap.rgbtGreen = RGBFile.rgbtGreen) and
(RGBBitmap.rgbtRed = RGBFile.rgbtRed));
end;
// skip row padding
fs.Seek(Padding, soCurrent);
end;
end;
A picture about finding the starting of pixel data of a bitmap file in a hex-editor:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With