delphi – 将ImgView32透明层保存到PNG

我在将ImgView32图层保存为TRANSPARENT PNG时遇到问题.
我使用 this question中的代码进行保存.
但是,图像以白色背景保存.

这是我如何初始化我的ImgView32,在其上创建一个图层,然后在其上绘制一条线:

procedure TputLine.FormCreate(Sender: TObject);
var
  P: TPoint;
  W,H: Single;
  imwidth: integer;
  imheight: integer;
begin
  imwidth := Iv1.Width;
  imheight := Iv1.Height;
  with iv1 do
  begin
    Selection := nil;
    Layers.Clear;
    Scale := 1;
    Scaled := True;
    Bitmap.DrawMode := dmTransparent;
    Bitmap.SetSize(imwidth,imheight);
    Bitmap.Canvas.Pen.Width := 4;
  end;
  BL := TBitmapLayer.Create(iv1.Layers);
  try
    BL.Bitmap.DrawMode := dmTransparent;
    BL.Bitmap.SetSize(imwidth,imheight);
    BL.Bitmap.Canvas.Pen.Width := penwidth;
    BL.Bitmap.Canvas.Pen.Color := pencolor;
    BL.Location := GR32.FloatRect(0,imwidth,imheight);
    BL.Scaled := False;
  except
    BL.Free;
    raise;
  end;
end;

所以iv1是我的ImgView32的名称.
然后我用这段代码画一条线:

var 
  bm32:TBitmapLayer;
  ...
begin
  bm32:=(iv1.Layers[0] as TBitmapLayer).Bitmap;
  bm32.canvas.pen.color:=clwhite;
  bm32.canvas.brush.color:=clwhite;
  bm32.canvas.rectangle(0,bm32.width-1,bm32.height-1);

  bm32.canvas.Pen.Color:=WinColor(ColorPickerGTK1.SelectedColor);
  bm32.canvas.brush.color:=clWhite;
  bm32.Canvas.Pen.Width:=3;
  bm32.Canvas.Moveto(0,bm32.Height);
  bm32.Canvas.Lineto(0+150,bm32.Height-250);
end;

如果我在绘制矩形时使用clWhite32作为上面的代码,那么在保存PNG时,imgView的背景会变黑……所以我真的不明白这个问题.

我像这样保存:

procedure TputLine.Button2Click(Sender: TObject);
var
  myLay:TBitmapLayer;
begin
  mylay := iv1.Layers.Items[0] as TBitmapLayer;
  SavePNGTransparentX(mylay.Bitmap);
end;

和实际的保存代码(来自上述链接)

procedure TPutLine.SavePNGTransparentX(bm32:TBitmap32);
var
  Y: Integer;
  X: Integer;
  Png: TPortableNetworkGraphic32;

  function IsWhite(Color32: TColor32): Boolean;
  begin
    Result:= (TColor32Entry(Color32).B = 255) and
             (TColor32Entry(Color32).G = 255) and
             (TColor32Entry(Color32).R = 255);
  end;

begin
    bm32.ResetAlpha;
    for Y := 0 to bm32.Height-1 do
      for X := 0 to bm32.Width-1 do
      begin
        if IsWhite(bm32.Pixel[X,Y]) then
          bm32.Pixel[X,Y]:=Color32(255,255,0);
      end;
    Png:= TPortableNetworkGraphic32.Create;
    Png.Assign(bm32);
    Png.SavetoFile('C:\ThisShouldBeTransparent.png');
    Png.Free;
end;

我不明白为什么它不将图层保存为透明PNG.
我该如何解决?欢迎任何想法.

您可以使用上面的代码复制我的问题.它使用GR32_PNG和GR32_PortableNetworkGraphic.您只需要在表单中添加TImgView32控件并添加上面列出的代码.

解决方法

问题的原因似乎是双重的.

>首先,当你打电话给Png.Assign(bm32);在单元格GR32_PNG中,它试图找出存储图像的最小格式.如果图像的颜色少于256种,则会创建一个调色板格式,并根据找到的颜色数量,位深度可以变为1,2,4或8.据我所知,只有具有TrueColor和Alpha的图像可以保存为可变透明度png图像.
>其次,您只使用一种颜色绘制,这会触发上述问题.这当然不是你的错,IMO上面提到的分析应该可以绕过.

TPortableNetworkGraphic32类有两个属性,BitDepth和ColorType控制png图像的格式,如果它们是可设置的,这将是有用的!试图在代码中将它们设置为:

Png.BitDepth := 8;
Png.ColorType := ctTrueColorAlpha;

导致例外

EPngError with message ‘Bit depth may not be specified directly yet!
EPngError with message ‘Color Type may not be specified directly yet!

从措辞上我们可以假设将来会有一些进一步的发展.

治愈

要绕过上述图像分析,您可以更改GR32_PNG.pas中的第459行.

procedure TPortableNetworkGraphic32.AssignPropertiesFromBitmap32()
var
  ...
begin
  ...
   IsPalette := True; // <--- change to False
  ...

如果少于256种颜色,那将处理位深度分析并防止调色板创建.
在此hack之后,您可以使用SavePNGTransparentX()过程将TBitmap32保存到.png文件并保持透明度.

然后,对于SavePNGTransparentX()过程,您可能感兴趣的更改一次.如您所见,它需要绘图表面的背景为白色,因为它专门为所有白色像素将Alpha通道设置为零.然而,TBitmapLayer被初始化为所有像素(RGBA)为全零,因此每个像素的颜色分量构成黑色(由于alpha通道为零,因此不可见).因此,您需要使用白色填充绘图图层,这会使其变得不透明,这会再次覆盖所有较低的图层(在绘图图层下方).

要对此进行更正,您可以

>将绘图层的初始化删除为全白
>将IsWhite函数更改为IsBlack函数
>更改透明像素的分配

代码将成为

procedure TForm8.SavePNGTransparentX(bm32:TBitmap32);
var
  Y: Integer;
  X: Integer;
  Png: TPortableNetworkGraphic32;

  function IsBlack(Color32: TColor32): Boolean;
  begin
    Result:= (TColor32Entry(Color32).B = 0) and
             (TColor32Entry(Color32).G = 0) and
             (TColor32Entry(Color32).R = 0);
  end;

  function IsWhite(Color32: TColor32): Boolean;
  begin
    Result:= (TColor32Entry(Color32).B = 255) and
             (TColor32Entry(Color32).G = 255) and
             (TColor32Entry(Color32).R = 255);
  end;

begin
    bm32.ResetAlpha;
    for Y := 0 to bm32.Height-1 do
      for X := 0 to bm32.Width-1 do
      begin
//        if IsWhite(bm32.Pixel[X,Y]) then
//          bm32.Pixel[X,0);
        if IsBlack(bm32.Pixel[X,Y]:=Color32(  0,0);
      end;

    Png:= TPortableNetworkGraphic32.Create;
    Png.Assign(bm32);
    Png.SavetoFile('C:\tmp\imgs\ThisShouldBeTransparent3.png');
    Png.Free;
end;

通过此更改,您可以看到绘图图层下方的图层,并且绘图图层的结果文件将使所有未绘制的像素都透明.
然而,对于上述过程,一般存在一个问题,即部分透明的像素会失去其透明度,但这仍然是未来的练习.

这是一些图像,首先是在底层加载位图的ImageView:

然后,我在BL层上绘制一个蓝色十字(参考你的代码)基本上使用Button1Click()中的代码,但没有白色矩形.

然后我将BL图层保存为.png文件并使用Windows资源管理器“预览”查看它:

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...