I really needed to create an absolutely transparent (and otherwise empty / empty) TBitmap of arbitrary size in 32-bit RGBA format. Many times. Lazarus is able to load such a bitmap into TBitmap and after loading it you can manipulate it using scanline and not use the RGBA format. But that just doesn't work when you create TBitmap yourself. The pixel format seems to be completely ignored. So I made it out of the box, and just that it is almost AMUZING (!). But it is practical, works great, and is completely independent of LCL and any third-party libraries. It doesnโt even depend on the graphic block, because it generates the actual 32-bit RGBA BMP file (I generate it in TMemoryStream, you can generate it in different ways). Then, when you have this, elsewhere in your code you can simply load it using the TBitmap.LoadFrom source or the TPicture.LoadFrom source.
Background History At first I wanted to create a correctly formatted BMP file, following the format described here: http://www.fileformat.info/format/bmp/egff.htm But there were several options for the BMP format, and I did not know which one I should use was to follow. So I decided to go with a reverse engineering approach, but the format description helped me later. I used a graphical editor (I used GIMP) to create an empty 1x1 pixel 32 RGBA BMP file and named it alpha1p.bmp, it just did not contain transparency. Then I resized the canvas to 10x10 pixels and saved it as an alpha10p.bmp file. Then I compared two files: matching two bmp files in vbindiff on Ubuntu So I found out that the only differences were the added pixels (each of them was 4 bytes of all RGBA zeros) and a few other bytes in the header. Due to the format documentation in the linked link, I realized that these were: FileSize (in bytes), BitmapWidth (in pixels), BitmapHeight (in pixels) and BitmapDataSize (in bytes). The latter was BitmapWidth*BitmapHeight*4 , because each pixel in RGBA is 4 bytes. So now I can just generate the whole sequence of bytes, as seen from the alpha1p.bmp files, minus 4 bytes from the end (1st from BitmapData ), and then add 4 bytes (all zero) of the RGBA data for each BMP pixel, which I I want to generate, then go back to the original sequence and update the variable parts: File size, width, height and BMP size. And it works flawlessly! I just had to add a test for BigEndian and change the words and word numbers before writing. This will be a problem for ARM platforms running on BigEndian.
The code
const C_BLANK_ALPHA_BMP32_PREFIX : array[0..137]of byte = ($42, $4D, $00, $00, $00, $00, $00, $00, $00, $00, $8A, $00, $00, $00, $7C, $00, $00, $00, $0A, $00, $00, $00, $0A, $00, $00, $00, $01, $00, $20, $00, $03, $00, $00, $00, $90, $01, $00, $00, $13, $0B, $00, $00, $13, $0B, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $00, $00, $FF, $00, $00, $FF, $00, $00, $FF, $00, $00, $00, $42, $47, $52, $73, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $02, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ); (...) Function RenderEmptyAlphaBitmap(AWidth,AHeight: integer): TMemoryStream; var buf : array[1..4096]of byte; i,p : int64; w : word; dw : dword; BE : Boolean; begin buf[low(buf)] := $00; //this is jyst to prevent compiler warning about not initializing buf variable Result := TMemoryStream.Create; if(AWidth <1)then AWidth := 1; if(AHeight<1)then AHeight := 1; //Write File Header: Result.Write(C_BLANK_ALPHA_BMP32_PREFIX, SizeOf(C_BLANK_ALPHA_BMP32_PREFIX)); //Now start writing the pixels: FillChar(buf[Low(buf)],Length(buf),$00); p := Result.Position; Result.Size := Result.Size+int64(AWidth)*int64(AHeight)*4; Result.Position := p; i := int64(AWidth)*int64(AHeight)*4; //4 because RGBA has 4 bytes while(i>0)do begin if(i>Length(buf)) then w := Length(buf) else w := i; Result.Write(buf[Low(buf)], w); dec(i,w); end; //Go back to the original header and update FileSize, Width, Height, and offset fields: BE := IsBigEndian; Result.Position := 2; dw := Result.Size; if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); Result.Position := 18; dw := AWidth; if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); Result.Position := 22; dw := AHeight; if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); Result.Position := 34; dw := AWidth*AHeight*4; if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); //Done: Result.Position := 0; end;
Note that the constant C_BLANK_ALPHA_BMP32_PREFIX is basically a copy of the sequence of bytes from my sample alpha1p.bmp file, minus the last 4 bytes that were an RGBA pixel .: D
In addition, I use the IsBigEndian function, which looks like this:
Function IsBigEndian: Boolean; type Q = record case Boolean of True : (i: Integer); False : (p: array[1..4] of Byte); end; var x : ^Q; begin New(x); x^.i := 5; Result := (x^.p[4]=5); Dispose(x); end;
This is copied from the Lazarus Wiki: http://wiki.freepascal.org/Writing_portable_code_regarding_the_processor_architecture you can skip this part if you are not dealing with BigEndian platforms, or you can use the IFDEF directive for compilers. The fact is that if you use {$IFDEF ENDIAN_BIG} , then this is what happens in the compiler, while the function actually checks the system. This is explained in the linked wiki.
Using example
Procedure TForm1.Button1Click(Sender: TObject); var MS : TMemoryStream; begin MS := RenderEmptyAlphaBitmap(Image1.Width, Image1.Height); try if Assigned(MS)then Image1.Picture.LoadFromStream(MS); //you can also MS.SaveToFile('my_file.bmp'); if you want finally FreeAndNil(MS); end; end;