![]() |
GDI+ Bilddrehung mit Transparenz
Moin!
ich versuche mittels GDI+ ein TBitmap (pf32Bit, bmDIP, afDefined) zu rotieren. Dieses klappt auch mit u.a. Procedure, leider geht dabei die Transparenz flöten und ich kenne mich mit GDI+ leider nicht ausreichend aus (Delphi 10.1 Berlin):
Code:
uses gdipapi, gdipobj; ...
procedure RotateBitmapGDI(Bmp: TBitmap; Degs: Integer; AdjustSize: Boolean; BkColor: TColor = clNone); var Tmp: TGPBitmap; Matrix: TGPMatrix; C: Single; S: Single; NewSize: TSize; Graphs, G2: TGPGraphics; P: TGPPointF; begin Tmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette); if tmp.getpixelformat=PixelFormatAlpha then showmessage('alpha'); if tmp.getpixelformat=PixelFormatPAlpha then showmessage('Prealpha'); if isalphapixelformat(tmp.GetPixelFormat) then ShowMessage('ALPHA'); if iscanonicalpixelformat(tmp.GetPixelFormat) then ShowMessage('Canonical'); if tmp.GetPixelFormat=PixelFormat32bppRGB then ShowMessage('32RGB'); if isindexedpixelformat(tmp.GetPixelFormat) then ShowMessage('Indexed'); showmessage(inttostr(getpixelformatsize(tmp.GetPixelFormat))); Matrix := TGPMatrix.Create; try Matrix.RotateAt(Degs, MakePoint(0.5 * Bmp.Width, 0.5 * Bmp.Height)); if AdjustSize then begin C := Cos(DegToRad(Degs)); S := Sin(DegToRad(Degs)); NewSize.cx := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S)); NewSize.cy := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C)); Bmp.Width := NewSize.cx; Bmp.Height := NewSize.cy; end; Graphs := TGPGraphics.Create(Bmp.Canvas.Handle); try Graphs.Clear(ColorRefToARGB(ColorToRGB(BkColor))); Graphs.SetTransform(Matrix); Graphs.DrawImage(tmp, (Cardinal(Bmp.Width) - Tmp.GetWidth) div 2, (Cardinal(Bmp.Height) - Tmp.GetHeight) div 2); finally Graphs.Free; end; finally Matrix.Free; Tmp.Free; end; end; Das Problem ist offenbar, dass bei
Code:
das Pixelformat nicht übernommen wird. Ich habe alternativ ausprobiert, die TGPBitmap aus einem Stream zu laden, auch das entfernt aber leider die Transparenz. Wenn ich die tmp-Bitmap selbst mit pixelFormat32bppARGB erzeuge, bin ich leider zu doof, meine TBitmap dort reinzukopieren (ich habe versucht, zu tmp ein TGPGraphics zu erzeugen und mit DrawImage reinzumalen, aber da bin ich offenbar zu blöd zu).
Tmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette);
Kann mir da jemand auf die Sprünge helfen? VG Sven |
AW: GDI+ Bilddrehung mit Transparenz
So... ich beantworte dann mal selbst.
Vorweg: ich habe es bislang nicht hinbekommen, eine TBitmap mit 32bit und Alpha in eine TGPBitmap oder ein TGPImage zu kopieren, ohne dass der Alphakanal verschwindet. Die TGPBitmap wird immer als 32bppRGB, nicht als 32bppARGB erzeugt. Mein Umweg läuft darüber, erst die TBitmap in ein TPNGImage zu wandeln, dieses in einen Stream zu schreiben und aus dem Stream ein TGPImage zu erzeugen:
Code:
procedure RotateBitmapGDI(Bmp: TBitmap; Degs: Integer; AdjustSize: Boolean; BkColor: TColor = clNone); var Tmp: TGPImage; Matrix: TGPMatrix; C: Single; S: Single; NewSize: TSize; Graphs: TGPGraphics; fstream: TStream; tmpi: TGPIMage; png: TPNGImage; begin //TBitmap (pf32, Alphatransparenz) nach PNG png:=TPngImage.Create; png:=Png4TransparentBitmap(bmp); //PNG in Stream und nach TGPImage fstream:=TMemoryStream.Create; try png.SaveToStream(fstream); fstream.Position:=0; tmp:=TGPImage.Create(TStreamAdapter.Create(fstream)); finally fstream.Free; png.Free; end; //die TBitmap löschen: bmp.SetSize(0,0); bmp.SetSize(120,120); Matrix := TGPMatrix.Create; try Matrix.RotateAt(Degs, MakePoint(0.5 * Bmp.Width, 0.5 * Bmp.Height)); //Rotation if AdjustSize then begin C := Cos(DegToRad(Degs)); S := Sin(DegToRad(Degs)); NewSize.cx := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S)); NewSize.cy := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C)); Bmp.Width := NewSize.cx; Bmp.Height := NewSize.cy; end; Graphs := TGPGraphics.Create(Bmp.Canvas.Handle); //den Canvas der ursprünglichen TBitmap auswählen try //Graphs.Clear(ColorRefToARGB(ColorToRGB(BkColor))); Graphs.SetTransform(Matrix); Graphs.DrawImage(tmp, (Cardinal(Bmp.Width) - Tmp.GetWidth) div 2, (Cardinal(Bmp.Height) - Tmp.GetHeight) div 2); //zurückschreiben finally Graphs.Free; end; finally Matrix.Free; Tmp.Free; end; end; Das ganze benutzt noch diese Hilfsprozedur:
Code:
Das ist natürlich irgendwie von-hinten-durch-die-Brust-ins-Auge und nicht gerade schnell (was in meinem Fall nichts ausmacht), aber alle anderen Varianten, die ich direkt mit der Ursprungsbitmap ausprobiert habe - ohne den Umweg über PNG - funktionierten nicht.
function PNG4TransparentBitMap(bmp:TBitmap):TPNGImage;
//201011 Thomas Wassermann var x, y:Integer; vBmpRGBA: ^TRGBAArray; vPngRGB: ^TRGB; begin Result := TPNGImage.CreateBlank(COLOR_RGBALPHA, 8, bmp.Width , bmp.Height); Result.CreateAlpha; Result.Canvas.CopyMode:= cmSrcCopy; Result.Canvas.Draw(0,0,bmp); for y := 0 to pred(bmp.Height) do begin vBmpRGBA := bmp.ScanLine[y]; vPngRGB:= Result.Scanline[y]; for x := 0 to pred(bmp.width) do begin Result.AlphaScanline[y][x] := vBmpRGBA[x].A; if bmp.AlphaFormat in [afDefined,afPremultiplied] then begin if vBmpRGBA[x].A <> 0 then begin vPngRGB^.b:= round(vBmpRGBA[x].b/vBmpRGBA[x].A*255); vPngRGB^.r:= round(vBmpRGBA[x].r/vBmpRGBA[x].A*255); vPngRGB^.g:= round(vBmpRGBA[x].g/vBmpRGBA[x].A*255); end else begin vPngRGB^.b:= round(vBmpRGBA[x].b*255); vPngRGB^.r:= round(vBmpRGBA[x].r*255); vPngRGB^.g:= round(vBmpRGBA[x].g*255); end; end; inc(vPngRGB); end; end; end; Ich kann aber nicht ganz glauben, dass das "normale" GDI (mittlerweile) ohne Probleme mit 32Bit-TBitmaps mit Alphatransparenz arbeitet, aber die TGPBitmaps von GDI+ die Alphainformation nicht aus einer TBitmap (direkt) übernehmen können. Kennt jemand eine schnellere Lösung? VG Sven |
AW: GDI+ Bilddrehung mit Transparenz
Es kann völlig falsch sein, aber mal eine Überlegung:
Ist es so, dass die Transparenzfarbe in dem Pixel links oben (oder so) festgelegt ist? Dann könntest Du möglicherweise alle Pixel drehen - außer die in den 4 Ecken. Wie gesagt, nur eine Überlegung - die vielleicht auch totaler Quatsch ist. |
AW: GDI+ Bilddrehung mit Transparenz
Hier mal eine Alternative zu GDI+, die ich persönlich übersichtlicher finde:
![]()
Delphi-Quellcode:
Da muss sicherlich noch irgendwie etwas aufgeräumt werden. Aber es läuft.
uses ...., Vcl.Graphics, Winapi.Wincodec
procedure TForm24.Button1Click(Sender: TObject); var myOriginal : TWICImage; myRotate : IWICBitmap; iDecoder : IWICBitmapDecoder; iDecoderFrame: IWICBitmapFrameDecode; iFlipRotator : IWICBitmapFlipRotator; hres : HRESULT; myFile : string; begin myFile := '..\..\testpilot-header.png'; myOriginal := TWICImage.Create; try myOriginal.LoadFromFile(myFile); hres := myOriginal.ImagingFactory.CreateDecoderFromFilename(PWideChar(myFile), GUID_NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, iDecoder); if Succeeded(hres) then begin hres := iDecoder.GetFrame(0, iDecoderFrame); if Succeeded(hres) then begin hres := myOriginal.ImagingFactory.CreateBitmapFlipRotator(iFlipRotator); if Succeeded(hres) then begin hres := iFlipRotator.Initialize(iDecoderFrame, WICBitmapTransformRotate90); if Succeeded(hres) then begin myOriginal.ImagingFactory.CreateBitmapFromSource(iFlipRotator, WICBitmapCacheOnDemand, myRotate); if Assigned(myRotate) then begin myOriginal.Handle := myRotate; myOriginal.SaveToFile('../../rotated.png'); end; end; end; end; end; finally myOriginal.Free; end; end; Sherlock |
AW: GDI+ Bilddrehung mit Transparenz
Zitat:
Delphi-Quellcode:
rausgekürzt hast, dann war das zuviel. :wink:
myRotate
|
AW: GDI+ Bilddrehung mit Transparenz
Moin!
Vielen Dank für Eure Vorschläge! Zitat:
Zitat:
VG Sven |
AW: GDI+ Bilddrehung mit Transparenz
Zitat:
Du lädst das Bitmap mit GDI+ wenn ich das richtig verstehe. Danach hast du ein Graphics Image <> TBitmap. Was hält dich nun davon ab das Image zu HBitmap zu konvertieren und dieses dann zur Rotation zu verwenden. Weder die Farben noch der Alpha Channel wird dadurch berührt da ändert sich nichts. ![]() Nachtrag: Mein Ansatz.
Delphi-Quellcode:
if img <> 0 then
begin // GetImageSize(Img, ImgW, ImgH); GdipGetImagePixelFormat(img, format); if format = PixelFormat32bppARGB then // Accept files with alpha channel begin GdipCreateHBITMAPFromBitmap(pointer(img), hbmReturn, $00ff0000); if hbmReturn <> 0 then Result := hbmReturn; end; GdipDisposeImage(img); end; gruss |
AW: GDI+ Bilddrehung mit Transparenz
Zitat:
nein, ich habe ursprünglich ein TBitmap "bmp" (aus einer ImageList, ich lade es also nicht direkt mit GDI+), das ich drehen möchte. Dazu möchte ich es mit Transparenz in eine TGPBitmap "tmp" wandeln/ kopieren, dann drehen und wieder zurückgeben. Mit
Delphi-Quellcode:
Tmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette);
hat die TGPBitmap Tmp nachher aber das Pixelformat 32bppRGB statt 32bppARGB, der Alphakanal verschwindet bei der Umwandlung. Mein Problem ist also der Schritt von TBitmap zu TGPBitmap… VG |
AW: GDI+ Bilddrehung mit Transparenz
Wie ich schon sagte..
TBitmap zu TGPBitmap := Graphics Image. Es spielt keine rolle welches Format du verwendest am ende ist es immer ein Graphics Image Object. PNG, JPG, BMP und Konsorte. Zitat:
Dann reicht ein GdipCreateBitmapFromHBITMAP.. Zitat:
Prüfe das doch mal vorher mit GdipGetImagePixelFormat. Du musst also sicherstellen das dein Bitmap in der ImgageList auch schon im 32Bit ARGB Format vorliegt. Teste das hier. Du must die Bitmaps vorher in ein GDI+ fähigen Bitmap Format (Graphics Image Object) konvertieren sonst wird das nichts!
Delphi-Quellcode:
gruss
function ImageListToGPBitmap(SourceImageList: TImageList): TGPBitmap;
var bmp: TGPBitmap; g: TGPGraphics; dc: HDC; i: Integer; x: Integer; procedure GdipCheck(Status: Winapi.GDIPAPI.TStatus); begin if Status <> Ok then raise Exception.CreateFmt('%s', [GetStatus(Status)]); end; begin //Note: Code is public domain. No attribution required. bmp := TGPBitmap.Create(SourceImageList.Width*SourceImageList.Count, SourceImageList.Height); GdipCheck(bmp.GetLastStatus); g := TGPGraphics.Create(bmp); GdipCheck(g.GetLastStatus); g.Clear($00000000); GdipCheck(g.GetLastStatus); dc := g.GetHDC; for i := 0 to dmGlobal.imgImages.Count-1 do begin x := i * dmGlobal.imgImages.Width; ImageList_DrawEx(dmGlobal.imgImages.Handle, i, dc, x, 0, dmGlobal.imgImages.Width, dmGlobal.imgImages.Height, CLR_NONE, CLR_DEFAULT, ILD_TRANSPARENT); end; g.ReleaseHDC(dc); g.Free; Result := bmp; end; |
AW: GDI+ Bilddrehung mit Transparenz
Zitat:
In der Tat. Ich hab die im Endergebnis 10 Minuten später nicht mehr gesehen und dachte "weg damit!" Sherlock |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:13 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz