uses
Windows,
[...]
{Header mit BitmapV5Header}
{$EXTERNALSYM CreateDIBSection}
function CreateDIBSection(
DC: HDC;
const p2: TBitmapV5Header; p3: UINT;
var p4: Pointer; p5: THandle; p6: DWORD): HBITMAP;
stdcall;
implementation
function CreateDIBSection;
external gdi32
name '
CreateDIBSection';
function PNGtoIcon(
const APNG : TPNGObject;
ACursor : Boolean = false;
AHotSpotX : Integer = 0;
AHotSpotY : Integer = 0) : HICON;
var
Width, Height : Integer;
BitmapHeader : TBitmapV5Header;
//TBitmapV5Header statt PBitmapV5Header
hNewBitmap,
hMonoBitmap : HBITMAP;
Bits : Pointer;
x,
y : Integer;
DC : HDC;
IconInfo : _ICONINFO;
Pixel : ^Integer;
ScanLine : PRGBTriple;
AlphaScanline : pByteArray;
begin
//Die Höhe und die Breite brauchen wir später noch ein paar Mal
Width := APNG.Width;
Height := APNG.Height;
//So ein Icon hat einen "Bitmap Version 5 Header"
//(Wichtig: Als Pointer, damit er später vom CreateDIBSection "genommen" wird)
{*** New(BitmapHeader); Nicht mehr notwendig }
//Die Größe der Struktur setzen, damit Windows weiß, "wie weit es gehen kann" :)
BitmapHeader.bV5Size := sizeOf(BITMAPV5HEADER);
BitmapHeader.bV5Width := Width;
//Wichtig: negative Höhe angeben, sonst ist das Ergebniss an der X-Achse gespiegelt
// ... bzw. der Ursprung des Bildes an der falschen Stelle (technisch richtiger)
BitmapHeader.bV5Height := -Height;
//Wir haben eine Ebene
BitmapHeader.bV5Planes := 1;
// ... und 32bit pro Bixel
BitmapHeader.bV5BitCount := 32;
//Das Bild wird nicht komprimiert
BitmapHeader.bV5Compression := BI_BITFIELDS;
//Und nun müssen wir noch sagen, wo sich die Farben und der Alpha-Wert innerhalb
// ... der 32bit eines Pixels befinden
BitmapHeader.bV5RedMask := $00FF0000;
BitmapHeader.bV5GreenMask := $0000FF00;
BitmapHeader.bV5BlueMask := $000000FF;
BitmapHeader.bV5AlphaMask := $FF000000;
DC := GetDC(0);
//Ein neues Bitmap anlegen
hNewBitmap := CreateDIBSection(
DC,
{*** Genau an dieser Stelle liegt die Ursache für das Problem...
PBitmapInfo(BitmapHeader)^, //Hier ein wenig tricksen
// ... damit der V5Header "reinpasst" }
BitmapHeader,
{ Statt der alten Methode passt jetzt der komplette Header rein }
DIB_RGB_COLORS,
//wir haben RGB-Farben
Bits,
//eine Pointer auf das erste Pixel
0,
//bedeutet, das wir das Bitmap im RAM haben wollen
0);
// ... und deswegen brauchen wir auch kein Offset
{*** Dispose(BitmapHeader); //der Header hat seine Schuldigkeit getan }
ReleaseDC(0,
dc);
// ... und auch unser DC
//man nehme ein Bitmap, welches wir später als Maske "verkaufen"
hMonoBitmap:=CreateBitmap(Width,Height,1,1,
nil);
//Und los gehts beim ersten Pixel
Pixel := Bits;
for y := 0
to Height-1
do
begin
//aus dem PNG die Farbwerte einer Zeile holen
ScanLine := APNG.Scanline[y];
// ... und dazu die Alpha-Werte
AlphaScanline := APNG.AlphaScanline[y];
for x := 0
to Width - 1
do
begin
//ein Pixel-Wert setzt sich aus ...
Pixel^ := AlphaScanLine[x];
// ... dem Alpha-Wert, ...
Pixel^ := Pixel^
shl 8;
Inc(Pixel^, Scanline^.rgbtRed);
// ... einem Rot-Anteil, ...
Pixel^ := Pixel^
shl 8;
Inc(Pixel^, Scanline^.rgbtGreen);
// ... einem Grün-Anteil, ...
Pixel^ := Pixel^
shl 8;
Inc(Pixel^, Scanline^.rgbtBlue);
// ... und einem Blau-Anteil zusammen
//weiter gehts mit dem nächsten Pixel innerhalb unseres RAM-Bitmaps
Inc(Pixel);
//und auch ein neues Pixel von unserem PNG währe nicht schlecht
Inc(ScanLine);
end;
end;
//Mit der IconInfo-Struktur können wir einige Eigenschaften des Icons setzen
// ... z.B. ob es ein Cursor ...
IconInfo.fIcon :=
not ACursor;
if ACursor
then
begin
// ... mit einem Hotspot ist
IconInfo.xHotspot := AHotSpotX;
IconInfo.yHotspot := AHotSpotY;
end;
//Aber auf jeden Fall brauchen wir ein Bitmap das als Maske dient
IconInfo.hbmMask := hMonoBitmap;
// ... und natürlich ein Bitmap mit dem eigentlichen Bild
IconInfo.hbmColor := hNewBitmap;
//Et voila ... ein schönes neues Icon
Result := CreateIconIndirect(IconInfo);
//natürlich räumen wir nachher auf
DeleteObject(hNewBitmap);
DeleteObject(hMonoBitmap);
//Wichtig: Das Icon, das zurückgegeben wird muss auch mit DestroyIcon
// ... freigegeben werden, wenn wir es nicht mehr brauchen.
end;