AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia GDI+ Bilddrehung mit Transparenz

GDI+ Bilddrehung mit Transparenz

Ein Thema von Duck-of-Devastation · begonnen am 25. Feb 2019 · letzter Beitrag vom 28. Feb 2019
Antwort Antwort
Duck-of-Devastation

Registriert seit: 31. Mär 2010
11 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#1

GDI+ Bilddrehung mit Transparenz

  Alt 25. Feb 2019, 09:44
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:
Tmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette);
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).


Kann mir da jemand auf die Sprünge helfen?


VG

Sven
Sven
  Mit Zitat antworten Zitat
Duck-of-Devastation

Registriert seit: 31. Mär 2010
11 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#2

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 09:21
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:
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;
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.

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
Sven
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.334 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 10:39
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.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli (27. Feb 2019 um 11:28 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sherlock
Sherlock

Registriert seit: 10. Jan 2006
Ort: Offenbach
3.748 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 12:21
Hier mal eine Alternative zu GDI+, die ich persönlich übersichtlicher finde: WIC.

Delphi-Quellcode:
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;
Da muss sicherlich noch irgendwie etwas aufgeräumt werden. Aber es läuft.

Sherlock
Oliver
Geändert von Sherlock (Morgen um 16:78 Uhr) Grund: Weil ich es kann

Geändert von Sherlock (28. Feb 2019 um 07:51 Uhr) Grund: eine Variable war doch nicht überflüssig
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.055 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 16:49
Geändert von Sherlock (Heute um 13:29 Uhr) Grund: eine Variable war überflüssig
Wenn du jetzt myRotate rausgekürzt hast, dann war das zuviel.
  Mit Zitat antworten Zitat
Duck-of-Devastation

Registriert seit: 31. Mär 2010
11 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#6

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 17:41
Moin!

Vielen Dank für Eure Vorschläge!

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.
Das ist es leider nicht, die Ausgangsbitmap hat einen "echten" Alphakanal (mit semitransparenten Pixeln).

Hier mal eine Alternative zu GDI+, die ich persönlich übersichtlicher finde: WIC.
Sherlock
Das probier ich mal aus, vielen Dank! Wobei ich mich ursprünglich für GDI+ entschieden hatte, weil das Antialiasing der rotierten Bitmap dort zu schön aussieht. Ich hatte zuerst die Rotation mit PlgBlt umgesetzt, aber das sind nach der Rotation so hässliche "Kanten" im rotierten Bild zu sehen. WIC hatte ich bislang allerdings noch gar nicht auf der Rechnung...

VG
Sven
Sven
  Mit Zitat antworten Zitat
EWeiss
(Gast)

n/a Beiträge
 
#7

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 19:26
Zitat:
Das ist es leider nicht, die Ausgangsbitmap hat einen "echten" Alphakanal (mit semitransparenten Pixeln).
Verstehe das Problem nicht.

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.

GDI++ ..

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

Geändert von EWeiss (11. Jul 2019 um 16:55 Uhr)
  Mit Zitat antworten Zitat
Duck-of-Devastation

Registriert seit: 31. Mär 2010
11 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#8

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 19:41
Verstehe das Problem nicht.

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.

gruss
Hi...

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

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
Sven
  Mit Zitat antworten Zitat
EWeiss
(Gast)

n/a Beiträge
 
#9

AW: GDI+ Bilddrehung mit Transparenz

  Alt 27. Feb 2019, 19:47
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:
aus einer ImageList, ich lade es also nicht direkt mit GDI+
Und das Bitmap wenn du es schon hast hat kein Handle?
Dann reicht ein GdipCreateBitmapFromHBITMAP..

Zitat:
hat die TGPBitmap Tmp nachher aber das Pixelformat 32bppRGB
Nicht schon vorher, also in der ImageList?
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:
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;
gruss

Geändert von EWeiss (27. Feb 2019 um 20:52 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sherlock
Sherlock

Registriert seit: 10. Jan 2006
Ort: Offenbach
3.748 Beiträge
 
Delphi 11 Alexandria
 
#10

AW: GDI+ Bilddrehung mit Transparenz

  Alt 28. Feb 2019, 07:48
Geändert von Sherlock (Heute um 13:29 Uhr) Grund: eine Variable war überflüssig
Wenn du jetzt myRotate rausgekürzt hast, dann war das zuviel.
Gnarf!
In der Tat. Ich hab die im Endergebnis 10 Minuten später nicht mehr gesehen und dachte "weg damit!"

Sherlock
Oliver
Geändert von Sherlock (Morgen um 16:78 Uhr) Grund: Weil ich es kann
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:33 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