下面这些结构说明来自http://blog.csdn.net/lanbing510/article/details/8176231,讲的很详尽。因为本文主要讲述24BIT的位图,所以关于16位的调色板就跳过了,至于原文,我觉得有一个地方是有误的,就是infoheader的biSizeImage 这个元素的描述,biSizeImage应该等于真正像素的数据的大小,并非宽为241就取244XheightX3.通过各种比较,这个数值应该等于每一行width填充byte[offset]后,offset如果不能被4整除,那么offset继续递加并往byte[offset]数组里面填充0(其他也可以,反正reader会跳过),它这个补齐,是基于储存像素时每行的byte长度不被4整齐时的补齐,并非单纯的width不被4整除时的补齐。
根据位图的结构,也即是这几个语句就可以构成一张Bitmap了。这些代码的前身是java版本,翻译过来才发现,java版那个也有bug,自己好不容易纠正了。。
class procedure YUVTool.JBitmapToBitmapStream(pixels: TIntegerDynArray;
w, h: integer; outStream: TStream);
var
w_headerinfo: tBITMAPINFOHEADER;
w_fileheader: TBitmapFileHeader;
w_rgb: TByteDynArray;
begin
w_rgb := addbmp_rgb_888(pixels, w, h);
w_fileheader := addBMPImageHeader(length(w_rgb) * sizeof(byte));
w_headerinfo := addBMPImageInfosHeader(w, h,length(w_rgb));
outStream.Write(w_fileheader, Sizeof(TBitmapFileHeader));
outStream.Write(w_headerinfo, SizeOf(w_headerinfo));
outStream.Write(w_rgb[0], Length(w_rgb));
end;
class function YUVTool.addBMPImageHeader(size: integer): TBitmapFileHeader;
begin
FillChar(Result, sizeof(Result), 0);
with Result do
begin
bfType := $4D42;
bfSize := size + sizeof(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader);
bfReserved1 := 0;
bfReserved2 := 0;
bfOffBits := $36;
end;
end;
class function YUVTool.addBMPImageInfosHeader(w, h: integer;imgSize:DWORD): TBitmapInfoHeader;
begin
FillChar(Result, sizeof(Result), 0);
with Result do
begin
biSize := SizeOf(Result);
biWidth := w;
biHeight := h;
biPlanes := 1;
biBitCount := 24;
biCompression := 0;//BI_RGB
biSizeImage :=imgSize;
biXPelsPerMeter := $1274;
biYPelsPerMeter := $1274;
biClrUsed := 0;
biClrImportant := 0;
{ buffer[0] := $28;
buffer[1] := $00;
buffer[2] := $00;
buffer[3] := $00;
buffer[4] := byte(w shr 0);
buffer[5] := byte(w shr 8);
buffer[6] := byte(w shr 16);
buffer[7] := byte(w shr 24);
buffer[8] := byte(h shr 0);
buffer[9] := byte(h shr 8);
buffer[10] := byte(h shr 16);
buffer[11] := byte(h shr 24);
buffer[12] := $01;
buffer[13] := $00;
buffer[14] := $18;
buffer[15] := $00;
buffer[16] := $00;
buffer[17] := $00;
buffer[18] := $00;
buffer[19] := $00;
buffer[20] := $00;
buffer[21] := $00;
buffer[22] := $00;
buffer[23] := $00;
buffer[24] := byte($E0);
buffer[25] := $01;
buffer[26] := $00;
buffer[27] := $00;
buffer[28] := $02;
buffer[29] := $03;
buffer[30] := $00;
buffer[31] := $00;
buffer[32] := $00;
buffer[33] := $00;
buffer[34] := $00;
buffer[35] := $00;
buffer[36] := $00;
buffer[37] := $00;
buffer[38] := $00;
buffer[39] := $00;
Result := buffer;
}
end;
end;
下面这个代码是今天临时写的对比两个位图文件header结构差异的,用它来验证生产的位图跟原图是否一直,方面多了,先对比header,OK后再用beyond compare对比两个文件。。
program bmpcomp;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, SysUtils, CustApp
{ you can add units after this };
type
FXPT2DOT30 = longint;
LPFXPT2DOT30 = ^longint;
PCIEXYZ = ^TCIEXYZ;
{$EXTERNALSYM tagCIEXYZ}
tagCIEXYZ = packed record
ciexyzX: FXPT2DOT30;
ciexyzY: FXPT2DOT30;
ciexyzZ: FXPT2DOT30;
end;
TCIEXYZ = tagCIEXYZ;
{$EXTERNALSYM CIEXYZ}
CIEXYZ = tagCIEXYZ;
PCIEXYZTriple = ^TCIEXYZTriple;
{$EXTERNALSYM tagICEXYZTRIPLE}
tagICEXYZTRIPLE = packed record
ciexyzRed: TCIEXYZ;
ciexyzGreen: TCIEXYZ;
ciexyzBlue: TCIEXYZ;
end;
TCIEXYZTriple = tagICEXYZTRIPLE;
{$EXTERNALSYM CIEXYZTRIPLE}
CIEXYZTRIPLE = tagICEXYZTRIPLE;
PBitmapV4Header = ^TBitmapV4Header;
{$EXTERNALSYM BITMAPV4HEADER}
BITMAPV4HEADER = packed record
bV4Size: DWORD;
bV4Width: longint;
bV4Height: longint;
bV4Planes: word;
bV4BitCount: word;
bV4V4Compression: DWORD;
bV4SizeImage: DWORD;
bV4XPelsPerMeter: longint;
bV4YPelsPerMeter: longint;
bV4ClrUsed: DWORD;
bV4ClrImportant: DWORD;
bV4RedMask: DWORD;
bV4GreenMask: DWORD;
bV4BlueMask: DWORD;
bV4AlphaMask: DWORD;
bV4CSType: DWORD;
bV4Endpoints: TCIEXYZTriple;
bV4GammaRed: DWORD;
bV4GammaGreen: DWORD;
bV4GammaBlue: DWORD;
end;
TBitmapV4Header = BITMAPV4HEADER;
PBitmapCoreHeader = ^TBitmapCoreHeader;
{$EXTERNALSYM tagBITMAPCOREHEADER}
tagBITMAPCOREHEADER = packed record
bcSize: DWORD;
bcWidth: word;
bcHeight: word;
bcPlanes: word;
bcBitCount: word;
end;
TBitmapCoreHeader = tagBITMAPCOREHEADER;
{$EXTERNALSYM BITMAPCOREHEADER}
BITMAPCOREHEADER = tagBITMAPCOREHEADER;
PBitmapInfoHeader = ^TBitmapInfoHeader;
{$EXTERNALSYM tagBITMAPINFOHEADER}
tagBITMAPINFOHEADER = packed record
biSize: DWORD;
biWidth: longint;
biHeight: longint;
biPlanes: word;
biBitCount: word;
biCompression: DWORD;
biSizeImage: DWORD;
biXPelsPerMeter: longint;
biYPelsPerMeter: longint;
biClrUsed: DWORD;
biClrImportant: DWORD;
end;
TBitmapInfoHeader = tagBITMAPINFOHEADER;
{$EXTERNALSYM BITMAPINFOHEADER}
BITMAPINFOHEADER = tagBITMAPINFOHEADER;
PBitmapFileHeader = ^TBitmapFileHeader;
{$EXTERNALSYM tagBITMAPFILEHEADER}
tagBITMAPFILEHEADER = packed record
bfType: word;
bfSize: DWORD;
bfReserved1: word;
bfReserved2: word;
bfOffBits: DWORD;
end;
TBitmapFileHeader = tagBITMAPFILEHEADER;
{$EXTERNALSYM BITMAPFILEHEADER}
BITMAPFILEHEADER = tagBITMAPFILEHEADER;
{ BmpInfoCmp }
BmpInfoCmp = class(TCustomApplication)
protected
procedure DoRun; override;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure WriteHelp; virtual;
end;
{ BmpInfoCmp }
procedure getBmpHeader(filepath:String;out outHeader:TBitmapFileHeader;out outInfo:TBitmapInfoHeader);
var w_filestream:TFileStream;
begin
w_filestream:=TFileStream.Create(filepath,fmOpenRead or fmShareDenyWrite);
try
w_filestream.Read(outHeader,sizeof(outHeader));
w_filestream.Read(outInfo,sizeof(outInfo));
finally
FreeAndNil(w_filestream);
end;
end;
procedure BmpInfoCmp.DoRun;
var
ErrorMsg: String;
f1,f2:String;
w_header1,w_header2:TBitmapFileHeader;
w_info1,w_info2:TBitmapInfoHeader;
begin
// quick check parameters
ErrorMsg:=CheckOptions('h','help');
if ErrorMsg<>'' then begin
ShowException(Exception.Create(ErrorMsg));
Terminate;
Exit;
end;
// parse parameters
if HasOption('h','help') then begin
WriteHelp;
Terminate;
Exit;
end;
f1:=Params[1];
f2:=Params[2];
getBmpHeader(f1,w_header1,w_info1);
getBmpHeader(f2,w_header2,w_info2);
writeln('FILEHEADER');
if (w_header1.bfSize<>w_header2.bfSize) then begin
WriteLn('L.bfSize:',IntToHex(w_header1.bfSize,8) ,' ',w_header1.bfSize);
WriteLn('R.bfSize:',IntToHex(w_header2.bfSize,8) ,' ',w_header2.bfSize);
end;
writeln();
if (w_header1.bfOffBits<>w_header2.bfOffBits) then begin
WriteLn('L.bfOffBits:',IntToHex(w_header1.bfOffBits,8),' ',w_header1.bfOffBits);
WriteLn('R.bfOffBits:',IntToHex(w_header2.bfOffBits,8),' ',w_header2.bfOffBits);
end;
writeln('BITMAPINFOHEADER');
if (w_info1.biSize<>w_info2.biSize) then begin
WriteLn('L.biSize:',IntToHex(w_info1.biSize,8),' ',w_info1.biSize);
WriteLn('R.biSize:',IntToHex(w_info2.biSize,8),' ',w_info2.biSize);
end;
if (w_info1.biWidth<>w_info2.biWidth) then begin
WriteLn('L.biWidth:',IntToHex(w_info1.biWidth,8),' ',w_info1.biWidth);
WriteLn('R.biWidth:',IntToHex(w_info2.biWidth,8),' ',w_info2.biWidth);
end;
writeln('--');
if (w_info1.biHeight<>w_info2.biHeight) then begin
WriteLn('L.biHeight:',IntToHex(w_info1.biHeight,8),' ',w_info1.biHeight);
WriteLn('R.biHeight:',IntToHex(w_info2.biHeight,8),' ',w_info2.biHeight);
end;
writeln('--');
if (w_info1.biBitCount<>w_info2.biBitCount) then begin
WriteLn('L.biBitCount:',IntToHex(w_info1.biBitCount,8),' ',w_info1.biBitCount);
WriteLn('R.biBitCount:',IntToHex(w_info2.biBitCount,8),' ',w_info2.biBitCount);
end;
writeln('--');
if (w_info1.biCompression<>w_info2.biCompression) then begin
WriteLn('L.biCompression:',IntToHex(w_info1.biCompression,8),' ',w_info1.biCompression);
WriteLn('R.biCompression:',IntToHex(w_info2.biCompression,8),' ',w_info2.biCompression);
end;
writeln('--');
if (w_info1.biSizeImage<>w_info2.biSizeImage) then begin
WriteLn('L.biSizeImage:',IntToHex(w_info1.biSizeImage,8),' ',w_info1.biSizeImage);
WriteLn('R.biSizeImage:',IntToHex(w_info2.biSizeImage,8),' ',w_info2.biSizeImage);
end;
writeln('--');
if (w_info1.biXPelsPerMeter<>w_info2.biXPelsPerMeter) then begin
WriteLn('L.biXPelsPerMeter:',IntToHex(w_info1.biXPelsPerMeter,8),' ',w_info1.biXPelsPerMeter);
WriteLn('R.biXPelsPerMeter:',IntToHex(w_info2.biXPelsPerMeter,8),' ',w_info2.biXPelsPerMeter);
end;
writeln('--');
if (w_info1.biYPelsPerMeter<>w_info2.biYPelsPerMeter) then begin
WriteLn('L.biYPelsPerMeter:',IntToHex(w_info1.biYPelsPerMeter,8),' ',w_info1.biYPelsPerMeter);
WriteLn('R.biYPelsPerMeter:',IntToHex(w_info2.biYPelsPerMeter,8),' ',w_info2.biYPelsPerMeter);
end;
{ add your program here }
// stop program loop
Terminate;
end;
constructor BmpInfoCmp.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException:=True;
end;
destructor BmpInfoCmp.Destroy;
begin
inherited Destroy;
end;
procedure BmpInfoCmp.WriteHelp;
begin
{ add your help code here }
writeln('Usage: ',ExeName,' -h');
end;
var
Application: BmpInfoCmp;
begin
Application:=BmpInfoCmp.Create(nil);
Application.Title:='My Application';
Application.Run;
Application.Free;
end.