相关文章推荐
至今单身的橙子  ·  ErrObject.Clear ...·  1 年前    · 
坏坏的海龟  ·  join - MongoDB query ...·  1 年前    · 
礼貌的椰子  ·  group 子句 - C# 参考 | ...·  2 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a TImageList which contains transparent icons (32bit, with alpha channel). What I want to do is to save individual icons based on image index as PNG file(s), while preserving alpha channel transparency. Using RAD Studio 2010 so it has TPngImage support, no need for third party libraries. Images are loaded into TImageList from PNG "sprite" image using the method here - Add a png image to a imagelist in runtime using Delphi XE - so the transparency is preserved upon loading. Now I need to save them out individually, in other words, extract individual images from sprite images which is already loaded into TImageList.

My code so far:

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");

The above works but it saves with the white background (transparency is not preserved after saving). I am probably missing a simple step but can't figure it out.

Delphi code is also welcome, shouldn't be hard to translate.

@KeremD, this is a wrong approach. Although icon will have transparency, it will not have standard PNG-file header, hence you will not be able to open this png-icon as a regular PNG-file. Even instance of TPngImage will fail with exception if you try to feed your png-icon to it. Simple setting extension like this: MyIcon.png, doesn't make icon real PNG-file. – Josef Švejk Oct 15, 2018 at 8:15

Yes, you can obtain PNG-image from TImageList where it was added. Code below allows you to do this!
Firstly, add PngImage to your uses clause.

procedure LoadPNGFromImageList(AImageList: TCustomImageList; AIndex: Integer; var ADestPNG: TPngImage);
const
  PixelsQuad = MaxInt div SizeOf(TRGBQuad) - 1;
  TRGBAArray = Array [0..PixelsQuad - 1] of TRGBQuad;
  PRGBAArray = ^TRGBAArray;
  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)
    Exit;
  ContentBmp := TBitmap.Create;
    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;
    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;
  finally
    ContentBmp.Free;

Look at the picture. It is depicts what will happen if we set or not set such line of code:

ContentBmp.AlphaFormat := afIgnored;
Figure 1 is a result of setting afIgnored and the second one figure is a result of not setting afIgnored, allowing to use previously set afDefined.  

Original image is an image named Figure 1

Using of code above in application:

procedure TForm1.aButton1Click(Sender: TObject);
  DestPNG: TPngImage;
begin
  DestPNG := TPNGImage.Create;
    // 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;
                Thank you for great answer - it works well (after some minor tweaks)! Turns out the main problem is that ADestPNG.Assign does not copy the alpha channel information so it has to be created and copied manually like you already did.
– Coder12345
                Oct 17, 2018 at 19:58
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.