![]() |
AW: PNG in TImage falsch dargestellt
@Redeemer
Das war mir jetzt auch klar. Da ich aber alle PNGs einheitlich abgespeichert habe, vermute ich mal, entweder sind alle Pallettenbilder oder keins. Wenn dem nicht so ist, verstehe ich den Algorithmus von Paint.net nicht :/ @Medium Perfekt, damit funktioniert es. Warum eine Zuweisung oder ein Assign nicht funktioniert, bleibt mir zwar ein Rätsel, aber das ist ja nicht so wichtig. Final werden die Bilder in meiner Klasse so geladen:
Delphi-Quellcode:
Und so werden sie dann dargestellt.
fSrcPic, fDestPic: TBitmap32;
(...) property SrcPicture: TBitmap32 read fSrcPic; property DestPicture: TBitmap32 read fDestPic; (...) fSrcPic.Clear; fDestPic.Clear; if FileExists(fSrcPath) then LoadBitmap32FromPNG(fSrcPic, fSrcPath); if FileExists(fDestPath) then LoadBitmap32FromPNG(fDestPic, fDestPath);
Delphi-Quellcode:
ist das ausgewählte Objekt.
e
Delphi-Quellcode:
Image1.Bitmap.SetSize(e.SrcPicture.Width, e.SrcPicture.Height);
e.SrcPicture.DrawTo(Image1.Bitmap); (...) //Image2.Bitmap.Clear; <-- Offenbar nicht nötig Image2.Bitmap.SetSize(e.DestPicture.Width, e.DestPicture.Height); e.DestPicture.DrawTo(Image2.Bitmap); |
AW: PNG in TImage falsch dargestellt
Liste der Anhänge anzeigen (Anzahl: 1)
Mit der PNG Unit von D7 kann man transparente PNG problemlos laden und anzeigen. Dass das in Rio nicht mehr geht hat ganz andere Gründe. Da hat nämlich irgend ein Witzbold einfach den transparenten Teil auf fix schwarz gesetzt. Keine Ahung was man damit errreichen wollte, aber auf jeden Fall kann man so keine transparentten PNG's mehr an eine Bitmap assignen. Die Hintergrundfarbe müsste auf jeden Fall vom Anwender bestimmt werden und nciht fix Schwarz sein. In meinem Fall will ich da nämlich weiss als Hintergrundfarbe und nicht schwarz. Dafür müsste es ein Property geben.
Habe noch eine Beispiel PNG angehängt, die einen Alphchannel hat und transparent sein sollte. Dieses wird nun fix mit schwarzem Hintergrund angezeigt, wenn es an ein Bitmap assigned wird. Das ist der entsprechende Code in der alten D7 PNGImage unit, der einwandfrei mit PNG's mit einem Alphachannel funktioniert:
Delphi-Quellcode:
Dieser Code wurde in Rio (oder auch schon füher), so abgeändert, dass da fix schwarz als Hintergrund verwendet wird. Folgende 2 Zeilen wurde da eingefügt, die nun den Effekt haben, das der Alpahachannel ncht merh funktioniert und alles, was transparent sein sollte, nun schwarz ist. (siehe ungefähr in der Mitte des Quelcodes)
**** PNGImage von D7 ****
{Assigns this TPNGImage to another object} procedure TPNGImage.AssignTo(Dest: TPersistent); {$IFDEF UseDelphi} function DetectPixelFormat: TPixelFormat; begin with Header do begin {Always use 24bits for partial transparency} if TransparencyMode = ptmPartial then DetectPixelFormat := pf24bit else case BitDepth of {Only supported by COLOR_PALETTE} 1: DetectPixelFormat := pf1bit; 2, 4: DetectPixelFormat := pf4bit; {8 may be palette or r, g, b values} 8, 16: case ColorType of COLOR_RGB, COLOR_GRAYSCALE: DetectPixelFormat := pf24bit; COLOR_PALETTE: DetectPixelFormat := pf8bit; else raise Exception.Create(''); end {case ColorFormat of} else raise Exception.Create(''); end {case BitDepth of} end {with Header} end; var TRNS: TChunkTRNS; {$ENDIF} begin {If the destination is also a TPNGImage make it assign} {this one} if Dest is TPNGImage then TPNGImage(Dest).AssignPNG(Self) {$IFDEF UseDelphi} {In case the destination is a bitmap} else if (Dest is TBitmap) and HeaderPresent then begin {Copies the handle using CopyImage API} TBitmap(Dest).PixelFormat := DetectPixelFormat; TBitmap(Dest).Width := Width; TBitmap(Dest).Height := Height; if Palette <> 0 then TBitmap(Dest).Palette := CopyPalette(Palette); if (TransparencyMode = ptmBit) then begin TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; TBitmap(Dest).TransparentColor := TRNS.TransparentColor; TBitmap(Dest).Transparent := True; SetStretchBltMode(TBitmap(Dest).Canvas.Handle, COLORONCOLOR); StretchDiBits(TBitmap(Dest).Canvas.Handle, 0, 0, Width, Height, 0, 0, Width, Height, Header.ImageData, pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS, SRCCOPY) end {if (TransparencyMode = ptmBit)} else TBitmap(Dest).Canvas.Draw(0, 0, Self); end else {Unknown destination kind} inherited AssignTo(Dest); {$ENDIF} end; TBitmap(Dest).Canvas.Brush.Color := 0; TBitmap(Dest).Canvas.FillRect(Bounds(0,0,Width, Height));
Delphi-Quellcode:
Eine Zuweisung einer PNG mit Alphachannel an eine Bitmap mit Assign, wird deswegen immer mit schwarzem Hintergrund dargestellt. Mit der alten Unit von D7 funktioniert das noch tadellos und ist bei mir seit Jahren so im Einsatz, ohne dass ich da auf GDI+ oder ähnliches zugreiffen müsste. In Rio ist da was "kaputt" gemacht worden.
**** PNGImage von 10.3 ***
{Assigns this TPngImage to another object} procedure TPngImage.AssignTo(Dest: TPersistent); {$IFDEF UseDelphi} function DetectPixelFormat: TPixelFormat; begin with Header do begin {Always use 24bits for partial transparency} if TransparencyMode = ptmPartial then DetectPixelFormat := pf24bit else case BitDepth of {Only supported by COLOR_PALETTE} 1: DetectPixelFormat := pf1bit; 2, 4: DetectPixelFormat := pf4bit; {8 may be palette or r, g, b values} 8, 16: case ColorType of COLOR_RGB, COLOR_GRAYSCALE: DetectPixelFormat := pf24bit; COLOR_PALETTE: DetectPixelFormat := pf8bit; else raise Exception.Create(''); end {case ColorFormat of} else raise Exception.Create(''); end {case BitDepth of} end {with Header} end; var TRNS: TChunkTRNS; {$ENDIF} begin {If the destination is also a TPngImage make it assign} {this one} if Dest is TPngImage then TPngImage(Dest).AssignPNG(Self) {$IFDEF UseDelphi} {In case the destination is a bitmap} else if (Dest is TBitmap) and HeaderPresent then begin TBitmap(Dest).SetSize(Width, Height); if (TransparencyMode = ptmPartial) then begin TBitmap(Dest).PixelFormat := pf32bit; TBitmap(Dest).AlphaFormat := afDefined; // Die 2 folgenden Zeilen sind neu: Wer hat sich diesen Blödsinn überlegt?! TBitmap(Dest).Canvas.Brush.Color := 0; // <<< zumindest das hier sollte vom Anwender wählbar sein, also als Property zur Verfügung stehen. TBitmap(Dest).Canvas.FillRect(Bounds(0,0,Width, Height)); end else begin TBitmap(Dest).PixelFormat := DetectPixelFormat; TBitmap(Dest).AlphaFormat := afIgnored; end; if Palette <> 0 then TBitmap(Dest).Palette := CopyPalette(Palette); if (TransparencyMode = ptmBit) then begin TRNS := TChunkTRNS(Chunks.ItemFromClass(TChunkTRNS)); TBitmap(Dest).TransparentColor := TRNS.TransparentColor; TBitmap(Dest).Transparent := True; SetStretchBltMode(TBitmap(Dest).Canvas.Handle, COLORONCOLOR); StretchDiBits(TBitmap(Dest).Canvas.Handle, 0, 0, Width, Height, 0, 0, Width, Height, Header.ImageData, pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS, SRCCOPY) end {if (TransparencyMode = ptmBit)} else TBitmap(Dest).Canvas.Draw(0, 0, Self); end else {Unknown destination kind} inherited AssignTo(Dest); {$ENDIF} end; |
AW: PNG in TImage falsch dargestellt
Das musst du schnell noch melden, damit der Bugfix heute noch ins Delphi 10.4 rein kommt, bevor es raus kommt. :stupid:
|
AW: PNG in TImage falsch dargestellt
Zitat:
Das Problem müsste später auftreten; da wo dieses Bitmap auf einen Bildschirm-Canvas gemalt wird. Und DAS ist wiederum Aufgabe der GDI. Eventuell wurde hier etwas an den Aufrufen geändert, das kann ich grad nicht prüfen, aber ich habe gute Erinnerung daran, dass ich mit Alphakanälen unter purer GDI immer irgendwo Probleme hatte. Mindestens in Delphi 7, aber auch noch in 2007. Mit neueren hatte ich bislang nicht mit Transparenzen zu tun. |
AW: PNG in TImage falsch dargestellt
BMP.Assign(PNG) klappt auch in D2009 schon nicht richtig. Die PNG wird zu Schwarz geglättet, die Transparenz bleibt aber. Sprich aus zu 50% deckendem Weiß wird zu 50% deckendes Mittelgrau. Man darf sich nach Assign dann die Scanlines kopieren. Ganz toll.
In Delphi 2005 gab es meines Wissens kein PNG, da musste man PNGDelphi installieren. |
AW: PNG in TImage falsch dargestellt
Da wird das Problem dann sein, dass .Draw() vermutlich die Transparenzen des Originalbildes wirklich zum Zeichnen benutzt, und ein Blend aus BMP und PNG erstellt. Das wäre für .Draw() auch richtig - aber nicht für .Assign()! Dank Alpha unterscheidet sich Zeichnen erheblich von Kopieren, und das ist in der Tat ein dicker Fehler in der gezeigten Methode. Allerdings auch schon in der alten Version... bei der ich fast vermute, dass .Draw() eigentlich falsch mit Transparenz umgegangen ist, sodass das richtige Verhalten für .Assign() entstand, und gleichzeitig für meine Probleme verantwortlich war.
Klingt für mich jetzt erstmal schlüssig. |
AW: PNG in TImage falsch dargestellt
Das Problem ist, dass nun, wenn man ein transparentes PNG auf eine Bitmap assigned eben der Hintergrund schwarz wird. Das war in einer älteren Version der PNGImage nicht so und da hat das mit der Trnasparenz und dem Alphachannel einwandfrei funktioniert. Da war der Hintergrund weiss, nähmlich so wie ein leeres Bitmap eben ist. Durch das fixe setzen der Farbe und einem FillRect wird die "leere" Bitmap mit schwarz "übermmalt" und der Transparente Teil wird schwarz. Meiner Meinung nach ist das falsch oder müsste zumindest mit einem Property gesteuert werden können, welche Farbe man da will.
Durch diese fixe Setzen auf die Farbe 0 (schwarz) ist der AssingTo praktisch unbrauchbar geworden. Ich musste mir dafür eine eigene Routine schreiben, die diese 2 Zeilen nicht enthält, damit mein D7 Programm nach der Migration auf 10.3 noch richtig mit den PNG's umgehen kann. Mir ist echt ein Rätsel wer das da mit fixem schwarzen Hintergund überhaupt so nutzen kann?! |
AW: PNG in TImage falsch dargestellt
Es gibt bei transparenten Bitmaps eigentlich keinen richtigen oder falschen Hintergrund - zumindest, solange es noch nicht auf das finale Bild gezeichnet wurde, auf dem es angezeigt werden soll. Weiß ist genauso falsch wie Schwarz (und technisch gesehen ist transparent-schwarz (=0) richtiger für "leer" als nicht-transparent-weiß (=$FFFFFFFF)). Was man eigentlich in dieser Situation haben will ist, dass sowohl Farbe als auch Alpha unverändert übernommen werden. "Assigned" eben, im Gegensatz zu "Drawn" wo die Farbwerte anhand der Transparenz miteinander verrechnet werden. Es ist schlicht nicht okay in einer Assign-Methode intern Draw(To) zu verwenden. Stattdessen müssten die Pixel 1:1 kopiert werden.
|
AW: PNG in TImage falsch dargestellt
Liste der Anhänge anzeigen (Anzahl: 3)
Ich muss meine Aussage von vorhin korrigieren: Mir fällt nämlich auf, dass der von mir beschriebene Bug nur mit TImageList.AddMaked(BMP, clNone) und InsertMasked auftritt (ReplaceMasked unterstützt allgemein keine Alphakanäle), nicht jedoch mit TImage, während mein Bugfix (Kopieren der Scanline-Bytes) in TImageList fehlerfrei funktioniert aber in TImage unter bestimmten Umständen zu einem extrem lustigen Fehler führt (siehe *):
* in einer RDP-Sitzung: Bild zeichnet nicht vollständig deckende Pixel zwar mit korrektem Alphakanal, aber als ob die Farbe des in der PNG gespeicherten Pixels komplett schwarz wäre. Vollständig deckende Pixel sind fehlerfrei. GDI macht über RDP wirklich was es will. Mich wundert vor allem, dass TCanvas.Draw überhaupt den Alphakanal des Zielbitmaps schreibt. In TPNGImage.Draw wird in dem Fall TPNGImage.DrawPartialTrans aufgerufen, das wahrscheinlich eine Spezialbehandlung für Bitmaps bietet, die ich aber nicht verstehen/finden kann. Und mich wundert, dass frischt assignte Bitmaps in TImage fehlerfrei dargetellt werden, in TImageList aber nicht. Die Daten müssen also irgendwo fehlerfrei vorhanden sein. Auch seltsam: Der zweite Parameter von AddMasked wird in allen oben getesteten Fällen ignoriert. Ich hab mal mein Beispielprojekt angehängt. imagelist.png enthält drei 16x16-Bilder nebeneinander:
(Ich weiß, dass der Code Memoryleaks hat.) |
AW: PNG in TImage falsch dargestellt
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:01 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