1

Since FMX TBitmap implementation has PixelFormat as Read-Only property, I can't set it to 32bits just after creating an empty TBitmap as i did before with VCL TBitmap. This example shows how to access pixels independent to PixelFormat using FMX rutines GetPixel, SetPixel: http://members.adug.org.au/2012/10/05/read-write-image-bitmap-pixels-in-delphi-xe3/

With the FMX TBitmap.Map() method I can access pointer to pixel data, but I can't assume its PixelFormat to 32 bits.

My intention is to have access to a 32bits bitmap, texture or surface with fixed format of 32 bits, so my 2D graphics library can work offscreen directly with that memory; without the need of checking and converting every time between pixel formats. At the end of the image processing it can be copied to any visible surface, texture or image in order to show in the form.

Help appreciated!

piXelicidio
  • 187
  • 9
  • Why do you need 32-bit specifically? FMX is unlikely to use less than that, because it needs to handle RGB and alpha, which most likely is going to be 4 * 8 bits. It is dependent on the platform and canvas implementation, so abstracting this away is part of the cross-platform support. If you know it is *very* unlikely to be less than 32 bits, does it matter what format it actually is? – David Feb 04 '14 at 11:54
  • Are you worried about byte order, eg ARGB, ABGR, etc? If so, FMX always uses the native format for its canvas class, and this is a good thing. Use [AlphaColorToPixel](http://docwiki.embarcadero.com/Libraries/XE5/en/FMX.PixelFormats.AlphaColorToPixel) to convert from a "normal" ARGB 32-bit value to the FMX internal one. (You can go [the other way too](http://docwiki.embarcadero.com/Libraries/XE5/en/FMX.PixelFormats.PixelToAlphaColor).) That way you don't need to worry about how the pixel is actually stored. – David Feb 04 '14 at 11:58
  • Hi David I'm the same Denys from G+, you help me there! Thanks again. The work around as you suggested is to work in an pre-allocated mem; then convert the scanlines to destination pixel format, it works. But still I think should be a better solution. – piXelicidio Feb 05 '14 at 02:30
  • Firemonkey should allow me to create bitmaps, textures or surfaces in my desired piXelFormat, specially 32bits; as DirectX allow that, also windows GDI, and OpenGL – piXelicidio Feb 05 '14 at 02:37
  • Small world - hi! I agree, btw: I think you're right, you should be able to specify the pixelformat. In the meantime I don't have any suggestion beyond what I wrote on G+: using your existing draw-to-buffer code or on Windows something like Graphics32, then copying over to a FMX bitmap as the final step. You should be able to optimise the copying code quite highly. – David Feb 05 '14 at 09:16
  • With good luck most time the dest. bitmap should be 32bits, so I can ask first then copy without conversion, or convert only in case the dest. bitmap is non-32 bits. :) – piXelicidio Feb 05 '14 at 18:37

1 Answers1

0

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;
Paolo Fassin
  • 361
  • 2
  • 11