Trochu více o ikonách a kurzorech v C++

V tomto článku si řekneme trochu více o možnostech použití ikon a kurzorů a o jejich vzájemném nahrazení.

Ve většině případů programátor používá běžné funkce LoadIcon a LoadCursor, které načítají tyto typy zdrojů ve standardní velikosti a barevné hloubce. Slovem „standardní“ je myšleno systémové nastavení, tj. velikost ikony a počet barev použitých pro zobrazování ikon. Většina ikon a kurzorů ve skutečnosti obsahuje více "obrázků" pro různá nastavení zobrazovacího zařízení, funkce LoadIcon nám automaticky vybere ten "vhodný". Podívejme se nejprve jako obvykle na screen-shot ukázkové aplikace a řekněme si, jak na to:

V horní části dialogu jsou 4 prvky typu "STATIC", jimiž lze nastavit ikonu (ve formě handle HICON), kterou mají zobrazovat. Pokud chceme specifikovat více možností načtení ikony, použijeme funkci

HANDLE LoadImage(
  HINSTANCE hinst, // handle modulu
  LPCTSTR lpszName, // jméno nebo identifkátor obrázku
  UINT uType, // typ obrázku
  int cxDesired, // požadovaná délka
  int cyDesired, // požadovaná výška
  UINT fuLoad // volby načtení
);

Tato funkce je univerzálním rozšířením funkcí LoadIcon, LoadCursor a LoadBitmap. Dává nám více možností, například načítání grafiky ze souboru. Stručně k jednotlivým parametrům:

  • hinst - handle modulu obsahujícího zdroje. V případě, že načítáme ze souboru, hodnota musí být NULL.
  • lpszName - jméno nebo identifikátor ve zdrojích (resources) nebo jméno grafického souboru.
  • uType - typ grafiky
    • IMAGE_BITMAP - načítáme bitmapu
    • IMAGE_CURSOR – načítáme kurzor
    • IMAGE_ICON – načítáme ikonu
  • cxDesired - požadovaná délka načteného obrázku
  • cyDesired - požadovaná výška obrázku
  • fuLoad - volby načtení. Úplný popis naleznete v dokumentaci, zde jen o některých
    • LR_DEFAULTSIZE - použije výchozí rozměry ze systémové metriky a ignoruje hodnoty cxDesired a cyDesired
    • LR_LOADFROMFILE - načítáme ze souboru s názvem, který je dán parametrem lpszName
    • LR_LOADTRANSPARENT - všechny pixely s barvou stejnou jako první pixel (souřadnice 0,0) jsou nahrazeny systémovou barvou oken (COLOR_WINDOW)
    • LR_MONOCHROME - načítáme černobíle
    • LR_SHARED - handle je sdílené, pokud načítáme opakovaně stejný obrázek. Pokud tuto volbu neuvedeme, měli bychom "po použití" uvolnit paměť příslušnou funkcí (pro bitmapu DeleteObject, pro ikonu DestroyIcon, pro kurzor DestroyCursor). K tomu ještě poznámku, že toto sdílení je u funkcí LoadIcon a LoadCursor „nastaveno natvrdo“, proto při jejich použití žádné funkce type Deletexxxxx nevoláme.

    Nyní se podívejme na praktickou demonstraci této funkce. V horní části dialogu jsou čtyři prvky "STATIC" (v editoru zdrojů jako "Picture", kde jako "Type" zvolíme Icon), kterým jsou ve funkci InitDialog přiřazeny handle ikon získané uvedenou funkcí:

    // … kód funkce InitDialog
    GetDlgItem(IDC_ICON16)->SendMessage(STM_SETICON,
        (WPARAM)(HICON)LoadImage(AfxGetInstanceHandle(),
          MAKEINTRESOURCE(IDI_ICON1),
          IMAGE_ICON, 16, 16, LR_SHARED));
    GetDlgItem(IDC_ICON32)->SendMessage(STM_SETICON,
        (WPARAM)(HICON)LoadImage(AfxGetInstanceHandle(),
          MAKEINTRESOURCE(IDI_ICON1),
          IMAGE_ICON, 32, 32, LR_SHARED));
    GetDlgItem(IDC_ICON48)->SendMessage(STM_SETICON,
        (WPARAM)(HICON)LoadImage(AfxGetInstanceHandle(),
          MAKEINTRESOURCE(IDI_ICON1),
          IMAGE_ICON, 48, 48, LR_SHARED));
    GetDlgItem(IDC_ICON_CUSTOM)->SendMessage(STM_SETICON,
        (WPARAM)(HICON)LoadImage(AfxGetInstanceHandle(),
          MAKEINTRESOURCE(IDI_ICON2),
          IMAGE_ICON, 128, 96, LR_SHARED));
    // ….

    Jak vidíte, lze ikonu roztáhnout do libovolného rozměru, který pak bude mít při svém zobrazení.

    Nyní si řekněme o další "rozšířené" funkci

    BOOL DrawIconEx(
      HDC hdc, // handle kontextu zařízení
      int xLeft, // x-ová souřadnice levého horního rohu
      int yTop, // y-ová souřadnice levého horního rohu
      HICON hIcon, // handle ikony
      int cxWidth, // délka ikony
      int cyWidth, // výška ikony
      UINT istepIfAniCur, // index obrázku v animovaném kurzoru
      HBRUSH hbrFlickerFreeDraw, // handle štětce pozadí
      UINT diFlags // volby kreslení
    );

    Tato funkce umožňuje kreslit ikony nebo kurzory, včetně jednotlivých obrázků animovaných kurzorů, jak napovídá parametr istepIfAniCur. Máme dále možnost specifikovat rozměry, do kterých je výsledný obrázek roztažen. Parametr hbrFlickerFreeDraw umožňuje redukovat případné blikání při překreslování ikony nebo kurzoru. Pokud je uveden platný handle na brush (štětec), systém použije tento štětec jako pozadí s tím, že vytvoří v paměti bitmapu s tímto pozadím, do ní nakreslí ikonu nebo kurzor a tuto bitmapu pak přímo kopíruje do kontextu zařízení.

    Použití této funkce vidíte na spodní části dialogu z ukázkového příkladu, kde jsou nakresleny jednotlivé kroky animovaného kurzoru, načteného ze souboru. Celý kód je realizován v handleru zprávy WM_PAINT:

    int xPos, yPos;
    xPos = 20;
    yPos = 200;
    hicon = (HICON)LoadImage(AfxGetInstanceHandle(),
      "anicur.ani", IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED | LR_LOADFROMFILE);
    DrawIconEx(dc.m_hDC, xPos, yPos, hicon, 32, 32, 0, NULL, DI_NORMAL);
    xPos += 50;
    DrawIconEx(dc.m_hDC, xPos, yPos, hicon, 32, 32, 1, NULL, DI_NORMAL);
    xPos += 50;
    DrawIconEx(dc.m_hDC, xPos, yPos, hicon, 32, 32, 2, NULL, DI_NORMAL);
    xPos += 50;
    DrawIconEx(dc.m_hDC, xPos, yPos, hicon, 32, 32, 3, NULL, DI_NORMAL);
    xPos += 50;
    DrawIconEx(dc.m_hDC, xPos, yPos, hicon, 32, 32, 4, NULL, DI_NORMAL);
    xPos += 50;
    DrawIconEx(dc.m_hDC, xPos, yPos, hicon, 32, 32, 5, NULL, DI_NORMAL);

    Na závěr tohoto článku si ještě ukážeme, jak oknu nastavit příslušný kurzor, s tím, že se přesvědčíme o možnosti záměny ikony a kurzoru. Jako kurzor tedy nastavíme ikonu načtenou ze zdrojů. Pro nastavení kurzoru oknu, přesněji řečeno třídě, ke které okno patří, použijme funkci SetClassLongPtr (popř. starší variantu SetClassLong, která však není kompatibilní s 64bitovou verzí Windows). Následující funkce nastaví oknu dialogu jako kurzor ikonu IDR_MAINFRAME s tím, že ji navíc roztáhne do velikosti 64 x 64 pixelů:

    void CUkazkaDlg::OnButton1()
    {
      SetClassLongPtr(m_hWnd, GCLP_HCURSOR,
        (LONG)LoadImage(AfxGetInstanceHandle(),
          MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 64,64, 0));
    }

    Druhá funkce použije jinou ikonu (konkrétně ikonu programu Microsoft Word 2000), načte ji černobíle a přiřadí oknu jako kurzor:

    void CUkazkaDlg::OnButton2()
    {
      SetClassLongPtr(m_hWnd, GCLP_HCURSOR,
        (LONG)LoadImage(AfxGetInstanceHandle(),
          MAKEINTRESOURCE(IDI_ICON2), IMAGE_ICON, 64,64, LR_MONOCHROME));
    }

    Chceme-li vrátit oknu zpět výchozí kurzor, použijeme příslušný systémový kurzor:

    void CUkazkaDlg::OnButton3()
    {
      SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW));
    }

    Ještě poznámku k "vrácení" výchozího kurzoru: V naší ukázce nastavujeme výchozí systémový kurzor, což v některých případech nemuselo být algoritmicky čisté. Pokud by náš modul byl částí nějakého rozsáhlejšího programu, mohlo by se stát, že dříve, než se nám okno "dostane do spárů", mu byl přiřazen nějaký specifický kurzor, a patřilo by tedy k dobrému vychování vrátit mu zase tento kurzor. Tuto možnost nám dává návratová hodnota funkce SetClassLongPtr, která vrací předchozí hodnotu příslušného parametru třídy, tedy v našem případě handle na kurzor. Znamenalo by to algoritmicky si ošetřit "první sáhnutí na kurzor", uložit si návratovou hodnotu funkce a při "rozloučení se s oknem" tuto hodnotu nastavit zpět jako kurzor.

    Tolik prozatím k ikonám a kurzorům. V některém z příštích článků se můžeme podívat podrobněji na to, jak lze „pracovat“ přímo s daty ikon a kurzorů, získanými ze zdrojů programu.

    Ukázkový příklad si můžete stáhnout zde: icon_cursor.zip

Diskuze (4) Další článek: Spoluzakladatel společnosti Akamai byl v uneseném letadle

Témata článku: , , , , , , , , , , , , , , , ,