Entendiendo TBitmap.Scanline en Delphi & C++ Builder
c++builder porting (1)
Un archivo de mapa de bits comienza con un BITMAPFILEHEADER
, el miembro bfOffBits
especifica la dirección de inicio de los datos de imagen. Este es un DWORD en Dh (11-14th bytes). Delphi VCL tiene la estructura definida como TBitmapFileHeader
en ''windows.pas''.
La última fila de la ScanLine
apunta a esta información de imagen (de abajo hacia arriba). El VCL tiene este valor en el miembro bmBits
miembro dsBm
(un BITMAP
) o DIBSECTION
de la imagen. Cuando se solicita una línea de escaneo, la VCL calcula una compensación en función de la fila solicitada, el número de píxeles en una fila (ancho de la imagen) y cuántos bits componen un píxel, y devuelve un puntero a una dirección que agrega esta compensación a bmBits
. En realidad, se trata de datos de imagen byte a byte.
El siguiente código de muestra Delphi lee un mapa de bits de 24 bits en una secuencia de archivos y compara cada píxel de lectura con los datos de píxel de la contraparte Bitmap.ScanLine
:
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;
Una imagen sobre cómo encontrar el inicio de los datos de píxeles de un archivo de mapa de bits en un editor hexadecimal:
Delphi y C ++ Builder tienen una clase TBitmap con una propiedad Scanline que devuelve la memoria de los píxeles del mapa de bits. Esto parece ser diferente cuando miro en un editor hexadecimal del archivo BMP.
Estoy tratando de portar una aplicación C ++ Builder a Java, y me gustaría entender el algoritmo en Scanline. Si tengo el archivo, ¿cómo puedo generar la matriz de memoria como lo hace Scanline? ¿Cuál es la especificación exacta detrás de Scanline?
Clarificación: el BMP es un DIB de Windows de 24 bits. No proporciono ninguna otra información en el código; C ++ Builder parece cargarlo en algún tipo de estructura de memoria, pero no es byte por byte. Me gustaría saber cuál es la especificación de esa estructura.