Lazarus

Programming => Graphics => Graphics and Multimedia => BGRABitmap and LazPaint => Topic started by: schlueter550v on April 22, 2021, 10:01:04 am

Title: BGRABitmap to TImage fault
Post by: schlueter550v on April 22, 2021, 10:01:04 am
Hello Lazarus Community,

this is my first post, and i hope i get good help. I am very new to Lazarus and ObjPascal, please be kind if i do simple stuff wrong.
To the Problem i have i found nothing in the Wiki nor the Forum.

I am currently doing experiments with BGRABitmap. I simply want to load an image from the clipboard and put it into a TBGRABitmap variable. Later i want to do something with this image, but in my test Code i want to show the TBGRABitmap variable in an Timage on the Form.

this is my current button function:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.btnClipboardClick(Sender: TObject);
  2. var
  3.   image : TBGRABitmap;
  4.   pic : TPicture;
  5. begin
  6.   try
  7.     try
  8.       //initialize
  9.       pic := TPicture.Create();
  10.  
  11.       if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap))) then
  12.        begin
  13.          pic.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfBitmap));
  14.          image := TBGRABitmap.Create(pic.bitmap);
  15.          Image1.Picture.Assign(image);
  16.          Image2.Picture.Assign(pic.Bitmap);
  17.        end
  18.       else if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfPicture))) then
  19.        begin
  20.          pic.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfPicture));
  21.          image := TBGRABitmap.Create(pic.Bitmap);
  22.          Image1.Picture.Assign(image);
  23.          Image2.Picture.Assign(pic.Bitmap);
  24.        end;
  25.     finally
  26.       try
  27.         pic.Free;
  28.         //image.free
  29.       except
  30.         on E : Exception do ShowMessage(E.Message);
  31.       end;
  32.     end;
  33.   except
  34.     on E : Exception do ShowMessage(E.Message);
  35.   end;
  36. end;
  37.  

Image1 on the Form looks corrupted, Image2 fine (see attachment). The Error appears in Windows, in Linux it works fine. What am i doing wrong?
Title: Re: BGRABitmap to TImage fault
Post by: circular on April 25, 2021, 08:46:05 am
Hello,

The problem may come arise for different reasons.

If it is related to the the way TPicture stores the image, you could read the file directly:
Code: Pascal  [Select][+][-]
  1. var stream: TStream;
  2.        ...
  3.        Stream := TMemoryStream.Create;
  4.        Clipboard.GetFormat(Clipboard.Formats[i],Stream);
  5.        Stream.Position := 0;
  6.        image := TBGRABitmap.Create(Stream);
  7.        if image.Empty then image.AlphaFill(255);  // you can try to always call AlphaFill
  8.        Stream.Free;

If for some reason the alpha channel or alpha mask is not set properly (sometimes Windows writes inconsistent alpha in opaque bitmaps), then you could try:
Code: Pascal  [Select][+][-]
  1.        pic.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfBitmap));
  2.        image := TBGRABitmap.Create(pic.bitmap);
  3.        image.AlphaFill(255);  // fix alpha channel

Another thing that might help is to assign via a TBitmap copy:
Code: Pascal  [Select][+][-]
  1. var copy: TBitmap;
  2.        ...
  3.        copy := image.MakeBitmapCopy(clNone);
  4.        Image1.Picture.Assign(copy);
  5.        copy.Free;
instead of:
Code: Pascal  [Select][+][-]
  1.        Image1.Picture.Assign(image);
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 05, 2021, 07:56:51 am
Hello circular,

your way with loading the image from stream worked, but now something else occures.
If i have an image with alpha channel, i want to replace it with white, but somehow it turns black?

In my example i copied from Gimp. If i copy from Gimp to Lazpaint it works. If i copy then from Lazpaint to my Program it works too?

Here is my code and in the attechment an example:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.btnFromClipboardClick(Sender: TObject);
  2. var
  3.   image : TBGRABitmap;
  4.   stream : TMemoryStream;
  5. begin
  6.   try
  7.     try
  8.       //initialize
  9.       stream := TMemoryStream.Create;
  10.  
  11.       if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap))) then
  12.        begin
  13.          Clipboard.GetFormat(PredefinedClipboardFormat(pcfBitmap),stream);
  14.          stream.Position := 0;
  15.          image := TBGRABitmap.Create(stream);
  16.          image.ReplaceTransparent(BGRAWhite);
  17.          ShowMessage('bitmap');
  18.          imgPreview.Picture.Bitmap.Assign(image.Bitmap);
  19.          //scaleAndPreviewImage(image); // frees image
  20.        end
  21.       else if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfPicture))) then
  22.        begin
  23.          Clipboard.GetFormat(PredefinedClipboardFormat(pcfPicture),stream);
  24.          stream.Position := 0;
  25.          image := TBGRABitmap.Create(stream);
  26.          image.ReplaceTransparent(BGRAWhite);
  27.          ShowMessage('picture');
  28.          imgPreview.Picture.Bitmap.Assign(image.Bitmap);
  29.          //scaleAndPreviewImage(image); // frees image
  30.        end;
  31.     except
  32.       on E : Exception do ShowMessage(E.Message);
  33.     end;
  34.   finally
  35.     try
  36.       stream.Free;
  37.     except
  38.       on E : Exception do ShowMessage(E.Message);
  39.     end;
  40.   end;
  41. end;
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 05, 2021, 01:01:21 pm
Hello,

ok i got it. I've looked at the uclipboard.pas from LazPaint. I had to define A Clipboardformat for PNG.
It works, so i hope i've done everything correctly:

Code: Pascal  [Select][+][-]
  1. implementation
  2.  
  3. {$R *.lfm}
  4.  
  5. var
  6.   pngClipboardFormat : TClipboardFormat;
  7.  
  8. ...
  9.  
  10.  
  11. procedure TForm1.btnFromClipboardClick(Sender: TObject);
  12. var
  13.   image : TBGRABitmap;
  14.   stream : TMemoryStream;
  15. begin
  16.   try
  17.     try
  18.       //initialize
  19.       stream := TMemoryStream.Create;
  20.  
  21.       if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap))) AND (NOT(Clipboard.HasFormat(pngClipboardFormat))) then
  22.        begin
  23.          Clipboard.GetFormat(PredefinedClipboardFormat(pcfBitmap),stream);
  24.          stream.Position := 0;
  25.          image := TBGRABitmap.Create(stream);
  26.          scaleAndPreviewImage(image); // frees image
  27.        end;
  28.       if (Clipboard.HasFormat(pngClipboardFormat)) then
  29.        begin
  30.          Clipboard.GetFormat(pngClipboardFormat,stream);
  31.          stream.Position := 0;
  32.          image := TBGRABitmap.Create;
  33.          image.LoadFromStream(stream);
  34.          //if image.Empty then image.AlphaFill(255);
  35.          scaleAndPreviewImage(image); // frees image
  36.        end;
  37.     except
  38.       on E : Exception do ShowMessage(E.Message);
  39.     end;
  40.   finally
  41.     try
  42.       stream.Free;
  43.     except
  44.       on E : Exception do ShowMessage(E.Message);
  45.     end;
  46.   end;
  47. end;
  48.  
  49. ...
  50.  
  51. initialization
  52.  
  53. pngClipboardFormat := RegisterClipboardFormat({$IfDef WINDOWS}'PNG'{$Else}{$IfDef DARWIN}'public.png'{$Else}'image/png'{$EndIf}{$EndIf});
  54.  
  55.  

Now i only have Problems with Alpha Images from the Browser. I only think, is it possible to change the default background of TBGRABitmap.create to white instead of Black?

Best regards
schlueter550v
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 05, 2021, 07:17:51 pm
Hi!

I have tested your code and the transparent pixel exist: Value 0/0/0/0.

The conversion from transparent results if you don't set the parameter opaque of BGRAbitmap.draw to false. So the correct way is

Code: Pascal  [Select][+][-]
  1. BGRAbitmap.draw (Image1.canvas,0,0, false);

That's the whole secret.

Tested with Lin64, fpc 3.2, Laz 2.0.12.0

Winni
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 05, 2021, 08:39:00 pm
Hmmmm

Draw with opaque =  false also fails.

The solution ist good old

Code: Pascal  [Select][+][-]
  1. Image1.Canvas.CopyRect (MyRect, BGRAImage.Canvas,MyRect);

It respects the transparent pixels. Strange.

inni
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 05, 2021, 09:23:09 pm
Maybe first fill the image canvas with white, then draw the image with transparency?
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 05, 2021, 09:24:52 pm
I had to define A Clipboardformat for PNG.
Indeed, some programs use PNG as clipboard format.
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 05, 2021, 10:28:35 pm
Hi!

Gimp for example uses PNG as Clipboard format.

The problems drawing the BGRAbitmap from the Clipboard stream:


Code: Pascal  [Select][+][-]
  1. Bmp.draw (Image.canvas, 0,0);
  2.  
draws the transparent pixels as black, even if you fill the Image with some color.

Code: Pascal  [Select][+][-]
  1. Bmp.draw (Image.canvas,0,0,false);

draws  nothing if you fill the image before with a color.

Code: Pascal  [Select][+][-]
  1. Image.canvas.CopyRect (Arect, Bmp.canvas,ARect);

does the job including respecting transparency.

The transparent pixel exist as 0/0/0/0. That is not the reason.

Talking about Linux.

Winni

Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 06, 2021, 08:07:45 am
Hello,

i tried the following now, but it did no change. Im not even sure if i used the CopyRect correct?
The image background keeps black on Windows:

Code: Pascal  [Select][+][-]
  1. if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap))) AND (NOT(Clipboard.HasFormat(pngClipboardFormat))) then
  2.        begin
  3.          Clipboard.GetFormat(PredefinedClipboardFormat(pcfBitmap),stream);
  4.          stream.Position := 0;
  5.          image := TBGRABitmap.Create(stream);
  6.          imgPreview.Canvas.CopyRect(Rect(0,0,image.Width,image.Height),image.Canvas,Rect(0,0,image.Width,image.Height));
  7.          //scaleAndPreviewImage(image); // frees image
  8.        end;

How to check if those pixels are actual transparent?

Best Regards
schlueter550v
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 06, 2021, 10:21:16 am
Hi!

First fill the imgPreview with a color. Otherwise the color is by default black. So the copyrect respects the transparency but the background is black.

Check the values of a pixel:
Code: Pascal  [Select][+][-]
  1. Const LE=LineEnding;
  2. var px : TBGRApixel;
  3. s : string;
  4. ...
  5. px := Image.GetPixel(x,y);
  6. s := IntToStr(px.red)+LE+IntToStr(px.green)+LE+IntToStr(px.blue)+
  7.               LE +IntToStr(px.alpha);
  8. showMessage (s);                
  9. ....
  10.  

If a pixel is transparent it should show 0/0/0/0

Winni
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 06, 2021, 12:46:21 pm
Hello winni,

i tried your suggestion:

Code: Pascal  [Select][+][-]
  1. if (Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap))) AND (NOT(Clipboard.HasFormat(pngClipboardFormat))) then
  2.        begin
  3.          Clipboard.GetFormat(PredefinedClipboardFormat(pcfBitmap),stream);
  4.          stream.Position := 0;
  5.          image := TBGRABitmap.Create(stream);
  6.          //image.LoadFromStream(stream, reader,[loBmpAutoOpaque]);
  7.          px := image.GetPixel(3,1);
  8.          s :=  IntToStr(px.red) + ' ' + IntToStr(px.green) + ' ' + IntToStr(px.blue) + ' ' + IntToStr(px.alpha);
  9.          ShowMessage(s);
  10.          scaleAndPreviewImage(image); // frees image
  11.        end;
  12.  

But somehow i get 0 0 0 255 from that Pixel, which definetly should be transparent.
I also tried to fill the complete Canvas (250x250) white and put the image (200x200) with CopyRect on it.
The (200x200) area background is black from the image, and the rest of the Canvas space is white.
Im talking about a Win 10 System.


Some more ideas??


Best Regards

schlueter550v
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 06, 2021, 01:07:03 pm
Hi!

The RGBA value 0/0/0/255 is black.

The value x/x/x/0  is transparent.


It seems that Windows behaves different from gtk2.
We should wait for  circular to give us an answer.

Winni
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 06, 2021, 01:21:19 pm
@schlueter550v

In your latest example, you read the BMP format, not PNG. On Windows, BMP clipboard is almost always opaque.
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 06, 2021, 01:29:42 pm
Hello,

so you suggest to use another Clipboardformat than pcfBitmap? But if i copy my PNG Picture from Firefox the Clipboard shows no PNG???
Or do i have to use that very long code from LazPaints uclipboard.pas' functions where some data is obtained from HTML? Is it possible to "use" this unit in uses?

In the attachment is an example of an PNG copied in Firefox. If i do it in IE or Edge it looks again different.
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 06, 2021, 02:01:22 pm
Hi!

I deleted the TBitmap part from the code to enshure that only PNGs are loaded.


The behaviour differs between gtk2 and Win7 - just tested.

* In all  cases the transparent pixels are 0/0/0/0 - good.
* In all cases a BGRAbitmap.draw(Image.canvas,0,0) converts the transparent pixels to black
* BGRAbitmap.draw(Image.canvas,0,0, false): In Windows the pixels appear in the background color, while gtk2 shows nothing.
* Image.canvas.CopyRect: In gtk2 the desired result is shown while Windows behaves like BGRAbitmap.draw(Image.canvas,0,0, false): The opaque pixel are shown in the backpmground color.

All results except copyrect and gtk2 are not satisfying.

Winni
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 06, 2021, 05:11:11 pm
Hi!

Now I got a working trick!

Dont draw on the canvas of the TImage but on the image.picture.PNG.Canvas!


Code: Pascal  [Select][+][-]
  1. image.Draw(Image1.Picture.PNG.Canvas,0,0,false);    

That preserves the transparency and everything is like it should!

Only tested with Linux/gtk2

Winni


PS.: Just tested with Win7: Works fine!
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 06, 2021, 07:33:19 pm
Well you could try to use uclipboard though it depend on some other units to fetch files online.

I am wonder, I can see CF_DIB and CF_DIBV5. Maybe the V5 one handles transparency. Though I am not sure how you could make the difference from the LCL.

I was also quite confused about the clipboard situation on Windows.
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 06, 2021, 08:50:13 pm

I am wonder, I can see CF_DIB and CF_DIBV5. Maybe the V5 one handles transparency. Though I am not sure how you could make the difference from the LCL.


Hi!

CF_DIBV5 contains a bV5AlphaMask.  That is the answer to all the questions.

M$ doc: https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header (https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header)

How the LCL handles this I don't know.

Winni
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 07, 2021, 08:21:27 am
hello circular,
hello winni,

for fun i tried the same with Lazpaint on Windows. It doesnt work there too. you can see it in the attchment.

I am clueless right now
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 07, 2021, 08:37:26 am
Does it work with any image software?
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 07, 2021, 10:19:34 am
Hello circular,

i tested a few things:

From Gimp to LazPaint it works, PNG in Clipboard
From Firefox to Lazpaint, black Background
From Edge to Lazpaint, black background,
From Paint to LazPaint, white background. Seems Paint canverts Transparency to white
From Faststone Viewer, white background.

In the attachment ar the example with the Clipboard content

Best Regards
schlueter550v
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 07, 2021, 10:30:14 am
Hi!

All these tests respect transpareny in both directions with a PNG:

Gimp <--> Inkskape
KolourPaint <--> Inkskape
KolourPaint <--> Gimp

Winni
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 07, 2021, 05:05:48 pm
I guess Inkscape provides a PNG as well.

If the source application doesn't provide an alpha channel, then there is nothing to do about it.  :'(
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 07, 2021, 05:52:51 pm
I guess Inkscape provides a PNG as well.

If the source application doesn't provide an alpha channel, then there is nothing to do about it.  :'(

Hi!
Yes, all tests were done with a PNG.

The source applications like Gimp provide an alpha channel and the transparency is transported via the clipboard. But the alpha channel disappears when drawn on a TImage.canvas or a Form.canvas.  But it stays if the BGRAbitmap is drawn on an Image.picture.PNG.canvas.

Either the problem can be solved or it should be documented.

Winni
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 07, 2021, 09:05:38 pm
The fact that the canvas does not store alpha channel is not new. That's another subject here. Isn't it?
Title: Re: BGRABitmap to TImage fault
Post by: winni on May 07, 2021, 09:21:55 pm
The fact that the canvas does not store alpha channel is not new. That's another subject here. Isn't it?

Hi!


But that was the original question of schlueter550v

Winni
Title: Re: BGRABitmap to TImage fault
Post by: schlueter550v on May 07, 2021, 09:26:26 pm
Hello circular,
hello winni,

my initial question is answered. Maybe we need a new thread for the alpha problem?? Anyway i answer one more time to it:


If the source application doesn't provide an alpha channel, then there is nothing to do about it.  :'(

doesnt CF_DIBV5 contain an alpha channel like thing?
In Microsofts documentation on
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header (https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header)
they say something like this:

Quote
bV5AlphaMask

Color mask that specifies the alpha component of each pixel.


I have no clue how to read them properly.
I am a very beginner with FPC and Lazarus.

The only thing i found to CF_DIBV5 in Pascal is an old Delphi Code that uses GR32.
https://stackoverflow.com/a/12265496 (https://stackoverflow.com/a/12265496)
I dont know if this works in Lazarus. Thats too much for me as beginner.

If we have to open a new thread, please tell me?

Best Regards
schlueter550v
Title: Re: BGRABitmap to TImage fault
Post by: circular on May 08, 2021, 05:24:18 am
Well in theory there can be some alpha channel in BMP files, but in practice it is not much used. In BGRABitmap and LazPaint, I propose to use it, but many programs cannot load them.

Maybe try to paste the image in other software like Gimp, PaintShopPro, Paint.NET etc. to see if any can find some transparency information.
TinyPortal © 2005-2018