Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Bilder [mit/ohne Transparenz] in universelles Format bringen (https://www.delphipraxis.net/189980-bilder-%5Bmit-ohne-transparenz%5D-universelles-format-bringen.html)

berens 16. Aug 2016 12:32

Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Hallo,
das ist ein indirekter Cross-Post zu https://delphigl.com/forum/viewtopic.php?f=10&t=11526 , bei dem ich das Problem nun von mehr von der "Delphi"-Seite als von der OpenGL-Seite angehen will.

Zusammenfassung:
Die Benutzer verwenden viele verschiedene Dateiformate (teilweise mit Transparenz), und die Graphiken sind viel manchmal zu groß für die Graphikkarte und sprengen den Arbeitsspeicher. Die Graphiken werden letztendlich als TglBitmap2D im Arbeitsspeicher gehalten und mit OpenGL dargestellt, müssen jedoch vorher verkleinert werden.

Problem:
Neben den vielfältigen Dateiformaten (JPG, BMP, PNG, TIF, ...) gibt es innerhalb dieser Dateiformate noch Unterschiedliche Einstellungen wie "PixelFormat" etc. Für die Zuweisung an TglBitmap2D fehlt mir das Hintergrundwissen 1) welche Dateiformate es alles gibt 2) welche Pixelformate es für die jeweiligen Dateiformate gibt 3) welche Besonderheiten zu beachten sind etc.

Genaugenommen sollte das eigentlich auch nicht mein Problem sein. Über TImage/TPicture kann ich ja alle verschiedenen Bild-Dateiformate korrekt laden und in einer VCL-Form mit TImage korrekt darstellen lassen. D.h. irgendwo sind ja die Pixeldaten für diese Graphik schon vorhanden.

Leider kann ich die Pixel des Bildes, das irgendwo in dem TPicture-gebilde abgespeichert ist, nicht 1:1 übernehmen (z.B. via ScanLine), da je nach Dateiformat und -codierung die Byte-Werte für Rot-Grün-Blau und Alpha (% Transparent 0-255, sofern vorhanden) in den unterschiedlichsten Reihenfolgen (RGB, BGR, RGBA ARGB, ABGR, ...) und Bitbreiten vorkommen, und ich somit auch wieder eine "Zuordnungstabelle" bräuchte, um je nach Situation die Bytes anders zu drehen...

Anmerkungen: Dass Texturen PowerOf2 sein müssen ist mir klar; mein Programm regelt das generell; es sollte nicht Thema dieses Threads sein.

Was ich brauche:
Ich brauche eine TImage/TPicture Kopie des originalen TImage/TPicture (nur halt verkleinert). Diese muss dann als Format noch lesbar sein, damit TglBitmap2D damit noch was anfangen kann.

Das habe ich bereits probiert:
-Google/DP/DelphiGL Suche: War leider nicht so erfolgreich: Meistens werden bei Programmen ja die Graphiken mitgeliefert (also die Texturen) und liegen in einem einheitlichen Format vor den Ansprüchen von OpenGL angemessen ist.

-TBitmap erzeugen und mit Draw/StretchDraw/StretchBlt zeichnen: Generell gute Idee. Wichtig zu Wissen, dass TBitmap <> dem Dateiformat .bmp ist, und somit auch generell in der Lage ist, die TransparenzInformationen zu "halten". Das Problem ist, dass TglBitmap2D mit 32-Bit Bitmaps nichts anfangen kann und die Textur verwirft (Fehler beim Laden vom Stream; falscher Header für Bitmap Format). D.h. ich muss alle Graphiken auf 24Bit reduzieren, und das würde also bedeuten, dass ich alle TransparenzInformationen verliere? Keine Option! Ich habe es mit AssignFromBitmap und LoadFromStream probiert.

-Selber Weg wie oben, nur das Bild auf ein TPNGImage zeichnen: Scheitert schon daran, dass ich kein "leeres" TPNGImage.CreateBlank erzeugen kann. Sowohl mit COLOR_RGB wie COLOR_RGBAlpha und Bittiefe 32, notfalls 24, jedesmal gibt es einen Fehler mit "Ungültige Werte". Wie kann ich ein leeres PNG-Bild erzeugen, dass die maximale Farbtiefe und Alpha-Informationen hat? Weil dann könnte ich drauf zeichnen und das Bild dann mit TglBitmap2D.LoadFromStream laden (TglBitmap2D unterstützt PNG).

-Ich habe auch diverse andere Sachen probiert, wie die Bilder umzuwandeln und als temporäre Datei auf Festplatte zu speichern und neu zu laden. Aber das bringt auch keine Besserung, wenn das Dateiformat (.bmp) wieder Beschränkungen mit der Farbtiefe hat oder spätestens beim Laden (pf32Bit) wieder inkompatibel zu TglBitmap2D wird. Auch PNG kann ich nicht zwischenspeichern, weil ich ja kein neues PNG als Objekt erzeugen kann...

Wie gehe ich da weiter vor?

Danke im Vorraus!

Neutral General 16. Aug 2016 12:43

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Also erstmal würde ich kein TImage benutzen. Das ist nämlich (wenn du das im DelphiGL-Forum schon ansprichst) unprofessionell ;)
Wenn der Benutzer ein Bild auswählt erzeuge anhand der Dateiendung ein Objekt der entsprechenden TGraphic-Klasse (TPNGImage, TBitmap, ...).
Schau dann ob, die jeweilige TGraphic-Klasse eine Möglichkeit bietet das Bild in der Größe zu ändern und danach gehst du wie bei deinem Versuch mit TPNGImage vor und speicherst das verkleinerte Bild in einem Stream und übergibst diesen dann an OpenGL.

EWeiss 16. Aug 2016 12:51

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

D.h. ich muss alle Graphiken auf 24Bit reduzieren, und das würde also bedeuten, dass ich alle TransparenzInformationen verliere?
Davon kannst du ausgehen keine 32Bit beim *.bmp dann ist die Transparenz futsch.
Das Problem ist du hast zwar viel geschrieben aber dein Anliegen kann ich jetzt nicht direkt ersehen.

Was möchtest du also?
Unabhängig vom Format!
Lade ein Bild und erstelle dann ein neues direkt aus dem Speicher.
Transparent oder was auch immer.

Ich hab da mal was in C# gemacht vielleicht hilft es dir ja.

Code:
  ///<summary>
  ///Graphics related funcs
  ///</summary>
  [SuppressUnmanagedCodeSecurity]
  public sealed class PicFromMem
  {

    private static bool GetImageFromBytes(byte[] data, out Image image)
    {
      try
      {
        using (var ms = new MemoryStream(data))
        {
          image = Image.FromStream(ms);
        }
      }
      catch (ArgumentException)
      {
        image = null;

        return false;
      }

      return true;
    }

    private static Image RemoveMetadata(byte[] data)
    {
      Image image;

      if (GetImageFromBytes(data, out image))
      {
        Bitmap bitmap = new Bitmap(image);

        using (var graphics = Graphics.FromImage(bitmap))
        {
          graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
          graphics.CompositingMode = CompositingMode.SourceCopy;
          graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

          graphics.DrawImage(image, 0, 0);
        }
        return image;
      }

      return null;
    }
    ///<summary>
    /// Get Image from Memory Pointer
    ///</summary>
    ///<returns>
    ///Image from Byte Array without MetaData
    ///</returns>
    public static Image GetImageFromMemPtr(IntPtr DataPtr, long Size)
    {
      byte[] bArray = new byte[Size];

      Marshal.Copy(DataPtr, bArray, 0, (int)Size);

      // Remove MetaData from File
      return RemoveMetadata(bArray);

    }
  }

gruss

berens 16. Aug 2016 13:42

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
> Was möchtest du also?

Ich möchte wissen, ob es ein universelles Objekt (ala TBitmap) gibt, von dem aus ich eine Graphik in TglBitmap2D verwandeln kann.


Spätestens wenn ich TBitmap voll ausreize und auf 32-Bit gehe, geht das ganze ja schon wieder nicht. Von PNG _kann_ TglBitmap2D aber 32-Bit (incl. Alpha) übernehmen. Was gibt es also besseres als TBitmap?

TPicture hatte halt den angenehmen vorteil, dass ich mich als solches nicht um das Dateiformat kümmern brauche. Wenn das (auch zukünftige) Betriebssystem die Bilder laden kann ist gut, Bibliotheken für JPG und PNG ergänzen den Rest. Wenn ich nun wirklich für _jeden_ Dateityp eine eigene Ableitung von TGraphic berücksichtigen muss (sofern es einen gibt!), muss ich erst mal wissen welche das sind (Wo steht das?) und welche es alles gibt (BMP, JPG, PNG, TIF, PCX, GIF, ...). Dafür, dass TPicture schon Alles "von Haus aus" (mithilfe der Bibliotheken) kann, steigert das separate verarbeiten nach TGraphic unterschieden -meines Erachtens(!)- das die Fehleranfälligkeit, erhöht unnötig die Redundanz aber leider nicht die Sicherheit, da ja TglBitmap2D leider je nach Pixelformat etc. trotzdem nicht mit allen Varianten von TGraphic zurechtkommt. Deshalb würde ich gerne 100% sicher sein, dass die von mir aufbereitete Graphik _vor_ der übergabe an OpenGL/TglBitmap2D garantiert in einem Datei-/Pixel-/Graphikformat ist (auch mit RGB<-->BGR Problematik), was später problemlos von TglBitmap2D verarbeitet werden kann.

Gerne speichere ich es als TPNGImage zwischen, aber da kommt ja die Fehlermeldung. Das genau werde ich jetzt nochmal detaillierter und hier im Forum suchen, das Oben nur mal als Zwischenbericht.

Danke nochmal; für weitere Tips habe ich die Ohren offen.

Edit: Da unten ging dein Beitrag ja noch weiter mit Code, den schaue ich mir an. Danke.

bytecook 16. Aug 2016 13:50

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Würde Graphics32 (www.graphics32.org in Kombination mit (gr32_png) einsetzen.

Aktuelle GR32 Version: https://github.com/graphics32/graphics32 (Support inkl. 10.1 Berlin)
Aktuelles Png Lib: https://sourceforge.net/projects/gr32pnglibrary/

Damit hast du mal die Basis, GR32 arbeitet per se mit 24 Bit Farbtiefe + 8 Bit Alphachannel.
Du kannst auch jederzeit einem TBitmap32 ein "normales" Bitmap zuordnen.

Gruß,

Peter

EWeiss 16. Aug 2016 14:01

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Mein Code erfüllt doch deinen zweck..
Das Format was dabei im Speicher liegt ist unwichtig.
Wichtig ist nur das mit meinem Code ein PNG zurückgegeben wird in höchster Qualität.

Dieses kannst du dann auf deine Größe zurecht stutzen.

Du benötigst nur den Pointer auf die im Speicher liegende Datei und die länge.
Ich habe das teil mal für einen Tag Editor (Coverart) geschrieben da weißt du auch nicht welches Bildformat im Tag abgelegt ist.

gruss

Sherlock 16. Aug 2016 14:19

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Unter Windows könntest Du TWICImage verwenden. Das kennt alle gängigen Formate und bietet Methoden zum Ändern der Größe unter Beibehaltung der Transparenz. Es ist jedoch (vermutlich weil es OS-Methoden nicht gut kapselt) nicht sonderlich schnell, wie hier gezeigt wurde. Ein Beispiel wie ein Image verkleinert wird ist hier zu finden.

Sherlock

berens 16. Aug 2016 14:39

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Danke für die Beiträge.

Zuerst nochmal "sorry" @EWeiss, beim schreiben meiner ersten Antwort hatte ich Deinen Post nur bis "Was möchtest du also?" gesehen/gelesen und dachte ursprünglich mein erster Post wäre unklar.

Deinen Code habe ich mir angeschaut, weiß allerdings nicht so Recht, wie ich das auf Delphi übertragen soll bzw. was der Code macht. Von den Prozedurnamen her scheinst Du eine Datei zu laden, die Metadaten zu entfernen und die Datei wieder zu speichern. Den eigentlichen Transfer machst du mit DrawImage, und scheinst direkt auf eine TGraphic zu zeichnen (geht in Delphi nicht?), bzw. auf ein Bitmap (wieder Problem mit 32Bit/Alpha bzw. dem Pixelformat ansich). Dafür bin ich leider nicht gut genug in C drin, damit ich das auch nur gedanklich übertragen könnte, vll. kannst du nochmal 1-2 Kommentare zu den Zeilen machen, bitte?

Mein aktueller Lösungsweg sieht dank @bytecook so aus, dass ich den Umweg über TBitmap32 und TPortableNetworkGraphic32 gehe. Abgesehen von vielen, vielen Units die nun zusätzlich in meinem Projekt für eine Quelltextzeile mitverwendet werden müssen (jammern auf hohem Nivea :lol:) kann ich nicht direkt von TBitmap32 in einen MemoryStream speichern, da das Format (logischerweise) anders ist als TBitmap, und somit TglBitmap2D das nicht laden kann. Also Umweg über PNG-Komponente, das geht generell, ABER: Trotz eines wirklich schnellen Rechners dauert png32.Assign bald über 10 Sekunden. Wenn der Benutzer also 10-20 Bilder einfügt, dauert das Starten vom Programm dann bald 2-3 Minuten, das kann ja dann wohl auch nicht sein.

Wo habe ich den Fehler, wie geht es anders?

@Sherlock: Ich befürchte, TPortableNetworkGraphic32 benutzt vielleicht auch WIC und ist deshalb so langsam?

Primär die Frage: Wie erzeuge ich ein leeres TPNGImage mit 32 Bit Farbtiefe incl. Alphakanal?

Delphi-Quellcode:
   png := TPngImage.CreateBlank(COLOR_RGBALPHA, 32, 2048, 1024);
erzeugt:
Zitat:

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt bla.exe ist eine Exception der Klasse EPNGInvalidSpec mit der Meldung '"Portable Network Graphics" konnte nicht erstellt werden, weil ungültige Bildtypparameter angegeben wurden.' aufgetreten.

Delphi-Quellcode:
uses
  ..., GR32, gr32_png;

var
  bmp32: TBitmap32;
  png32: TPortableNetworkGraphic32;
begin
   bmp32 := TBitmap32.Create;
   bmp32.Width := 2048; // zum testen
   bmp32.Height := 1024;
   bmp32.Canvas.Draw(0, 0, tmpImage.Picture.Graphic); // zum Testen auch keine Skalierung etc.

   png32 := TPortableNetworkGraphic32.Create;
   png32.Assign(bmp32);

   ms := TMemoryStream.Create;
   ms.Position := 0;
   png32.SaveToStream(ms);
//       bmp32.SaveToStream(ms); // geht nicht, TglBitmap2D kann das dann nicht wieder einlesen
   ms.Position := 0;
   LoadFromStream(ms);
   FreeAndNil(ms);

   GenTexture(True); // geladene Textur an OpenGL übergeben

   FreeAndNil(png32);
   FreeAndNil(bmp32);
Edit:
Ich habe es nun mit WIC versucht, und es geht einfach und schnell:

Delphi-Quellcode:
var
  wic: TWICImage;
begin
ms := TMemoryStream.Create;
ms.Position := 0;
wic := TWICImage.Create;
wic.LoadFromFile(_Filename);
wic.ImageFormat := wifPng;
wic.SaveToStream(ms);
ms.Position := 0;
   LoadFromStream(ms);
   FreeAndNil(ms);

   GenTexture(True); // geladene Textur an OpenGL übergeben
Aktuell kann ich nun sowohl meine .png wie auch .bmp-Datei korrekt laden. Es ist halt nur die Frage, wie ich das nun noch ohne AlphaVerlust skaliere. Der Link zum verkleinern von TWicImage erfordert das IWICBitmapScaler aus der windowscodec.dll. Gibt es bei Delphi 2010 standardmäßig nicht. Muss ich gleich mal schauen, wie ich das Bild klein bekomme...

bytecook 16. Aug 2016 14:59

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von berens (Beitrag 1344987)
Danke für die Beiträge.

Delphi-Quellcode:
uses
  ..., GR32, gr32_png;

var
  bmp32: TBitmap32;
  png32: TPortableNetworkGraphic32;
begin
   bmp32 := TBitmap32.Create;
   bmp32.Width := 2048; // zum testen
   bmp32.Height := 1024;
   bmp32.Canvas.Draw(0, 0, tmpImage.Picture.Graphic); // zum Testen auch keine Skalierung etc.

   png32 := TPortableNetworkGraphic32.Create;
   png32.Assign(bmp32);

   ms := TMemoryStream.Create;
   ms.Position := 0;
   png32.SaveToStream(ms);
//       bmp32.SaveToStream(ms); // geht nicht, TglBitmap2D kann das dann nicht wieder einlesen
   ms.Position := 0;
   LoadFromStream(ms);
   FreeAndNil(ms);

   GenTexture(True); // geladene Textur an OpenGL übergeben

   FreeAndNil(png32);
   FreeAndNil(bmp32);


Delphi-Quellcode:

uses
     ...
     Gr32,
     GR32_Png,
     GR32_PortableNetworkGraphic;

...

  (** PNG - Routinen **)
  function SaveAsPng (Const FileName : String; ABmp : TBitmap32) : Boolean;
  function LoadPng  (Const FileName : String; ABmp : TBitmap32) : Boolean;


(******************************************************************************)

(** PNG - Routinen - QnD                                                    **)

(******************************************************************************)

function SaveAsPng (Const FileName : String; ABmp : TBitmap32) : Boolean;

  var APng : TPortableNetworkGraphic32;

  begin
    try
      APng := TPortableNetworkGraphic32.Create;
      APng.Assign(ABmp);
      APng.SaveToFile(FileName);
      Result := True;
    except
      Result := False;
    end;
    APng.Free;
  end;
(******************************************************************************)
function LoadPng (Const FileName : String; ABmp : TBitmap32) : Boolean;

  var APng : TPortableNetworkGraphic32;

  begin
    try
      APng := TPortableNetworkGraphic32.Create;
      APng.LoadFromFile(FileName);
      APng.AssignTo(ABmp);
      Result := True;
    except
      Result := False;
    end;
    APng.Free;
  end;
(******************************************************************************)


procedure TForm1.LadePng

  var ABmp : TBitmap32;

  begin
    ABmp := TBitmap.Create();
    LoadPng ('Bart.png', ABmp);

    ... tuwas ...

    FreeAndNil(ABmp);

  end;

So sollte es klappen. TPortableNetworkGraphic32 funktioniert nativ, ohne DirectX ...

Sherlock 16. Aug 2016 15:01

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Also sooo langsam ist ein TWICImage dann doch nicht. Ein LoadFromFile dauert weniger als eine Sekunde...natürlich hängt es etwas von der Dateigröße und dem Speicherort ab.

Sherlock

himitsu 16. Aug 2016 15:04

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von Neutral General (Beitrag 1344971)
Wenn der Benutzer ein Bild auswählt erzeuge anhand der Dateiendung ein Objekt der entsprechenden TGraphic-Klasse (TPNGImage, TBitmap, ...).

Nja, bezüglich TImage ... joar, ist blöd, vorallem wenn nicht-visuell.

Aber man kann auch TPicture direkt verwenden.
Bei dem registrieren sich standardmäßig alle TGraphic-Klassen mit ihren Dateiendungen.
Picture.LoadFromStream geht da leider nicht, da diese Delphi-Klassen ausschließlich auf die Dateiendung achten, welche es beim Stream nicht gibt.
Picture.LoadFromFile nimmt dann anhand der Dateiendung eine der registrierten TGraphic, erstellt davon eine Instanz in Picture.Graphic und läd da rein dann das Bild.

bytecook 16. Aug 2016 15:08

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von Sherlock (Beitrag 1344989)
Also sooo langsam ist ein TWICImage dann doch nicht. Ein LoadFromFile dauert weniger als eine Sekunde...natürlich hängt es etwas von der Dateigröße und dem Speicherort ab.

Sherlock

Stimmt :) Hängt aber davon ab, wie groß die Texturen oder Graphiken sein sollen - und ob du auf deinem System DirectX installieren kannst...

berens 16. Aug 2016 15:10

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Mit TWICImage geht es durchaus schnell und zuverlässig! (Oben habe ich den Code mit Edit nachgetragen um nicht Antworten zu spammen).

Nur wie verkleinere ich die Bilder? Wie gesagt, der verlinkte Code erfordert Units, die ich nicht habe...

Edit: Danke @bytecook für den Code, aber ich lade explizit nicht über die TPortableNetworksGraphic32, weil die Dateien ja auch was anderes als .png sein können. Kann natürlich sein, dass die wegen Vererbung von TBitmap32 auch JPEG etc. lädt, aber naja...

Natürlich ist TPicture stilvoller als TImage, mal schauen, was im fertigen Code übrigbleibt. :)

EWeiss 16. Aug 2016 15:12

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Ich kenn das TWICImage nicht..
Aber wenn das auf GDI+ basiert könnte das verkleinern mit DrawImageRectRectI funktionieren.

gruss

bytecook 16. Aug 2016 15:16

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von EWeiss (Beitrag 1344994)
Ich kenn das TWICImage nicht..
Aber wenn das auf GDI+ basiert könnte das verkleinern mit DrawImageRectRectI funktionieren.

gruss

TWicImage ist bei Delphi mit an Bord...

Aber: Hier schon mal nachgesehen? http://www.dev-center.de/header/glbitmap
Imho solltest du ganz einfach die Textur laden können, da png unterstützt wird.

Ansonsten mal im DGL Forum Bergmann89 fragen, der gited ja die TGlBitmap...

Sherlock 16. Aug 2016 15:18

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Die Unit winapi.wincodec gibt es unter D2010 noch nicht? :shock:

Eventuell ohne Namespace...oder nur winapi.

Sherlock

berens 16. Aug 2016 15:20

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Leider verfügt weder TWicImage nach TPNGImage (als "Mittelsmann"), noch TPortableNetworkGraphic32 über einen Canvas, auf den ich mit Stretchdraw zeichen könnte. :shock:

> Ich kenn das TWICImage nicht..
Der Aussage kam nicht von mir, ich kann es ja problemlos benutzen.

Auf glBitmap soll das ganze ja letztendlich drauf. Leider _muss_ ich die Graphiken bearbeiten (verkleinern) bevor ich sie an glBitmap übergebe. Und je nach Dateiformat kann es mit glBitmap zu gewaltigen Problemen kommen, deshalb bin _ich_ für eine gültige Graphik zuständig und muss diese sicherstellen.

Edit: Es gibt tatsächlich die WinCodec Unit, damit geht das Resize (hatte versucht "WindowsCodec" wie in der Hilfe beschrieben zu verwenden). Jetzt muss ich den Code nur noch für Breite und Höhe anpassen:

Delphi-Quellcode:
procedure ResizeImage(aWICImage: TWICImage; NewWidth:Integer; NewHeight: Integer);
var
  scale: IWICBitmapScaler;
  wicBitmap: IWICBitmap;
begin
  if Assigned(aWICImage) then
  begin
    aWICImage.ImagingFactory.CreateBitmapScaler( scale);
    scale.Initialize(aWICImage.Handle, NewWidth, NewHeight, WICBitmapInterpolationModeFant);
    aWICImage.ImagingFactory.CreateBitmapFromSourceRect(scale, 0,0,NewWidth, NewHeight, wicBitmap);
    if Assigned(wicBitmap) then
      aWICImage.Handle := wicBitmap;
  end;
end;

himitsu 16. Aug 2016 15:25

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Liegt im XE in der Graphics.pas und Wincodec.pas

Einen HDC finde ich auch nicht, wo man ein TCanvas dran hängen könnte.

EWeiss 16. Aug 2016 15:27

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Ich verstehe das Problem jetzt nicht.
Du lädst eine Grafik (Bild) über TWicImage.

Danach konvertierst du dieses in eine Texture ?
Dann sollte doch nach einem glBindTexture über Quader diese sich verkleinern lassen.

Oder sehe ich da was falsch?

gruss

bytecook 16. Aug 2016 15:28

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von berens (Beitrag 1344997)
Leider verfügt weder TWicImage nach TPNGImage (als "Mittelsmann"), noch TPortableNetworkGraphic32 über einen Canvas, auf den ich mit Stretchdraw zeichen könnte. :shock:

> Ich kenn das TWICImage nicht..
Der Aussage kam nicht von mir, ich kann es ja problemlos benutzen.

Auf glBitmap soll das ganze ja letztendlich drauf. Leider _muss_ ich die Graphiken bearbeiten (verkleinern) bevor ich sie an glBitmap übergebe. Und je nach Dateiformat kann es mit glBitmap zu gewaltigen Problemen kommen, deshalb bin _ich_ für eine gültige Graphik zuständig und muss diese sicherstellen.

Deswegen würde ich eben Graphics32 nehmen...

1) Bild via LoadPng it ein TBitmap32 laden.
2) Bild auf gewünschte Größe resamplen. (Am besten je nach Geschmack/Quali mit einem Lanczos Resampler). Die meisten Lowlevel-Routinen in Gr32 sind SSE2 optimiert, also seeeehr schnell...
3) Verkleinertes Bild auf GlBitmap via Scanline übertragen. (Siehe https://delphigl.com/forum/viewtopic...hics32#p100074)

oder ...

3b) eine Titan X kaufen :P

Raster Resampling Infos:
http://graphics32.org/documentation/...terization.htm
http://graphics32.org/documentation/...lers/_Body.htm

Neutral General 16. Aug 2016 15:29

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von EWeiss (Beitrag 1344999)
Dann sollte doch nach einem glBindTexture über Quader diese sich verkleinern lassen.

Oder sehe ich da was falsch?

Das Problem war, dass die Bilder z.T. sehr groß sind und nicht in den GPU-Speicher passen, bzw. diesen überlasten.

EWeiss 16. Aug 2016 15:34

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von Neutral General (Beitrag 1345001)
Zitat:

Zitat von EWeiss (Beitrag 1344999)
Dann sollte doch nach einem glBindTexture über Quader diese sich verkleinern lassen.

Oder sehe ich da was falsch?

Das Problem war, dass die Bilder z.T. sehr groß sind und nicht in den GPU-Speicher passen, bzw. diesen überlasten.

Ahh ok ;)

gruss

berens 16. Aug 2016 15:55

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Also wie gesagt, das eigentliche Laden und Umwandeln von Graphiken klappt nun, das sogar sehr gut und schnell.

Jetzt habe ich vorhin groß getönt, dass ich mich um das verkleinern im PowerOf2-Maßstab kümmere... Tja...

Mit dem jetzigen Code
Delphi-Quellcode:
  ms := TMemoryStream.Create;
  ms.Position := 0;
  wic := TWICImage.Create;
  wic.LoadFromFile(_Filename);
  ResizeImage(wic, TexturWidth, TexturHeight);
  wic.ImageFormat := wifPng;
  wic.SaveToStream(ms);
  ms.Position := 0;
  LoadFromStream(ms);
  FreeAndNil(ms);

  GenTexture(True); // geladene Textur an OpenGL übergeben
wird die Graphik tatsächlich auf eine sinnvolle Texturgröße geschrumpft, allerdings haben nun -situationsbezogen- die X- und Y-Achsen unterschiedliche Skalierungen. Textur Width/Height sind ja ein vielfaches von 2, also 2^x, und meist sind die Texturen quadratisch. Somit würde nun, wenn ich das Bild stupide render, alle Bilder die nicht exakt quadratisch sind, in Höhe oder Breite gequetscht oder auseinandergezogen erscheinen (verzerrt).

Eigentlich wollte ich auf die (quadratischen) Texturen die Graphik im richtigen Seitenverhältnis größtmöglich zeichnen, und den -wegen Seitenverhältnis- ungenutzten Bereich dann zukünftig beim rendern einfach ignorieren (ich merke mir Breite und Höhe des "wirklichen" Bildes auf der Textur).

Jetzt habe ich das Problem, dass das Bild (verzerrt) auf der ganzen Textur vollflächig dargestellt wird (ich stauche ja die komplette Graphik aus egal welchem Seitenverhältnis z.B. auf 2048x1024). Andererseits kann man das auch positiv sehen: Eine Seite der Textur wäre ja eh vollständig genutzt worden (Breite oder Höhe), weil das Bild ja nicht auf beiden Achsen kleiner gemacht wird, als die Textur ist; wäre ja auch Quatsch. Auf der anderen Achse (also nun die verbleibende Höhe oder Breite) bleiben nun durch das nicht-beibehalten des Seitenverhältnisses mehr "Bildinformationen" von der ursprünglichen Graphik mit vll. 20'000px über, d.h. auf der kompletten Breite habe ich nun 2048 Pixel vom Originalbild über anstatt vielleicht nur 2000. Wenn man die Graphik später auf dem Bildschirm noch kleiner darstellt ist vielleicht die Skalierung einen Hauch feiner, als ohne die beispielhaften 48px? Es könnte natürlich auch sein, dass es um so schlimmer aussieht? Meinungen?

Wie könnte ich mit WICImage Stretchdraw durchführen, es hat ja keinen Canvas...?

@bytecook:
> Bild via LoadPng it ein TBitmap32 laden.
Ja was passiert denn, wenn es eine .tif oder .jpg-Datei ist. Geht dann LoadPNG trotzdem?

> Bild auf gewünschte Größe resamplen. (Am besten je nach Geschmack/Quali mit einem Lanczos Resampler). Die meisten Lowlevel-Routinen in Gr32 sind SSE2 optimiert, also seeeehr schnell...
Sollte irgendwie gehen.

> Bild auf GlBitmap via Scanline übertragen.
Da wird's schon wieder lustig, weil ich habe keine Ahnung, wie das gehen soll. TglBitmap2d bietet meines Wissens keine ScanLine an, weil es ja kein TBitmap oder so in dem Sinne ist. :(

EWeiss 16. Aug 2016 16:13

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Da wird's schon wieder lustig, weil ich habe keine Ahnung, wie das gehen soll. TglBitmap2d bietet meines Wissens keine ScanLine an, weil es ja kein TBitmap oder so in dem Sinne ist.
Ich denke schon..

Delphi-Quellcode:
function TglBitmap2D.GetScanline(Index: Integer): Pointer;


gruss

bytecook 16. Aug 2016 16:26

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zitat:

Zitat von EWeiss (Beitrag 1345006)
Zitat:

Da wird's schon wieder lustig, weil ich habe keine Ahnung, wie das gehen soll. TglBitmap2d bietet meines Wissens keine ScanLine an, weil es ja kein TBitmap oder so in dem Sinne ist.
Ich denke schon..

Delphi-Quellcode:
function TglBitmap2D.GetScanline(Index: Integer): Pointer;
gruss

Ja, es gibt sogar in einem meiner vorherigen Posts einen speziellen Link mit einer überladenen Methode für GR32 dazu. Siehe: https://delphigl.com/forum/viewtopic...hics32#p100074

CTOP!

Delphi-Quellcode:
     
    uses [...] {$IFDEF GLB_DELPHI}           Dialogs, Graphics, Types, GR32,    {$ENDIF} [...]
     
    function TglBitmapData.AssignFromBitmap(const aBitmap: TBitmap32): Boolean;
    var
      pSource: PColor32Array;
      pData, pTempData: PByte;
      Row, RowSize, TempWidth, TempHeight: Integer;
      IntFormat: TglBitmapFormat;
    begin
      result := false;
     
      if (Assigned(aBitmap)) then
      begin
        IntFormat := tfBGRA8ub4;
        TempWidth := aBitmap.Width;
        TempHeight := aBitmap.Height;
        RowSize   := TFormatDescriptor.Get(IntFormat).GetSize(TempWidth, 1);
        GetMem(pData, TempHeight * RowSize);
     
        try
          pTempData := pData;
          for Row := 0 to TempHeight -1 do
          begin
            pSource := aBitmap.Scanline[Row];
            if (Assigned(pSource)) then
            begin
              Move(pSource^, pTempData^, RowSize);
              Inc(pTempData, RowSize);
            end;
          end;
          SetData(pData, IntFormat, TempWidth, TempHeight);
          result := true;
        except
          if Assigned(pData) then
            FreeMem(pData);
          raise;
        end;
      end;
    end;

@berens: <Ja was passiert denn, wenn es eine .tif oder .jpg-Datei ist. Geht dann LoadPNG trotzdem?>
Nomen est omen?

Dafür hat man dann andere (in meinem Falle) <native> Importroutinen, oder nimmt halt das TWicImage. Ich bin ja davon ausgegangen, dass du korrekte Skalierung benötigst, da beim
Bitmap-Resizen große Verluste entstehen. Deswegen bevorzuge ich TBitmap32...

> Bild auf GlBitmap via Scanline übertragen.
<Da wird's schon wieder lustig, weil ich habe keine Ahnung, wie das gehen soll. TglBitmap2d bietet meines Wissens keine ScanLine an, weil es ja kein TBitmap oder so in dem Sinne ist>

Eine Bitmap hat nicht unbedingt was mit einem Bild zu tun - eher eine zweidimensionale Speichermöglichkeit. Eine TBitmap beherbergt ja auch nur ein zweidimensionales Bytearray für die Pixelinfo plus einen Header. Das Bildformat im Header bestimmt dann, wieviele Bytes pro Pixel verwendet werden. In unserem Falle bei Gr32 oder glBitmap intern immer 32 Bit, deswegen sind die Scanlines auch schön nett kompatibel :) Die greift übrigens dann direkt auf den Lowlevel Bildbuffer zu und ist entsprechend schnell.

Und zu den Texturen und deren Position: Dazu musst du dann ggf auch mal UV Mapping generell verstehen. Richard hat da ein paar nette Tutorials für Modo... https://vimeo.com/ondemand/uvmapping/168397312

Medium 16. Aug 2016 17:17

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Nur kurz am Rande: Power-of-two Texturen sind seit ca. DirectX 9 Zeiten keine Notwendigkeit mehr, so dass praktisch jede GPU die nicht älter als 10 Jahre ist damit klar kommen sollte.

berens 16. Aug 2016 19:37

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Zu "TFormatDescriptor" finde ich leider auch über Google nichts, bzw. dieser Thread hier ist schon auf Platz 3 der Suchergebnisse. Deshalb funktioniert die verlinkte Prozedur nicht. Was genau verwendest du da?

Ausserdem musste ich einiges anpassen:
Delphi-Quellcode:
function TglBitmap.AssignFromBitmap(const aBitmap: TBitmap32): Boolean;
var
  pSource: PColor32Array;
  pData, pTempData: PByte;
  Row, RowSize, TempWidth, TempHeight: Integer;
  IntFormat: TglBitmapInternalFormat;
begin
  result := false;

  if (Assigned(aBitmap)) then
  begin
    IntFormat := ifBGRA8; // hier
    TempWidth := aBitmap.Width;
    TempHeight := aBitmap.Height;
    RowSize := trunc(FormatGetSize(IntFormat) * TempWidth); // hier
    GetMem(pData, TempHeight * RowSize);

    try
      pTempData := pData;
      for Row := 0 to TempHeight -1 do
      begin
        pSource := aBitmap.Scanline[Row];
        if (Assigned(pSource)) then
        begin
          Move(pSource^, pTempData^, RowSize);
          Inc(pTempData, RowSize);
        end;
      end;
      SetDataPointer(pData, IntFormat, TempWidth, TempHeight); //hier
      result := true;
    except
      if Assigned(pData) then
        FreeMem(pData);
      raise;
    end;
  end;
end;
Das Ganze wird soweit korrekt dargestellt, die Frage ist halt, ob Alles rechnerisch bzw. Inhaltlich richtig ist. Leider muss man diese Prozedur direkt in die glBitmap-Unit einbinden, da einige interne Prozeduren verwendet werden, die von außerhalb der Unit nicht erreichbar sind (naja, die kann man ja kopieren oder über Deklarationen nach Außen verfügbar machen.


Hiermit kann ich nun die Graphik tatsächlich proportional verkleinert auf die Textur zeichnen, aktuell noch unschön mit tmpImage:
Delphi-Quellcode:
  bmp32 := TBitmap32.Create;
  bmp32.Width := TexturWidth;
  bmp32.Height := TexturHeight;
  bmp32.Canvas.StretchDraw(Rect(0, 0, ImageWidth, ImageHeight), tmpImage.Picture.Graphic);

  AssignFromBitmap(bmp32);
  GenTexture(True);
Bitte mal gegenlesen ob das insgesamt passt. Ich versuche dann später noch das "SmoothResize" aus den GR32-Units zu verwenden und dann hier noch zu posten.

-- Aktuell sieht es so gut aus! Sowohl .bmp und .png werden korrekt dargestellt, die anteilige Verwendung der Texturen scheint auch zu klappen, dass muss ich nochmal im Detail morgen testen. --

Übrigens hat der TWICImage-Loader lustigerweise bei dem Test mit einer 18'000 x 13'648 Graphik mit Exception versagt. TImage/TPicture hat jedoch problemlos geklappt. (Ich glaube Euch, dass die GR32-Loader besser/stabiler sind/sein können, aber wenn's so funktioniert... Warum nicht?)

Nochmal großes Dank an Alle!

bytecook 17. Aug 2016 09:16

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
> Das Ganze wird soweit korrekt dargestellt, die Frage ist halt, ob Alles rechnerisch bzw. Inhaltlich richtig ist. Leider muss man diese Prozedur direkt in die glBitmap-Unit einbinden, da einige interne Prozeduren verwendet werden, die von außerhalb der Unit nicht erreichbar sind (naja, die kann man ja kopieren oder über Deklarationen nach Außen verfügbar machen.

Hmmm - wenn die Routinen als Protected deklariert sind, kannst du ja mittels Trick drauf zugreifen...

Deklariere einfach in deiner Unit unter der Implementation folgenden Typ:

TGlBitmapAccess = Class(TGlBitmap);

danach kannst du mittels TGlBitmapAccess(Variable).methode Cast drauf zugreifen, oder, noch schöner, leite dir eine neue Klasse Klasse dafür ab und override / overloade die AssignFromBitmap Routine.

berens 17. Aug 2016 10:34

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
Ok, einen essenziellen Fehler habe ich gefunden, der letztentlich auch erklären würde, warum die komplette Aktion mit TBitmap vielleicht nicht funktioniert hat:
Wenn ich nun eine nicht-transparente Graphik lade, wird Diese unter keinen Umständen angezeigt. K.a. warum mir das gestern Abend nicht aufgefallen ist.

Ich habe nun den Loader wie folgt angepasst:
Delphi-Quellcode:
   bmp32 := TBitmap32.Create;
   bmp32.Width := TexturWidth;
   bmp32.Height := TexturHeight;
   bmp32.Canvas.StretchDraw(Rect(0, 0, ImageWidth, ImageHeight), tmpPicture.Graphic);
   if not tmpPicture.Graphic.Transparent then begin
     bmp32.ResetAlpha;
   end;

   AssignFromBitmap(bmp32);
Auf den ersten Blick scheint mir Graphic.Transparent tatsächlich zuverlässig zu sagen, ob die geladene Datei mit oder ohne Transparenzinformationen kommt. Zumindest ein .bmp, .png-mit und .png-ohne hat er korrekt unterschieden. Wenn nun also eine Graphik ohne Alpha kommt, wird mit .ResetAlpha jeder Pixel auf "sichtbar" gesetzt. Scheint aktuell zu klappen?

Für die Schönheit und den Seelenfrieden der Community habe ich übrigen endlich TImage durch TPicture ausgetauscht :stupid:

Jens01 17. Aug 2016 12:49

AW: Bilder [mit/ohne Transparenz] in universelles Format bringen
 
@berens
Bitte stell die Änderungen noch mal ins DelphiGL Forum bei GLBitmap ein. Hat sich da vlt etwas bei TglBitmap geändert?


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:55 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz