To address the pixels of the scanlines of a BitMap, you can use the property TBitMap.Scanlines [0], which gives you the pointer to the first line of the image.
To understand how a ScanLine is structured in a 24 Bit BitMap, you can see the article by David Heffernan and TLama on:
How to use ScanLine property for 24-bit bitmaps?
I have created Assembly programming language functions that run under Delphi and allow you to read / write a Pixel from / to a 32 Bit / Pixel BitMap (and 2 more for 24 Bit / Pixel BitMap).
Here are the prototypes and features of my GetPixelBMp32() and PutPixelBMp32() functions:
type T_RGB_Temp=record {RGB color RECORD type}
Blue, {BLUE component from 0 to 255}
Green, {GREEN component from 0 to 255}
Red, {RED component from 0 to 255}
Mask {ALPHA channel from 0 to 255} :byte;
end;
T_Coord_XY=record {X and Y position RECORD type}
X, {X position}
Y {Y position} :smallint;
end;
T_Dim_XY=record {Width and height of bitmap RECORD type}
DimX, {Width}
DimY {Height} :word;
end;
function GetPixelBMp32(XY:T_Coord_XY;
BMp32_Dim_XY:T_Dim_XY;
ScanLine0:pointer):T_RGB_Temp;
procedure PutPixelBMp32(XY:T_Coord_XY;
BMp32_Dim_XY:T_Dim_XY;
ScanLine0:pointer;Pix:T_RGB_Temp);
Given BitMap32: TBitMap defined with PixelFormat = (pf32Bit, pfCustom), the ScanLine0 parameter must always be BitMap32.ScanLine [0]; if ScanLine0 = Nil, nothing happens.
BMp32_Dim_XY.DimX and BMp32_Dim_XY.DimY are the dimensions of the given image; if you use type cast BMp32_Dim_XY.DimX: = Word (Width: SmallInt) or BMp32_Dim_XY.DimX: = Word (Height: SmallInt), and Width or Height are negative, nothing happens.
XY.X and XY.Y are the coordinates of the Pixel; they can be negative or larger than the size of the given image, but, in this case, nothing happens.
Pix.Red, Pix.Green, Pix.Blue, Pix.Mask are the 8 Bit color components of the Pixel.
Pix is of type T_RGB_Temp; the result of GetPixelBMp32 () is also of the same type.
The organization of the scanlines of a BitMap is in reverse order, as if the image in memory were mirrored with respect to the X axis; for this reason all scanlines following TBitMap.ScanLine [0] are in memory in lower address positions, as if TBitMap.ScanLine [0] were the last, and not the first, scanline.
Furthermore, if the number of Bytes of a scanline is not a multiple of 4 (alignment to the DWord), as in the 24 Bit / Pixel format, 0 to 3 Bytes are added at the end to the scanline (alignment).
The alignment for 24 Bit BitMaps is calculated with: A: = (TBitMap.Width + 3) And Not 3.
Below is the definition of the above functions (32 Bit / Pixel images):
procedure PutPixelBMp32(XY:T_Coord_XY;
BMp32_Dim_XY:T_Dim_XY;
ScanLine0:pointer;Pix:T_RGB_Temp); assembler;
{ eax edx ecx are 1°, 2° and 3° parameters.
Can freely modify the eax, ecx, and edx registers. }
asm
push ebx
push esi
push edi
mov ebx,Pix {ebx <- Pix}
mov edi,BMp32_Dim_XY {edi <- edx <- BMp32_Dim_XY}
mov esi,ScanLine0 {esi <- ecx <- ScanLine0}
mov edx,XY {edx <- eax <- XY}
or esi,esi {If ScanLine0=Nil, ...}
je @@00 {... Exit}
movsx eax,di {eax <- DimX}
shr edi,16
movsx edi,di {edi <- DimY}
movsx ecx,dx {ecx <- X}
shr edx,16
movsx edx,dx {edx <- Y}
or eax,eax {If DimX<0, ...}
js @@00 {... Exit}
cmp ecx,eax {If X<0 or if X>=DimX, ...}
jae @@00 {... Exit}
or edi,edi {If DimY<0, ...}
js @@00 {... Exit}
cmp edx,edi {If Y<0 or if Y>=DimY, ...}
jae @@00 {... Exit}
lea ecx,[4*ecx] {ecx <- X position in byte}
lea esi,[esi+ecx] {Update source pointer and free ecx}
lea eax,[4*eax] {eax <- scanline size in byte}
mul edx {eax <- Y * (scanline size in byte)}
sub esi,eax {esi <- Pixel write target pointer}
mov [esi],ebx {bl <- Blue; bh <- Green; byte2(ebx) <- Red; byte3(ebx) <- Mask}
@@00:pop edi
pop esi
pop ebx
end;
function GetPixelBMp32(XY:T_Coord_XY;
BMp32_Dim_XY:T_Dim_XY;
ScanLine0:pointer):T_RGB_Temp; assembler;
{ eax edx ecx are 1°, 2° and 3° parameters.
Can freely modify the eax, ecx, and edx registers. }
asm
push esi
push edi
mov edi,BMp32_Dim_XY {edi <- edx <- BMp32_Dim_XY}
mov esi,ScanLine0 {esi <- ecx <- ScanLine0}
mov edx,XY {edx <- eax <- XY}
xor eax,eax {Inizializza @Result a 0}
or esi,esi {If ScanLine0=Nil, ...}
je @@00 {... Exit}
movsx eax,di {eax <- DimX}
shr edi,16
movsx edi,di {edi <- DimY}
movsx ecx,dx {ecx <- X}
shr edx,16
movsx edx,dx {edx <- Y}
or eax,eax {If DimX<0, ...}
js @@00 {... Exit}
cmp ecx,eax {If X<0 or if X>=DimX, ...}
jae @@00 {... Exit}
or edi,edi {If DimY<0, ...}
js @@00 {... Exit}
cmp edx,edi {If Y<0 or if Y>=DimY, ...}
jae @@00 {... Exit}
lea ecx,[4*ecx] {ecx <- X position in byte}
lea esi,[esi+ecx] {Update source pointer and free ecx}
lea eax,[4*eax] {eax <- scanline size in byte}
mul edx {eax <- Y * (scanline size in byte)}
sub esi,eax {esi <- Pixel write target pointer}
mov eax,[esi] {al <- Blue; ah <- Green; byte2(eax) <- Red; byte3(eax) <- Mask}
@@00:pop edi
pop esi
end;
Below is the definition of the functions for which the parameter types have already been defined (24 Bit / Pixel images):
procedure PutPixelBMp24(XY:T_Coord_XY;
BMp24_Dim_XY:T_Dim_XY;
ScanLine0:pointer;Pix:T_RGB_Temp); assembler;
{ eax edx ecx are 1°, 2° and 3° parameters.
Can freely modify the eax, ecx, and edx registers. }
asm
push ebx
push esi
push edi
mov ebx,Pix {ebx <- Pix}
mov edi,BMp24_Dim_XY {edi <- edx <- BMp24_Dim_XY}
mov esi,ScanLine0 {esi <- ecx <- ScanLine0}
mov edx,XY {edx <- eax <- XY}
or esi,esi {If ScanLine0=Nil, ...}
je @@00 {... Exit}
movsx eax,di {eax <- DimX}
shr edi,16
movsx edi,di {edi <- DimY}
movsx ecx,dx {ecx <- X}
shr edx,16
movsx edx,dx {edx <- Y}
or eax,eax {If DimX<0, ...}
js @@00 {... Exit}
cmp ecx,eax {If X<0 or if X>=DimX, ...}
jae @@00 {... Exit}
or edi,edi {If DimY<0, ...}
js @@00 {... Exit}
cmp edx,edi {If Y<0 or if Y>=DimY, ...}
jae @@00 {... Exit}
lea ecx,[ecx+2*ecx]{ecx <- X position in byte}
lea esi,[esi+ecx] {Update source pointer and free ecx}
lea eax,[eax+2*eax]{eax <- scanline size in byte}
add eax,3 {Process in eax ...}
and eax,0FFFFFFFCH {... The dword aligned scanline size in byte}
mul edx {eax <- Y * (dword aligned scanline size in byte)}
sub esi,eax {esi <- Pixel write target pointer}
mov [esi],bx {bl <- Blue; bh <- Green}
shr ebx,8 {bh <- Red}
mov [esi+2],bh {bh <- Red}
@@00:pop edi
pop esi
pop ebx
end;
function GetPixelBMp24(XY:T_Coord_XY;
BMp24_Dim_XY:T_Dim_XY;
ScanLine0:pointer):T_RGB_Temp; assembler;
{ eax edx ecx are 1°, 2° and 3° parameters.
Can freely modify the eax, ecx, and edx registers. }
asm
push esi
push edi
mov edi,BMp24_Dim_XY {edi <- edx <- BMp24_Dim_XY}
mov esi,ScanLine0 {esi <- ecx <- ScanLine0}
mov edx,XY {edx <- eax <- XY}
xor eax,eax {Inizializza @Result a 0}
or esi,esi {If ScanLine0=Nil, ...}
je @@00 {... Exit}
movsx eax,di {eax <- DimX}
shr edi,16
movsx edi,di {edi <- DimY}
movsx ecx,dx {ecx <- X}
shr edx,16
movsx edx,dx {edx <- Y}
or eax,eax {If DimX<0, ...}
js @@00 {... Exit}
cmp ecx,eax {If X<0 or if X>=DimX, ...}
jae @@00 {... Exit}
or edi,edi {If DimY<0, ...}
js @@00 {... Exit}
cmp edx,edi {If Y<0 or if Y>=DimY, ...}
jae @@00 {... Exit}
lea ecx,[ecx+2*ecx]{ecx <- X position in byte}
lea esi,[esi+ecx] {Update source pointer and free ecx}
lea eax,[eax+2*eax]{eax <- scanline size in byte}
add eax,3 {Process in eax ...}
and eax,0FFFFFFFCH {... The dword aligned scanline size in byte}
mul edx {eax <- Y * (dword aligned scanline size in byte)}
sub esi,eax {esi <- Pixel write target pointer}
mov ax,[esi+1] {al -> Green; ah -> Red}
shl eax,8 {B2 -> Red; B1 -> Green}
mov al,[esi] {B0 -> Blue}
@@00:pop edi
pop esi
end;
If you need higher transfer speeds, here are the simplified versions, but without check of the correctness of the incoming parameters, of the GetPixelBMp32 () and PutPixelBMp32 () functions:
procedure PutPixelBMp32(XY:T_Coord_XY;
BMp24_Dim_XY:T_Dim_XY;
ScanLine0:pointer;Pix:T_RGB_Temp); assembler;
{ eax edx ecx are 1°, 2° and 3° parameters.
Can freely modify the eax, ecx, and edx registers. }
asm
push ebx
push esi
mov ebx,Pix {ebx <- Pix}
mov esi,ScanLine0 {esi <- ecx <- ScanLine0}
mov eax,XY {eax <- eax <- XY}
mov edx,BMp32_Dim_XY {edx <- edx <- BMp32_Dim_XY}
movzx edx,dx {edx <- DimX}
movzx ecx,ax {ecx <- X}
shr eax,16
movzx eax,ax {eax <- Y}
lea ecx,[4*ecx] {ecx <- X position in byte}
lea esi,[esi+ecx] {Update source pointer and free ecx}
lea edx,[4*edx] {edx <- scanline size in byte}
mul edx {eax <- Y * (scanline size in byte)}
sub esi,eax {esi <- Pixel write target pointer}
mov [esi],ebx {bl <- Blue; bh <- Green; byte2(ebx) <- Red; byte3(ebx) <- Mask}
pop esi
pop ebx
end;
function GetPixelBMp32(XY:T_Coord_XY;
BMp24_Dim_XY:T_Dim_XY;
ScanLine0:pointer):T_RGB_Temp; assembler;
{ eax edx ecx are 1°, 2° and 3° parameters.
Can freely modify the eax, ecx, and edx registers. }
asm
push esi
mov esi,ScanLine0 {esi <- ecx <- ScanLine0}
mov eax,XY {eax <- eax <- XY}
mov edx,BMp32_Dim_XY {edx <- edx <- BMp32_Dim_XY}
movzx edx,dx {edx <- DimX}
movzx ecx,ax {ecx <- X}
shr eax,16
movzx eax,ax {eax <- Y}
lea ecx,[4*ecx] {ecx <- X position in byte}
lea esi,[esi+ecx] {Update source pointer and free ecx}
lea edx,[4*edx] {edx <- scanline size in byte}
mul edx {edx <- Y * (scanline size in byte)}
sub esi,eax {esi <- Pixel write target pointer}
mov eax,[esi] {al <- Blue; ah <- Green; byte2(eax) <- Red; byte3(eax) <- Mask}
pop esi
end;