Recent

Author Topic: bitmap raw image  (Read 2120 times)

user5

  • Full Member
  • ***
  • Posts: 232
bitmap raw image
« on: February 27, 2019, 05:37:03 pm »
    I'm sure that some of you are familiar with the code below. It's appeared on this forum and other places. I don't understand it very well myself. I'm working on something that requires the comparison and alteration of each pixel in a bitmap and this code is appropriate except for the line "PixelPtr^ := newcolor;". If newcolor = clRed then when I run the code the bitmap appears as blue. If it's set to blue then the result is a red bitmap. If it's set to yellow then the result is aqua but other colors like white and black turn out okay.
    Does anyone know what I need to do to get better results? I assume it has something to do with some kind of color adjustment or conversion.
    By the way, I only recently discovered that in code like this the Y loop should always appear on the outside and I learned that on this forum. What a shock that was.
    Thank you.

Code: Pascal  [Select]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   X, Y: Integer;
  4.   PixelPtr: PInteger;
  5.   PixelRowPtr: PInteger;
  6.   P: TPixelFormat;
  7.   RawImage: TRawImage;
  8.   BytePerPixel: Integer;
  9.   Bitmap:TBitmap;
  10.   newcolor:TColor;
  11. begin
  12.  newcolor := clRed;
  13.  application.processmessages;
  14.  X := image1.width;
  15.  Y := image1.height;
  16.  Bitmap := TBitmap.create;
  17.  Bitmap.PixelFormat := pf24bit;
  18.  bitmap.width := X;
  19.  bitmap.height := Y;
  20.  try
  21.     Bitmap.BeginUpdate(False);
  22.     RawImage := Bitmap.RawImage;
  23.     PixelRowPtr := PInteger(RawImage.Data);
  24.     BytePerPixel := RawImage.Description.BitsPerPixel div 8;
  25.  
  26.     for Y := 0 to Y - 1 do begin
  27.       PixelPtr := PixelRowPtr;
  28.       for X := 0 to X - 1 do
  29.        begin
  30.         PixelPtr^ := newcolor;
  31.         Inc(PByte(PixelPtr), BytePerPixel);
  32.        end;
  33.       Inc(PByte(PixelRowPtr), RawImage.Description.BytesPerLine);
  34.     end;
  35.   finally
  36.     Bitmap.EndUpdate(False);
  37.   end;
  38.   image1.picture.bitmap.assign(Bitmap);
  39.   Bitmap.free;
  40. end;

Handoko

  • Hero Member
  • *****
  • Posts: 3186
  • My goal: build my own game engine using Lazarus
Re: bitmap raw image
« Reply #1 on: February 27, 2019, 05:46:30 pm »
That was BGR vs RGB issue, the Blue and Red are swapped.

That's all I know, sorry not much help. :'(

    Does anyone know what I need to do to get better results?

Don't manually read the pixel data but use a well-tested library like BGRABitmap. For learning purpose, it is okay to manually read/modify the pixel data but your code won't be cross platform, cross device. BGR or RGB is device and/or OS depended. If you use a library, they will make it uniform.

I ever wrote a demo showing how to read the raw pixel data. Maybe you can learn something from there:
https://forum.lazarus.freepascal.org/index.php/topic,37242.msg252828.html#msg252828
Also don't forget to check @User137, @molly, @taazz comments, which added some valuable tips for it.

Even the code works correctly on Linux, I am still doubt it will work correctly on other devices or platforms.

    By the way, I only recently discovered that in code like this the Y loop should always appear on the outside and I learned that on this forum. What a shock that was.

That is how the pixel data stored. Most (or maybe all) graphics libraries do like this. Why shock?
« Last Edit: February 27, 2019, 06:12:46 pm by Handoko »

lucamar

  • Hero Member
  • *****
  • Posts: 2076
Re: bitmap raw image
« Reply #2 on: February 27, 2019, 05:55:39 pm »
As Handoko said, clRed, as all TColor's is defined in BGR order, as:
Code: [Select]
const clRed = TColor($0000FF)while your image's pixels are RGB. Use ColorToRGB() to convert it:
Code: Pascal  [Select]
  1.     PixelPtr^ := ColorToRGB(newcolor);
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

user5

  • Full Member
  • ***
  • Posts: 232
Re: bitmap raw image
« Reply #3 on: February 27, 2019, 06:46:34 pm »
    I can't use BGRABitmap with my old version of Lazarus because the program I'm working on won't compile on a newer version of Lazarus for reasons that I've never been able to solve. I explored the issue on this forum a long time ago but no one could explain the error message I got when I tried to use a newer version. That's why I would really like to use this code since I don't have any option that I know other than the old slow way to manipulate pixels.
    I followed your suggestion to use PixelPtr^ := ColorToRGB(newcolor) but the result was the color blue again. Can you give me an example of how to use  RedGreenBlue(rgb: TColor; out Red, Green, Blue: Byte) correctly? Maybe that's what I need. Thank you for your responses.

Handoko

  • Hero Member
  • *****
  • Posts: 3186
  • My goal: build my own game engine using Lazarus
Re: bitmap raw image
« Reply #4 on: February 27, 2019, 07:26:01 pm »
I haven't tested, but I believe it will work:

Code: Pascal  [Select]
  1. function SwapRedBlue(aColor: TColor): TColor; inline;
  2. begin
  3.   Result := RGBToColor(Blue(aColor), Green(aColor), Red(aColor));
  4. end;

To use it, put it before NewColor:
       PixelPtr^ := SwapRedBlue(newcolor);

If it works on your test, it's only half-solved the problem. As I said before, it may not run correctly on certain devices and OSes. If you want to make it 'real' cross platform, you have to check the specification of different PixelFormats, image formats, devices, etc.

    I can't use BGRABitmap with my old version of Lazarus because the program I'm working on won't compile on a newer version of Lazarus for reasons that I've never been able to solve.

You should submit a bug report or at least post it here.
« Last Edit: February 27, 2019, 07:34:31 pm by Handoko »

lucamar

  • Hero Member
  • *****
  • Posts: 2076
Re: bitmap raw image
« Reply #5 on: February 27, 2019, 07:43:25 pm »
Can you give me an example of how to use  RedGreenBlue(rgb: TColor; out Red, Green, Blue: Byte) correctly? Maybe that's what I need. Thank you for your responses.

Sure! Here it is:
Code: Pascal  [Select]
  1. function SwapRedBlue(AColor: TColor): TColor;
  2. var
  3.   R, G, B: Byte;
  4. begin
  5.   RedGreenBlue(AColor, R, G, B);
  6.   Result := RGBToColor(B, G, R);
  7. end;

And this time I actually tested that it works! :)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

user5

  • Full Member
  • ***
  • Posts: 232
Re: bitmap raw image
« Reply #6 on: February 27, 2019, 08:39:07 pm »
    I tried using the example for RedGreenBlue that you gave me and it worked like a charm! I thank you from my heart. It means that much to me. On WindowsXP it worked four times faster than the old pixels method and on Windows10 it worked EIGHT times faster. This is a real breakthrough for me. From the research that I have done the raw image method is the fastest of them all.
    As far as platforms are concerned, all I care about is that the program runs okay on all Windows platforms from WindowsXP to Windows10 which it does in either default mode or compatitility mode.
    I was shocked to learn that the Y loop should be first because I've always started with X so I'm going to eventually have to change to the correct faster way and that's going to take some time.
    I thank you guys again for your help and the time and effort that you took to provide it. For that reason I looked up the pasts posts of mine dealing with the Lazarus compatibility problems that I had. I couldn't find those posts but when I get the chance then I'll try to compile the program again on a copy of the newest version of Lazarus that I have and will hopefully report the error message on this same thread. I've never made a Lazarus bug report before.
    Thanks again. You guys are the greatest.
    Wow!




Handoko

  • Hero Member
  • *****
  • Posts: 3186
  • My goal: build my own game engine using Lazarus
Re: bitmap raw image
« Reply #7 on: February 27, 2019, 08:48:33 pm »
    I was shocked to learn that the Y loop should be first because I've always started with X so I'm going to eventually have to change to the correct faster way and that's going to take some time.

Because of how the data stored and in the code it uses inc:
      Inc(PByte(PixelRowPtr), RawImage.Description.BytesPerLine);

So the y loop must be at the outside.

For performance optimization, programmers usually put the smaller loop at the outside. For example this below will have better performance:

Code: Pascal  [Select]
  1. for i := 0 to 3 do
  2.   for j := 0 to 100000 do

And this below is slower:

Code: Pascal  [Select]
  1. for i := 0 to 100000 do
  2.   for j := 0 tho 3 do

lainz

  • Hero Member
  • *****
  • Posts: 3276
    • Lainz
Re: bitmap raw image
« Reply #8 on: February 28, 2019, 12:23:42 am »
You can try using an older version of BGRABitmap, no need to use the latest!
https://sourceforge.net/projects/lazpaint/files/src/oldbgrabitmap/

lucamar

  • Hero Member
  • *****
  • Posts: 2076
Re: bitmap raw image
« Reply #9 on: February 28, 2019, 12:41:19 am »
Oh, by the way, regarding this:

    I'm sure that some of you are familiar with the code below. It's appeared on this forum and other places. I don't understand it very well myself. I'm working on something that requires the comparison and alteration of each pixel in a bitmap [...etc...]

All the code does is create a bitmap with the same dimensions that a reference TImage, fill it up with the given color (clRed in this case) and assign it to the image so it's shown there.

IMHO, it would have been quicker just calling Canvas.FillRect() but it's not bad as an example of iterating a bitmap's pixels. :)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

circular

  • Hero Member
  • *****
  • Posts: 3022
    • Personal webpage
Re: bitmap raw image
« Reply #10 on: March 01, 2019, 11:27:33 am »
This may be related to the following topic on how to access red/green/blue components of raw image:
https://forum.lazarus.freepascal.org/index.php/topic,43001.msg300332.html

Quote
There are some surprising things about the pixel formats. It can be ARGB, BGRA or RGBA and you don't have much control over it. So you are better of leaving the PixelFormat to the default value (it is pfDevice but you don't need to set it) and detect what format you have:

Code: Delphi  [Select]
  1. function GetRandom32BitMap(aX, aY: integer): TBitMap;
  2. var
  3.   bytesPerPixel, redOffset, greenOffset, blueOffset: byte;
  4.   LinePixs: PByte;
  5.   X, Y: integer;
  6.   color: TColor;
  7. begin
  8.   Result := Tbitmap.Create;
  9.   Result.SetSize(aX, aY);
  10.   bytesPerPixel := Result.RawImage.Description.BitsPerPixel div 8;
  11.   redOffset := Result.RawImage.Description.RedShift div 8;
  12.   greenOffset := Result.RawImage.Description.GreenShift div 8;
  13.   blueOffset := Result.RawImage.Description.BlueShift div 8;
  14.   {$IFNDEF ENDIAN_LITTLE}
  15.   redOffset := bytesPerPixel - 1 - redOffset;
  16.   greenOffset := bytesPerPixel - 1 - greenOffset;
  17.   blueOffset := bytesPerPixel - 1 - blueOffset;
  18.   {$ENDIF}
  19.  
  20.   color := TColor($00A5FF);
  21.   for Y := 0 to aY - 1 do
  22.   begin
  23.     LinePixs := Result.RawImage.GetLineStart(Y);
  24.     for X := 0 to aX - 1 do
  25.     begin
  26.       (LinePixs+blueOffset)^ := GetBValue(color);
  27.       (LinePixs+greenOffset)^ := GetGValue(color);
  28.       (LinePixs+redOffset)^ := GetRValue(color);
  29.       inc(LinePixs, bytesPerPixel);
  30.     end;
  31.   end;
  32. end;
Conscience is the debugger of the mind