Alpha blending v Delphi - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



Delphi

Alpha blending v Delphi

12. června 2001, 00.00 | Chcete mít na formuláři v menu či toolbaru vyhlazené obrázky, které přesně pasují do barvy pozadí ? Přestaňte používat nevyhlazené obrázky - použijte kreslení s Alpha průhledností popsané v tomto článku !



Jistě ve svých aplikacích používáte obrázky. Ať již v menu, toolbaru, about boxu, no, zkrátka všude, kde to jen jde. V tom případě jste se již určitě setkali s problémem vyhlazení obrázků. Tedy to, že vytvoříte hezký obrázek například na bílém pozadí, s vyhlazenými kraji. Potom jej dáte na formulář do komponenty Image, Transparent nastavíte na true - a výsledek? Je to hrůza. Jak toto vyřešit, to si ukážeme v tomto článku.

Jako vždy i v tomto případě existuje více řešení. To první jistě napadne každého - dělat obrázky s nevyhlazenými okraji. U položek v menu nebo ikonek toolbaru to samozřejmě nevadí, obrázek 16x16 pixelů nevypadá ani kostrbatý špatně. Ale co u obrázků větších? Další možností je použít stejnou barvu pozadí jako je barva pozadí obrázku. To znamená nepoužívat windows barvy bsBtnFace, bsMenu..., které má každý uživatel nastavené podle sebe, ale natvrdo si nastavit barvu vlastní. Menu bude bílé a hotovo. To se ale nejspíš nebude zase prozměnu líbit každému uživateli. Nastaví si okna na červenou barvu a do toho mu bude svítit váš nádherný zelený toolbar. Přistoupíme k možnosti třetí - kreslení obrázku s různou průhledností.

Princip spočívá v tom, že máte jakýkoliv obrázek. Zjistíte si barvu pozadí (clBtnFace) a potom postupně zkopírujete každý pixel s různou průhledností do nového obrázku. To, co budete chtít vidět, bude bez průhlednosti, kolem krajů se bude průhlednost zvyšovat, až nakonec tam, kde chcete pozadí, bude průhlednost největší. To znamená, že z vašeho obrázku nebude vidět nic, pouze barva formuláře. Hodnoty alpha průhlednosti budeme mít uložené v dalším černobílém obrázku - čím tmavší bude pixel, tím bude větší průhlednost.
Než ale začneme s obrázkem, ukážeme si, jak se vůbec taková alpha průhlednost počítá.

Ukázku provedeme na dvou proměnných, které si definujeme například jako c1 a c2, výsledek našeho snažení uložíme do proměnné c3, vše bude typu TColor. Teď na vás vyvalím rovnici, podle které budeme počítat výslednou barvu, a v zápětí si vysvětlíme, na co kterou věc použijeme:


Budeme tedy míchat dvě barvy, které si nastavíme například na černou a bílou. Dále si definujeme proměnnou Alpha typu real, a nastavíme ji například na 0.5, tedy na 50%. Nesmíme však zapomenout, že musíme míchat každou složku zvlášť (r,g a b). Výsledek by měl vypadat nějak takto:


Zde si to můžete i sami vyzkoušet, pokud máte nainstalovený plugin Macromedia Flash 5:


Takže si to celé převedeme do delphi. Proměnné máme již nadefinované, ještě si je v proceduře Form1.OnCreate nastavíme:


 c1:=clBlack;
 c2:=clWhite;

 Alpha:=0.5;


Dále přidáme na formulář tři komponenty Shape a pojmenujeme je C1Shape, C2Shape a C3Shape. Následuje button, ke kterému vytvoříme proceduru OnClick. V ní si vypočítáme všechny tři složky výsledné barvy, a následně výslednou barvu vytvoříme. Abychom i něco viděli, přiřadíme barvy komponentám Shape. Zde je výsledný kód:


procedure TForm1.SpeedButton1Click(Sender: TObject);
var r,g,b:integer;
begin
 // result = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel

 r:= Trunc(alpha * GetRValue(C1) + (1-alpha) * GetRValue(C2));
 g:= Trunc(alpha * GetGValue(C1) + (1-alpha) * GetGValue(C2));
 b:= Trunc(alpha * GetBValue(C1) + (1-alpha) * GetBValue(C2));

 c3:=RGB(r,g,b);

 // draw it !!!
 C1Shape.Brush.Color:=c1;
 C2Shape.Brush.Color:=c2;
 C3Shape.Brush.Color:=c3;
end;


A zde je screenshot aplikace:


Jak jednoduché ! A ani tvorba složitějších věcí není o nic složitější, to mi věřte. Zůstaneme však ještě chvíli u tohoto - pro komponenty C1Shape a C2Shape vytvoříme proceduru OnMouseUp, ve které nasatvíme náhodnou barvu. Aby byla opravdu nádodná, musíme ještě v proceduře OnCreate zavolat funkci Randomize. zde je výpis procedury pro obě komponenty:


procedure TForm1.C1ShapeMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
 // nastavení náhodné barvy...
 C1:=RGB(Random(255),Random(255),Random(255));
 C2:=RGB(Random(255),Random(255),Random(255));

 C1Shape.Brush.Color:=c1;
 C2Shape.Brush.Color:=c2;

end;


Přidáme ještě komponentu TrackBar, které budeme nastavovat Alpha průhlednost. U ní si nastavíme tyto hodnoty: Min = 0, Max = 100, Position = 50, Frequency = 4. A vytvoříme si proceduru OnChange. V ní si nastavíme proměnnou Alpha podle hodnoty TrackBaru. Výsledný kód je opět zde:


procedure TForm1.TrackBar1Change(Sender: TObject);
begin
 Alpha:=TrackBar1.Position/100;

 Form1.SpeedButton1Click(nil);
end;


A pro změnu screenshot.


Aplikace je sice velice nedokonalá, ale k pochopení, jak pracuje Alpha průhlednost, snad stačí.

Alpha blending obrázků

Nyní se vrhneme na alpha průhlednost celých obrázků. Například do menu, nebo toolbaru. Zaprvé potřebujeme nějaký obrázek. Zde je:


Horší to bude s alpha obrázkem. Otevřeme si tento například ve Photoshopu a vybereme pozadí (bílou barvu) kouzelnou hůlkou. Dáme opačný výběr (Select / Inverse) a vytvoříme novou vrstvu kopírováním (Layer / New / Layer via Copy). Tuto vrstvu vyplníme černou barvou (Edit / Fill - Black). Trochu rozostříme (Filter / Blur / Gaussian Blur) a sloučíme dolů s pozadím (Layer / Merge Down). Vzniklou vrstvu invertujeme (Image / Adjust / Invert) a můžeme uložit. Výsledek by měl být podobný tomuto:


Na formulář si přidáme 3 komponenty Image, a pojmenujeme je SourceImage, AlphaImage a FinishImage. Do prvních dvou nahrajeme naše vytvořené obrázky. Přidáme ještě SpeedButton a k němu vytvoříme proceduru OnClick. V ní smícháme první obrázek s barvou formuláře podle průhlednosti uložené v obrázku AlphaImage. Zde je celý zdrojový kód. Je ještě trochu upravený - pokud je pixel v Alpha obrázku černé barvy, zkopírujeme rovnou barvu pozadí, pokud je pixel bílý, zkopírujeme pixel prvního obrázku. Ostatní případy pracně počítáme:


procedure TForm1.SpeedButton2Click(Sender: TObject);
var x,y,r,g,b:integer;
    cs,ca:TColor;
    Alfa:real;
begin
 FinishImage.Canvas.Pixels[0,0]:=Form1.Color;
 ca:=FinishImage.Canvas.Pixels[0,0];

 FinishImage.Width:=SourceImage.Width;
 FinishIMage.Height:=SourceImage.Height;

 Application.ProcessMessages;

 for x:= 0 to SourceImage.Width do
  begin
   for y:= 0 to SourceImage.Height do
    begin
     // když je černá, tak nemáme co počítat
     if AlphaImage.Canvas.Pixels[x,y] = clBlack then
      begin
       FinishImage.Canvas.pixels[x,y]:=ca;
      end
     // u bílé taky
     else if AlphaImage.Canvas.Pixels[x,y] = clWhite then
      begin
       FinishImage.Canvas.Pixels[x,y]:=SourceImage.Canvas.Pixels[x,y];
      end
     // a to ostatní musíme spočítat
     else
      begin
       // Alpha podle AlphaImage...    od 0 do 1
       Alfa:=GetGValue(AlphaImage.Canvas.Pixels[x,y])/255;

       cs:=SourceImage.Canvas.Pixels[x,y];

       r:= Trunc(alfa * GetRValue(Cs) + (1-alfa) * GetRValue(Ca));
       g:= Trunc(alfa * GetGValue(Cs) + (1-alfa) * GetGValue(Ca));
       b:= Trunc(alfa * GetBValue(Cs) + (1-alfa) * GetBValue(Ca));

       FinishImage.Canvas.Pixels[x,y]:=RGB(r,g,b);
      end;

     Application.ProcessMessages;

    end;
  end;
end;


Výsledek je opravdu vyhlazený obrázek na pozadí formuláře:



Pokud máte nainstalovaný plugin Flash 5, můžete si zde znovu zkusit, jak vypadá obrázek po zvětšení, a přesvědčit se, že opravdu do pozadí hezky pasuje:


Možnosti tohoto efetku jsou velice rozmanité. Pokud zůstaneme u obrázků menu nebo popup menu, je dobré vytvořit obrázky pouze jednou, a poté je uložit do souboru na disk. Také je dobré si uložit barvu pozadí a při spouštění aplikace kontrolovat, jestli se náhodou barva nezměnila - pokud ano, vygenerujeme obrázky znovu, pokud ne, nahrajeme ty staré z disku a ušetříme tak hodně času. U obrázků můžete pomocí Alpha obrázku dělat i různé stíny, představivosti se meze nekladou...

Nakonec pár slov o optimalizaci. Jak jste si jistě všimli, je použitá technika dosti pomalá. Není to tím, že používáme Delphi, ale tím, že používáme obecný tvar rovnice, Alpha typu real a jiné věci, které výpočet značně zpomalují. Úplně nejlepší řešení je provést výpočet v assembleru, ovšem ten není mojí silnou stránkou, a proto nechám prostor pro ostatní. Své zoptimalizované kódy můžete uveřejnit v diskuzi pod článkem.

Bonus - částečně optimalizovaná procedura

Bonus proto, že zde nic takového být nemělo, ale nakonec jsem si řekl, že vám to alespoň trochu ulehčím. Zde máte kód částečně optimalizované procedury, Alpha je typu byte, tudíž může nabývat hodnot od 0 do 255, a i rovnice je v trochu hezčím tvaru. Pokud odstraníte vyřizování zpráv windows (Application.ProcessMessage), budou si sice windows myslet, že se vaše aplikace sekla, ale výpočet bude o poznání kratší. Program s obrázkem čtyřlístku (jak je velký, se můžete podívat na obrázku úplně nahoře) mi vytvoří na mém PC (Athlon 1GHz) výsledný obrázek ani ne za 1 sekundu, což je myslím docela dobrý čas. Zde je již procedura, a tím pádem i konec článku.


procedure TForm1.SpeedButton1Click(Sender: TObject);
var x,y,r,g,b:integer;
    cs,ca:TColor;
    Alfa2:byte;
begin
 FinishImage.Canvas.Pixels[0,0]:=Form1.Color;
 ca:=FinishImage.Canvas.Pixels[0,0];

 FinishImage.Width:=SourceImage.Width;
 FinishIMage.Height:=SourceImage.Height;

 Application.ProcessMessages;

 for x:= 0 to SourceImage.Width do
  begin
   for y:= 0 to SourceImage.Height do
    begin

     if AlphaImage.Canvas.Pixels[x,y] = clBlack then    
// když je černá, tak nemáme co počítat
      begin
       FinishImage.Canvas.pixels[x,y]:=ca;
      end

     else if AlphaImage.Canvas.Pixels[x,y] = clWhite then // u bílé taky
      begin
       FinishImage.Canvas.Pixels[x,y]:=SourceImage.Canvas.Pixels[x,y];
      end

     else
      begin
       Alfa2:=GetGValue(AlphaImage.Canvas.Pixels[x,y]);

       cs:=SourceImage.Canvas.Pixels[x,y];

       // result =  ( ALPHA * ( srcPixel - destPixel ) ) / 256 + destPixel
       r:= Trunc(alfa2 * (GetRValue(Cs) - GetRValue(Ca))/256 + GetRValue(Ca));
       g:= Trunc(alfa2 * (GetGValue(Cs) - GetGValue(Ca))/256 + GetGValue(Ca));
       b:= Trunc(alfa2 * (GetBValue(Cs) - GetBValue(Ca))/256 + GetBValue(Ca));

       FinishImage.Canvas.Pixels[x,y]:=RGB(r,g,b);
      end;

     //Application.ProcessMessages; - opravdu to je rychlejší...

    end;
  end;

end;


Download
ZDE si můžete stáhnout dnes vytvořený program a jeho zdrojáky (192 kB)

Tématické zařazení:

 » Rubriky  » Delphi  

 » Rubriky  » Windows  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: