Search code examples
delphiwinapialphavcl

How can I create alpha blended icon/cursor (indirect) from the TBitmap instance of 32 bpp w/o making an temporary DIB section?


According to MS KB entry, there is a quirk in CreateIconIndirect which recognizes HBITMAPs been created with BITMAPV5HEADER passed to CreateDIBSection (and BGRA channel layout).

However, TBitmap instances with (PixelFormat = pf32bit) and (AlphaFormat = afDefined) (behaving as alpha blended for the other purposes) when referred by its Handles are not being recognized as valid alpha blended bitmaps for creation of icons or cursors.

Currently, I have to create a full copy of TBitmap using described API calls (see) to make CreateIconIndirect accept a bitmap handle as alpha blended. How do I overcome this clumsiness?


Solution

  • Here is an example:

    procedure TForm1.Button1Click(Sender: TObject);
    const
      crAlpha = TCursor(-25);
    var
      Bmp: TBitmap;
      Px: PRGBQuad;
      X, Y: Integer;
    
      BmpMask: TBitmap;
      II: TIconInfo;
      AlphaCursor: HCURSOR;
    begin
      Bmp := TBitmap.Create;
      Bmp.PixelFormat := pf32bit;
      Bmp.Canvas.Brush.Color := clWhite;
      Bmp.SetSize(32, 32);
      Bmp.Canvas.Font.Style := [fsBold];
      Bmp.Canvas.Font.Color := clRed;
      Bmp.Canvas.TextOut(1, 10, 'alpha');
    
      for Y := 0 to (Bmp.Height - 1) do
      begin
        Px := Bmp.ScanLine[Y];
        for X := 0 to (Bmp.Width - 1) do begin
          if DWORD(Px^) = DWORD(clWhite) then
            Px.rgbReserved := $00
          else
            Px.rgbReserved := $FF;
          Inc(Px);
        end;
      end;
    
      BmpMask := TBitmap.Create;
      BmpMask.SetSize(Bmp.Width, Bmp.Height);
    
      II.fIcon := False;
      II.xHotspot := 32;
      II.yHotspot := 32;
      II.hbmMask := BmpMask.Handle;
      II.hbmColor := Bmp.Handle;
    
      AlphaCursor := CreateIconIndirect(II);
      Win32Check(AlphaCursor <> 0);
      BmpMask.Free;
      Bmp.AlphaFormat := afDefined;  // AlphaBlend below, premultiply channels
      Canvas.Draw(0, 0, Bmp);        // test draw
      Bmp.Free;
    
      Screen.Cursors[crAlpha] := AlphaCursor;
      Cursor := crAlpha;
    
    end;
    


    sample image (Top 'alpha' is test draw, the other is a cursor)