{"id":331,"date":"2016-09-26T18:14:42","date_gmt":"2016-09-26T18:14:42","guid":{"rendered":"http:\/\/www.bverhue.nl\/delphisvg\/?p=331"},"modified":"2024-02-24T06:27:42","modified_gmt":"2024-02-24T06:27:42","slug":"save-bitmap-with-transparency-as-png-in-vcl","status":"publish","type":"post","link":"https:\/\/www.bverhue.nl\/delphisvg\/2016\/09\/26\/save-bitmap-with-transparency-as-png-in-vcl\/","title":{"rendered":"Save bitmap with transparency as .png in VCL"},"content":{"rendered":"<p>Here is an example how to save a bitmap containing an alpha channel as a .png image using Delphi VCL.<\/p>\n<p>In Delphi VCL you can use the TPngImage component to work with .png images. The only problem is that if you assign a bitmap to a TPngImage using PngImage.Assign(Bitmap) the alpha channel is lost.<\/p>\n<p>I found the following solution thanks to <a href=\"http:\/\/www.entwickler-ecke.de\/topic_Bitmap+pf32bit+mit+Alpha+afPremultipied+zu+PNG+speichern_103159,0.html\">this discussion on a German forum<\/a>. So in stead of assigning the bitmap to the TPngImage object, you create a new blank TPngImage with alpha channel enabled and draw the bitmap on the TPngImage canvas.<\/p>\n<p>After that you copy the alpha channel of the bitmap to the TPngImage in a separate step.<\/p>\n<p>So in this example I render an SVG image to a bitmap and save the bitmap as a .png image (thanks to Thomas Wassermann).<\/p>\n<p>[code language=&#8221;Delphi&#8221;]<br \/>\nunit Unit1;<\/p>\n<p>interface<\/p>\n<p>uses<br \/>\n\u00a0 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,<br \/>\n\u00a0 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;<\/p>\n<p>type<br \/>\n\u00a0 TForm1 = class(TForm)<br \/>\n\u00a0\u00a0\u00a0 Button1: TButton;<br \/>\n\u00a0\u00a0\u00a0 OpenDialog1: TOpenDialog;<br \/>\n\u00a0\u00a0\u00a0 procedure Button1Click(Sender: TObject);<br \/>\n\u00a0 private<br \/>\n\u00a0\u00a0\u00a0 { Private declarations }<br \/>\n\u00a0 public<br \/>\n\u00a0\u00a0\u00a0 { Public declarations }<br \/>\n\u00a0 end;<\/p>\n<p>var<br \/>\n\u00a0 Form1: TForm1;<\/p>\n<p>implementation<br \/>\nuses<br \/>\n\u00a0 BVE.SVG2SaxParser,<br \/>\n\u00a0 BVE.SVG2Intf,<br \/>\n\u00a0 BVE.SVG2Types,<br \/>\n\u00a0 BVE.SVG2ELements.VCL,<br \/>\n\u00a0 Vcl.Imaging.pngimage;<\/p>\n<p>{$R *.dfm}<\/p>\n<p>\/\/ Source: http:\/\/www.entwickler-ecke.de\/topic_Bitmap+pf32bit+mit+Alpha+afPremultipied+zu+PNG+speichern_103159,0.html<\/p>\n<p>type<br \/>\n\u00a0 TRGB = packed record B, G, R: byte end;<br \/>\n\u00a0 TRGBA = packed record B, G, R, A: byte end;<br \/>\n\u00a0 TRGBAArray = array[0..0] of TRGBA;<\/p>\n<p>function PNG4TransparentBitMap(aBitmap:TBitmap): TPNGImage;<br \/>\nvar<br \/>\n\u00a0 X, Y: integer;<br \/>\n\u00a0 BmpRGBA: ^TRGBAArray;<br \/>\n\u00a0 PngRGB: ^TRGB;<br \/>\nbegin<br \/>\n\u00a0 \/\/201011 Thomas Wassermann<\/p>\n<p>\u00a0 Result := TPNGImage.CreateBlank(COLOR_RGBALPHA, 8, aBitmap.Width , aBitmap.Height);<\/p>\n<p>\u00a0 Result.CreateAlpha;<br \/>\n\u00a0 Result.Canvas.CopyMode:= cmSrcCopy;<br \/>\n\u00a0 Result.Canvas.Draw(0, 0, aBitmap);<\/p>\n<p>\u00a0 for Y := 0 to Pred(aBitmap.Height) do<br \/>\n\u00a0 begin<br \/>\n\u00a0\u00a0\u00a0 BmpRGBA := aBitmap.ScanLine[Y];<br \/>\n\u00a0\u00a0\u00a0 PngRGB:= Result.Scanline[Y];<\/p>\n<p>\u00a0\u00a0\u00a0 for X := 0 to Pred(aBitmap.width) do<br \/>\n\u00a0\u00a0\u00a0 begin<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 Result.AlphaScanline[Y][X] :=\u00a0 BmpRGBA[X].A;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 if aBitmap.AlphaFormat in [afDefined, afPremultiplied] then<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 begin<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if BmpRGBA[X].A &lt;&gt; 0 then<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 begin<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PngRGB^.B := Round(BmpRGBA[X].B \/ BmpRGBA[X].A * 255);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PngRGB^.R := Round(BmpRGBA[X].R \/ BmpRGBA[X].A * 255);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PngRGB^.G := Round(BmpRGBA[X].G \/ BmpRGBA[X].A * 255);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 end else begin<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PngRGB^.B := Round(BmpRGBA[X].B * 255);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PngRGB^.R := Round(BmpRGBA[X].R * 255);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 PngRGB^.G := Round(BmpRGBA[X].G * 255);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 end;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 end;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 Inc(PngRGB);<br \/>\n\u00a0\u00a0\u00a0 end;<br \/>\n\u00a0 end;<br \/>\nend;<\/p>\n<p>procedure TForm1.Button1Click(Sender: TObject);<br \/>\nvar<br \/>\n\u00a0 FileName: string;<br \/>\n\u00a0 SVGParser: TSVGSaxParser;<br \/>\n\u00a0 SVGRoot: ISVGRoot;<br \/>\n\u00a0 Bitmap: TBitmap;<br \/>\n\u00a0 Png : TPngImage;<br \/>\nbegin<br \/>\n\u00a0 if OpenDialog1.Execute then<br \/>\n\u00a0 begin<br \/>\n\u00a0\u00a0\u00a0 FileName := OpenDialog1.FileName;<\/p>\n<p>\u00a0\u00a0\u00a0 \/\/ Create a root to store the SVG rendering tree<\/p>\n<p>\u00a0\u00a0\u00a0 SVGRoot := TSVGRootVCL.Create;<\/p>\n<p>\u00a0\u00a0\u00a0 \/\/ Create a SAX parser instance<\/p>\n<p>\u00a0\u00a0\u00a0 SVGParser := TSVGSaxParser.Create(nil);<br \/>\n\u00a0\u00a0\u00a0 try<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Parse SVG document and build the rendering tree<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 SVGParser.Parse(&#8221;, FileName, SVGRoot);<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Create a bitmap<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap := TBitmap.Create;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 try<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap.PixelFormat := TPixelFormat.pf32bit;\u00a0\u00a0 \/\/ 32bit bitmap<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap.AlphaFormat := TAlphaFormat.afDefined; \/\/ Enable alpha channel<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap.SetSize(480, 320);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Set desired size<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap.Canvas.Brush.Color := clNone; \/\/ Fill background with transparent<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap.Canvas.FillRect(Rect(0, 0, 480, 320));<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Render the SVG onto the bitmap<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SVGRenderToBitmap(<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SVGRoot, \/\/ The root containing the rendering tree<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap\u00a0\u00a0 \/\/ The destination bitmap<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 );<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Create the png component<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Png := PNG4TransparentBitMap(Bitmap);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 try<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FileName := ChangeFileExt(FileName, &#8216;.png&#8217;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Png.SaveToFile(FileName);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 finally<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Png.Free;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 end;<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0 finally<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Bitmap.Free;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 end;<\/p>\n<p>\u00a0\u00a0\u00a0 finally<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0 SVGParser.Free;<br \/>\n\u00a0\u00a0\u00a0 end;<br \/>\n\u00a0 end;<br \/>\nend;<\/p>\n<p>end.<\/p>\n<p>[\/code]<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here is an example how to save a bitmap containing an alpha channel as a .png image using Delphi VCL. In Delphi VCL you can use the TPngImage component to work with .png images. The only problem is that if you assign a bitmap to a TPngImage using PngImage.Assign(Bitmap) the alpha channel is lost. I &#8230; <a title=\"Save bitmap with transparency as .png in VCL\" class=\"read-more\" href=\"https:\/\/www.bverhue.nl\/delphisvg\/2016\/09\/26\/save-bitmap-with-transparency-as-png-in-vcl\/\" aria-label=\"Read more about Save bitmap with transparency as .png in VCL\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-331","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts\/331","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/comments?post=331"}],"version-history":[{"count":5,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts\/331\/revisions"}],"predecessor-version":[{"id":336,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts\/331\/revisions\/336"}],"wp:attachment":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/media?parent=331"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/categories?post=331"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/tags?post=331"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}