Recent

Author Topic: Transparency...again  (Read 5377 times)

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Transparency...again
« on: March 23, 2021, 04:06:48 pm »
I'm getting brain fog doing what should be a simple operation - and I know someone is going to point out the blindingly obvious and I'll then proceed to kick myself . I've copied the code from another project, which was written in Delphi, which does work (in Delphi, that is - that project is a tad complicated to port across to Lazarus...not that I'm not going to do it, just not yet!!).

Basically, I'm creating a bitmap (well, loads of them) by building them from scratch, in a byte array - gives me maximum control (or so I thought) and speed. However, I'm wanting them to be transparent (which, I know, generally bitmaps are not). So I've coloured the transparent pixels as magenta ($FF00FF).

Even when I transfer it to a PNG image and save it (specifying as transparent) it still does not do it. The bitmaps can range from any bit depth (1, 4, 8, 16, 24 or 32), but for arguments sake, I'm currently playing with the 8bpp ones (so there is a palette, which I've created). I've tried setting the alpha channel of the transparent colour to $FF, which doesn't do anything either.

The code for display is:
Code: Pascal  [Select][+][-]
  1.    //Load the buffer into the bitmap
  2.    ms.Position:=0;
  3.    ms.WriteBuffer(buffer[0],Length(buffer));
  4.    ms.Position:=0;
  5.    SpriteList[x].Image.LoadFromStream(ms);
  6.    //Display it
  7.    image[x].Picture.Bitmap.Transparent:=True;
  8.    image[x].Picture.Bitmap.TransparentColor:=BGColour mod $1000000;
  9.    image[x].Picture.Assign(SpriteList[x].Image);
SpriteList[ x ].Image is a TBitmap array and image[ x ] is a TImage array. BGColour is the transparent colour.
I also tried Canvas.Draw(0,0,SpriteList[ x ].Image), but I get nothing. I've also tried setting image[ x ].Transparent  to True, which results in a transparent image, just not the bits that should be!!
Earlier in the code (and I'm debating whether this is actually needed or not) I have:
Code: Pascal  [Select][+][-]
  1. SpriteList[x].Image.Transparent:=True;
  2. SpriteList[x].Image.TransparentColor:=BGColour mod $1000000;

And the code for saving is:
Code: Pascal  [Select][+][-]
  1.     //Save PNG
  2.     png:=TPortableNetworkGraphic.Create;
  3.     png.Assign(SpriteList[x].Image);
  4.     png.Transparent:=True;
  5.     png.TransparentColor:=BGColour mod $1000000;
  6.     png.SaveToFile(Dir+validateFilename(SpriteList[x].Name)+'.png');
  7.     png.Free;
I've seen something about TLazIntfImage (IIRC), but my brain is fogging over trying to think of a way of using it.

This is on Lazarus 2.0.12 on macOS (although will be multiplatform).

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #1 on: March 24, 2021, 04:48:50 pm »
OK, I've got somewhere. I tried that LazIntFImage again, but could not get it to play ball, so gave up.

The following works:
Code: Pascal  [Select][+][-]
  1.    image[x]:=TImage.Create(Scrollbox1 as TComponent);
  2.    image[x].Parent:=ScrollBox1 as TWinControl;
  3.    image[x].Width:=SpriteList[x].Image.Width;
  4.    image[x].Height:=SpriteList[x].Image.Height;
  5.    image[x].Canvas.Create();
  6.    image[x].Canvas.CreateBrush;
  7.    image[x].Picture.Create;
  8.    image[x].OnDblClick:=@Image1DblClick;
  9.    image[x].Tag:=x;
  10.    image[x].Visible:=True;
  11.    image[x].Canvas.AntialiasingMode:=amOff;
  12.    image[x].Stretch:=True;
  13.    image[x].Proportional:=True;
  14.    if (ix+image[x].Width>=512) then
  15.    begin
  16.     ix:=4;
  17.     inc(iy,bigH+4);
  18.     bigH:=0;
  19.    end;
  20.    image[x].Top:=iy;
  21.    image[x].Left:=ix;
  22.    image[x].Canvas.Pen.Color:=BGColour mod $1000000;
  23.    image[x].Canvas.Brush.Color:=BGColour mod $1000000;
  24.    image[x].Canvas.Rectangle(0,0,image[x].Width,image[x].Height);
  25.    image[x].Picture.Bitmap.Transparent:=True;
  26.    image[x].Picture.Bitmap.TransparentColor:=BGColour mod $1000000;
  27.    image[x].Canvas.Draw(0,0,SpriteList[x].Image);
  28.    image[x].Transparent:=True;
  29.    //Adjust for the next sprite position
  30.    ix:=ix+image[x].Width+4;
  31.    if image[x].Height>bigH then bigH:=image[x].Height;
  32.    image[x].Hint:=SpriteList[x].Name;
However, I cannot resize it. If I do, the transparency (or mask) remains the same size with the graphic stretching beneath it. The result is that you don't get all the graphic (or any, in some cases). The same happens if I try a StretchDraw to draw the bitmap onto the canvas.
Next problem is saving as a transparent PNG - now, I understand that TPortableNetworkGraphic has a bug so won't deal with transparencies. Again, TLazIntFImage was of no help, and the TBGRAImage (or whatever it is known as...my memory isn't that great when it comes to names) just won't work...Lazarus complains that it does not know of the type, and when I add the unit to the uses clause, it then complains it can't find them.
So, it looks like I'll need to work out how to write a PNG at the lowest level.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: Transparency...again
« Reply #2 on: March 24, 2021, 05:15:25 pm »
Stop -- too much information for me... Let's begin with the basics. You want to create a bitmap image with transparency? Here is some elemental code for a form with a TImage and a TButton:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   bmp: TBitmap;
  4.   phi: Double;
  5.   x, y: Integer;
  6.   cx, cy: Integer;
  7. begin
  8.   bmp := TBitmap.Create;
  9.   try
  10.     bmp.SetSize(128, 128);
  11.     bmp.Transparent := true;
  12.     bmp.TransparentColor := clFuchsia;
  13.     bmp.Canvas.Brush.Color := bmp.TransparentColor;
  14.     bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);
  15.     bmp.Canvas.Pen.Color := clYellow;
  16.     bmp.Canvas.Pen.Width := 5;
  17.     bmp.Canvas.Brush.Color := clYellow;
  18.     bmp.Canvas.Ellipse(32, 32, 96, 96);
  19.     cx := 64;
  20.     cy := 64;
  21.     phi := 0;
  22.     while phi < 360 do
  23.     begin
  24.       x := cx + round(64*cos(DegToRad(phi)));
  25.       y := cy + round(64*sin(DegToRad(phi)));
  26.       bmp.Canvas.Line(cx, cy, x, y);
  27.       phi := phi + 30;
  28.     end;
  29.     Image1.Transparent := true;
  30.     Image1.Picture.Assign(bmp);
  31.   finally
  32.     bmp.Free;
  33.   end;
  34. end;

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #3 on: March 24, 2021, 05:22:12 pm »
Hiya,

I've got that, but when I squash it, I lose it (see pictures).

Saving as a transparent PNG is just another problem.

Cheers,

Gerald.

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #4 on: March 24, 2021, 05:36:44 pm »
As a fudge, I can display them by drawing the rectangle in the Main Form's colour and dispensing with the transparencies in the TImage.

This then just leaves saving a transparent PNG.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: Transparency...again
« Reply #5 on: March 24, 2021, 06:06:11 pm »
One further step backward: What do you want to achieve?

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #6 on: March 24, 2021, 06:38:00 pm »
One further step backward: What do you want to achieve?

I'd like to save out PNGs with a transparency. I create the original bitmaps and mark the background with a purple ($FF00FF) colour, which is intended as being transparent.

I've just downloaded the TBGRABitmap package and tried the following method:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.MakePng(screen:TBitmap;FileName: String; Trans: TColor);
  2. var
  3.   c: TBGRABitmap;
  4.   i,j: integer;
  5.   xcolor: TBGRAPixel;
  6. begin
  7.   xcolor:= ColorToBGRA(trans,255);
  8.  
  9.   c := TBGRABitmap.Create(screen);
  10.   for i:=0 to c.Width-1 do
  11.   begin
  12.       for j:=0 to c.Height-1 do
  13.       begin
  14.         if(c.GetPixel(i,j)=xcolor)then
  15.           c.SetPixel(i,j, BGRAPixelTransparent);
  16.       end;
  17.   end;
  18.   c.SaveToFile(FileName);
  19.   c.free;
  20. end;  
which I found in another thread on here. It works...almost.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: Transparency...again
« Reply #7 on: March 24, 2021, 07:35:31 pm »
You will never get good transparency with the "transparent-color" approach. You need alpha-channel transparency which is supported by BGRABitmap out of the box. But even with the standard graphics classes you can achieve alpha-channel transparency, a bit more complicated, though - see attachment.

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #8 on: March 24, 2021, 08:19:08 pm »
Looks good.
I was trying using TLazIntfImage but could not work out how to draw a bitmap onto it. I've modified your code, managed to get a bitmap onto it, but the background is still purple (using cnv.Draw(0,0,screen.CreateIntfImage) after filling the canvas with colTransparent).

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Transparency...again
« Reply #9 on: March 24, 2021, 08:20:47 pm »
Thanks, Werner, for this sunny example.
I notice on Linux using gtk2 that there is a warning about an unreleased DC, and two unreleased GDIObjects (gdiBitmaps).
Do you think is owing to a bug somewhere in the gtk2 implementation of TLazCanvas or TLazIntfImg?

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #10 on: March 24, 2021, 08:37:57 pm »
Yes, thank you Werner.

I've just had a brain wave...instead of creating the bitmap from scratch, I could just start out with a TLazCanvas and build up the image onto there. I'm writing it pixel by pixel, then going over it with the transparency.

Then I had another brain wave...but this didn't work. First I create an FPColor:
Code: Pascal  [Select][+][-]
  1.   col.Alpha:=0;
  2.   col.Red:=BGColour and $FF;
  3.   col.Green:=(BGColour and $FF00)>>8;
  4.   col.Blue:=(BGColour and $FF0000)>>16;
Once I'd written the bitmap using the above, I then go through the canvas and change it for a transparent one:
Code: Pascal  [Select][+][-]
  1.       for y:=0 to img.Height-1 do
  2.        for x:=0 to img.Width-1 do
  3.         if img.Colors[x,y]=col then
  4.          img.Colors[x,y]:=colTransparent;  

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #11 on: March 24, 2021, 10:23:31 pm »
Got it...this appears to to the trick:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.SaveAsPng(screen:TBitmap;AFileName: String);
  2. var
  3.   png: TPortableNetworkGraphic;
  4.   img: TLazIntfImage;
  5.   cnv: TLazCanvas;
  6.   col: TFPColor;
  7.   x,y: Integer;
  8. begin
  9.   png := TPortableNetworkGraphic.Create;
  10.   col.Alpha:=(BGColour and $FF000000)>>24;
  11.   col.Red:=BGColour and $FF;
  12.   col.Green:=(BGColour and $FF00)>>8;
  13.   col.Blue:=(BGColour and $FF0000)>>16;
  14.   col.Alpha:=col.Alpha or col.Alpha<<8;
  15.   col.Red:=col.Red or col.Red<<8;
  16.   col.Green:=col.Green or col.Green<<8;
  17.   col.Blue:=col.Blue or col.Blue<<8;
  18.   try
  19.     png.PixelFormat:= pf32Bit;  // 32 bit --> with alpha channel
  20.     png.SetSize(screen.Width,screen.Height);
  21.     png.Transparent:=True;
  22.     img := png.CreateIntfImage;
  23.     try
  24.       cnv := TLazCanvas.Create(img);
  25.       cnv.Brush.FPColor := colTransparent;
  26.       cnv.FillRect(0, 0, img.Width, img.Height);
  27.       cnv.Draw(0,0,screen.CreateIntfImage);
  28.       for y:=0 to img.Height-1 do
  29.        for x:=0 to img.Width-1 do
  30.         if cnv.Colors[x,y]=col then
  31.          cnv.Colors[x,y]:=colTransparent;
  32.       try
  33.         png.LoadFromIntfImage(img);
  34.         png.SaveToFile(AFileName);
  35.       finally
  36.         cnv.Free;
  37.       end;
  38.     finally
  39.       img.Free;
  40.     end;
  41.   finally
  42.     png.Free;
  43.   end;
  44. end;
Although I'm not sure I need to fill the canvas with colTransparent to start with, but I'll tweak it.

Thank you Werner for your help.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: Transparency...again
« Reply #12 on: March 24, 2021, 10:31:23 pm »
Although I'm not sure I need to fill the canvas with colTransparent to start with, but I'll tweak it.
At least you must fill it with something well-defined. So why not fill it to be completely transparent? This way you don't need the old-style transparent color any more. The pixels that you set later will be fully opaque and thus overwrite the initially transparent full.

Maybe I should note that you can also fill pixels with partial transparency. This way you can achieve antialiasing of lines. But if you're after this you should switch to BGRABitmap which has all this as buit-in feature.

VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: Transparency...again
« Reply #13 on: March 24, 2021, 11:20:57 pm »
But if you're after this you should switch to BGRABitmap which has all this as buit-in feature.

BGRABitmap supports full alpha channels. Make your life easier and use it.
“Talk is cheap. Show me the code.” -Linus Torvalds

Free Pascal Compiler 3.2.2
macOS 12.1: Lazarus 2.2.6 (64 bit Cocoa M1)
Ubuntu 18.04.3: Lazarus 2.2.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.2.6 (64 bit on VBox)

geraldholdsworth

  • Full Member
  • ***
  • Posts: 195
Re: Transparency...again
« Reply #14 on: March 25, 2021, 09:12:36 am »
BGRABitmap supports full alpha channels. Make your life easier and use it.
I tried it (see above), but it didn't work as well.

 

TinyPortal © 2005-2018