Файл растрового изображения начинается с BITMAPFILEHEADER
, элемент bfOffBits
указывает начальный адрес данных изображения.Это DWORD в Dh (11-14-е байты).Delphi VCL имеет структуру, определенную как TBitmapFileHeader
в «windows.pas».
Последняя строка ScanLine
указывает на эти данные изображения (снизу вверх).VCL имеет это значение в bmBits
члене dsBm
(a BITMAP
) члена или DIBSECTION
изображения.Когда запрашивается строка сканирования, VCL вычисляет смещение в зависимости от запрошенной строки, количества пикселей в строке (ширины изображения) и того, сколько бит составляют пиксель, и возвращает указатель на адрес, добавляющий это смещениеbmBits
.Это действительно побайтовые данные изображения.
Приведенный ниже пример кода Delphi считывает 24-битное растровое изображение в файловый поток и сравнивает каждый считанный пиксель с данными пикселя 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;
Картинка о нахождении начальных данных пикселей растрового файла в hex-редакторе: