Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Další funkce pro práci s grafikou.

20.1.2002

V minulém pokračování jsme si ukázali ten nejjednodušší způsob, jak vykreslit bitmapu. V tomto pokračování si ukážeme některé další funkce pro kreslení grafických objektů, které mají více možností. Podívejme se na výsledek dnešního ukázkového příkladu. Je zde opět vykreslena bitmapa (jiným způsobem než v minulém článku) a dále ikona v různých režimech zobrazení, a nakonec ikona o velikosti 48x48.

win-api-20

Pokud chceme například na grafický objekt (včetně výpisu textu) aplikovat efekty jako je například "disabled", tedy vykreslení objektu tak jak bývá obvyklé u zakázaných prvků, jako tlačítka na panelu nástrojů nebo texty i obrázky u zakázaných položek menu, můžeme použít funkci DrawState:

BOOL DrawState(
  HDC hdc,                     // handle kontextu zařízení
  HBRUSH hbr,                  // handle štětce
  DRAWSTATEPROC lpOutputFunc,  // callback funkce
  LPARAM lData,                // informace o grafickém objektu
  WPARAM wData,                // další info o objektu
  int x,                       // horizontální souřadnice
  int y,                       // vertikální souřadnice
  int cx,                      // šířka
  int cy,                      // výška
  UINT fuFlags                 // typ objektu a další volby
);

Tato funkce nám například v případě bitmapy umožňuje její přímé vykreslení bez nutnosti vytvářet pro ni kontext zařízení. Stačí nám pouze mít platný handle bitmapy. Kresleným objektem může být kromě bitmapy také ikona, kurzor nebo text. V parametru fuFlags máme pak možnost například specifikovat tyto parametry:

  • DSS_DISABLED - obrázek nebo text je vykreslen ve stavu "zakázán"
  • DSS_MONO - objekt je vykreslen monochromaticky s tím, že můžeme specifikovat handle štětce (parametr hbr) určující tento monochromatický štětec.

Při použití této funkce navíc nemusíme uvádět rozměry kresleného objektu, pokud uvedeme hodnoty 0, funkce sama vypočítá velikost objektu a vykreslí jej celý. Typ grafického objektu určíme v parametru fuFlags, který může nabývat například následujících hodnot:

  • DST_BITMAP - objektem je bitmapa. Handle bitmapy je v parametru lData.
  • DST_ICON - Ikona, handle ikony je lData.
  • DST_TEXT - Vypisujeme text. Parametr lData je adresa textového řetězce.
  • DST_PREFIXTEXT - Text může obsahovat akcelerátory, tedy znak '&' je považován za příkaz k podtržení následujícího znaku tak, jak se to používá u zkratkových kláves nabídek.

Dále si řekněme o univerzálnější a šikovnější funkci pro načítání bitmap, ikon a kurzorů, kterou je LoadImage:

HANDLE LoadImage(
  HINSTANCE hinst,   // handle  instance
  LPCTSTR lpszName,  // grafický objekt
  UINT uType,        // typ grafického objektu
  int cxDesired,     // požadovaná šířka
  int cyDesired,     // požadovaná výška
  UINT fuLoad        // volby načtení
);

Tato funkce umí například načítat všechny uvedené typy grafických objektů nejen ze zdrojů, ale také ze souboru. Uveďme si některé volby, které můžeme specifikovat v parametru fuLoad:

  • LR_LOADFROMFILE - načte objekt ze souboru místo ze zdrojů
  • LR_LOADTRANSPARENT - všechny pixely, které mají stejnou barevnou hodnotu jako první pixel (0,0) jsou nahrazeny barvou pozadí okna.
  • LR_MONOCHROME - načte obrázek černobíle

Dále máme možnost také u ikon a kurzorů specifikovat vlastní rozměry. Lze tak například načíst ikonu nebo kurzor rozměru 48x48, což je zejména ve Windows XP jeden z běžných rozměrů obrázku obsažených v ikoně.

V souvislosti s kreslením ikon či kurzorů si řekněme ještě o jedné funkci, kterou je DrawIconEx:

BOOL DrawIconEx(
  HDC hdc,                   // handle kontextu zařízení
  int xLeft,                 // x-ová souřadnice
  int yTop,                  // y-ová souřadnice
  HICON hIcon,               // handle ikony (kurzoru)
  int cxWidth,               // šířka ikony
  int cyWidth,               // výška
  UINT istepIfAniCur,        // pro animovaný kurzor index obrázku
  HBRUSH hbrFlickerFreeDraw, // brush pozadí, může být NULL
  UINT diFlags               // volby kreslení
);

Jak je patrné z parametrů, můžeme zde například vykreslit libovolný "krok" v animovaném kurzoru. Můžeme samozřejmě také specifikovat požadované výsledné rozměry obrázku, lze tak tedy ikonu či kurzor kreslit roztažené do libovolné velikosti.

Další možnosti všech uvedených funkcí naleznete samozřejmě v dokumentaci a můžete tedy sami dále experimentovat.

Nyní se podívejme na kód handleru WM_PAINT dnešního doprovodného příkladu:

void OnPaint(HDC hdc)
{
  HBITMAP hBitmap = (HBITMAP)LoadImage(hInst, "eso.bmp", IMAGE_BITMAP, 0,0,
      LR_DEFAULTSIZE | LR_LOADFROMFILE);
  DrawState(hdc, NULL, NULL, (LPARAM)hBitmap, 0, 10, 10, 0, 0, DST_BITMAP);
  DeleteObject(hBitmap);
  DrawState(hdc, NULL, NULL,
    (LPARAM)LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)),
    0, 190, 10, 0, 0, DST_ICON);
  HBRUSH hBrush = CreateSolidBrush(0x00A0A0FF);
  DrawState(hdc, hBrush, NULL, (LPARAM)LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)),
    0, 190, 50, 0, 0, DST_ICON | DSS_MONO);
  DeleteObject(hBrush);
  DrawState(hdc, NULL, NULL,
    (LPARAM)LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)),
    0, 190, 90, 0, 0, DST_ICON | DSS_DISABLED);
  HICON hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON2),
    IMAGE_ICON, 48,48, LR_SHARED);
  DrawIconEx(hdc, 190, 130, hIcon, 0, 0, 0, NULL, DI_NORMAL);
}

Nejprve je zde ukázka načtení bitmapy ze souboru a její vykreslení (bez použití dalšího kontextu zařízení) pomocí funkce DrawState. Stejnou funkcí je také vykreslena ikona, v prvním případě v běžném stavu, ve druhém monochromaticky, přičemž je použit vlastní štětec definující monochromatickou barvu, a dále tatáž ikona jako "zakázaná". Na konec je nakreslena ikona obsahující obrázek velikosti 48 x 48 tak, že je načten právě tento obrázek. O tom, že je vybrán právě tento a nikoliv 32x32, který by byl roztažen, se lze přesvědčit z parametrů funkce DrawIconEx, kde jsou rozměry uvedeny jako 0, a tedy funkce použije velikost obrázku, který je načten.

Nakonec ještě důležitá poznámka pro spuštění projektu. Bitmapa "eso.bmp" musí být v adresáři, kde běží program, tedy obvykle \debug nebo \release. Já jsem ji přidal mezi zdrojové kódy a další soubory projektu. Samozřejmě můžete ve funkci LoadImage uvést libovolnou plnou cestu k vlastní bitmapě.

Doprovodný projekt je ke stažení zde: win_api_20.zip