Transparency...again lazarus / 39-Graphics ---------------------------------------------------------------------- geraldholdsworth · 2021-03-23 15:06 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    //Load the buffer into the bitmap   ms.Position:=0;   ms.WriteBuffer(buffer[0],Length(buffer));   ms.Position:=0;   SpriteList[x].Image.LoadFromStream(ms);   //Display it   image[x].Picture.Bitmap.Transparent:=True;   image[x].Picture.Bitmap.TransparentColor:=BGColour mod $1000000;   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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i SpriteList[ x].Image.Transparent:=True;SpriteList[x].Image.TransparentColor:=BGCol our mod $1000000; And the code for saving 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     //Save PNG    png:=TPortableNetworkGraphic.Create;    png.Assign(SpriteList[x].Image);    png.Transparent:=True;    png.TransparentColor:=BGColour mod $1000000;    png.SaveToFile(Dir+validateFilename(SpriteList[x].Name)+'.png');    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 · 2021-03-24 15:48 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    image[x]:=TImage.Create(Scrollbox1 as TComponent);   image[x].Parent:=ScrollBox1 as TWinControl;   image[x].Width:=SpriteList[x].Image.Width;   image[x].Height:=SpriteList[x].Image.Height;   image[x].Canvas.Create();   image[x].Canvas.CreateBrush;   image[x].Picture.Create;   image[x].OnDblClick:=@Image1DblClick;   image[x].Tag:=x;   image[x].Visible:=True;   image[x].Canvas.AntialiasingMode:=amOff;   image[x].Stretch:=True;   image[x].Proportional:=True;   if (ix+image[x].Width>=512) then   begin    ix:=4;    inc(iy,bigH+4);    bigH:=0;   end;   image[x].Top:=iy;   image[x].Left:=ix;   image[x].Canvas.Pen.Color:=BGColour mod $1000000;   image[x].Canvas.Brush.Color:=BGColour mod $1000000;   image[x].Canvas.Rectangle(0,0,image[x].Width,image[x].Height);   image[x].Picture.Bitmap.Transparent:=True;   image[x].Picture.Bitmap.TransparentColor:=BGColour mod $1000000;   image[x].Canvas.Draw(0,0,SpriteList[x].Image);   image[x].Transparent:=True;   //Adjust for the next sprite position   ix:=ix+image[x].Width+4;   if image[x].Height>bigH then bigH:=image[x].Height;   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 · 2021-03-24 16:15 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);var  bmp: TBitmap;  phi: Double;  x, y: Integer;  cx, cy: Integer;begin  bmp := TBitmap.Create;  try    bmp.SetSize(128, 128);    bmp.Transparent := true;    bmp.TransparentColor := clFuchsia;    bmp.Canvas.Brush.Color := bmp.TransparentColor;    bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);    bmp.Canvas.Pen.Color := clYellow;    bmp.Canvas.Pen.Width := 5;    bmp.Canvas.Brush.Color := clYellow;    bmp.Canvas.Ellipse(32, 32, 96, 96);    cx := 64;    cy := 64;    phi := 0;    while phi < 360 do    begin      x := cx + round(64*cos(DegToRad(phi)));      y := cy + round(64*sin(DegToRad(phi)));      bmp.Canvas.Line(cx, cy, x, y);    phi := phi + 30;    end;    Image1.Transparent := true;    Image1.Picture.Assign(bmp);  finally    bmp.Free;  end;end; ---------------------------------------------------------------------- geraldholdsworth · 2021-03-24 16:22 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 · 2021-03-24 16:36 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 · 2021-03-24 17:06 One further step backward: What do you want to achieve? ---------------------------------------------------------------------- geraldholdsworth · 2021-03-24 17:38 Quote from: wp on March 24, 2021, 06:06:11 pmOne 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TMainForm.MakePng(screen:TBitmap;FileName: String; Trans: TColor);var  c: TBGRABitmap;  i,j: integer;  xcolor: TBGRAPixel;begin  xcolor:= ColorToBGRA(trans,255);   c := TBGRABitmap.Create(screen);  for i:=0 to c.Width-1 do  begin      for j:=0 to c.Height-1 do      begin      if(c.GetPixel(i,j)=xcolor)then          c.SetPixel(i,j, BGRAPixelTransparent);      end;  end;  c.SaveToFile(FileName);  c.free;end;  which I found in another thread on here. It works...almost. ---------------------------------------------------------------------- wp · 2021-03-24 18:35 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 · 2021-03-24 19:19 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 · 2021-03-24 19:20 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 · 2021-03-24 19:37 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   col.Alpha:=0;  col.Red:=BGColour and $FF;  col.Green:=(BGColour and $FF00)>>8;  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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       for y:=0 to img.Height-1 do       for x:=0 to img.Width-1 do        if img.Colors[x,y]=col then         img.Colors[x,y]:=colTransparent; ---------------------------------------------------------------------- geraldholdsworth · 2021-03-24 21:23 Got it...this appears to to the trick: 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 TMainForm.SaveAsPng(screen:TBitmap;AFileName: String);var  png: TPortableNetworkGraphic;  img: TLazIntfImage;  cnv: TLazCanvas;  col: TFPColor;  x,y: Integer;begin  png := TPortableNetworkGraphic.Create;  col.Alpha:=(BGColour and $FF000000)>>24;  col.Red:=BGColour and $FF;  col.Green:=(BGColour and $FF00)>>8;  col.Blue:=(BGColour and $FF0000)>>16;  col.Alpha:=col.Alpha or col.Alpha<<8;  col.Red:=col.Red or col.Red<<8;  col.Green:=col.Green or col.Green<<8;  col.Blue:=col.Blue or col.Blue<<8;  try    png.PixelFormat:= pf32Bit;  // 32 bit --> with alpha channel    png.SetSize(screen.Width,screen.Height);    png.Transparent:=True;    img := png.CreateIntfImage;    try      cnv := TLazCanvas.Create(img);      cnv.Brush.FPColor := colTransparent;    cnv.FillRect(0, 0, img.Width, img.Height);      cnv.Draw(0,0,screen.CreateIntfImage);      for y:=0 to img.Height-1 do       for x:=0 to img.Width-1 do        if cnv.Colors[x,y]=col then         cnv.Colors[x,y]:=colTransparent;      try        png.LoadFromIntfImage(img);        png.SaveToFile(AFileName);      finally        cnv.Free;      end;    finally      img.Free;    end;  finally    png.Free;  end;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 · 2021-03-24 21:31 Quote from: geraldholdsworth on March 24, 2021, 10:23:31 pmAlthough 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 · 2021-03-24 22:20 Quote from: wp on March 24, 2021, 10:31:23 pmBut 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. ---------------------------------------------------------------------- geraldholdsworth · 2021-03-25 08:12 Quote from: VTwin on March 24, 2021, 11:20:57 pmBGRABitmap supports full alpha channels. Make your life easier and use it. I tried it (see above), but it didn't work as well. ---------------------------------------------------------------------- loaded · 2021-03-25 12:54 In the link, https://forum.lazarus.freepascal.org/index.php?topic=29498.0 The answer given by derek.john.eva is working. ---------------------------------------------------------------------- circular · 2021-04-25 06:19 I am surprised by the result you get with BGRABitmap. Seems like black becomes transparent. I suggest try: Code: [Select]procedure TMainForm.MakePng(screen:TBitmap;FileName: String; Trans: TColor); var   c: TBGRABitmap;   xcolor: TBGRAPixel; begin   xcolor:= ColorToBGRA(trans,255);   c := TBGRABitmap.Create(screen);   c.ReplaceColor(color, BGRAPixelTransparent);   c.ReplaceColor(clBlack, clBlackOpaque); // to fix black being transparent   c.SaveToFile(FileName);   c.free; end; ---------------------------------------------------------------------- Eager · 2022-04-08 15:50 Hi all. New user here, learning the ropes as I go along.  I found myself having a similar need as the OP: I have a .bmp with a bunch of images against a fuchsia background, and I need to replace the fuchsia with transparency to produce a PNG file.  I've been looking and searching over the past few days and finally got to BGRABitmap, but now I'm stuck with the issue of black getting replaced by a weird clear color.  Unfortunately the last suggestion by circular -replacing black with blackopaque- didn't work. Moreover, all my attempts to replace black with ANY color using ReplaceColor were fruitless. But then I saved as a .bmp and it looked correct, only with no transparency. So I think I managed to narrow the problem - it happens when saving to a filename with the extension '.png'. So my guess is the issue may lie within the call to "savetofile"? That's as far as I got based on my current skill level. I'd like to keep using BGRABitmap as it seems pretty straightforward but I wonder if there's any easy solution to this behavior. TIA for your help! ---------------------------------------------------------------------- winni · 2022-04-08 16:57 Hi! Maybe what you think is black in not pure black. Pure black is 0/0/0 but the human eye cant distinguish it from 0/0/1 or even 5/5/5. Load your original file into the Gimp and look what the color pipette tells you. Winni ---------------------------------------------------------------------- winni · 2022-04-08 21:01 Hi! I made a little demo how to convert all colors near black to another color - here cssLawnGreen. Play around with the constant trigger. The more you increase it the more you filter out some dark greys. The range is 0..255. 0 filters only pure black = 0/0/0. 255 replaces all pixels. 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.Button10Click(Sender: TObject);const trigger = 20;var i: Integer;  bmp : TBgraBitmap;  p: PBGRAPixel;  kindOfBlack: Boolean;begin  bmp := TBGRAbitmap.create ('MyPicture.png');  p := bmp.data;  for i := 0 to bmp.NbPixels - 1 do    begin    KindOfBlack := (p^.red <= trigger) and (p^.green <= trigger) and (p^.blue <= trigger);    if kindOfBlack then p^ := CSSLawnGreen;    inc(p);    end;  bmp.invalidateBitmap;  bmp.draw (Image1.canvas,0,0);  bmp.saveToFile('LawnGreen.png');  bmp.free;end;   Perhaps this helps you. Winni ---------------------------------------------------------------------- Eager · 2022-04-09 12:36 Winni, you were right, there were "near black" pixels. The problem was right in front of me... *facepalm* Thanks so much!