W piątej lekcji kursu GDI zajmiemy się bardzo ważnym elementem GDI jakim jest kontekst urządzenia. Programowo kontekst urządzenia to pewna struktura, która określa jak i gdzie mamy rysować. Wyobraźmy sobie biurko na, którym mamy położoną kartkę papieru, na której będziemy rysować, a obok kartki przybory, którymi będziemy rysować. Czymś takim jest kontekst urządzenia. Kartka na biurku, to obszar pulpitu lub okna po którym będziemy rysować, natomiast przybory koło kratki, to obiekty GDI w kontekście, które są używane do malowania na obszarze tego kontekstu.

Kontekst jest bardzo skomplikowanym obiektem, składa się z wielu atrybutów:

Atrybuty pióra

Obiekt pióra
Kontekst posiada własny obiekt pióra(poznałeś go w poprzedniej lekcji), którym rysuje linnie na swoim obszarze. Domyślnie(zaraz po utworzeniu) pióro jest koloru czarnego o stałej długości i grubości jednego piksela. Pióro w kontekście możemy podmienić funkcją SelectObject(), natomiast pobrać uchwyt aktualnego pióra w kontekście funkcją GetCurrentObject(). Funkcje te omówione są w dalszej część lekcji.

Aktualna pozycja pióra(miejsce wskazujące)
Jest to pewien punkt na obszarze, na którym rysuje kontekst urządzenia, niektóre funkcje rysujące piórem zaczynają rysować właśnie od niego. Domyślnie po utworzeniu, punkt ten ma współrzędne (0,0). Funkcja, która zmienia jego położenie to MoveToEx(), natomiast punkt ten możemy pobrać funkcją GetCurrentPositionEx().

BOOL MoveToEx(HDC hdc,INT X,INT Y,LPPOINT lpPoint);

Pierwszy parametr to uchwyt do kontekstu, w którym mamy zmienić pozycję, drugi i trzeci to współrzędne nowego punktu, natomiast czwarty parametr to wskaźnik na strukturę w której zostaną umieszczone poprzednie współrzędne pozycji(te zastąpione). Jeżeli nie potrzebujemy znać poprzednich punktów, podajemy tu 0. Jeżeli funkcja się nie powiedzie, zwracane jest 0.

BOOL GetCurrentPositionEx(HDC hdc,LPPOINT lpPoint);

Pierwszy parametr to uchwyt do kontekstu urządzenia z którego pobieramy wspołrzędne pozycji pióra, natomiast drugi to wskaźnik na strukturę POINT, w której zostaną umieszczone pobierane współrzędne. Funkcja w razie niepowodzenia zwraca 0.

Tryb rysowania

Tryb rysowania określa nam w jaki sposób, stare piksele mają zostać przykryte przez naszą rysowaną figurę. Domyślnie zostają przykryte w pełni, natomiast GDI umożliwia odpowiednie mieszanie barw.

Atrybuty pędzla

Obiekt pędzla

Pierwszym atrybutem pędzla jest sam obiekt pędzla, którego kontekst urządzenia będzie używał do wypełniania wnętrza figur. Domyślnie tworzony jest jednolity biały pędzel. Możemy go zmienić funkcją SelectObject(), natomiast pobrać funkcją GetCurrentObject().

Punkt odniesienia pędzla

Jest to punkt, którego używa się w przypadku pędzla z bitmapy, określa on skąd ma się zaczynać rysowanie bitmapy. Domyślnie jest to punkt (0,0). Ustawiamy go funkcją SetBrushOrgEx(), natomiast pobieramy GetBrushOrgSet()

BOOL SetBrushOrgEx(HDC hdc,INT x,INT y,LPPOINT lpPoint);

Parametry i zwracna wartość są takie same jak w przypadku MoveToEx()

BOOL GetBrushOrgEx(HDC hdc,LPPOINT lpPoint);

Parametry i zwracna wartość są takei same jak w przypadku GetCurrentPositionEx().

Atrybuty bitmapy

Obiekt bitmapy

Każdy kontekst ma swój obszar rysowania, jest nim właśnie obiekt bitmapy, to właśnie przestrzeń po, której rysuje. Domyślnie tworzona jest bitmapa monochromatyczna(czarno-biała) o wymiarach 1x1piksel. Zamienić obiekt możemy funkcją SetObject(), a pobrać GetCurrentObject(). Zwykle podczas tworzonego kontekstu, wybieramy od czego ma powstać obiekt bitmapy.

Tryb rozciągania

Tryb rozciągania przydaje się przy operacji skalowania bitmapy w danym kontekście urządzenia.

Atrybuty tekstu

Atrybut ten określa pracę naszego kontekstu z operacjami tekstowymi.

Kolor tekstu

Ty chyba wszystko jasne. Określa jakiego koloru ma być wypisywany tekst w danym kontekście urządzenia. Domyślnie przyjmuje czarny kolor. Zmienić go możemy funkcją SetTextColor(), natomiast pobrać aktualny kolor funkcją GetTextColor().

COLORREF SetTextColor(HDC hdc,COLORREF crColor);

Pierwszym parametr to uchwyt do modyfikowanego kontekstu, natomiast drugi to barwa koloru tekstu. Funkcja zwraca barwę koloru, który zmieniliśmy.

COLORREF GetTextColor(HDC hdc);

Parametrem jest uchwyt sprawdzanego kontekstu, natomiast funkcja zwraca aktualny kolor tekstu.

Tryb tła tekstu

Do wyboru mamy 2 tryby wypisywania tekstu, pierwszy z nich OPAQUE, wypisuje tekst z tłem w danym kolorze, natomiast drugi TRANSPARENT, wypisuje tekst bez tła, tło jest przezroczyste. Atrybut ten ustawia sie funkcją SetBkMode(), natomiast pobiera GetBkMode.

INT SetBkMode(HDC hdc,INT iBkMode)

Pierwszy parmaetr do uchwyt do modyfikowanego kontekstu, drugi to nowy tryb tekstu(OPAQUE - tło, TRANSPARENT - przezroczystość). Funkcja zwraca poprzedni tryb tekstu.

INT SetBkMode(HDC hdc)

Parametrem jest uchwyt do modyfikowanego kontekstu urządzenia, funkcja zwraca aktualny styl.

Kolor tła tekstu

W przypadku ustawienia trybu OPAQUE, jest to kolor tła tekstu. Funkcja, którą ustawia się dany kolor tła to SetBkColor(), natomiast pobrać kolor możemy funkcją GetBkColor().

COLORREF SetBkColor(HDC hdc,COLORREF clColor)

Pierwszy parametr do uchwyt do modyfikowanego kontekstu, drugi to nowy kolor tła tekstu, funkcja zwraca poprzedni kolor.

COLORREF GetBkColor(HDC hdc)

Parametr do uchwyt do modyfikowanego kontekstu, funkcja zwraca aktualny kolor.

Obiekt czcionki

Jest to obiekt czcionki, którą aktualnie używa kontekst urządzenia do operacji tekstowych. Domyślną czcionką jest czcionka systemowa SYSTEM_FONT, obiekt ten możemy zmienić funkcją SelectObject(), natomiast pobrać GetCurrentObject().

Odstęp między znakami

Ten atrybut daje nam możliwość ustalenia odległości pomiędzy literami w danym kontekście. Domyślnie ustawiony jest na 0 pikseli. Możemy go ustawić funkcją SetTextCharacterExtra(), pobrać jak się zapewnie domyślacie GetTextCharacterExtra().

INT SetTextCharacterExtra(HDC hdc,INT nCharExtra);

Pierwszy parametr to uchwyt do kontekstu, drugi to nowa odległość, funkcja zwraca starą wielkość, natomiast jeżeli się nie powiedzie, zwracane jest 0x80000000.

INT GetTextCharacterExtra(HDC hdc);

Parametr to wiadomo co i już nie będę się powtarzał, natomiast funkcja zwraca aktualny odstęp w pikeslach(o ile nie zmieniliśmy). Jeżeli funkcja się nie powiedzie zwracana jest wartość 0x8000000.

Wyrównanie tekstu

Atrybut ten odpowiada za umieszczenie tekstu według określonego układu. Dokładniej zajmiemy się nim przy wypisywaniu tekstu.

Atrybuty Regionów

Region przycinania

Region przycinania pozwala ustawić część bitmapy, która będzie wyświetlana. Domyślnie wyświetlana jest cała bitmapa.

Typ wypełniania wielokątów

Określa jak mają się łączyć regiony nakładające się na siebie.

Funkcje obiektów w kontekście

Jak się dowiedzieliśmy, aby podmieniać obiekty GDI w kontekście urządzenia używamy funkcji SelectObject().

HGDIOBJ SelectObject(HDC hdc,HGDIOBJ hgdiobj);

Jako pierwszy parametr podajemy uchwyt do kontekstu urządzenia, w którym ma nastapić podmiana, drugim parametr to uchwyt do obiektu, który na zostać umieszczony w kontekście urządzenia. Uwaga, tutaj trzeba uważać na zwracaną wartość, otóż jest to uchwyt do poprzedniego kontekstu, tego który został podmieniony, gdyż będzie go trzeba zwolnić ręcznie funkcją DeleteObject(), która jako parametr przyjmuje uchwyt do zwalnianego obiektu. Obiekty, które są w danym kontekście urzadzenia są zwalniane w momencie zwalniania kontekstu, całą resztę trzeba zwolnić. Podczas podmiany obiekt, który wcześniej był w kontekście już nie będzie częścią kontekstu, więc jesteśmy odpowiedzialni za zwolnienie go, a nie kontekstu, mozna to robić tak:

HBRUSH brush=CreateSolidBrush(RGB(
0
,
34
,
255
));
//tworzymy pędzel

brush=SelectObject(hdc,brush);
//podmieniamy obiekty, ten stworzony teraz będzie umieszczony w kontekście
//natomiast temu, który wcześniej był w kontekście, nadajemy uchwyt tego co stworzyliśmy(zamiana obiektów)

DeleteObject(brush);
//usuwamy obiekt, który wcześniej był w kontekście, chyba, że będziemy z niego korzystać

lub odrazu w jednym zapisie:

HBRUSH brush=CreateSolidBrush(RGB(
0
,
34
,
255
));
DeleteObject(SelectObject(hdc,brush));

Zwalniany jest wynik funkcji SelectObject(), czyli stary obiekt.

Inną funkcją, która służy do operacji na obiektach kontekstu, to GetCurrentObject, służy ona do uzyskiwania uchwytu do obiektów, które aktualnie znajdują się w kontekście urzadzenia.

GetCurrentObject

Pobranie kontekstu urzadzenia.

Tworząc kontekst urządzenia, musimy wiedzieć od czego będzie ten kontekst urządzenia.

Jedną z możliwości jest pobranie kontekstu okna

HDC kontekst_okna=GetDC(hWnd);

GetDC pobiera kontekst okna, jako argument podajemy uchwyt do pobieranego okna, kiedy uzyskamy kontekst, będziemy mieli możliwość rysowania po roboczej części okna, poprzez kontekst urządzenia.

Możemy także pobrać, kontekst do całego okna, a nie tylko obszaru roboczego, czyli także paska tytułu, oraz reszty systemowych elementów okna, ale nie tylko tej częsci po której powinniśmy pracować. Robimy to funkcją GetWindowDC().

HDC kontekst_okna=GetWindowDC(hWnd);

Kolejnym obszarem do którego możemy pobrać kontekst to cały pulpit, tak, możemy stworzyć program, który będzie mógł rysować po całym pulpicie. Robi się to Podając jako parametr 0 w funkcji GetDC.

HDC caly_pulpit=GetDC(
0
);

Oczywiście każdy utworzony kontekst trzeba później zwolnić, robimy to funkcją ReleaseDC(), jako pierwszy parametr podajemy uchwyt do okna z jakiego powstał dany kontekst, drugi parametr to uchwyt do zwalnianego kontekstu.

ReleaseDC(hWnd,hdc);

Odrysowywanie okna

Teraz trzeba sobie powiedzieć o bardzo ważnym procesie w windowsie, jakim jest odrysowywanie okienek windowsowych. Gdy mamy na pulpicie otwartych kilka okienek, a one w pewnych miejscach nakładają się na siebie, to aby ponownie pokazać okienko które było wcześniej zasłonięte, windows musi je odrysować, czasmi gdy komputer jest obciążony jakimś zadaniem lub program przestał odpowiadać, możemy zaobserwować, że fragment okna, który odsłoniliśmy jest biały nie odrysował się. Już wyjaśniam jak to się odbywa. Otóż, gdy nasze okienko(bądź fragment) było zasłonięte przez jakieś inne i nagle zostało odsłonięte do okienka wysyłany jest komunikat WM_PAINT, który właśnie mówi nam o konieczności odrysowania okna, to właśnie w obsłudze tego komunikatu powinniśmy umieścić polecenia rysujące nasz program, aby windows mógł odrysować fragment okienka. W tym celu musimy pobrać kontekst do fragmentu odrysowywanego okna. Robimy to funkcją BeginPaint(), a gdy już skończymy powinniśmy zwolnić go funkcją EndPaint().

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  WNDCLASSEX wc;
  wc.hInstance=hInst;
  wc.lpszClassName=
"Klasa okna"
;
  wc.lpfnWndProc=WndProc;
  wc.style=CS_DBLCLKS;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=LoadIcon(
0
,IDI_APPLICATION);
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  if(RegisterClassEx(&wc)==
0
) return
0
;
  HWND Okno=CreateWindowEx(
0
,
"Klasa okna"
,
"Tytuł okna"
,WS_OVERLAPPEDWINDOW,
50
,
50
,
100
,
100
,
0
,
0
,hInst,
0
);
  MSG msgs;
  ShowWindow(Okno,nShow);
  UpdateWindow(Okno);
//odrysowanie całego okna

  while(GetMessage(&msgs,
0
,
0
,
0
))
  {
    TranslateMessage(&msgs);
    DispatchMessage(&msgs);
  }
  return msgs.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wPar,LPARAM lPar)
{
  HDC hdc;
//uchwyt do kontekstu odrysowywanego fragmentu okna

  PAINTSTRUCT ps;
//struktura określająca jaki fragment okna odrysować

  switch(msg)
  {
    case WM_PAINT:
      hdc=BeginPaint(hwnd,&ps);
//pobieramy uchwyt od odrysowywanego fragmentu

      
//polecenia rysujące na naszym oknie, te poznamy w nastepnej lekcji

      EndPaint(hwnd,&ps);
//po zakończeniu odrysowywania, zwalniamy kontekst

      break;
    case WM_DESTROY:
      PostQuitMessage(
0
);
      break;
    default:
      return DefWindowProc(hwnd,msg,wPar,lPar);
  }
  return
0
;
}

W tym miejscu trzeba sobie powiedzieć o funkcji UpdateWindow(), jej parametr to uchwyt do okna. Funkcja wysyła komunikat WM_PAINT do danego okna, powiadamiając, że trzeba odświeżyć całe okno. Innymi słowo funkcja rysuje całe okno jeszcze raz na nowo. Zwykle po pokazaniu okna funkcją ShowWindow(), uzywamy jej, gdyż zachodzi potrzeba odrysowania całego okna.

W następnej lekcji zajmiemy się już rysowaniem konkretnych figur na naszym oknie.