bitmap raw image lazarus / 39-Graphics ---------------------------------------------------------------------- user5 · 2019-02-27 16:37 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button2Click(Sender: TObject);var  X, Y: Integer;  PixelPtr: PInteger;  PixelRowPtr: PInteger;  P: TPixelFormat;  RawImage: TRawImage;  BytePerPixel: Integer;  Bitmap:TBitmap;  newcolor:TColor;begin newcolor := clRed; application.processmessages; X := image1.width; Y := image1.height; Bitmap := TBitmap.create; Bitmap.PixelFormat := pf24bit; bitmap.width := X; bitmap.height := Y; try    Bitmap.BeginUpdate(False);    RawImage := Bitmap.RawImage;    PixelRowPtr := PInteger(RawImage.Data);    BytePerPixel := RawImage.Description.BitsPerPixel div 8;     for Y := 0 to Y - 1 do begin      PixelPtr := PixelRowPtr;      for X := 0 to X - 1 do       begin        PixelPtr^ := newcolor;        Inc(PByte(PixelPtr), BytePerPixel);       end;      Inc(PByte(PixelRowPtr), RawImage.Description.BytesPerLine);    end;  finally    Bitmap.EndUpdate(False);  end;  image1.picture.bitmap.assign(Bitmap);  Bitmap.free;end; ---------------------------------------------------------------------- Handoko · 2019-02-27 16:46 That was BGR vs RGB issue, the Blue and Red are swapped. That's all I know, sorry not much help. Quote from: user5 on February 27, 2019, 05:37:03 pm    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.h tml#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. Quote from: user5 on February 27, 2019, 05:37:03 pm    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? ---------------------------------------------------------------------- lucamar · 2019-02-27 16:55 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     PixelPtr^ := ColorToRGB(newcolor); ---------------------------------------------------------------------- user5 · 2019-02-27 17:46 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 · 2019-02-27 18:26 I haven't tested, but I believe it will work: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function SwapRedBlue(aColor: TColor): TColor; inline;begin  Result := RGBToColor(Blue(aColor), Green(aColor), Red(aColor));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. Quote from: user5 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. You should submit a bug report or at least post it here. ---------------------------------------------------------------------- lucamar · 2019-02-27 18:43 Quote from: user5 on February 27, 2019, 06:46:34 pmCan 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function SwapRedBlue(AColor: TColor): TColor;var  R, G, B: Byte;begin  RedGreenBlue(AColor, R, G, B);  Result := RGBToColor(B, G, R);end; And this time I actually tested that it works! ---------------------------------------------------------------------- user5 · 2019-02-27 19:39 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 · 2019-02-27 19:48 Quote from: user5 on February 27, 2019, 08:39:07 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for i := 0 to 3 do  for j := 0 to 100000 do And this below is slower: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for i := 0 to 100000 do  for j := 0 tho 3 do ---------------------------------------------------------------------- lainz · 2019-02-27 23:23 You can try using an older version of BGRABitmap, no need to use the latest! https://sourceforge.net/projects/lazpaint/files/src/oldbgrabitmap/ ---------------------------------------------------------------------- lucamar · 2019-02-27 23:41 Oh, by the way, regarding this: Quote from: user5 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 [...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. ---------------------------------------------------------------------- circular · 2019-03-01 10:27 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.h tml QuoteThere 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function GetRandom32BitMap(aX, aY: integer): TBitMap;var  bytesPerPixel, redOffset, greenOffset, blueOffset: byte;  LinePixs: PByte;  X, Y: integer;  color: TColor;begin  Result := Tbitmap.Create;  Result.SetSize(aX, aY);  bytesPerPixel := Result.RawImage.Description.BitsPerPixel div 8;  redOffset := Result.RawImage.Description.RedShift div 8;  greenOffset := Result.RawImage.Description.GreenShift div 8;  blueOffset := Result.RawImage.Description.BlueShift div 8;  {$IFNDEF ENDIAN_LITTLE}  redOffset := bytesPerPixel - 1 - redOffset;  greenOffset := bytesPerPixel - 1 - greenOffset;  blueOffset := bytesPerPixel - 1 - blueOffset;  {$ENDIF}   color := TColor($00A5FF);  for Y := 0 to aY - 1 do  begin    LinePixs := Result.RawImage.GetLineStart(Y);    for X := 0 to aX - 1 do    begin      (LinePixs+blueOffset)^ := GetBValue(color);      (LinePixs+greenOffset)^ := GetGValue(color);    (LinePixs+redOffset)^ := GetRValue(color);      inc(LinePixs, bytesPerPixel);    end;  end;end;