Recent

Author Topic: Incorrect behavior writing pixels[x,y] to a TImage (Bitmap.Assign bug)  (Read 4309 times)

QuinnMartin

  • Jr. Member
  • **
  • Posts: 56
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #15 on: May 29, 2024, 09:52:42 am »
Well the pre-existing image I am working with is a 1600x800 TBGRABitmap image copied over to TImage, where I am running these tests.  Since I have a 24-bit image I figured the alpha channels would be ignored and I'd just be getting the RGB data.  The image that ends up drawn in Image1 looks perfectly correct.

Here's an interesting development, though: If I clear the image with a FillRect, then black plots correctly when I draw a line.  If I don't do this and I draw a line, I get the erroneous light gray.

Code: Pascal  [Select][+][-]
  1.   // Clear bitmap first. If we don't do this then the line below plots as light gray.
  2.   Image1.Picture.Bitmap.SetSize(Image1.Width, Image1.Height);
  3.   Image1.Canvas.Brush.Color := clWhite;
  4.   Image1.Canvas.FillRect(0, 0, Image1.Width, Image1.Height);
  5.  
  6.   // Draw a line.
  7.   Image1.Canvas.Pen.Color := clBlack;
  8.   Image1.Canvas.Pen.Width := 2;
  9.   Image1.Canvas.MoveTo(100, 100);
  10.   Image1.Canvas.LineTo(500, 500);  

Is there any possible explanation for this behavior?

In case it matters, the code I have to copy TBGRABitmap to TImage is:

Code: Pascal  [Select][+][-]
  1. bmp : TBGRABitmap;
  2.  
  3. if not bmp.Empty then
  4.     begin
  5.       if Assigned(Image1) then
  6.         begin
  7.           Image1.Picture.Bitmap.Assign(bmp);
  8.           Image1.Width := bmp.Width;
  9.           Image1.Height := bmp.Height;
  10.           Image1.update;  // update immediately
  11.         end;
  12.     end;
  13.  

QuinnMartin

  • Jr. Member
  • **
  • Posts: 56
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #16 on: May 30, 2024, 04:39:54 am »
OK, I don't know what the problem is.  I'm still getting an incorrect pen color when drawing on top of TImage.  Here is the full test application with an attached PNG, which draws a white line when it is supposed to be black.  Surely there must be an explanation for why TImage is not drawing the correct color.  Is there a setting that I am overlooking or is my process incorrect?

EDIT: Removed a dumb redundancy in the final "with" statement, just syntax.

Code: Pascal  [Select][+][-]
  1. unit graphicstest2main;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  9.   BGRABitmap, BGRABitmapTypes,
  10.   StdCtrls;
  11.  
  12. type
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     Button1: TButton;
  18.     Image1: TImage;
  19.     Label1: TLabel;
  20.     procedure Button1Click(Sender: TObject);
  21.   private
  22.  
  23.   public
  24.  
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. {$R *.lfm}
  33.  
  34. { TForm1 }
  35.  
  36. procedure TForm1.Button1Click(Sender: TObject);
  37. var
  38.   x, y : integer;
  39.   bmp : TBGRABitmap;
  40.   rs : integer;
  41. begin
  42.  
  43.   label1.caption := 'Loading...';
  44.   label1.update;
  45.  
  46.   // Load into TBGRABitmap and copy to TImage.
  47.   bmp := TBGRABitmap.Create('pngtest.png');
  48.   try
  49.  
  50.     if not bmp.Empty then
  51.       begin
  52.         if Assigned(Image1) then
  53.         begin
  54.           Image1.Picture.Bitmap.PixelFormat := pf24bit;
  55.           Image1.Picture.Bitmap.Assign(bmp);
  56.           Image1.Width := bmp.Width;
  57.           Image1.Height := bmp.Height;
  58.           Image1.update;  // update immediately
  59.         end;
  60.       end;
  61.  
  62.     // If you uncomment this block and let it overwrite the contents, the
  63.     // line draw will work correctly.
  64.     (*
  65.     Image1.Picture.Bitmap.SetSize(Image1.Width, Image1.Height);
  66.     Image1.Canvas.Brush.Color := clWhite;
  67.     Image1.Canvas.FillRect(0, 0, Image1.Width, Image1.Height);
  68.     *)
  69.  
  70.     // Draw a line using a black color.
  71.     with Image1.picture.bitmap.canvas do
  72.     begin
  73.       Brush.Style := bsClear;
  74.       Pen.Color := clBlack;
  75.       Pen.Width := 2;
  76.       MoveTo(100,100);
  77.       LineTo(500,500);
  78.     end;
  79.   finally
  80.     bmp.free;
  81.     label1.caption := 'Idle.';
  82.   end;
  83.  
  84. end;
  85.  
  86. end.
  87.  
  88.  
« Last Edit: May 30, 2024, 09:55:25 am by QuinnMartin »

Paolo

  • Hero Member
  • *****
  • Posts: 538
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #17 on: May 30, 2024, 06:51:57 am »
What about you change the brush style to bsSolid ?
Code: Pascal  [Select][+][-]
  1. Image1.Canvas.Brush.Style := bsSolid;
  2.  

QuinnMartin

  • Jr. Member
  • **
  • Posts: 56
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #18 on: May 30, 2024, 07:54:50 am »
What about you change the brush style to bsSolid ?
Code: Pascal  [Select][+][-]
  1. Image1.Canvas.Brush.Style := bsSolid;
  2.  

I tried it, no change, same problem.

Thaddy

  • Hero Member
  • *****
  • Posts: 16145
  • Censorship about opinions does not belong here.
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #19 on: May 30, 2024, 08:13:17 am »
This seems a bug because it works in Delphi.
If I smell bad code it usually is bad code and that includes my own code.

paweld

  • Hero Member
  • *****
  • Posts: 1268
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #20 on: May 30, 2024, 08:15:17 am »
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   bmp : TBGRABitmap;
  4.   ms: TMemoryStream;
  5. begin
  6.  
  7.   label1.caption := 'Loading...';
  8.   label1.update;
  9.  
  10.   // Load into TBGRABitmap and copy to TImage.
  11.   bmp := TBGRABitmap.Create('d:\Downloads\pngtest.png');
  12.   try
  13.  
  14.     if not bmp.Empty then
  15.       begin
  16.         if Assigned(Image1) then
  17.         begin
  18.           ms := TMemoryStream.Create;
  19.           bmp.SaveToStreamAs(ms, ifBmp);
  20.           ms.Position := 0;
  21.           Image1.Picture.LoadFromStream(ms);
  22.           ms.Free;
  23.         end;
  24.       end;
  25.  
  26.     // Draw a line using a black color.
  27.     with Image1.canvas do
  28.     begin
  29.       Image1.Canvas.Brush.Style := bsClear;
  30.       Image1.Canvas.Pen.Color := clBlack;
  31.       Image1.Canvas.Pen.Width := 2;
  32.       Image1.Canvas.MoveTo(100,100);
  33.       Image1.Canvas.LineTo(500,500);
  34.     end;
  35.   finally
  36.     bmp.free;
  37.     label1.caption := 'Idle.';
  38.   end;
  39. end;
Best regards / Pozdrawiam
paweld

rvk

  • Hero Member
  • *****
  • Posts: 6572
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #21 on: May 30, 2024, 08:59:38 am »
OK, I don't know what the problem is.  I'm still getting an incorrect pen color when drawing on top of TImage.  Here is the full test application with an attached PNG, which draws a white line when it is supposed to be black.  Surely there must be an explanation for why TImage is not drawing the correct color.  Is there a setting that I am overlooking or is my process incorrect?
Why are you using TBGRABitmap ?????
Just loading the image in the TImage and then writing on TImage.Picture.Bitmap.Canvas works fine.

Also note: You need to use TImage.Picture.Bitmap.Canvas. You CAN'T use TImage.Canvas after you loaded an image to it !!!!!!
TImage.Picture.Bitmap has 'taken over' and you need to do your changes on that Canvas !

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   label1.Caption := 'Loading...';
  4.   label1.Update;
  5.   try
  6.     Image1.Picture.LoadFromFile('c:\temp\pngtest.png');
  7.     Image1.Update;
  8.     Image1.Picture.Bitmap.Canvas.Brush.Style := bsClear;
  9.     Image1.Picture.Bitmap.Canvas.Pen.Color := clBlack;
  10.     Image1.Picture.Bitmap.Canvas.Pen.Width := 2;
  11.     Image1.Picture.Bitmap.Canvas.MoveTo(100, 100);
  12.     Image1.Picture.Bitmap.Canvas.LineTo(500, 500);
  13.   finally
  14.     label1.Caption := 'Idle.';
  15.   end;
  16. end;

QuinnMartin

  • Jr. Member
  • **
  • Posts: 56
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #22 on: May 30, 2024, 09:50:46 am »
Why are you using TBGRABitmap ?????
Just loading the image in the TImage and then writing on TImage.Picture.Bitmap.Canvas works fine.

As mentioned upthread, I wrote that code to test a very specific problem.  That problem is why are the TImage canvas methods painting in some random color instead of clBlack?  Me using TBGRAbitmap is beside the point, I am using that because I stumbled into the problem that way, and now I am trying to find out why TImage is misbehaving so I can understand the problem better and figure out if TImage is bugged.  I am guessing maybe the bitmap copy from TBGRABitmap is corrupting the TImage.

I switched the code as you suggested to paint to Image1.picture.bitmap.canvas and there is no change, clBlack still paints as rgb 240,240,240.

Obviously I should be using something like TBGRAVirtualScreen to display the TBGRABitmap and I likely will start adapting to use that component, but I was hoping to figure out a quick fix so I could continue using TImage in my old legacy apps as a quick visual displayer for TBGRABitmap without a lot of additional coding.

All my code is there upthread along with the sample PNG, anyone willing to test this will probably get the same weird results.
« Last Edit: May 30, 2024, 09:57:10 am by QuinnMartin »

QuinnMartin

  • Jr. Member
  • **
  • Posts: 56
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #23 on: May 30, 2024, 10:00:54 am »
          ms := TMemoryStream.Create;
          bmp.SaveToStreamAs(ms, ifBmp);
          ms.Position := 0;
          Image1.Picture.LoadFromStream(ms);
          ms.Free;

Wow, this fixes it.  Why would this TMemoryStream work and not Bitmap.Assign?  Is Bitmap.Assign hosed?

paweld

  • Hero Member
  • *****
  • Posts: 1268
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #24 on: May 30, 2024, 10:23:58 am »
Because when saving as Bmp stream set color palette to 24 bit, and using Assign you copy along with the alpha canal. Setting PixelFormat to 24 bit is done before assigning, so assigning overrides the previously set option, and trying to change PixelFormat after assigning doesn't give the expected results - there may be an error as @Thaddy mentioned.
Best regards / Pozdrawiam
paweld

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #25 on: May 30, 2024, 12:35:32 pm »
Just loading the image in the TImage and then writing on TImage.Picture.Bitmap.Canvas works fine.
No, it does not, drawing a line on TImage.Picture.Bitmap.Canvas results in a gray line. The line is gray because TImage expands the loaded image to 32 bpp, i.e. the image has an alpha channel now. In case of a non-transparent jpg, all alpha values are 255 (opaque). But when you draw on such an image, e.g. black (with R=0, G=0, B=0), the canvas erases the alpha byte to zero (black = $00000000 = (red=0, green=0, blue=0, alpha=0), and alpha=0 means: transparent. You can see this, when you change the color of the form - the color of the drawn line on the image changes, too!

Maybe there are other ways, but for me the simplest way to directly draw on the TImage.Picture.Bitmap.Canvas is via a LazIntfImage:
Code: Pascal  [Select][+][-]
  1. uses
  2.   FPImage, IntfGraphics, LazCanvas;
  3.  
  4. procedure TForm1.Button4Click(Sender: TObject);
  5. var
  6.   img: TLazIntfImage;
  7.   canv: TLazCanvas;
  8. begin
  9.   Image1.Picture.LoadFromFile('C:\Lazarus\lazarus-main_fpc3.2.2\images\splash_source\cheetah.jpg');
  10.   img := Image1.Picture.Bitmap.CreateIntfImage;
  11.   try
  12.     canv := TLazCanvas.Create(img);
  13.     canv.pen.FPColor := colRed;
  14.     canv.Pen.Width := 5;
  15.     canv.Line(0, 0, 100, 100);
  16.     Image1.Picture.Bitmap.LoadFromIntfImage(img);
  17.   finally
  18.     canv.Free;
  19.     img.Free;
  20.   end;
  21. end;
But since an intermediate object is already needed here, and since LazIntfImage is a bit tricky sometimes, it really is the best to load the image into a temporary bitmap, do the drawing here, and then load this bitmap into the image as already suggested by KodeZwerg in the beginning. (However, I'd transfer the bitmap via stream (rather than using Assign) because this way the transparency is preserved).
« Last Edit: May 30, 2024, 12:43:00 pm by wp »

QuinnMartin

  • Jr. Member
  • **
  • Posts: 56
Because when saving as Bmp stream set color palette to 24 bit, and using Assign you copy along with the alpha canal. Setting PixelFormat to 24 bit is done before assigning, so assigning overrides the previously set option, and trying to change PixelFormat after assigning doesn't give the expected results - there may be an error as @Thaddy mentioned.

Thanks all, I appreciate you all checking into this.  I thought there was some oversight on my part and I couldn't figure it out.

I just went ahead and replaced TImage with TBGRAVirtualScreen and am doing all the pixel and line draws on the TBGRABitmap now, which I think is the recommended way.

However I have saved a note of this TMemoryStream method and will use it if I have to pass a bitmap to TImage again.
« Last Edit: May 31, 2024, 04:14:59 am by QuinnMartin »

jipété

  • Full Member
  • ***
  • Posts: 182
Re: Incorrect behavior writing pixels[x,y] to a TImage
« Reply #27 on: June 16, 2024, 11:05:32 am »
Hello,

... The line is gray because TImage expands the loaded image to 32 bpp, i.e. the image has an alpha channel now.

in Windows, maybe ? But not in Linux, clearly not !

I"ve done the following :

Code: Pascal  [Select][+][-]
  1.   ...
  2.   Image1.Picture.LoadFromFile('/usr/share/lazarus/3.4.0/images/splash_source/cheetah.jpg'); // first working line of the Button4 procedure
  3.   Image1.Picture.SaveToFile('/tmp/test32','bmp'); // second line added by me
  4.   ...

in https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_(bitmap_information_header) one can read, in the table, line 1C,
Quote
the number of bits per pixel, which is the color depth of the image. Typical values are 1, 4, 8, 16, 24 and 32.
If value is 24 [bpp] then one read 18 at address 1C of the bitmap file ; il value is 32 one read 20.

And the value of the beginning of the generated file attached below is 18, so 24 bpp...
Regards,
« Last Edit: June 16, 2024, 11:09:23 am by jipété »

jamie

  • Hero Member
  • *****
  • Posts: 6733
Hmm
has anyone looked into the PEN.MODE ?

Pen.Mode can cause adverse effects if not set to pmCopy.
The only true wisdom is knowing you know nothing

jipété

  • Full Member
  • ***
  • Posts: 182
Hello,

Hmm
has anyone looked into the PEN.MODE ?

Pen.Mode can cause adverse effects if not set to pmCopy.

The problem was not here but simply the fact that for getting a 32bits result file, one must start with a 32bits source file !
Tip not reported in wp's post, which uses a .jpg file, so 24 bits.
« Last Edit: June 17, 2024, 08:47:44 am by jipété »

 

TinyPortal © 2005-2018