问题
While using a 32 bit TBitmap, I switched from Canvas.Pixels to ScanLine.
I then set the value to Red, only to find it was displayed as blue.
Any idea why?
Here's a code excerpt:
procedure TForm1.FormPaint(Sender: TObject);
var
varBitmap: TBitmap;
pLock: PIntegerArray;
iColor: integer;
begin
varBitmap := TBitmap.Create;
varBitmap.PixelFormat := pf32bit;
varBitmap.Width := 800;
varBitmap.Height := 600;
// Set Pixels to Red
varBitmap.Canvas.Pixels[0, 0] := $0000FF;
// Shows $FF0000 (blue)
pLock := varBitmap.ScanLine[0];
iColor := pLock[0];
ShowMessageFmt('%x', [iColor]);
// Set ScanLine to Red
pLock[0] := $0000FF;
// Displays a blue pixel
Canvas.Draw(0, 0, varBitmap);
end;
It seems that somehow TColor is not the same as what is in memory, but that makes no sense.
Any suggestions welcome. ;)
回答1:
32bit pixel data is in $AARRGGBB
format. You are setting the Blue component, not the Red component. Use $FF0000
instead of $0000FF
. Or better, use the RGB()
function.
回答2:
The VCL bitmap class, TBitmap
is a wrapper around the Windows native device independent bitmap (DIB). These bitmap objects can store bitmaps in a wide variety of different pixel formats. They can be monochrome with a single bit per pixel, up to 32 bits per pixel, the format that you are using. They can also be used to store palette based bitmaps where each pixel holds an index into a color table.
The two methods of accessing the pixel data that you refer to are the Pixels
property of TCanvas
and the ScanLine
property of TBitmap
.
The Pixels
property of TCanvas
is a wrapper around the GDI GetPixel and SetPixel functions. These is a high-level functions that operate on COLORREF values. The documentation for COLORREF says:
The low-order byte contains a value for the relative intensity of red; the second byte contains a value for green; and the third byte contains a value for blue. The high-order byte must be zero. The maximum value for a single byte is 0xFF.
In other words, a COLORREF
value has a fixed way of encoding the pixel color. The GetPixel
and SetPixel
functions much handle the transformation between the fixed COLORREF
form and the underlying raw bitmap pixel data. Note also that a COLORREF
cannot represent the alpha value. The COLORREF
value is in $00BBGGRR format.
On the other hand, the ScanLine
property of TBitmap
returns you a pointer into the raw pixel data of the underlying DIB object. The data you work with here is 32bpp pixel data and the convention for that data is that it is stored in $AARRGGBB format. The Windows documentation for 32bpp data says:
The bitmap has a maximum of 2^32 colors. If the biCompression member of the BITMAPINFOHEADER is BI_RGB, the bmiColors member of BITMAPINFO is NULL. Each DWORD in the bitmap array represents the relative intensities of blue, green, and red for a pixel. The value for blue is in the least significant 8 bits, followed by 8 bits each for green and red. The high byte in each DWORD is not used.
So in fact this text is incorrect and out-of-date. The high byte in each DWORD
is, in fact, the alpha channel, if it is used.
来源:https://stackoverflow.com/questions/22291162/delphi-tbitmap-why-are-pixels-and-scanline-different