【问题标题】:Saving transparent (alpha channel) PNG from TImageList从 TImageList 保存透明(alpha 通道)PNG
【发布时间】:2019-03-18 14:05:39
【问题描述】:

我有一个TImageList,其中包含透明图标(32 位,带 alpha 通道)。我想要做的是将基于图像索引的单个图标保存为 PNG 文件,同时保留 alpha 通道透明度。使用 RAD Studio 2010 支持TPngImage,无需第三方库。使用此处的方法 - Add a png image to a imagelist in runtime using Delphi XE 将图像从 PNG“精灵”图像加载到 TImageList 中,因此在加载时会保留透明度。现在我需要单独保存它们,换句话说,从已经加载到 TImageList 中的精灵图像中提取单个图像。

到目前为止我的代码:

int imageindex = 123;
boost::scoped_ptr<TPngImage>         png(new TPngImage);
boost::scoped_ptr<Graphics::TBitmap> bmp(new Graphics::TBitmap);

MyImageList->GetBitmap(imageindex, bmp.get()); // Using GetBitmap to copy TImageList image into separate TBitmap

png->Assign(bmp.get()); // Assign that bitmap to TPngImage
png->SaveToFile("C:\\filename.png");

上述方法有效,但它以白色背景保存(保存后不保留透明度)。我可能错过了一个简单的步骤,但无法弄清楚。

也欢迎使用 Delphi 代码,应该不难翻译。

【问题讨论】:

  • 试试MyImageList-&gt;GetIcon(...),直接用文件扩展名.png保存图标
  • @KeremD,这是一种错误的方法。虽然图标将具有透明度,但它没有标准的 PNG 文件头,因此您将无法将此 png-icon 作为常规 PNG 文件打开。如果您尝试将 png-icon 提供给它,即使 TPngImage 的实例也会失败并出现异常。像这样的简单设置扩展:MyIcon.png,不会使图标 real PNG 文件。
  • @Dima Sure,星期一早上...

标签: delphi png c++builder imagelist timagelist


【解决方案1】:

是的,您可以从添加它的TImageList 获取PNG 图像。下面的代码允许您这样做!
首先,将PngImage 添加到您的uses 子句中。

procedure LoadPNGFromImageList(AImageList: TCustomImageList; AIndex: Integer; var ADestPNG: TPngImage);
const
  PixelsQuad = MaxInt div SizeOf(TRGBQuad) - 1;
type
  TRGBAArray = Array [0..PixelsQuad - 1] of TRGBQuad;
  PRGBAArray = ^TRGBAArray;
var
  ContentBmp: TBitmap;
  RowInOut: PRGBAArray;
  RowAlpha: PByteArray;
  X: Integer;
  Y: Integer;
begin
  if not Assigned(AImageList) or (AIndex < 0) or
     (AIndex > AImageList.Count - 1) or not Assigned(ADestPNG)
  then
    Exit;

  ContentBmp := TBitmap.Create;
  try
    ContentBmp.SetSize(ADestPNG.Width, ADestPNG.Height);
    ContentBmp.PixelFormat := pf32bit;

    // Allocate zero alpha-channel
    for Y:=0 to ContentBmp.Height - 1 do
      begin
        RowInOut := ContentBmp.ScanLine[Y];
        for X:=0 to ContentBmp.Width - 1 do
          RowInOut[X].rgbReserved := 0;
      end;
    ContentBmp.AlphaFormat := afDefined;

    // Copy image
    AImageList.Draw(ContentBmp.Canvas, 0, 0, AIndex, true);

    // Now ContentBmp has premultiplied alpha value, but it will
    // make bitmap too dark after converting it to PNG. Setting
    // AlphaFormat property to afIgnored helps to unpremultiply
    // alpha value of each pixel in bitmap.
    ContentBmp.AlphaFormat := afIgnored;

    // Copy graphical data and alpha-channel values
    ADestPNG.Assign(ContentBmp);
    ADestPNG.CreateAlpha;
    for Y:=0 to ContentBmp.Height - 1 do
      begin
        RowInOut := ContentBmp.ScanLine[Y];
        RowAlpha := ADestPNG.AlphaScanline[Y];
        for X:=0 to ContentBmp.Width - 1 do
          RowAlpha[X] := RowInOut[X].rgbReserved;
      end;
  finally
    ContentBmp.Free;
  end;
end;

看图。它描述了如果我们设置或不设置这样的代码行会发生什么:

ContentBmp.AlphaFormat := afIgnored;


图一是设置afIgnored的结果,第二个图是not设置afIgnored的结果,允许使用之前设置的afDefined

原图是一张名为图1的图片

以上代码在应用中的使用:

procedure TForm1.aButton1Click(Sender: TObject);
var
  DestPNG: TPngImage;
begin
  DestPNG := TPNGImage.Create;
  try
    // Initialize PNG
    DestPNG.CreateBlank(COLOR_RGBALPHA, 8, 60, 60);

    // Obtain PNG from image list
    LoadPNGFromImageList(ImageList1, 0, DestPNG);

    // Output PNG onto Canvas
    DestPNG.Draw(Canvas, Rect(0, 0, 60, 60));
    DestPNG.SaveToFile('C:\MyPNGIcon.png');
  finally
    DestPNG.Free;
  end;
end;  

【讨论】:

  • 感谢您的出色回答 - 它运行良好(经过一些小的调整)!原来主要的问题是ADestPNG.Assign 没有复制 alpha 通道信息,所以它必须像你已经做的那样手动创建和复制。
猜你喜欢
  • 2010-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 2018-11-04
  • 2011-10-26
  • 2018-10-25
相关资源
最近更新 更多