[SOLVED] Drawing a RED rectangle on a TPicture.Bitmap is shown in GRAY (WIN) lazarus / 39-Graphics ---------------------------------------------------------------------- Hartmut · 2024-11-19 12:55 I have an issue which only occurs on Windows, not on Linux. When I draw a red rectangle on a TPicture.Bitmap and show this Bitmap on a TPaintBox, then the picture itself is correct, but on Windows the rectangle has a gray color. When I set TForm1.Color to any value (see line 83), then this value will be the color of my rectangle. I'm still a beginner to Graphics. I could imagine that this issue has to do with Transparency-Mode. So I tried to disable this mode (see line 60), but this did not help. And I noticed, that this mode is still active on Windows (although I disabled it), while it is off on Linux (see line 61). This is my code: 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 {demonstrates that a red rectangular is shown in grey = TForm.Color} unit Unit1; {$mode objfpc}{$H+}{$IFNDEF LINUX} {$apptype console} {$ENDIF} {neccessary for writeln} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls; const PictureFile = 'demo1.jpg'; {picture file to show} type  { TForm1 }  TForm1 = class(TForm)  PaintBox1: TPaintBox;   procedure loadAndShowPicture(fspec: string);  procedure apply_Edits(BM: TBitmap);  procedure PaintBox1Paint(Sender: TObject);  procedure initialize;  procedure FormActivate(Sender: TObject);  procedure FormDestroy(Sender: TObject);  private  Picture1: TPicture; {contains the loaded picture file}  BM_valid: boolean;  {is the Bitmap for Event 'PaintBox1Paint' valid?} public end; var Form1: TForm1; implementation {$R *.lfm} procedure TForm1.loadAndShowPicture(fspec: string);   {loads and shows picture file 'fspec'}   begin   BM_valid:=false;              {disables Event PaintBox1Paint()}   Picture1.LoadFromFile(fspec);   self.SetBounds(20,20,Picture1.Width,Picture1.Height);    apply_Edits(Picture1.Bitmap); {draws a RED rectangle}   BM_valid:=true;               {enables Event PaintBox1Paint()}   PaintBox1.Invalidate;   end; {loadAndShowPicture} procedure TForm1.apply_Edits(BM: TBitmap);   {in real world: draws some rectangles in different colors and can make    other changes to the Bitmap 'BM'}   var CV: TCanvas;   begin   CV:=BM.Canvas;BM.Transparent:=false; // did not help on Windowswriteln('BM.Transparent=', BM.Transparent); // => TRUE on WIN / FALSE on Linux   CV.Pen.Color:=clRed;   CV.Pen.Width:=3;   CV.Brush.Style:=bsClear;   CV.Rectangle(100,100,200,200);   end; {apply_Edits} procedure TForm1.PaintBox1Paint(Sender: TObject);   {Event "OnPaint" of PaintBox1}   begin   if BM_valid then {only if the Bitmap for this Event is valid}      PaintBox1.Canvas.Draw(0,0,Picture1.Bitmap);   end; procedure TForm1.initialize;   {does the complete initialization}   begin   BM_valid:=false; {disables Event PaintBox1Paint()}   Picture1:=TPicture.Create;   PaintBox1.Align:=alClient;    if ParamCount > 0 then      self.Color:=clLime; // influences the wrong rectangular color   end; {initialize} procedure TForm1.FormActivate(Sender: TObject);   const first: boolean = true;   begin   if not first then exit; {run the following code only once}    first:=false;   initialize; {does the complete initialization}   loadAndShowPicture(PictureFile); {loads and shows a picture file}   end; procedure TForm1.FormDestroy(Sender: TObject);   begin   Picture1.Free;   end; end. I attached a compilable demo with a demo picture file. Versions:  - Windows 7 and 10 both with Lazarus 2.0.10 and 3.6.0  - Linux Ubuntu 22.04 with Lazarus 2.0.10 and 3.4.0 Can somebody help? Thanks in advance. ---------------------------------------------------------------------- Dzandaa · 2024-11-19 15:20 Hi, Just a quick test with BGRABitmap: B-> ---------------------------------------------------------------------- Hartmut · 2024-11-19 15:59 Thank you Dzandaa for your post. But it uses BGRABitmap and does not solve my issue, which is part of a bigger program, which does not use BGRABitmap and I don't want to change the horse in the home stretch of the race. ---------------------------------------------------------------------- LV · 2024-11-19 16:39 Maybe: 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.PaintBox1Paint(Sender: TObject);{Event "OnPaint" of PaintBox1}begin  if BM_valid then {only if the Bitmap for this Event is valid}    PaintBox1.Canvas.Draw(0, 0, Picture1.Bitmap);   // add here  PaintBox1.Canvas.Pen.Color := clRed;  PaintBox1.Canvas.Pen.Width := 3;  PaintBox1.Canvas.Brush.Style := bsClear;  PaintBox1.Canvas.Rectangle(100, 100, 200, 200);end; and see the screenshot (on Windows) ---------------------------------------------------------------------- wp · 2024-11-19 16:52 Yes, you probably only want to draw on the Canvas of the Paintbox and keep the loaded bitmap untouched like LV showed you. But if you really want to draw on the bitmap itself (and maybe later save it together with your drawing) you must know that a bitmap too easily gets the 32-bit pixelformat with the transparency channel (alpha) as 4th bit. When you now draw on this canvas in clRed you are using a 4-byte number with the alpha byte zero (clRed = $000000FF). This means that the color is considered to be transparent, and thus you see the background of the paintbox. In order to avoid this you must make sure that your Picture has a 24bit canvas. Maybe there are better ways to do this, but this works: - Draw the image on a bitmap created with a pf24bit pixelformat. - Assign this image to the Picture.Bitmap 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.loadAndShowPicture(fspec: string);   {loads and shows picture file 'fspec'}var  bmp: TBitmap;begin  BM_valid:=false;              {disables Event PaintBox1Paint()}  Picture1.LoadFromFile(fspec);   // Make sure that the Picture.Bitmap does not have pixelformat pf32Bit.  bmp := TBitmap.Create;  try    bmp.PixelFormat := pf24bit;    bmp.SetSize(Picture1.Width, Picture1.Height);    bmp.Canvas.Draw(0, 0, Picture1.Bitmap);    Picture1.Bitmap.Assign(bmp);  finally    bmp.Free;  end;   self.SetBounds(20,20,Picture1.Width,Picture1.Height);   apply_Edits(Picture1.Bitmap); {draws a RED rectangle}  BM_valid:=true;               {enables Event PaintBox1Paint()}  PaintBox1.Invalidate;end; {loadAndShowPicture} ---------------------------------------------------------------------- Hartmut · 2024-11-19 17:44 Thank you LV for your post, but this trick is not applicable in my real world program, where multpile changes to the original picture are possible (incl. cropping and rotating and scaling) and for speed and scrolling reasons I need 1 Bitmap with the complete changed picture, which then can easily be displayed in Event 'PaintBox1Paint'. Thank you very much wp for this explanations and your suggestion. Maybe I have underestimated the issue and abbreviated my real world program too drastically to this demo. In my real world program there are 2 more Bitmaps which "connect" Picture1.Bitmap and the PaintBox:  - Picture1.Bitmap always contains the original picture and is only changed in the moment, when and if changes have to be written to disk (this works, even on Windows)  - from Picture1.Bitmap the original picture is copied to a temporary Bitmap 'BM0', in which several changes can be made (drawing colored rectangles, cropping and rotating incl. an undo feature)  - this temporary Bitmap 'BM0' is then copied and scaled by a selectable scaling factor to another Bitmap 'BM3', which is used in Event 'PaintBox1Paint' to draw it (incl. scrolling on a TScrollBox). This Bitmap 'BM3' is mainly for displaying and scrolling speed. I will see, how I can apply your suggestion to this "chain" in my real world program and report then. Maybe tomorrow. Because applying all changes to Picture1.Bitmap before saving works (even on Windows!), my "only problem" is the chain from Picture1.Bitmap => temporary Bitmap 'BM0' => Bitmap 'BM3' => PaintBox. Do you have an idea why this issue only occurs on Windows and not on Linux? ---------------------------------------------------------------------- wp · 2024-11-19 18:43 If I understand your requirements correctly then the intermediate bitmap on which you draw, BM0, must be 24 bits per pixel. I did not mention, though, that your input image must not be alpha-channel transparent because its transparency would be removed when going to 24 bpp. In this case you could do all the drawing (and scaling) in a TLazIntfImage (https://wiki.freepascal.org/Developing_with_Graphics#Wo rking_with_TLazIntfImage,_TRawImage_and_TLazCanvas). But, to be honest, I think needing some more or less complex image processing is a good argument for switching to TBGRABitmap. Quote from: Hartmut on November 19, 2024, 06:44:00 pmDo you have an idea why this issue only occurs on Windows and not on Linux? When you insert the following code after loading the jpeg image 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    Picture1.LoadFromFile(fspec);   Caption := Format('%d bpp, Depth=%d', [     Picture1.Bitmap.RawImage.Description.BitsPerPixel,     Picture1.Bitmap.RawImage.Description.Depth   ]); and run this in both Windows and Linux you'll see that both of them reserve 32 bits for a pixel, but Linux evaluates only 24 of them (Depth = 24), i.e. Linux ignores the alpha channel which would be included in the ignored 8 bits. However, when you load an alpha-transparent png image, Linux will also switch to Depth=32, and you will have the same issue as in Windows. ---------------------------------------------------------------------- Hartmut · 2024-11-21 10:11 As you had said, I only had to set the PixelFormat of Bitmap 'BM0' to pf24bit. My old code was: 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 BM0:=TBitmap.Create;BM0.Assign(Picture1.Bitmap);apply_Edits(BM0);... I changed it to: 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 BM0:=TBitma p.Create;BM0.PixelFormat:=pf24bit;BM0.SetSize(Picture1.Width,Picture1. Height);BM0.Canvas.Draw(0,0,Picture1.Bitmap);apply_Edits(BM0);... This works on Windows and Linux pefectly with all JPG and PNG files I could test. With GIF and ICO files sometimes the background color gets wrong, but this is not a problem for me, because this files are not writeable via Picture1.SaveToFile. Now I know that this could be solved by switching to TLazIntfImage or TBGRABitmap. I checked your link https://wiki.freepascal.org/Developing_with_Graphi cs#Working_with_TLazIntfImage,_TRawImage_and_TLazCanvas but did not understand much, because there too many things are referenced which I don't know. I can confirm that Picture1.Bitmap.RawImage.Description.Depth on Windows is 32 while on Linux it is 24. One last question: does Picture1.Bitmap.RawImage.Description.BitsPerPixel contain always the number of bits, which are stored in the picture file, which was loaded via Picture1.LoadFromFile? Or where do I find this value? Again thank you very much wp for your valuable help! ---------------------------------------------------------------------- wp · 2024-11-21 11:24 An excellent description of FPImage has been written by Michael Van Canneyt: https://www.freepascal.org/~michael/articles/canvas/canvas.pdf It does not cover TRawImageDescription, though, which describes the arrangement and meaning of the individual bits of each pixel. For example, when you CTRL+click on the "Description" in "Picture1.Bitmap.RawImage.Description", the IDE will open the declaration of TRawImageDescription, and you will see lots of comments explaining the elements of this record: 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 // in GraphType type  TRawImageDescription = object    Format: TRawImageColorFormat;    Width: cardinal;    Height: cardinal;    Depth: Byte; // used bits per pixel    BitOrder: TRawImageBitOrder;  ByteOrder: TRawImageByteOrder;    LineOrder: TRawImageLineOrder;    LineEnd: TRawImageLineEnd;    BitsPerPixel: Byte; // bits per pixel. can be greater than Depth.    RedPrec: Byte;      // red or gray precision. bits for red    RedShift: Byte;     // bitshift. Direction: from least to most significant    GreenPrec: Byte;    GreenShift: Byte;    BluePrec: Byte;    BlueShift: Byte;    AlphaPrec: Byte;    AlphaShift: Byte;     // The next values are only valid, if there is a mask (MaskBitsPerPixel > 0)    // Masks are always separate with a depth of 1 bpp. One pixel can occupy    // one byte at most    // a value of 1 means that pixel is masked    // a value of 0 means the pixel value is shown    MaskBitsPerPixel: Byte; // bits per mask pixel, usually 1, 0 when no mask    MaskShift: Byte;        // the shift (=position) of the mask bit    MaskLineEnd: TRawImageLineEnd;  MaskBitOrder: TRawImageBitOrder;     // The next values are only valid, if there is a palette (PaletteColorCount > 0)    PaletteColorCount: Word;   // entries in color palette. 0 when no palette.    PaletteBitsPerIndex: Byte; // bits per palette index, this can be larger than the colors used    PaletteShift: Byte;        // bitshift. Direction: from least to most significant    PaletteLineEnd: TRawImageLineEnd;    PaletteBitOrder: TRawImageBitOrder;    PaletteByteOrder: TRawImageByteOrder;        // don't use a constructor here, it will break compatibility with a record    procedure Init;     // 1-bit mono format    procedure Init_BPP1(AWidth, AHeight: integer);     // 16-bits formats    procedure Init_BPP16_R5G6B5(AWidth, AHeight: integer);     // Formats in RGB order    procedure Init_BPP24_R8G8B8_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP24_R8G8B8_BIO_TTB_UpsideDown(AWidth, AHeight: integer);    procedure Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP32_R8G8B8A8_BIO_TTB(AWidth, AHeight: integer);     // Formats in Windows pixels order: BGR    procedure Init_BPP24_B8G8R8_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP24_B8G8R8_M1_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP32_B8G8R8_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP32_B8G8R8_M1_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight: integer);    procedure Init_BPP32_B8G8R8A8_M1_BIO_TTB(AWidth, AHeight: integer);   function GetDescriptionFromMask: TRawImageDescription;    function GetDescriptionFromAlpha: TRawImageDescription;     //returns indices of channels in four-element array    procedure GetRGBIndices(out Ridx, Gidx, Bidx, Aidx:Byte);     function BytesPerLine: PtrUInt;    function BitsPerLine: PtrUInt;    function MaskBytesPerLine: PtrUInt;    function MaskBitsPerLine: PtrUInt;     function AsString: string;  function IsEqual(ADesc: TRawImageDescription): Boolean;  end;  PRawImageDescription = ^TRawImageDescription;  Here, BitsPerPixel is to be understood as the number of bits occupied in memory by each pixel. Depth, on the other hand, is the number of bits used from the bits per pixel for displaying the image - the comment says that Depth can be smaller than BitsPerPixel. Reserving 32 bpp for a depth of 24bpp has the advantage that memory can be scanned faster if pixels are arranged in 4-byte-steps. ---------------------------------------------------------------------- Hartmut · 2024-11-21 17:02 Thanks wp for this additional informations. I read the PDF from Michael Van Canneyt and added it to my list of Graphic documentation links. I checked type 'TRawImageDescription' and it contains more comments than many sources of the LCL have. What I did not find and still would like to know: how can I query the number of bits per pixel, which are stored in the picture file, which was loaded via Picture1.LoadFromFile? Does somebody know this? ---------------------------------------------------------------------- wp · 2024-11-21 18:42 Read the file headers. But be aware that upon loading the graphics unit may convert the pixelformat to anything appropriate. Therefore my question: What do you need this information for? ---------------------------------------------------------------------- Hartmut · 2024-11-22 11:32 I only wanted to display this value just for curiosity together with other properties of the picture file. I thought this value would exist somewhere in class 'TPicture'. But from your answer I understand that this is not the case. It's not worth to spend the effort to analyze the file headers of each possible file type.