Autor Téma: jak vytvořit průhledný obrázek v Delphi?  (Přečteno 601 krát)

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
jak vytvořit průhledný obrázek v Delphi?
« kdy: 13-11-2022, 10:03:39 »
Zase se trápím s průhledností v Delphi. Dokážete mi někdo poradit, jak v Delphi udělat to, co umí každý grafický editor - vytvořit obrázek, který bude mít průhledné pozadí a na něm cokoliv nakresleného (text, shape, to je jedno). Je mi skoro jedno, jestli to bude Bitmap32, Png, TBitmap s formátem pf32bit.
Co jsem zatím zkoušel:
  • PNG - umím vytvořit průhledné, ale když na něj nakreslím text pomocí winapi funkce DrawText(), blenduje se to s černou, nikoliv s alfakanálem.
  • TBitmap32 - nedokážu vytvořit průhlednost vůbec. Zkoušel jsem třeba jen udělat prázdnou průhlednou bitmapu bez ničeho, metodou Clear(TColor($00000000)) a SaveTo('test.bmp'), ale to prostě nikdy neudělá průhlednou bitmapu, i když je 32-bitová. Vždy je tam černé pozadí.
Asi by mi stačilo, kdybych věděl alespoň jak udělat TBitmap32 opravdu průhledný (vynulovaný alfakanál), pak už by snad mohly fungovat jeho kreslící metody.
nil

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #1 kdy: 13-11-2022, 10:58:27 »
Tak narychlo...

Kód: Delphi [Vybrat]
  1. program Test90;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. {$R *.res}
  6.  
  7. uses
  8.   System.SysUtils,
  9.   GdipApi,
  10.   GdipObj,
  11.   GdipUtil,
  12.   System.UIConsts;
  13.  
  14.  
  15. procedure Test;
  16.  var Bmp: TGPBitmap;
  17.      Gr: tGPGraphics;
  18.      Pen: tGPPen;
  19.      Encoder: TGUID;
  20.      Font: tGPFont;
  21.      Brush: tGPSolidBrush;
  22.  begin
  23.  Bmp:=TGPBitmap.Create(300, 300);
  24.  try
  25.   Gr:=TGPGraphics.Create(bmp);
  26.   try
  27.    Gr.Clear(claNull);
  28.    // line
  29.    Pen:=tGPPen.Create(claGreen, 3);
  30.    try
  31.     Gr.DrawLine(Pen, 10, 10, 290, 290);
  32.    finally
  33.     FreeAndNil(Pen);
  34.    end;
  35.    // text
  36.    Font:=tGPFont.Create('Arial', 15);
  37.    try
  38.     Brush:=tGPSolidBrush.Create(claBlue);
  39.     try
  40.      Gr.DrawString('Pokus', 5, Font, MakePoint(10.0, 150.0), Brush);
  41.     finally
  42.      FreeAndNil(Brush);
  43.     end;
  44.    finally
  45.     FreeAndNil(Font);
  46.    end;
  47.   finally
  48.    FreeAndNil(Gr);
  49.   end;
  50.   if GetEncoderClsid('image/png', Encoder) <> -1
  51.    then Bmp.Save('Test.png', Encoder)
  52.  finally
  53.   FreeAndNil(bmp);
  54.  end;
  55. end;
  56.  
  57. begin
  58.  try
  59.   Test;
  60.  except
  61.   on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  62.  end;
  63. end.
  64.  

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #2 kdy: 13-11-2022, 11:19:23 »
Jinak tady už jsem Ti kdysi dával ukázku s Direct2D:
https://forum.delphi.cz/index.php/topic,17673.msg112882.html#msg112882


Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #3 kdy: 14-11-2022, 14:05:43 »
Moc děkuji za odpověď.

GDI+: DrawString() mi produkuje nevzhledné výsledky, zřejmě je tam potřeba ještě něco doladit.
Direct2D: Díky za připomenutí, mám fakt hosipa :/ Kvalita je to slušná, ale stejně to není tak pěkné, jako prostý DrawText() na canvas.

O co se vlastně snažím...chci udělat animaci změny Caption u nějakého prvku, kde je pod textem gradient. Proto se snažím si ten text vyrenderovat do nějaké průhledné bitmapy, abych pak mohl v té animaci s tím různě hýbat. Škoda, že DrawText() neumí kreslit jakoby do Viewportu, pak bych mohl hýbat jen s tím Viewportem.
nil

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #4 kdy: 14-11-2022, 14:23:32 »
Excellent
Rated 1 time
Nenastavil jsi tam špatně barvu pro ten Brush?
U mě to udělá takovýhle výsledek:


Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #5 kdy: 14-11-2022, 14:25:26 »
Zvětšené to vypadá takhle (snímek obrazovky z Gimpu):

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #6 kdy: 14-11-2022, 14:31:00 »
Mám to takhle:
Kód: Delphi [Vybrat]
  1. var Bmp:=TGPBitmap.Create(300, 300);
  2.  try
  3.   var Gr:=TGPGraphics.Create(bmp);
  4.   try
  5.    Gr.Clear(claNull);
  6.    // line
  7.    var Pen:=tGPPen.Create(claGreen, 3);
  8.    try
  9.     Gr.DrawLine(Pen, 10, 10, 290, 290);
  10.    finally
  11.     FreeAndNil(Pen);
  12.    end;
  13.    // text
  14.    var Font:=tGPFont.Create(GetDC(0), FHeaderFont.Handle);
  15.    try
  16.     var Brush:=tGPSolidBrush.Create(claBlue);
  17.     try
  18.      Gr.DrawString('Pokus', 5, Font, MakePoint(10.0, 150.0), Brush);
  19.     finally
  20.      FreeAndNil(Brush);
  21.     end;
  22.    finally
  23.     FreeAndNil(Font);
  24.    end;
  25.   finally
  26.    FreeAndNil(Gr);
  27.   end;
  28.   var Encoder: TGUID;
  29.   if GetEncoderClsid('image/png', Encoder) <> -1
  30.    then Bmp.Save('c:\tmp\Test.png', Encoder)
  31.  finally
  32.   FreeAndNil(bmp);
  33.  end;
  34.  
nil

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #7 kdy: 14-11-2022, 14:35:36 »
V podstatě jediná změna oproti Tvému kódu je tam ten Font, ale i když použiju 100% Tvého kódu (t.j. vrátím zpět Font:=tGPFont.Create('Arial', 15);), tak je výsledek stejný.
nil

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #8 kdy: 14-11-2022, 14:41:08 »
Můžeš sem prosím dát přímo ten .png, který z programu vypadne, bez jeho úprav dalším editorem?

Jinak možná se musí ještě někde něco nastavit, ale GDIP nepoužívám, tak nevím...

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #9 kdy: 14-11-2022, 14:45:33 »
Jasně, tady je.
nil

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #10 kdy: 14-11-2022, 14:53:05 »
Pomohlo by před volání DrawString přidat tohle?
Kód: Delphi [Vybrat]
  1. Gr.SetTextRenderingHint(TextRenderingHintAntiAlias);

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #11 kdy: 14-11-2022, 15:30:07 »
Excellent
Rated 1 time
Tady je upravená procedura, zkouším tam nastavit lepší kvalitu i pro čáry:

Kód: Delphi [Vybrat]
  1. procedure Test;
  2.  var Bmp: TGPBitmap;
  3.      Gr: tGPGraphics;
  4.      Pen: tGPPen;
  5.      Encoder: TGUID;
  6.      Font: tGPFont;
  7.      Brush: tGPSolidBrush;
  8.      Text: string;
  9.  begin
  10.  Bmp:=TGPBitmap.Create(300, 300, PixelFormat32bppARGB);
  11.  try
  12.   Gr:=TGPGraphics.Create(bmp);
  13.   try
  14.    Gr.Clear(claNull);
  15.    Gr.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
  16.    Gr.SetSmoothingMode(SmoothingModeAntiAlias);
  17.    Gr.SetCompositingQuality(CompositingQualityHighQuality);
  18.    // line
  19.    Pen:=tGPPen.Create(claGreen, 3);
  20.    try
  21.     Gr.DrawLine(Pen, 10, 10, 290, 150);
  22.     Gr.DrawLine(Pen, 10, 290, 290, 150);
  23.    finally
  24.     FreeAndNil(Pen);
  25.    end;
  26.    // text
  27.    Text:='Pokus';
  28.    Font:=tGPFont.Create('Arial', 15);
  29.    try
  30.     Brush:=tGPSolidBrush.Create(claBlue);
  31.     try
  32.      Gr.DrawString(Text, length(Text), Font, MakePoint(10.0, 150.0), Brush);
  33.     finally
  34.      FreeAndNil(Brush);
  35.     end;
  36.    finally
  37.     FreeAndNil(Font);
  38.    end;
  39.   finally
  40.    FreeAndNil(Gr);
  41.   end;
  42.   if GetEncoderClsid('image/png', Encoder) <> -1
  43.    then Bmp.Save('Test.png', Encoder)
  44.  finally
  45.   FreeAndNil(Bmp);
  46.  end;
  47. end;

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #12 kdy: 14-11-2022, 15:41:58 »
Excellent
Rated 1 time
Jo, rozhodně to pomohlo. Díky. Teď už je to antialisované proti průhlednému pozadí. Ještě porovnám kvalitu oproti DrawText() a uvidím, jestli by se to dalo použít. Ještě jednou díky za čas, který jsi mi věnoval.
nil

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #13 kdy: 14-11-2022, 16:49:24 »
Tak kvalita výstupu je s GDI+ (po vandrovnikových úpravách nastavení) velmi blízká kvalitě winapi.DrawText(), což je super. Jen je s tím teda voser, že se kvůli jednoduchému vykreslení textu musí navytvářet hromada objektů, pak pro uložení GPBitmap do PNG je potřeba StreamAdapter, no je to jako kanón na vrabce...ale funkční.
nil

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1435
  • Karma: 52
    • Verze Delphi: 10.3
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #14 kdy: 14-11-2022, 16:56:23 »
Tak jestli to potřebuješ jen pro zobrazení na monitoru, dá se těch objektů dělat méně, protože existuje i
Kód: Delphi [Vybrat]
  1. constructor TGPGraphics.Create(hdc: HDC);

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 3182
  • Karma: 111
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #15 kdy: 14-11-2022, 17:12:43 »
O co se vlastně snažím...chci udělat animaci změny Caption u nějakého prvku, kde je pod textem gradient. Proto se snažím si ten text vyrenderovat do nějaké průhledné bitmapy, abych pak mohl v té animaci s tím různě hýbat. Škoda, že DrawText() neumí kreslit jakoby do Viewportu, pak bych mohl hýbat jen s tím Viewportem.

Opet pripominam Skia4Delphi
Embarcadero MVP - Czech republic

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #16 kdy: 14-11-2022, 21:26:05 »
vandrovnik: bohužel se nejedná o kreslení přímo na obrazovku, potřebuji si vykreslit jenom samotný text do bitmapy (resp. Png), abych ho pak mohl relativně snadno animovat na standardní GDI Canvas vizuální komponenty. Animuji nějaký Caption a chci například, aby nový Caption vyšoupnul ten původní třeba shora dolů, nebo zleva doprava, to je jedno. Jelikož jsem nepřišel na žádný způsob, jak k tomu využít standardní windowsáckou api funkci DrawText(), tak to řeším takhle.

Radek Červinka: Skia je úchvatná, ale na legacy vcl věci to afaik moc není, leda tak začít úplně od znova. V tomhle případě například odvozuji novou komponentu od TCategoryPanel (udělal jsem z něj standalone panel, který umí Expand a Collapse, což je pro GUI celkem užitečná fíčurka).
nil

Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 377
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4.2, D11
Re:jak vytvořit průhledný obrázek v Delphi?
« Odpověď #17 kdy: 16-11-2022, 00:24:01 »
Jo tak další megaopruz s GDI+ bude vykreslení tabstopů (09h). V GDI+ se totiž musí nastavit pozice každého tabstopu zvlášť a relativně k předchozímu. Je to asi prima na vykreslování tabulek, ale naprosto na zabití pro vykreslení tabu v normálním textu. Takže to vidím na rozsekání stringu podle tabstopů, změření velikosti pro vykreslení, dopočítání pozic tabstopů, nastavení StringFormat.SetTabStops a pak teprve vlastní vykreslení stringu.
nil