-1

I'm sorry, my english is not very good.

I need to use semi-transparent bitmap pictures in my D7 app. So, i should use XPManifest and ImageList version6 instead of 5.8 standard one. But in this case, I faced a problev: all images loses their transparency while I load them form stream!

type
  TForm2 = class(TForm)
    btn4: TButton;
    btn5: TButton;
    lst1: TbtkListView;
    il1: TImageList;
    btn1: TButton;
    tlb1: TToolBar;
    btn2: TToolButton;
    btn3: TToolButton;
    xpmnfst1: TXPManifest;
    procedure btn4Click(Sender: TObject);
    procedure btn5Click(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  private
    FS: TFileStream;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.btn4Click(Sender: TObject);
var
  Bmp       : TBitmap;
  ImageList: TbtkImageList;
begin
  ImageList := TbtkImageList.Create(nil);
  Bmp       := TBitmap.Create;
  FS        := TFileStream.Create('c:\temp\1.cmp',fmCreate);
  try
    Bmp.LoadFromFile('c:\temp\1.bmp');
    ImageList.Add(Bmp, nil);
    FS.WriteComponent(ImageList);
  finally
    Bmp.Free;
  end;
end;

procedure TForm2.btn5Click(Sender: TObject);
var
  Bmp       : TBitmap;
  ImageList : TbtkImageList;
begin
  ImageList := TbtkImageList.Create(nil);
  Bmp := TBitmap.Create;
  try
    FS.Position := 0;
    FS.ReadComponent(ImageList);
    ImageList.GetBitmap(0, Bmp);
    Bmp.SaveToFile('c:\temp\3.bmp');
  finally
    Bmp.Free;
    ImageList.Free;
  end;
end;

ImageListCreationCode:
constructor TbtkImageList.Create(AOwner: TComponent);
begin
  inherited;

  if HandleAllocated then
    ImageList_Destroy(Handle);
  Handle := ImageList_Create(32, 32, ILC_COLOR32, AllocBy, AllocBy);
end;

http://s020.radikal.ru/i720/1403/36/c2702a8b5c1a.png Before http://s001.radikal.ru/i195/1403/e2/1dd5ff14aa51.png After

Can somebody help me?

user1443993
  • 371
  • 4
  • 10
  • @Ken That question is different. There the user was trying to work with the comctl32 v5.8 control. – David Heffernan Mar 05 '14 at 06:47
  • @user I see no evidence that the image list ever has transparency. You are not setting the color depth anywhere. IIRC that property does not exist in D7 and you have to create the image list handle manually. – David Heffernan Mar 05 '14 at 06:48
  • David Heffernan, iam sorry, forgot to add this peace of code. Fixed. – user1443993 Mar 05 '14 at 07:30
  • @SertacAkyuz but TBitmap is not a descedant of TComponent. We only can stream components. I suppose, i just doing something wrong. There should be some nice deсision... – user1443993 Mar 05 '14 at 08:06
  • Ok. That looks fine. Where is the transparency lost? My instincts tell me that saving/loading to/from .bmp is the real problem. – David Heffernan Mar 05 '14 at 08:47
  • Transperency loses while reading the stream. btn4Click writes List to the file. After that we start application again and press btn5Click. If we use app with v6 comctrl, 3.bmp became spoiled. But in case we use v5.8 conctrl, 3.bmp is okay. – user1443993 Mar 05 '14 at 09:01
  • @user - I didn't mean to use Read/WriteComponent on TBitmaps, just load/save them any way you like. Anyway, as I'm not sure if it'll help, I had deleted the comment. But you can try and see, leave the image list alone, load a bitmap, save it, load it, ... transparent? – Sertac Akyuz Mar 05 '14 at 09:05
  • @SertacAkyuz then i should design a mechanism to serialize bitmap. I suppose, its very costly and can make errors. It cannot be, that new ImageList support semitransparency, but cannot stream transparent bitmaps! It should be something wrong in my own code! – user1443993 Mar 05 '14 at 11:39
  • @user - Well, I don't even know if it would solve the problem. The point was to isolate the problem. – Sertac Akyuz Mar 05 '14 at 11:41
  • @SertacAkyuz Yeap, there is TBitmap.SaveToStream method))) Yes, in this case, everything is all right) – user1443993 Mar 05 '14 at 12:05
  • @user - It's not your code that's wrong (well, apart from leaking image list, misplaced try-finally ..). `ImageList.GetBitmap` will not preserve alpha channel, it just draws on the canvas of the passed bitmap. – Sertac Akyuz Mar 05 '14 at 12:40

2 Answers2

2

Once you put a bitmap having alpha channel information in an image list, there's no easy (*) way you can get it out in its original bitmap form. TImageList.GetBitmap just sets the dimensions of the bitmap you pass to it, and draws on its canvas. It doesn't use the overload that could draw transparently BTW, but it's not all that important as instead of using GetBitmap you can call the Draw overload yourself.

As a result, instead of streaming the image list in and out, I suggest to stream the bitmaps themselves if you need to preserve their original form.


Try the below and see if it fits your needs (it is transparent but it may not be identical with the source bitmap as it is again drawn):

var
  Bmp       : TBitmap;
  ImageList : TImageList;
  FS: TFileStream;
begin
  ImageList := TImageList.Create(nil);
    try
      FS := TFileStream.Create('c:\temp\1.cmp',fmOpenRead or fmShareDenyWrite);
      try
        FS.ReadComponent(ImageList);
      finally
        FS.Free;
      end;
      Bmp := TBitmap.Create;
      try
        Bmp.PixelFormat := pf32bit;
        Bmp.Canvas.Brush.Color := clNone;
        Bmp.Width := ImageList.Width;
        Bmp.Height := ImageList.Height;
        ImageList.Draw(Bmp.Canvas, 0, 0, 0, dsNormal, itImage);
        Bmp.SaveToFile('c:\temp\3.bmp');
      finally
        Bmp.Free;
      end;
  finally
    ImageList.Free;
  end;
end;
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • (*) - It may even be impossible. It is an implementation detail of the OS that whether an image list, once a bitmap is added, preserves the exact information that had been contained in the bitmap or not. Which I don't know. – Sertac Akyuz Mar 05 '14 at 20:10
  • Actually it doesn't fit my needs. Cuz of I should copy ImageLists, but ImageList2.Assign(ImageList) makes images spoiled. Also, ImageList2.add(BMP, nil); kills transparency =( – user1443993 Mar 06 '14 at 09:55
  • ImageList2.add(BMP, nil); will work fine, and save transparency if BMP was loaded from file, but will not work if it was loaded from deserialized image list.. I don't understand why =( – user1443993 Mar 06 '14 at 10:10
  • @user - i tried to explain the reason in the answer: image lists are not bitmap storages. Besides i could only try to help with what's asked. Sorry it doesn't help.. – Sertac Akyuz Mar 06 '14 at 10:34
  • @user - I just tested adding the 'Bmp' to another image list created like the first one, and it draws just as transparent. Anyway, I'm downvoting the question as it apparently fails to ask what you intended to ask, since making the code in it work does not qualify as an answer. – Sertac Akyuz Mar 10 '14 at 22:55
0

I suppose, I found a kind of solution.

var
  BMP: TBitmap;
  ImageList : TImageList;
  FS: TFileStream;
  ico: TIcon;
  IconInfo: TIconInfo;
begin
  ImageList := TImageList.Create(nil);
    try
      FS := TFileStream.Create('c:\temp\1.cmp',fmOpenRead or fmShareDenyWrite);
      try
        FS.ReadComponent(ImageList);
      finally
        FS.Free;
      end;
      Bmp := TBitmap.Create;
      Ico := TIcon.Create;
      try
        ImageList.GetIcon(0, ico);

        GetIconInfo(ico.Handle, IconInfo);

        BMP.Handle := IconInfo.hbmColor;
        BMP.PixelFormat := pf32bit;
        BMP.Canvas.Brush.Color := clNone;

        Bmp.SaveToFile('c:\temp\3.bmp');
      finally
        ico.Free;
        Bmp.Free;
      end;
  finally
    ImageList.Free;
  end;
end;

This code will get exactly the same bitmap, as was put into ImageList;

To copy one ImageList to another without losses, we can use copying with streams:

procedure TbtkImageList.Assign(Source: TPersistent);
var
  IL: TCustomImageList;
  BIL: TbtkImageList;
var
  st: TMemoryStream;
begin
  st := TMemoryStream.Create;
  try
    st.WriteComponent(TbtkImageList(Source));
    st.Seek(0, soFromBeginning);
    st.ReadComponent(Self);
  finally
    st.Free;
  end;
end;
user1443993
  • 371
  • 4
  • 10