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

Kreslení čárových objektů a další typy per.

16.1.2002

V minulém článku jsem si ukázali úplné základy kreslení čárových objektů a použití per. Dnes si ukážeme další funkce pro kreslení čárových objektů a předvedeme další typy per. Opět si založíme projekt typu "Win32 aplikace" a necháme si vygenerovat jeho"kostru". Na rozdíl od minulého článku budeme dnes kreslící funkce používat v handleru zprávy WM_PAINT, takže požadovaný grafický výsledek se bude na ploše okna automaticky překreslovat bez ohledu na akce uživatele, jako tomu bylo v minulém článku. Podívejme se nejprve na výsledek a pak si postupně popíšeme jak je ho dosaženo:

win-api-16

Nebudeme zde popisovat a demonstrovat úplný přehled všech funkcí kreslících čárové objekty, ukážeme si 2 z nich jako ukázku. Další si můžete sami vyzkoušet podle popisu v dokumentaci. Jako první si tedy ukážeme funkci, která vykreslí sadu navazujících úseček na základě definovaných bodů. Jde o funkci PolyLine:
BOOL Polyline(
  HDC hdc,            // handle to kontextu zařízení
  CONST POINT *lppt,  // body konců úseček
  int cPoints         // počet návazných bodů
);
Příklad použití této funkce je modrá hvězda na screen-shotu, nakreslená takto:
void DrawPolyLine(HDC hdc)
{
  HPEN hPen, hpOld;
  POINT MyPoints[6] = {50,2, 2,98, 98,33, 2,33, 98,98, 50,2};
  hPen = CreatePen(PS_SOLID, 4, 0x00FF0000);
  hpOld = (HPEN)SelectObject(hdc, hPen);
  Polyline(hdc, MyPoints, 6);
  // Před zrušením objektu ho vyjmeme z DC
  SelectObject(hdc, hpOld);
  DeleteObject(hPen);  
}
Dále vidíme kružnici, skládající se ze 4 navazujících oblouků, odlišených vybraným perem. Jde o demonstraci funkce AngleArc:
BOOL AngleArc(
  HDC hdc,            // handle kontextu zařízení
  int X,              // x-souřadnice středu kružnice
  int Y,              // y-souřadnice středu kružnice
  DWORD dwRadius,     // poloměr kružnice
  FLOAT eStartAngle,  // úhel počátku oblouku
  FLOAT eSweepAngle   // úhel oblouku
);
Tato funkce vykreslí oblouk na kružnici definované souřadnicemi středu a poloměrem. Parametr eStartAngle určuje úhel počátku oblouku ve stupních měřeno od osy x, tedy úhel 0 je bod na pravém okraji kružnice. Délka oblouku je určena úhlem eSweepAngle (také ve stupních, tedy 90 je čtvrt kružnice). Podívejme se opět na příklad vykreslení oblouků v naší ukázce:
void DrawAngles(HDC hdc)
{
  HPEN hPen1, hPen2, hpOld;
  hPen1 = CreatePen(PS_SOLID, 4, 0x0000FF00);
  hPen2 = CreatePen(PS_DASHDOT, 1, 0x000000FF);
  hpOld = (HPEN)SelectObject(hdc, hPen2);
  MoveToEx(hdc, 250, 50, NULL);
  AngleArc(hdc, 200, 50, 50, 0, 90);
  SelectObject(hdc, hPen1);
  AngleArc(hdc, 200, 50, 50, 90, 90);
  SelectObject(hdc, hPen2);
  AngleArc(hdc, 200, 50, 50, 180, 90);
  SelectObject(hdc, hPen1);
  AngleArc(hdc, 200, 50, 50, 270, 90);
  SelectObject(hdc, hpOld);
  DeleteObject(hPen1);
  DeleteObject(hPen2);
}
K oběma ukázkám ještě poznámku o korektním rušení grafických objektů (v našem případě per): Dříve než objekt zrušíme funkcí DeleteObject, musíme ho vyjmout z kontextu zařízení. Toto provedeme nejlépe vložením původního objektu stejného typu (tedy například pera). Již víme, že funkce SelectObjekt vrátí hodnotu objektu, který byl novým objektem nahrazen. Tuto hodnotu (handle pera typu HPEN) si při prvním vložení vlastního objektu uložíme a před zrušením našeho objektů ji opět vložíme. Jako poslední ukázka je vykreslení různých nespojitých typů per. Jak můžete vidět na obrázku, doprovodný text je umístěn tak, že jeho střed je (plus/mínus 1 pixel v případě lichých čísel) přesně na y-ové souřadnici dané čáry. Samozřejmě metoda pokus/omyl by v daném případě dosáhla téhož. Pokud ale chceme mít kód nezávislý na daném systémovém fontu, musíme na to trochu sofistikovaněji s využitím funkce GetTextExtentPoint32, kterou můžeme zjistit rozměry textu s fontem, který je aktuálně vybrán v daném kontextu zařízení. Funkce nám naplní strukturu SIZE, určující "opsaný bdélník" textového řetězce.
BOOL GetTextExtentPoint32(
  HDC hdc,           // handle kontextu zařízení
  LPCTSTR lpString,  // textový řetězec
  int cbString,      // počet znaků
  LPSIZE lpSize      // rozměry obdélníka textu
);
V naší ukázce jsem vytvořil funkci pro vykreslení jednoho typu pera, která nakreslí daným typem úsečku a vedle ní příslušný popis:
void DrawPenType(HDC hdc, LPCTSTR lpText, int nType, int yPos)
{
  HPEN hPen, hpOld;
  MoveToEx(hdc, 10, yPos, NULL);
  hPen = CreatePen(nType, 1, 0x00);
  hpOld = (HPEN)SelectObject(hdc, hPen);
  LineTo(hdc, 100, yPos); 
  SIZE size;
  GetTextExtentPoint32(hdc, lpText, strlen(lpText), &size);
  TextOut(hdc, 110, yPos-(size.cy/2),
    lpText, lstrlen(lpText));
}
A by byl ukázkový kód kompletní, na závěr handler zprávy WM_PAINT, ze kterého jsou volány zde uvedené funkce:
void OnPaint(HDC hdc)
{
  DrawPolyLine(hdc);
  DrawAngles(hdc);
  int yPos = 120;
  DrawPenType(hdc, "PS_DASH", PS_DASH, yPos);
  yPos += 20;
  DrawPenType(hdc, "PS_DOT", PS_DOT, yPos);
  yPos += 20;
  DrawPenType(hdc, "PS_DASHDOT", PS_DASHDOT, yPos);
  yPos += 20;
  DrawPenType(hdc, "PS_DASHDOTDOT", PS_DASHDOTDOT, yPos);
}
V příštím pokračování se budeme zabývat kreslením plošných objektů a použití štětců. Doprovodný projekt je ke stažení zde: win_api_16.zip