Einzelnen Beitrag anzeigen

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, 08: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