Sunteți pe pagina 1din 21

Noţiuni elementare de WinApi

1 Structura de bază a unui program windows


Introducere - cum arată un program de windows?
Un program de windows este alcătuit dintr-o fereastră principală cu o structură standard (de obicei).
Această fereastră se află pe ecran pe parcursul întregii desfăşurări a aplicaţiei, aşteptând input de la
utilizator/system la care reacţionează în mod adecvat.. Aplicaţiile windows sunt aplicaţii care au la
bază evenimente (event driven), de exemplu de la mouse, menu, tastatură, butoane etc.
Evenimentele generează mesaje, care sunt plasate într-o coadă de mesaje, urmând a fi procesate
secvenţial. Coada de mesaje aşteaptă mesaje pe parcursul întregii durate a aplicaţiei .

Structura de bază a unui program windows


Un program windows conţine cel puţin două funcţii de bază:
(1) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR
szCmdLine, int iCmdShow)
(2) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

(1) Funcţia WinMain (echivalentă cu void main() in C):


• Creează o clasă window pentru fereastra principal şi înregistează această clasă.
• Creează o nouă fereastră ca instanţă a acestei clasei window.
• Afişază fereastra creată pe ecran.
• Lansează bucla de mesaje Observaţii:
• Fiecare fereastră are un handle (identificator de fereastră) pe baza căreia poate fi referenţiată
• Fiecare fereastră aparţine unei instanţe a programului
• Evenimentele unei ferestre sunt gestionate de către procedura de ferastră
(2) Funcţia WndProc (poate avea orice nume! Nu doar WndProc):
• Reprezintă procedura de fereastră
• Determină răspunsul la diferitele mesaje generate de evenimente

Explicitarea structurii programului principal


Funcţia WinMain
(1) Explicitatea parametrilor
Funcţia WinMain are 4 parametri:
• hInstance (instance handle) : - reprezintă un număr care identifică în mod unic programul aflat în
execuţie (este posibil ca utilizatorul să ruleze mai multe copii ale aceluiaşi program, copii denumite
instanţe. Fiecare instanţă are o valoare hInstance diferită).
• hPrevInstance - este identificatorul celei mai recente instanţe anterioare instanţei curente. Dacă
nu există instanţe anterioare hPrevInstance are valoarea 0/NULL
• szCmdLine – este o variabilă pointer care punctează către un şir de caractere ce conţine
parametrii din linia de comandă şi care sunt transferaţi programului
• iCmdShow – este un număr ce reprezintă modul de afişare al primei ferestre executate
(1=normal/7=pictogramă)
(2) Crearea unei ferestre
Definirea clasei de ferestre
O fereastră este creată dintr-o clasă window. O astfel de clasă se crează prin definirea
caracteristicilor ei, printer care şi a procedurii de fereastră care prelucrează mesajele transmise de
fereastra creată. După crearea clasei este necesar să o înregistrăm prin apelarea funcţiei
RegisterClass. Doar prima instanţă a unui program trebuie să înregistreze clasa window.
WNDCLASS este o structură care îmi permite să definesc clase de obiecte de tip fereastră. Acest tip
conţine 10 câmpuri (variabile).care descriu caracteristicile tuturor ferestrelor create pe baza acestei
clase
Style - Stilul ferestrei
lpfnWndProc - Numele procedurii de fereastră.
cbClsExtra, cbWndExtra - Două câmpuri care permit rezervarea de spaţiu suplimentar în structura
class şi în structura window. Un program poate utiliza acest spaţiu în scopuri personale. În general
nu avem nevoie de acest spaţiu suplimentar şi setăm câmpurile la 0.
hInstance - Identificatorul instanţei ferestrei
hIcon - Stabileşte o pictogramă pentru toate ferestrele create pe baza clasei window.
hCursor - Stabileşte o pictogramă pentru cursor
hbrBackground - Specifică culoarea de fundal a ferestrei
lpszMenuName -Specifică meniul feresteri
lpszClassName -Specifică numele ferestrei
Pentru crearea unei clase de ferestre trebuie iniţializate aceste 10 câmpuri. Acest lucru se face
numai la început, înainte de crearea primei ferestre (deci înainte de prima instanţă).
După iniţializarea câmpurilor trebuie înregistrată clasa cu ajutorul funcţiei RegisterClass(). Acest
lucru se face de asemenea doar pentru prima instanţă. De aceea înainte de aceste operaţii se
verifică condiţia hPrevInstance= =NULL (adică nu există instanţă anterioară). În continuare este
explicată iniţializarea câmpurilor în cazul programului minapp.cpp Stilul ferestrei
wcmain.style = CS_BYTEALIGNWINDOW|CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS
fereastra va fi reactualizată complet la fiecare modificare orizontală sau verticală şi recunoaşte
double-click de mouse (CS = class style)
Procedura de fereastră este WndProc. Ea tratează mesajele transmise de fereastra curentă
wcmain.lpfnWndProc = WndProc
Instanţa ferestrei: este instanţa curentă hInstance (dată de primul parametru al WinMain())
wcmain.hInstance = hInstance;
Pictograma pentru toate ferestrele create pe baza clasei wcmain se obţine prin încărcarea unei
pictograme (o imagine de tip bitmap) cu funcţia LoadIcon. Pentru obţinerea unei pictograme
predefinite se foloseşte funcţia LoadIcon cu primul parametru NULL, iar al doilea este un identificator
cu prefixul IDI (IDentificator de Icon) şi este definit în fişierul windows.h
wcmain.hIcon = LoadIcon(NULL, IDI_APPLICATION);
Cursorul pentru fereastră se obţine prin încărcarea unui cursor (o imagine de tip bitmap) cu funcţia
LoadCursor. Pentru obţinerea unui cursor predefinit valoarea primul parametru al funcţiei este NULL
iar al doilea este un identificator care începe cu prefixul IDC şi este definit în fişierul windows.h
wcmain.hCursor = LoadCursor(NULL, IDC_ARROW);
hbrBackground este un handle către o pensulă (hbr = h + br unde h este pentru handle şi br pentru
brush) pentru culoarea de fundal. O pensulă reprezintă un model colorat de pixeli utilizat în
umplerea unei zone. Există mai multe pensule standard, care pot fi obţinute cu funcţia
GetStockObject (cu care pot fi obţinute şi alte obiecte grafice).
wcmain.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
Menu:
wcmain.lpszMenuName = MAKEINTRESOURCE(IDR_DBVAPP);
Dacă nu avem menu atunci:
wcmain.lpszMenuName = NULL;
Numele clasei:- definesc numele clasei de ferstre
wcmain.lpszClassName= szKlassenName;
Urmează înregistrarea clasei de ferestre, prin transmiterea adresei variabilei wcmain:
RegisterClass(&wcmain)
şi se testează dacă a putut fi înregistrată clasa. În caz contrar se returnează mesaj de eroare.
Crearea unei ferestre din clasa definită
Crearea unei instanţe a clasei definite anterior se realizează cu ajutorul funcţiei CreateWindow.
Această funcţie returnează un handle (o referinţă) la obiectul fereastră creat.
Funcţia CreateWindow are ca parametri:
• numele clasei de ferestre căreia îi aparţine obiectul fereastră creat: szClassName
• titlul ferestrei (care apare în bara de titlu): szWindowTitle
• stilul ferestrei:
WS_SYSMENU|WS_CAPTION|WS_OVERLAPPED|WS_BORDER| WS_MINIMIZEBOX,
• coordonatele colţului stânga sus: xPos, yPos
• dimensiunea ferestrei: width, height
• referinţa către fereastra părinte: NULL (pentru că nu avem fereastră părinte)
• referinţa către meniu: LoadMenu(hInstance, MAKEINTRESOURCE(IDR_DBVAPP))
• instanţa căreia îi aparţine fereastra: hInstance (parametru al funcţiei WinMain)
• creation parameters: NULL
Referinţa (handle) către fereastra creată este în exemplul de mai sus hwndmain şi este de tip
HWND (handle to a window). Fiecare fereastră are un handle propriu, pe baza căruia se poate
stabili de unde vine un mesaj sau unde se efectuează anumite operaţii. Pot crea mai multe ferestre
pe baza aceleiaşi clase, dar fiecare va avea un handle diferit. După ce creez fereastra cu ajutorul
funcţiei CreateWindow, testez dacă programul a putut să o creeze. Altfel (if(hwndmain==NULL)) se
returnează eroare.

(3) Afişarea unei ferestre


După ce am creat obiectul de tip fereastră, acesta trebuie afişat pe ecran. Acest lucru se face prin
apelul succesiv a două funcţii:
ShowWindow(hwndmain, iCmdShow);
afişează fereastra pe ecran. Primul parametru specifică fereastra care este afişată, iar al doilea
tipul de afişare (SW_SHOWNORMAL = normal sau SW_SHOWMINNOACTIVE = pictogramă).
UpdateWindow(hwndmain);
transmite către procedura de fereastră un mesaj de tip WM_PAINT, care are ca rezultat
redesenarea ferestrei (reactualizarea zonei client a ferestrei).
(4) Bucla de mesaje
Programul trebuie să prelucreze date primite de la utilizator prin intermediul tastaturii şi a mouse-
ului. Cu ajutorul tastaturii şi a mouse-ului se produc evenimente de intrare care sunt transmise
procedurii de fereastră sub formă de mesaje.
Sistemul Windows include o structură de date – message queue (coadă de mesaje) – care
converteşte fiecare acţiune a utilizatorului într-un mesaj şi îl plasează în coada de mesaje.
Cu ajutorul funcţiei GetMessage pot extrage mesajul aflat la începutul cozii într-o structură de tip
MSG. Acest mesaj este convertit cu ajutorul funcţiei Translate Message şi apoi acesta este
transmis către procedura de fereastră cu ajutorul funcţiei DispatchMessage. Pentru preluarea
mesajelor de fereastră se defineşte o buclă de mesaje, care este activă pe tot parcursul existenţei
ferestrei:
while (GetMessage(&msg, NULL, 0, 0))
{ TranslateMessage(&msg); // Traducerea mesajului
DispatchMessage(&msg); // Transmiterea mesajului către
//procedura de fereastră }
Variabila msg este o structură de tip MSG şi are următoarele câmpuri:
• HWND hwnd – identifică fereastra de la care vine mesajul
• UINT message – este o constantă ce identifică mesajul. Aceste constante încep cu prefixul WM
(Window Message) (de ex. WM_PAINT sau WM_LBUTTONDOWN)
• WPARAM wParam – un parametru (pe 16 biţi)
• LPARAM lParam – un parametru (pe 32 biţi). Aceşti 2 param. depind de tipul mesajului şi
furnizează informaţii suplimentare referitoare la mesaj
• DWORD time – conţine momentul de timp la care mesajul a fost plasat în coada de mesaje
• POINT pt – conţine coordonatele mouse-ului la momentul generării mesajului
Parametrii 2, 3 şi 4 se setează la NULL respectiv 0, 0 pentru a indica faptul că sunt recepţionate
mesaje de la toate ferestrele create de program.
Pentru orice mesaj în afară de WM_QUIT funcţia GetMessage( ) returnează o valoare diferită de 0.
Mesajul WM_QUIT determină ieşirea din buclă, iar programul returnează câmpul wparam al
structurii msg.

Prelucrarea mesajelor cu procedura de fereastră


(1) Procedura de fereastră
Procedura de fereastră (în cazul nostru WndProc) determină conţinutul ce va fi afişată în zona client
a ferestrei corespunzătoare şi modul de răspuns al ferestrei la evenimentele produse de utilizator
(de ex. click de mouse, apăsarea unui buton, selectarea unei opţiuni de meniu etc.).
O procedură de fereastră este întotdeauna asociată unei clase window particulară şi poate avea
orice nume. Definiţia procedurii de fereastră:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
Procedura are 4 parametri:
• HWND hwnd – identifică fereastra de la care a primit mesajul
• UINT message – identifică mesajul primit (de ex. WM_PAINT)
• WPARAM wParam
• LPARAM lParam – aceşti 2 parametri conţin informaţii suplimentare despre mesaj şi depind de
fiecare mesaj în parte.
(2) Generarea şi prelucrarea mesajelor
Atât sistemul de operare cât şi aplicaţia în sine pot genera mesaje. care sunt plasate în coada de
mesaje ale aplicaţiei (mesage application queue). Ulterior aceste mesaje sunt extrase din coadă de
către aplicaţie (prin funcţia GetMessage( ) ) şi expediate către procedura de fereastra, care le
prelucrează. Există anumite mesaje care nu sunt plasate în coada de mesaje ci sunt trimise direct
către procedura de fereastră (unqueued messages). De exemplu funcţia CreateWindow( ) transmite
un mesaj WM_CREATE procedurii de fereastră (fără ca acest mesaj să fie plasat întâi în coada de
mesaje). Extragerea mesajelor din coada de mesaje se face cu ajutorul funcţiei GetMessage în
bucla de mesaje discutată mai sus. Fiecare mesaj este prelucrat de către procedura de fereastră.
Acest lucru se întâmplă într-o construcţie de tip switch, care în funcţie de mesaj efectuează anumite
operaţii (stabilite de către programator).
Exemplu: Scrie în fereastră mesajul “Primul program”
switch (message) { case WM_PAINT:
HDC hdc; //contextul grafic
PAINTSTRUCT ps; RECT rect; //Obiect dreptunghi
hdc = BeginPaint(hwnd, &ps); //Obţinerea contextului grafic
GetClientRect(hwnd,&rect); //Obţinerea suprafeţei de desenare
//Scrierea unui text în fereastră
DrawText(hdc,"Primul program",-1,&rect,DT_CENTER|DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default: // Alte mesaje sunt trimise către funcţia implicită de
//tratare
return DefWindowProc(hwnd, message, wParam, lParam);
}
În general programatorul va stabili operaţii numai pentru anumite mesaje, caz în care se va returna
valoarea 0 (adică mesajul a fost prelucrat). Mesajele care nu sunt prelucrate în mod explicit de către
procedura de fereastră, trebuie pasate către funcţia implicită de prelucrare a
mesajelor DefWindowProc.
(3) Mesajul WM_PAINT
Acest mesaj generează redesenarea zonei client a ferestre. El este transmis atunci când o anumită
porţiune a zonei client este invalidă şi trebuie redesenată, de exemplu atunci când:
• se creează o fereastră pentru prima dată (generat de funcţia UpdateWindow)
• se redimensionează fereastra (atunci când câmpul style al structurii wcmain a fost definit de tip
CS_HREDRAW şi CS_VREDRAW – adică invalidarea întregii ferestre în cazul redimensionării
orizontale sau verticale)
• se minimizează şi apoi se restaurează fereastra • atunci când se deplasează fereastra
• atunci când se revine la fereastra aplicaţiei după ce aceasta a fost acoperită de alte ferestre
Obţinerea şi eliberarea contextului graphic (BeginPaint / EndPaint)
Pentru a putea desena în fereastră este necesară obţinerea unui context graphic (device context)
păstrat într-o variabilă hdc de tip HDC. Pentru aceasta se apelează funcţia BeginPaint:
hdc = BeginPaint(hwnd, &ps);
Primul parametru hwnd identifică fereastra care a generat mesajul. Al doilea parametru este un
pointer către o structură de tip PAINTSTRUCT, care conţine informaţii ce pot fi utilizate la
redesenarea ferestrei.
Câmpurile structurii PAINTSTRUCT sunt:
• HDC hdc – handle către un dispozitiv de ieşire (vezi mai jos)
• BOOL fErase – pentru ştergerea zonei client
• RECT rcPaint – dreptunghiul care reprezintă zona invalidă a zonei client. Acest câmp îmi permite
de ex. să realizeze redesenarea numai a porţiunii invalide, nu neapărat a întregii zone client. • BOOL
fRestore • BOOL fIncUpdate
• BYTE rgbReserved[6] ultimele trei câmpuri sunt rezervate pentru sistemul Windows.
Aceste câmpuri sunt iniţializate de către funcţia BeginPaint. Funcţia BeginPaint realizează
ştergerea zonei client prin acoperirea ei cu culoarea setată la câmpul hbrBackground al
structurii wcmain. Apoi este validată zona client şi se înapoiază o variabilă hdc denumită handle to a
device context, adică un handle către un dispozitiv de ieşire (de ex. ecran sau imprimantă). Înainte
de încheierea prelucrării mesajului, trebuie eliberat contextual graphic prin intermediul funcţiei
EndPaint. EndPaint(hwnd,&ps) ;
Obţinerea zonei client pentru desenare
Funcţia
GetClientRect(hwnd, &rect)
permite plasarea în variabila rect (structură de tip rectangle) a dimensiunilor zonei client a ferestrei.
Structura rect conţine 4 câmpuri: left, top, right şi bottom. Left şi top sunt fixate la valoarea 0 şi
reprezintă colţul din stânga sus al zonei client. Right şi bottom conţin lăţimea şi respectiv înălţimea
zonei client (dau colţul dreapta jos) măsurate în pixeli.
Mesajul WM_DESTROY
Mesajul rezultă în urma selectării opţiunii Close din meniul System aferent programului sau prin
apăsarea combinaţiei de taste Alt+F4 sau dacă am o opţiune de meniu/buton căruia îi asociez
distrugerea ferestrei. Acest mesaj semnalează programului că urmează distrugerea ferestrei.
Tratarea mesajului se face în mod standard prin apelarea funcţiei PostQuitMessage(0) care
inserează în coada de mesaje un mesaj WM_QUIT. Atunci când funcţia GetMessage va extrage
mesajul WM_QUIT, va returna valoarea 0, ceea ce va determina încheierea buclei de mesaje şi
implicit terminarea programului.
Primitive GDI

1 Indicații teretice
Noţiuni teoretice
Interfaţa pentru dispozitive grafice (GDI - Graphics Device Interface) este o componentă a
sistemului de operare Windows şi are ca sarcină afişarea elementelor grafice (inclusiv a textului) pe
ecran şi la imprimantă.
De asemenea, trebuie să ştiţi că interfaţă Windows GDI îşi are limitele ei. Cel puţin în acest moment,
interfaţă GDI nu poate să facă tot ce v-aţi putea dori de la o interfaţă grafică. Deşi puteţi să mutaţi pe
ecran obiecte grafice, GDI este, în general, un sistem de afişare static, ce permite numai animaţii
limitate. Aşa cum este implementată în Windows 95, interfaţă GDI nu asigură un suport direct pentru
afişarea tridimensională sau pentru rotirea obiectelor. De exemplu, atunci când desenaţi o elipsă,
axele acesteia trebuie să fie paralele cu axele sistemului de coordonate. Deşi unele limbaje grafice
folosesc numere în virgulă mobilă pentru coordonatele virtuale. Windows 95 - din motive legate de
performanţă - foloseşte numai numere întregi pe 16 biţi aceasta este una dintre deficienţele
sistemului de operare Windows 95. Windows NT permite folosirea coordonatelor pe 32 de biţi.
Din punctul de vedere al programatorului, interfaţa GDI este formată din câteva sute de apeluri de
funcţii şi unele tipuri de date, macroinstrucţiuni şi structuri asociate acestor funcţii, înainte de a studia
în detaliu câteva dintre aceste funcţii, haideţi să vedem care este structura generală a interfeţei GDI.
Tipuri de apeluri de funcţii
În general, apelurile de funcţii GDI pot fi clasificate în mai multe categorii. Chiar dacă nu sunt foarte
stricte şi există unele suprapuneri, aceste categorii pot fi enunţate astfel:

 Funcţii care obţin (sau creează) şi eliberează (sau distrug) un context de dispozitiv.
 Funcţii care obţin informaţii despre contextul de dispozitiv.
 Funcţii care desenează ceva.
 Funcţii care stabilesc sau obţin atribute ale contextului de dispozitiv.
 Funcţii care lucrează cu obiecte GDI.

Elementele grafice pe care le afişaţi pe ecran sau le tipăriţi la imprimantă pot fi împărţite în mai multe
categorii, numite „primitive". Iată care sunt aceste categorii:

 Linii şi curbe. Liniile reprezintă baza oricărui sistem de desenare vectorial. GDI permite folosirea
liniilor drepte, a dreptunghiurilor, a elipselor (inclusiv subsetul de elipse cunoscute sub numele
de cercuri), a arcelor - care sunt curbe reprezentând porţiuni din circumferinţa unei elipse sau a
curbelor Bezier. Liniile sunt desenate folosind peniţa curentă selectată în contextul de dispozitiv.
 Suprafeţe pline. Dacă o serie de linii sau de curbe închid o suprafaţă, aceasta poate fi „umplută"
folosind pensula GDI curentă. Această pensulă poate fi o culoare compactă, un model (haşuri
orizontale, verticale sau pe diagonală) sau o imagine bitmap repetată pe verticală sau pe
orizontală.
 Imagini bitmap. Imaginile bitmap sunt matrice dreptunghiulare de biţi, care corespund pixelilor
unui dispozitiv de afişare. În general, acestea sunt folosite pentru afişarea imaginilor complexe
(deseori preluate din lumea reală) pe ecran sau pentru tipărirea acestora la imprimantă. De
asemenea, imaginile bitmap sunt folosite pentru afişarea unor mici imagini (cum ar fi pictograme,
indicatoare de mouse şi butoane de pe barele cu instrumente de lucru ale aplicaţiilor) care
trebuie afişate foarte rapid.
 Text. Textul este mai puţin „matematic" decât alte aspecte ale graficii pe calculator. Structurile
create pentru definirea fonturilor şi pentru obţinerea informaţiilor despre fonturi sunt printre cele
mai mari din Windows. Începând cu versiunea Windows 3.1, interfaţa GDI acceptă fonturile
TrueType, bazate pe contururi umplute, care pot fi manipulate de alte funcţii GDI. Windows 95
acceptă în continuare şi fonturile mai vechi, de tip bitmap (cum este fontul sistem prestabilit)
pentru compatibilitate şi pentru economisirea spaţiului de memorie.

Alte aspecte ale interfeţei GDI nu sunt la fel de uşor de clasificat. Printre acestea se numără:

 Moduri de mapare şi transformări. Modurile de mapare GDI vă permit să desenaţi folosind ca


unitate de măsură inci (sau fracţiuni de inci), milimetri sau orice altă unitate de măsură. De
asemenea. Windows 95 asigură suportul pentru o „transformare reală" exprimată printr-o
matrice 3x3. Această transformare permite deformarea şi rotirea obiectelor grafice. Din păcate,
această transformare nu este acceptată sub Windows 95.
 Metafişiere (metafiles). Un metafişier este o colecţie de comenzi GDI stocate într-o formă binară.
Metafişierele sunt folosite pentru transferarea reprezentărilor unor elemente grafice vectoriale
prin intermediul memoriei temporare (clipboard).
 Regiuni (regions). O regiune este o suprafaţă complexă de orice formă, definită ca o combinaţie
booleană de regiuni mai simple. În general, regiunile sunt stocate intern de GDI ca o serie de
linii de scanare, diferite de combinaţia de linii folosită iniţial pentru definirea regiunii. Puteţi să
folosiţi regiunile pentru contururi, pentru umplere sau pentru decupare.
 Căi (paths). O cale este o colecţie de linii drepte şi curbe stocate intern de GDI. Căile pot fi
folosite pentru desenare, pentru umplere sau pentru decupare. De asemenea, căile pot fi
transformate în regiuni.
 Decupare (clipping). Desenarea poate fi limitată la o anumită secţiune a zonei client, numită
zonă de decupare, care poate fi dreptunghiulară sau poate avea o altă formă, definită printr-o
serie de linii. Zona de decupare este definită, în general, de o cale sau de o regiune.
 Palete (palettes). Folosirea paletelor proprii este limitată, în general, numai la monitoarele care
pot reda 256 de culori. Windows rezervă 20 dintre aceste culori pentru sistemul de operare.
Celelalte 236 de culori pot fi modificate pentru afişarea corespunzătoare a imaginilor din lumea
reală, stocate ca imagini bitmap.
 Tipărire (printing). Deşi discuţiile din acest capitol sunt limitate doar la afişarea pe ecran, tot
ceea ce învăţaţi în acest capitol poate fi aplicat şi operaţiilor de tipărire. (Vezi Capitolul 15 pentru
alte informaţii despre tipărire.)

Contextul de dispozitiv
Atunci când vreţi să desenaţi la un dispozitiv de ieşire grafic (cum ar fi ecranul sau imprimanta)
trebuie să obţineţi mai întâi o variabilă handle a contextului de dispozitiv (DC - device context).
Variabila handle este apoi inclusă ca parametru în apelul unei funcţii GDI, identificând dispozitivul Ia
care vreţi să desenaţi. Contextul de dispozitiv conţine mai multe atribute curente, care specifică
modul de lucru al funcţiilor GDI pentru dispozitivul respectiv. Aceste atribute permit ca la apelarea
funcţiilor GDI să fie specificate numai coordonatele de început sau dimensiunea, nu şi toate celelalte
informaţii de care sistemul de operare are nevoie pentru desenarea obiectelor pe dispozitivul folosit.
Atunci când doriţi să modificaţi unul dintre aceste atribute ale contextului de dispozitiv, apelaţi o
funcţie specializată.
Obţinerea variabilei handle a contextului de dispozitiv
Sistemul de operare Windows vă pune la dispoziţie mai multe metode pentru obţinerea variabilei
handle a contextului de dispozitiv. Dacă obţineţi o variabilă handle a contextului de dispozitiv în
timpul prelucrării unui mesaj, ar trebui să ştergeţi această variabilă înainte de ieşirea din procedura
de fereastră. După ce este ştearsă, variabila handle nu mai poate fi folosită (nu mai este validă).
Cea mai cunoscută metodă de obţinere şi de ştergere a variabilei handle a contextului de dispozitiv
implică folosirea funcţiilor BeginPaint şi EndPaint în timpul prelucrării mesajului WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
[alte linii de program]
EndPaint (hwnd, &ps);
Variabila ps este o structură de tip PAINTSTRUCT. Câmpul hdc al acestei structuri conţine variabila
handle a contextului de dispozitiv. Folosind variabila handle a contextului de dispozitiv, obţinută prin
apelarea funcţiei BeginPaint, nu puteţi să desenaţi decât în regiunea invalidă a ferestrei.
Funcţia BeginPaint validează regiunea invalidă.
Programele Windows pot să obţină variabila handle a contextului de dispozitiv şi în timpul prelucrării
altor mesaje decât WM_PAINT:
hdc = GetDC (hwnd);
(alte linii de program]
ReleaseDC (hwnd, hdc);
Acest context de dispozitiv se aplică zonei client a ferestrei care are variabila
handle hwnd. Principala diferenţă între apelul de mai sus şi metoda folosirii
funcţiilor BeginPaint şi EndPainteste că variabila handle returnată de funcţia GetDC vă permite să
desenaţi în toată zona client a ferestrei. În plus, funcţiile GetDC şi ReleaseDC nu validează
eventualele regiuni invalide ale zonei client.
Un program Windows poate să obţină şi o variabilă handle a unui context de dispozitiv care se
aplică întregii ferestre, nu numai zonei client a ferestrei:
hdc = GetWindowDC (hwnd);
[alte linii de program]
ReleaseDC (hwnd, hdc);
[alte linii de program]
DeleteDC (hdc);
Obţinerea informaţiilor despre culori
Funcţia GetDeviceCaps vă permite să determinaţi modul de organizare a memoriei în adaptoarele
video şi numărul de culori care pot fi reprezentate. Apelul de mai jos returnează numărul de planuri
de culoare:
iPlanes = GetDeviceCaps (hdc, PLANES);
Apelul următor returnează numărul de biţi de culoare folosiţi pentru fiecare pixel:
iBitsPixel = GetDeviceCaps (hdc, BITSPIXEL);
Majoritatea adaptoarelor video care pot afişa culori folosesc fie mai multe planuri de culoare, fie mai
mulţi biţi de culoare pentru fiecare pixel, dar nu pe amândouă; cu alte cuvinte, unul dintre cele două
apeluri de mai sus va returna valoarea 1. Numărul de culori care pot fi redate de o placă video se
poate calcula cu formula următoare:
iColors = 1<<(iPlanes * iBitsPixel);
Această valoare poate să nu fie identică cu numărul de culori obţinut prin apelarea
funcţiei GetDeviceCaps cu parametrul NUMCOLORS:
iColors = GetDeviceCaps (hdc, NUMCOLORS);
Windows foloseşte pentru reprezentarea culorilor o valoare întreagă fără semn, pe 32 de biţi. Tipul
de date folosit pentru culori se numeşte COLORREF. Ultimii trei octeţi ai numărului (cei mai puţin
semnificativi) specifică valorile pentru culorile roşu, verde şi albastru, de la O la 255, aşa cum se
poate vedea în Figura 4-3. Rezultă o paletă potenţială de 224 culori (aproximativ 16 milioane de
culori).
Valoarea pe 32 de biţi de mai sus e numită deseori „culoare RGB". În fisierele antet din Windows
sunt definite mai multe macroinstrucţiuni pentru lucrul cu valorile RGB. Macroinstructiunea RGB
acceptă trei argumente, care reprezintă valorile pentru culorile roşu, verde şi albastru şi le combină
într-o valoarea întreagă pe 32 de biţi, fără semn.
Astfel, valoarea
RGB (255, 0, 255)
este de fapt 0x00FF00FF, valoarea RGB pentru magenta. Dacă toate cele trei argumente au
valoarea 0, se obţine negrul, iar dacă au valoarea 255, se obţine albul. Macroinstrucţiunile
GetRValue, GetGValue şi GetBValue extrag valorile pentru culorile primare, sub forma unor octeţi
fără semn, din valoarea RGB a culorii. Aceste macroinstructiuni sunt utile atunci când apelaţi funcţii
Windows care returnează culori RGB.
Salvarea contextului de dispozitiv
În mod normal, Windows creează un nou context de dispozitiv cu valori prestabilite atunci când
apelaţi una dintre funcţiile GetDC sau BeginPaint. Toate modificările făcute în atributele contextului
de dispozitiv se pierd atunci când contextul de dispozitiv este şters din memorie prin apelarea
funcţiei ReleaseDC sau a funcţiei EndPaint. Dacă programul trebuie să folosească un atribut cu o
valoarea diferită de cea prestabilită va trebui să iniţializaţi contextul de dispozitiv de fiecare dată
când obţineţi o variabilă handle:
caseWM_Paint:
hdc = BeginPaint (hwnd, &ps);
[iniţializează atributele contextului de dispozitiv]
[desenează zona client a ferestrei]
EndPaint (hwnd, &ps);
return 0;
Deşi această abordare este în general satisfăcătoare, s-ar putea să preferaţi să salvaţi modificările
făcute asupra contextului de dispozitiv ia distrugerea acestuia, astfel încât valorile salvate să
redevină active la apelarea funcţiilor GetDC sau BeginPaint.
Desenarea liniilor
Interfaţa Windows GDI conţine şi funcţiile SetPixel şi GetPixel. Deşi vom folosi funcţia SetPixel în
programul CONNECT din Capitolul 7, în programele de grafică propriu-zisă aceste funcţii sunt
rareori folosite. În majoritatea cazurilor, cea mai simplă primitivă grafică vectorială trebuie să fie
considerată linia.
Windows poate să deseneze linii drepte, linii eliptice (linii curbe, pe circumferinţa unei elipse) şi
curbe Bezier. Cele şapte funcţii acceptate de Windows 95 pentru desenarea liniilor sunt LineTo (linii
drepte), Polyline şi PolylineTo (o serie de linii drepte conectate), PolyPolyline (mai multe linii
poligonale), Arc (linii eliptice), PolyBezier şi PolyBezierTo. (Interfaţa GDI din Windows NT acceptă
încă trei funcţii pentru desenarea liniilor: ArcTo, AngleArc şi PolyDraw. Aceste funcţii nu sunt însă
acceptate în Windows 95.) Aspectul liniilor desenate cu aceste funcţii poate fi influenţat de cinci
atribute ale contextului de dispozitiv: poziţia curentă a peniţei (numai pentru
funcţiile LineTo, PolylineTo şi PolyBezierTo), peniţa selectată, modul de afişare a fondului (numai
pentru peniţele care nu desenează cu o culoare compactă), culoarea fondului (pentru modul
OPAQUE de afişare a fondului) şi modul de desenare.
Funcţia LineTo este una dintre puţinele funcţii GDI care nu are nevoie de dimensiunile complete ale
obiectului ce urmează să fie desenat. Funcţia LineTo desenează o linie de la „poziţia curentă"
definită în contextul de dispozitiv pană la punctul specificat la apelarea funcţiei (exclusiv acest
punct). Poziţia curentă este folosită ca punct de plecare şi de alte funcţii GDI. În contextul de
dispozitiv prestabilit, poziţia curentă are iniţial valoarea (0,0). Dacă apelaţi funcţia LineTo fără să
stabiliţi mai întâi poziţia curentă, funcţia desenează o linie pornind din colţul din stânga-sus al zonei
client.
Dacă doriţi să desenaţi o linie din punctul (xStart, yStart) pană în punctul (xEnd, yEnd) trebuie să
apelaţi mai întâi funcţia MoveToEx ca să stabiliţi ca poziţie curentă punctul (xStart, ySfart):
MoveToEx (hdc, xStart, yStart, &pt) ;
unde pt este o structură de tip POINT, definită în fişierele antet.
Funcţia MoveToEx nu desenează nimic, ci doar schimbă poziţia curentă. Poziţia curentă anterioară
este stocată în structura de tip POINT. Puteţi apoi să folosiţi funcţia LineTo ca să desenaţi linia:
LineTo (hdc, xEnd, yEnd);
Apelul de mai sus desenează o linie până în punctul (xEnd, yEnd), exclusiv acest punct. După apelul
funcţiei LineTo, poziţia curentă devine (xEnd, yEnd).
Puteţi să obţineţi poziţia curentă a peniţei apelând funcţia GetCurrentPosition astfel:
GetCurrentPositionEx (hdc, &pt) ;
Atunci când aveţi o matrice de puncte pe care vreţi să le conectaţi prin linii, puteţi să desenaţi mai
uşor liniile folosind funcţia Polyline. Instrucţiunea de mai jos desenează acelaşi dreptunghi ca şi
secvenţa de cod din exemplul anterior:
Polyline (hdc, pt, 5);
Ultimul parametru reprezintă numărul de puncte. Această valoare putea fi reprezentată şi prin
expresia (sizeof(pt) / sizeof(POINT)). Funcţia Polyline are acelaşi efect ca şi o
funcţie MoveToEx urmată de mai multe funcţii LineTo, exceptând faptul că funcţia Polyline nu
schimbă poziţia curentă. Funcţia PolylineTo este puţin diferită. Aceasta foloseşte ca punct de
plecare poziţia curentă şi stabileşte ca poziţie curentă sfârşitul ultimei linii desenate. Codul de mai
jos desenează acelaşi dreptunghi folosit ca exemplu şi mai sus:
MoveToEx (hdc, pt[0].x, pt[0].y, NULL) ;
PolylineTo (hdc, pt + 1,4) ;
Deşi puteţi să folosiţi funcţiile Polyline şi PolylineTo şi pentru un număr mic de funcţii, acestea sunt
mai utile atunci când desenaţi o curbă complexă formată din sute sau chiar mii de linii.
În continuare aş vrea să discutăm despre funcţia Arc, funcţie care desenează o curbă eliptică. Dar
funcţia Arc nu prea are sens dacă nu discutăm mai întâi despre funcţia Ellipse, care, la rândul ei, nu
are sens fără o discuţie despre funcţia Rectangle. Si dacă discutăm despre
funcţiile Rectangle şi Ellipse, putem la fel de bine să discutăm şi despre funcţiile RoundRect,
Chordşi Pie.
Problema este că funcţiile Rectangle, Ellipse, RoundRect, Chord şi Pie nu sunt tocmai nişte funcţii
de desenare a liniilor. Desigur, ele desenează şi linii, dar şi colorează zona închisă, cu pensula
curentă de colorare a suprafeţelor. În mod prestabilit, această pensulă are culoarea albă, aşa că s-ar
putea ca operaţia să nu fie atât de evidentă atunci când încercaţi pentru prima dată să utilizaţi
aceste funcţii. Deşi, în sens strict, funcţiile aparţin unei alte secţiuni din capitolul de faţă, „Desenarea
suprafeţelor pline", vom discuta acum despre fiecare.
Funcţiile de mai sus sunt asemănătoare prin faptul că sunt construite pe baza unui „dreptunghi de
încadrare" („bounding box"). Dumneavoastră definiţi o casetă care încadrează obiectul -
dreptunghiul de încadrare - şi Windows desenează obiectul în acest dreptunghi.
Cea mai simplă dintre aceste funcţii desenează un dreptunghi:
Rectangle (hdc, xLeft, yTop, xRight, yBottom) ;
Punctu cu coordonatele (xLeft, yTop) este punctul din stânga sus, iar punctul cu coordonatele
(xRight, yBottom) este punctul din dreapta jos.
După ce aţi aflat cum se desenează un dreptunghi, folosind aceiaşi parametri, veţi putea, la fel de
simplu, să desenaţi şi o elipsă:
Ellipse (hdc, xLeft, yTop, xRight, yBottom) ;
Funcţia pentru desenarea unui dreptunghi cu colţurile rotunjite foloseşte acelaşi dreptunghi de
încadrare ca şi funcţiile Rectangle şi Ellipse, dar are încă doi parametri:
RoundRect (hdc, xLeft, yTop, xRight, yBottom, xCornerEllipse, yCornerEllipse);
Pentru desenarea colţurilor rotunjite, Windows foloseşte o mică elipsă. Lăţimea elipsei
este xCornerEllipse iar înălţimea acesteia este yCornerEllipse. Colţurile dreptunghiului sunt cu atât
mai rotunjite cu cât valorile parametrilor xCornerEllipse şi yCornerEllipse sunt mai mari.
Dacă xCornerEllipse este egal cu jumătate din diferenţa dintre xLeft şi xRight iar yCornerEllipseeste
egal cu jumătate din diferenţa dintre yTop şi yBottom, atunci funcţia RoundRect va desena o elipsă.
Funcţiile Arc, Chord şi Pie primesc aceiaşi parametri:
Arc (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Chord (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Pie (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
În cazul funcţiei Arc, Windows şi-a terminat treaba, deoarece arcul este o linie eliptică, nu o
suprafaţă plină. În cazul funcţiei Chord, Windows uneşte capetele arcului, iar în cazul funcţiei Pie,
uneşte capetele arcului cu centrul elipsei. Interioarele suprafeţelor închise obţinute astfel sunt
colorate cu pensula curentă.
Folosirea peniţelor de stoc
Atunci când apelaţi oricare dintre funcţiile de desenare a liniilor, Windows foloseşte pentru
desenarea liniilor „peniţa" (pen) curentă selectată în contextul de dispozitiv. Peniţa selectată
determină culoarea, lăţimea şi tipul de linie (acestea pot fi continue, punctate sau întrerupte). Peniţa
din contextul prestabilit de dispozitiv se numeşte BLACK_PEN. Aceasta desenează o linie
compactă, de culoare neagră, cu grosimea de un pixel, indiferent de modul de mapare.
BLACK_PEN este una dintre cele trei peniţe „de stoc" (stock pen) furnizate de Windows. Celelalte
două peniţe de stoc sunt WHITE_PEN şi NULL_PEN. NULL_PEN este o peniţă care nu desenează
nimic. În plus, puteţi să creaţi peniţe proprii.
Peniţe sunt determinate de propriile variabile handle. În fişierele antet din Windows este definit tipul
de date HPEN, care reprezintă o variabilă handle a unei peniţe. Puteţi să definiţi o variabilă (de
exemplu, hPen) folosind această definiţie de tip:
HPEN hPen ;
Puteţi să obţineţi variabila handle a uneia dintre peniţele de stoc apelând funcţia GetStockObject. De
exemplu, să presupunem că vreţi să folosiţi peniţa de stoc numită WHITE_PEN. Obţineţi variabila
handle a acesteia astfel:
hPen = GetStockObject (WHITE_PEN);
Apoi trebuie să selectaţi în contextul de dispozitiv peniţa a cărei variabilă aţi obţinut-o, apelând
funcţia SelectObject:
SelectObject (hdc, hPen) ;
După acest apel toate liniile pe care le desenaţi vor folosi peniţa WHITE_PEN, până când selectaţi o
altă peniţă în contextul de dispozitiv sau ştergeţi contextul de dispozitiv.
În loc să definiţi explicit variabila hPen, puteţi să combinaţi
funcţiile GetStockObject şi SelectObject într-o singură instrucţiune:
SelectObject (hdc, GetStockObject (WHITE_PEN)) ;
Dacă apoi vreţi să reveniţi la peniţa BLACK_PEN, puteţi să obţineţi o variabilă handle a acesteia şi
să o selectaţi în contextul de dispozitiv tot cu o singură instrucţiune:
SelectObject (hdc, GetStockObject (WHITE_PEN));
Funcţia SelectObject returnează variabila handle a peniţei selectate anterior în contextul de
dispozitiv. Dacă deschideţi un nou context de dispozitiv şi executaţi instrucţiunea:
hPen = SelectObject (hdc, GetStockObject (WHITE_PEN)) ;
atunci peniţa curentă selectată în contextul de dispozitiv va fi WHITE_PEN şi hPen va fi variabila
handle a peniţei BLACK_PEN. Puteţi apoi să selectaţi peniţa BLACK_PEN în contextul de dispozitiv
cu următoarea instrucţiune:
SelectObject (hdc, hPen) ;
Pentru a crea o peniţă proprie apelaţi funcţia:
hPen = CreatePen (iPenStyle, iWidth, rgbColor) ;
Parametrul iPenStyle precizează dacă se desenează o linie continuă, o linie punctată sau una
întreruptă. Parametrul iPenStyle poate fi unul dintre identificatorii definiţi în fişierele antet din
Windows.
Parametrul rgbColor din funcţia CreatePen este un număr întreg fără semn reprezentând culoarea
peniţei. Pentru toate stilurile de peniţe, exceptând PS_INSIDEFRAME, atunci când selectaţi peniţa
în contextul de dispozitiv, Windows converteşte acest parametru la cea mai apropiată culoare pură
pe care o poate reprezenta dispozitivul de afişare. PS_INSIDEFRAME este singurul stil care poate
folosi culori amestecate, dar numai pentru grosimi mai mari de un pixel.
Desenarea suprafeţelor pline
Cele şapte funcţii Windows pentru desenarea figurilor sunt prezentate în tabelul următor:

Funcţia Figura
Rectangle Dreptunghi cu colţuri drepte
Ellipse Elipsă
RoundRect Dreptunghi cu colţuri rotunjite
Chord Arc pe circumferinţa unei elipse, având
capetele unite printr-o coardă
Pie Suprafaţă de forma unei felii de plăcintă,
reprezentând un segment de elipsă.
Polygon Figură geometrică având mai multe laturi
PolyPolygon Mai multe figuri geometrice cu mai multe
laturi
Windows desenează conturul figurilor folosind peniţa curentă selectată în contextul de dispozitiv.
Pentru acest contur sunt folosite atributele stabilite pentru modul de desenare a fondului, culoarea
fondului şi modul de desenare, ca şi în cazul desenării unei linii simple. Tot ceea ce aţi învăţat
despre linii se aplică şi în cazul contururilor.
Figurile sunt umplute folosind pensula selectată în contextul de dispozitiv. În mod prestabilit, aceasta
este pensula de stoc WHITE_BRUSH, ceea ce înseamnă că interiorul figurilor va fi umplut cu alb.
Windows defineşte şase pensule de stoc: WHITE-_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,
DKGRAY_BRUSH, BLACK_BRUSH şi NULL_BRUSH (sau HOLLOW_BRUSH).
Puteţi să selectaţi una dintre aceste pensule în contextul de dispozitiv la fel cum selectaţi o peniţă de
stoc. Windows defineşte tipul HBRUSH ca variabilă handle a unei pensule, aşa că puteţi să definiţi
mai întâi o variabilă de acest tip:
HBRUSH hBrush ;
Puteţi să obţineţi variabila handle a pensulei de stoc GRAY_BRUSH apelând
funcţia GetStockObject:.
hBrush = Get&toCkObject (GBAY_BRUSH) ;
Puteţi să o selectaţi apoi în contextul de dispozitiv folosind funcţia SelectObject:
SelectObject (hdc, hBrush) ;
Din acest moment, figurile desenate vor avea interiorul gri.
Dacă vreţi să desenaţi o figură fără contur, selectaţi în contextul de dispozitiv peniţa NULL_PEN:
SelectObject (hdc, GetStockObject (NULL_PEN)) ;
Dacă vreţi să desenaţi o figură fără să îi umpleţi interiorul, selectaţi în contextul de dispozitiv pensula
NULL_BRUSH:
SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
Aşa cum puteţi să creaţi peniţe proprii, puteţi să creaţi şi pensule proprii.
Sistemele de coordonate ale dispozitivului
Windows mapează coordonatele logice specificate în funcţiile GDI la coordonatele dispozitivului.
Înainte de a discuta despre sistemele de coordonate logice folosite de diferitele moduri de mapare,
vom discuta despre diferitele sisteme de coordonate de dispozitiv definite în Windows pentru zona
de afişare. Deşi în general am lucrat doar în zona client a ferestrei, în anumite situaţii Windows
foloseşte alte două sisteme de coordonate de dispozitiv. În toate sistemele de coordonate de
dispozitiv sunt folosiţi pixelii ca unitate de măsură. Valorile de pe axa orizontală (x) cresc de la
stânga la dreapta iar valorile de pe axa verticală (y) cresc de sus în jos.
Atunci când folosim întregul ecran, spunem că lucrăm în „coordonate ecran". Colţul din stânga-sus
este punctul de coordonate (0, 0). Coordonatele ecran sunt folosite în mesajul WM_MOVE (pentru
alte ferestre decât ferestrele descendent) şi în următoarele funcţii
Windows: CreateWinriow şi MoveWindow (ambele pentru alte ferestre decât ferestrele
descendent), GetMessagePos, GetCursorPos, SetCursorPos, GetWindowRect,
WindowFromPoint şi SetBrushOrgEx. Acestea sunt funcţii care fie nu au o fereastră asociată (cum
ar fi cele două funcţii pentru cursor), fie trebuie să mute (sau să găsească) o fereastră pe baza unui
punct de pe ecran. Dacă folosiţi funcţia CreateDC cu parametrul DISPLAY ca să obţineţi un context
de dispozitiv pentru întregul ecran, atunci coordonatele logice specificate la apelarea funcţiilor GDI
vor fi mapate la coordonatele ecranului.
„Coordonatele de fereastră" se referă la întreaga fereastră a ecranului, inclusiv bara de titlu, meniu,
barele de derulare şi chenarul ferestrei. Pentru o fereastră normală, punctul (0, 0) este colţul din
stânga-sus al chenarului de redimensionare. Coordonatele de fereastră sunt folosite mai rar în
Windows, dar dacă obţineţi un context de dispozitiv cu ajutorul funcţiei GetWindowDC, atunci
coordonatele logice specificate la apelarea funcţiilor GDI vor fi mapate la coordonatele ferestrei.
Al treilea sistem de coordonate de dispozitiv - cu care vom lucra cel mai des -foloseşte
„coordonatele zonei client". Punctul (0,0) este colţul din stânga-sus al zonei client. Dacă obţineţi un
context de dispozitiv cu ajutorul funcţiei GetDC sau al funcţiei BeginPaint, atunci coordonatele logice
specificate Ia apelarea funcţiilor GDI vor fi mapate la coordonatele zonei client.
Puteţi să transformaţi coordonatele zonei client în coordonatele ecranului şi invers folosind
funcţiile ClientToScreen şi ScreenToClient. De asemenea, puteţi şă obţineţi poziţia şi dimensiunea
întregii ferestre în coordonate ecran folosind funcţia GetWindowRect.
Curbe Bezier

1 Curbe Bezier

Termenul „spline" se referea odată la o riglă flexibilă din lemn, metal sau cauciuc (florar), folosită
pentru desenarea curbelor. De exemplu, dacă aveaţi un număr de puncte aparţinând unui grafic şi
doreaţi să desenaţi o curbă între acestea (prin interpolare sau extrapolare) trebuia să marcaţi mai
întâi punctele pe hârtie. Ancorând apoi rigla în punctele respective, nu vă rămânea decât să trasaţi
curba de-a lungul riglei. În prezent, „spline" se referă la o funcţie matematică. Aceste funcţii au
diferite forme. Curbele Bezier sunt unele dintre cele mai cunoscute pentru programarea grafică. În
anii '60, compania de automobile Renault trecea de la proiectarea manuală a caroseriilor (care
implica folosirea argilei) la o proiectare asistată de calculator. Erau necesare instrumente
matematice, iar Pierre Bezier a pus la punct un set de formule care s-au dovedit foarte utile pentru
rezolvarea acestei probleme.
De atunci, datorită formei bidimensionale, curba Bezier s-a dovedit a fi cea mai utilă curbă pentru
grafica pe calculator. De exemplu, în limbajul PostScript funcţiile Bezier sunt folosite pentru toate
curbele - elipsele sunt aproximare prin curbe Bezier De asemenea, funcţiile Bezier sunt folosite
pentru definirea contururilor caracterelor din fonturile PostScript. (Fonturile TrueType folosesc o
formă simplificată, mai rapida, a curbelor.)
O curbă Bezier este definită prin patru puncte - două capete şi două puncte de control. Capetele
curbei sunt ancorate în cele două puncte finale. Punctele de control acţionează ca nişte „magneţi"
care deformează linia dreaptă dintre cele două puncte finale.
Funcţiile Bezier sunt considerate utile pentru proiectarea asistată de calculator, datorită câtorva
caracteristici:

 În primul rând, cu puţină practică, de obicei puteţi să manipulaţi curba până ajunge la o formă
apropiată de cea dorită.
 În al doilea rând, curbele Bezier sunt foarte uşor de controlat. În unele variante ale funcţiilor
spline, curba nu trece prin nici unul din punctele care o definesc. Curbele Bezier, însă, sunt
întotdeauna ancorate în cele două puncte finale. (Aceasta este una dintre ipotezele de la care
porneşte calculul formulei Bezier.) De asemenea, unele forme ale funcţiilor spline au puncte de
singularitate, din care curba se întinde la infinit. În proiectarea asistată de calculator acest lucru
este de cele mai multe ori evitat. Ca urmare, curbele Bezier sunt întotdeauna limitate de un
patrulater (numit „carcasă convexă" - „convex hull") format prin unirea punctelor finale şi a
punctelor de control.
 În al treilea rând, o altă caracteristică a curbelor Bezier implică relaţiile dintre punctele finale şi
punctele de control. Curba este întotdeauna tangentă la linia trasată de la primul punct final la
primul punct de control şi are întotdeauna aceeaşi direcţie cu această linie. (Această
caracteristică este ilustrată vizual de programul BEZIER.) De asemenea, curba este întotdeauna
tangentă la linia trasată de la al doilea punct final la al doilea punct de control şi are întotdeauna
aceeaşi direcţie cu această linie. (Acestea sunt alte două ipoteze de la care este derivată
formula Bezier.)
 În al patrulea rând, curbele Bezier sunt satisfăcătoare şi din punct de vedere estetic. Ştiu că
acesta este un criteriu subiectiv, dar nu este numai părerea mea.
Înainte de apariţia sistemului de operare Windows 95, trebuia să creaţi propriile curbe Bezier
folosind funcţia Polyline. De asemenea, trebuia să cunoaşteţi următoarele ecuaţii parametrice ale
curbelor Bezier:
x(t) = (1-t)3x0 + 3t(1-t)2x1 + 3t2(1-t)x2 + t3x3
y(t) = (1-t)3y0 + 3t(1-t)2y1 + 3t2(1-t)y2 + t3y3
unde (x0, y0) este punctul de început al curbei, (x3, y3) este punctul de sfârşit al curbei, iar cele două
puncte de control sunt (x1, y1) şi (x2, y2). Curba este trasată pentru t având valori de la 0 la 1.
În Windows 95 nu mai este nevoie să ştiţi aceste formule. Pentru trasarea uneia sau a mai multor
curbe Bezier conexe, puteţi să folosiţi instrucţiunea:
PolyBezier (hdc, pt, iCount) ;
sau instrucţiunea:
PolyBezierTo (hide, pt, iCount) ;
În ambele cazuri, pt este o matrice de structuri POINT.
Pentru funcţia PolyBezier primele patru puncte specifică (în această ordine) punctul de început,
primul punct de control, al doilea punct de control şi punctul final al curbei Bezier. Următoarele curbe
Bezier au nevoie doar de trei puncte, deoarece punctul de început al următoarei curbe Bezier este
punctul de sfârşit al primei curbe, şi aşa mai departe. Parametrul iCount reprezintă numărul de
puncte din matrice, adică unu plus de trei ori numărul curbelor pe care vreţi să le desenaţi.
Funcţia PolyBezierTo foloseşte poziţia curentă ca punct de început pentru prima curbă Bezier.
Fiecare curbă desenată are nevoie doar de trei puncte. La returnarea funcţiei, poziţia curentă este
punctul final al ultimei curbe desenate.
Atenţie: atunci când trasaţi o serie de curbe Bezier conectate între ele, legătura va fi lină numai dacă
al doilea punct de control al primei curbe, punctul de sfârşit al primei curbe (care este şi punctul de
început al celei de-a doua curbe) şi primul punct de control al celei de-a doua curbe sunt coliniare,
adică se află pe aceeaşi linie dreaptă.

Principiile de lucru cu tastatura

1 Tastatura
Arhitectura bazată pe mesaje a sistemului de operare Windows este ideală pentru lucrul cu
tastatură. Programul „află" despre apăsarea unor taste prin intermediul mesajelor care ajung la
procedura de fereastră.
De fapt, lucrurile sunt puţin mai complicate: atunci când utilizatorul apasă şi eliberează tastele,
driverul de tastatură transmite sistemului de operare informaţiile legate de acţiunile asupra tastelor.
Windows salvează aceste acţiuni (sub formă de mesaje) în coada de aşteptare a sistemului.
Mesajele de la tastatură sunt apoi transferate, unul câte unul, în coada de mesaje a programului
căruia îi aparţine fereastra ce deţine „cursorul de intrare" (despre care vom discuta imediat).
Programul distribuie mesajele procedurii de fereastră corespunzătoare.
Motivul acestui proces în două etape - stocarea mesajelor mai întâi în coada de mesaje a sistemului
şi apoi transferarea acestora în coada de mesaje a aplicaţiilor - este legat de sincronizare. Atunci
când utilizatorul apasă pe taste într-un ritm mai rapid decât cel în care programul prelucrează
mesajele primite, Windows stochează acţiunile neprelucrate în coada de aşteptare a sistemului,
deoarece una dintre acţiunile asupra tastaturii poate avea ca efect comutarea cursorului de intrare
(input focus) către un alt program. În consecinţă, următoarele taste trebuie transmise celui de-al
doilea program.
Deşi tastatura este principala sursă de intrări de la utilizatori a unui program pentru Windows,
programele nu trebuie să reacţioneze la toate mesajele pe care le primeşte de la tastatură. Multe
funcţii ale tastaturii sunt tratate chiar de Windows. De exemplu, puteţi să ignoraţi apăsările de taste
legate de funcţii sistem. Acestea sunt, în general, combinaţii cu tasta Alt.
Programul nu este obligat să monitorizeze aceste taste, deoarece Windows îi comunică efectul
acestora. (Totuşi, dacă este nevoie, programul poate face şi acest lucru.) De exemplu, dacă
utilizatorul selectează un articol dintr-un meniu cu ajutorul tastaturii. Windows trimite programului un
mesaj prin care îi comunică articolul de meniu selectat, indiferent dacă utilizatorul a folosit mouse-ul
sau tastatura.
Unele programe pentru Windows folosesc „acceleratori" (sau „taste de accelerare") pentru opţiunile
de meniu folosite mai frecvent. Acceleratorii sunt, de obicei, combinaţii de taste funcţionale - sau alte
taste corespunzătoare unor caractere - cu tasta Ctrl. Tastele de accelerare sunt definite în fişierul de
resurse al programului.
Tastatura trebuie să fie partajată de toate aplicaţiile rulate simultan sub Windows. Unele aplicaţii pot
avea mai multe ferestre, iar tastatura trebuie să fie partajată de toate ferestrele din cadrul aceleiaşi
aplicaţii. Atunci când este apăsată o tastă, o singură fereastră trebuie să primească mesajul privind
apăsarea tastei respective. Fereastra care primeşte acest mesaj este fereastra care deţine „cursorul
de intrare" („input focus").
Dacă fereastra activă a fost redusă la o pictogramă, atunci nici o fereastră nu deţine cursorul de
intrare. Windows continuă să trimită programului mesaje de la tastatură, dar acestea sunt trimise
într-o altă formă decât mesajele trimise unei ferestre active normale.
O procedură de fereastră poate să afle când are cursorul de intrare prin interceptarea mesajelor
WM_SETFOCUS şi WM_KILLFOCUS. Mesajul WM_SETFOCUS indică faptul că fereastra primeşte
cursorul de intrare (input focus), iar mesajul WM_KILLFOCUS indică faptul că fereastra pierde
cursorul de intrare.
Atunci când apăsaţi o tastă, Windows inserează în coada de aşteptare a ferestrei care deţine
cursorul de intrare un mesaj WM_KEYDOWN sau un mesaj WM_SYSKEYDOWN. Atunci când
eliberaţi fasta, Windows inserează în coada de aşteptare a ferestrei un mesaj WM_KEYUP sau un
mesaj WM_SYSKEYUP.

Tasta a fost apăsată Tasta a fost


eliberată
Tastă obişnuită WM_KEYDOWN WM_KEYUP
Tastă de sistem WM_SYSKEYDOWN WM_SYSKEYUP

De obicei, mesajele de apăsare şi de eliberare a tastei sunt trimise în pereche. Totuşi, dacă ţineţi
apăsată o tastă pană când aceasta se autorepetă, Windows trimite procedurii de fereastră o serie de
mesaje WM_KEYDOWN (sau WM_SYSKEYDOWN) şi un singur mesaj WM_KEYUP sau
(WM_SYSKEYUP) după eliberarea tastei. Ca toate mesajele trimise prin coada de aşteptare,
mesajele pentru acţionări de taste conţin informaţii de timp. Puteţi să obţineţi momentul relativ în
care a fost apăsată sau eliberată o tastă apelând funcţia GetMessageTime.

Taste obişnuite şi taste de sistem


Particula „SYS" din mesajele WM_SYSKEYDOWN şi WM_SYSKEYUP provin de la cuvântul
„system" şi se referă la acţionările de taste care prezintă mai multă importanţă pentru Windows
decât pentru aplicaţiile Windows. Mesajele WM_SYSKEYDOWN şi WM_SYSKEYUP sunt generate,
de obicei, pentru taste apăsate în combinaţie cu tasta Alt. Aceste acţionari de taste apelează opţiuni
din meniul programului ori din meniul sistem, sunt folosite pentru funcţii ale sistemului, cum ar fi
comutarea ferestrei active (Alt+Tab sau Alt+Esc) sau sunt folosite pentru acceleratori de meniu (Alt
în combinaţie cu o tastă funcţională). De obicei, programul ignoră mesajele WM_SYSKEYDOWN şi
WM_SYSKEYUP şi le retransmite funcţiei DefWindowProc. Deoarece Windows manipulează
combinaţiile Alt+tastă, nu este nevoie să interceptaţi aceste mesaje. Procedura de fereastră va primi
alte mesaje, legate de rezultatul acestor acţionări de taste (cum ar fi selectarea unei opţiuni de
meniu). Chiar dacă doriţi să includeţi în program codul pentru interceptarea acestor mesaje (aşa
cum vom face în programul KEYLOCK din acest capitol), retransmiteţi mesajele
funcţiei DefWindowProc după ce le prelucraţi, astfel încât Windows să le poată folosi în scopurile
obişnuite.
Mesajele WM_KEYDOWN şi WM_KEYUP sunt generate, de obicei, pentru tastele apăsate şi
eliberate fără tasta Alt. Programul poate să folosească sau să ignore aceste mesaje. Sistemul de
operare le ignoră.
Pentru toate mesajele legate de acţionările de taste variabila lParam (pe 32 de biţi) transmisă
procedurii de fereastră este împărţită în şase câmpuri: contorul de repetare, codul de scanare OEM,
indicatorul flag pentru taste extinse, codul de context, starea anterioară a tastei şi starea
de tranziţie.
Contorul de repetare (Repeat Count) specifică numărul de acţionari de taste reprezentat de un
mesaj. În majoritatea cazurilor, contorul de repetare are valoarea 1. Totuşi, dacă procedura de
fereastră nu reuşeşte să prelucreze mesajele de apăsare a unei taste în ritmul de autorepetare (în
mod prestabilit aproximativ 10 caractere pe secundă). Windows combină mai multe mesaje
WM_KEYDOWN sau WM_SYSKEYDOWN într-un singur mesaj şi incrementează corespunzător
contorul de repetare. Contorul de repetare are întotdeauna valoarea 1 pentru mesajele WM_KEYUP
şi WM_SYSKEYUP.
Codul de scanare OEM (OEM Scan Code) este codul de scanare al tastaturii, generat de
componentele hardware. (Dacă aţi scris programe în limbaj de asamblare, acest cod este identic cu
cel transmis în registrul AH, în timpul întreruperii apelului BIOS 16H.) În general, aplicaţiile Windows
ignoră acest cod, deoarece există metode mai bune de decodificare a informaţiilor de la tastatură.
Indicatorul flag pentru taste extinse (Extended Key Flag) are valoarea 1 dacă mesajul este generat
de una dintre tastele suplimentare de pe tastatura IBM extinsă. (Tastatura IBM extinsă are tastele
funcţionale în partea de sus şi un bloc separat sau combinat de taste pentru tastele de deplasare şi
tastele numerice.) Acest indicator are valoarea 1 pentru tastele Alt şi Ctrl din partea dreaptă a
tastaturii, pentru tastele de deplasare (inclusiv tastele Insert şi Delete) care nu fac parte din blocul de
taste numerice, pentru tastele Slash (/) şi Enter din blocul de taste numerice şi pentru tasta Num
Lock. În general, programele Windows ignoră acest indicator.
Codul de context (Context Code) are valoarea 1 dacă este apăsată tasta Alt. Acest bit va avea
întotdeauna valoarea 1 pentru mesajele WM_SYSKEYUP şi WM_SYSKEYDOWN şi valoarea 0
pentru mesajele WM_KEYUP si WM_KEYDOWN, cu două excepţii:

1. O fereastră activă redusă la o pictogramă nu deţine cursorul de intrare. Toate acţionările de


taste generează mesaje WM_SYSKEYUP şi WM_SYSKEYDOWN. Dacă tasta Alt nu este
apăsată, bitul pentru codul de context are valoarea 0. (Windows foloseşte aceste mesaje
astfel încât fereastra activă redusă la o pictogramă să nu prelucreze mesajele de la tastatură.)
2. În cazul folosirii unui driver de tastatură pentru alte limbi decât limba engleză, unele caractere
sunt generate prin combinarea tastelor Shift, Ctrl sau Alt cu alte taste. În această situaţie, bitul
pentru codul de context din variabila IParam care însoţeşte mesajele WM_KEYUP şi
WM_KEYDOWN are valoarea 1, dar mesajele nu reprezintă acţionări de taste de sistem.

Starea anterioară a tastei (Previous Key State) are valoarea 0 dacă tasta nu a fost anterior apăsată,
şi valoarea 1 dacă tasta a fost apăsată. Are întotdeauna valoarea 1 pentru mesajele WM_KEYUP şi
WM_SYSKEYUP, dar poate fi 0 sau 1 pentru mesajele WM_KEYDOWN şi WM_SYSKEYDOWN.
Valoarea 1 indică faptul că s-a primit un mesaj generat de autorepetarea unei taste.
Starea de tranziţie (Transition State) are valoarea 0 dacă tasta este apăsată şi valoarea 1 dacă tasta
este eliberată. Acest bit are valoarea 1 pentru mesajele WM_KEYUP si WM_SYSKEYUP si
valoarea 0 pentru mesajele WM_KEYDOWN şi WM_SYSKEYDOWN.

Coduri virtuale de taste


Deşi unele informaţii din parametrul lParam pot fi utile pentru prelucrarea mesajelor WM_KEYUP,
WM_SYSKEYUP, WM_KEYDOWN şi WM_SYSKEYDOWN, parametrul wParam este mult mai
important. Acest parametru conţine codul virtual care identifică tasta apăsată sau eliberată. Codurile
virtuale pe care le veţi folosi cel mai des au nume definite în fişierele antet din Windows.
Parametrii lParam şi wParam care însoţesc mesajele WM_KEYUP, WM_SYSKEYUP,
WM_KEYDOWN şi WM_SYSKEYDOWN nu spun nimic programului despre starea tastelor de
modificare. Puteţi să obţineţi starea oricărei taste virtuale folosind funcţia GetKeyState. Această
funcţie este folosită, de obicei, pentru obţinerea stării tastelor de modificare (Shift, Alt şi Ctrl) şi a
tastelor de comutare (Caps Lock, Num Lock şi Scroll Lock). De exemplu:
GetKeyState (VK_SHIFT);
returnează o valoare negativă (adică un număr în care primul bit are valoarea 1) dacă tasta Shift
este apăsată. Valoarea returnată de apelul:
GetKeyState (VK_CAPITAL);
are un 1 în bitul cel mai puţin semnificativ dacă tasta Caps Lock este activă.
Dacă într-adevăr aveţi nevoie de starea curentă a unei taste, puteţi să folosiţi
funcţia GetAsyncKeyState.

Utilizarea mesajelor de acţionare a tastelor


Ideea unui program care să obţină informaţii despre toate acţiunile exercitate asupra tastelor este
bună. Cu toate acestea, majoritatea programelor Windows ignoră cea mai mare parte a acestor
mesaje. Mesajele WM_SYSKEYUP şi WM_SYSKEYDOWN sunt folosite pentru funcţiile sistemului,
aşa că nu este nevoie să le interceptaţi. De asemenea, dacă prelucraţi mesajele WM_KEYDOWN,
de obicei puteţi să ignoraţi mesajele WM_KEYUP.
În general, programele pentru Windows folosesc mesajele WM_KEYDOWN pentru tastele care nu
generează caractere. Deşi s-ar putea să vă vină ideea să folosiţi mesajele pentru acţionări de taste
în combinaţie cu informaţiile despre tastele de modificare (furnizate de funcţia GetKeyState) ca să
transformaţi mesajele pentru acţionări de taste în mesaje pentru caractere, nu faceţi acest lucru. Veţi
avea probleme datorită diferenţelor între tastaturile internaţionale. De exemplu, dacă primiţi un
mesaj WM_KEYDOWN pentru care parametrul wParam are valoarea 33H, ştiţi că utilizatorul a
apăsat tasta 3. Până aici totul este bine. Dacă apelaţi funcţia GetKeyState veţi afla că tasta Shift
este apăsată şi s-ar putea să presupuneţi că utilizatorul a introdus un caracter diez (#), lucru care
poate să nu fie adevărat. Un utilizator britanic ar fi introdus caracterul £. Mesajele WM_KEYDOWN
sunt utile pentru tastele de deplasare, tastele funcţionale şi tastele speciale, precum Insert, Delete.
Uneori tastele Insert, Delete si tastele funcţionale sunt folosite ca acceleratori pentru comenzi de
meniu. Deoarece Windows transformă acceleratorii în comenzi de meniu, nu este nevoie să
prelucraţi în program mesajele pentru acţionarea tastelor de accelerare. Unele programe non-
Windows folosesc foarte des tastele funcţionale în combinaţie cu tastele Alt, Ctrl şi Shift. Puteţi face
acest lucru şi în programele pentru Windows, dar nu este recomandat. Dacă vreţi să folosiţi tastele
funcţionale, acestea trebuie să dubleze comenzi din meniuri. Unul dintre obiectivele sistemului de
operare Windows este să nu oblige utilizatorii să memoreze sau să folosească liste complicate de
comenzi.
Am reuşit să eliminăm aproape totul, cu excepţia unui singur lucru: de cele mai multe ori veţi
prelucra mesajele WM_KEYDOWN numai pentru tastele de deplasare a cursorului. Atunci când
prelucraţi mesajele trimise de tastele de deplasare puteţi să verificaţi şi starea tastelor Shift şi Ctrl,
folosind funcţia GetKeyState. Unele funcţii Windows folosesc de multe ori tastele de deplasare în
combinaţie cu tasta Shift pentru extinderea unei selecţii - de exemplu într-un procesor de texte.
Tasta Ctrl este folosită deseori pentru a schimba semnificaţia unei taste de deplasare. De exemplu,
tasta Ctrl în combinaţie cu săgeata spre dreapta mută cursorul cu un cuvânt Ia dreapta.
Am discutat mai devreme despre ideea transformării mesajelor generate de acţiona rea tastelor în
mesaje caracter ţinând cont de starea tastelor de modificare şi v-am avertizat că acest lucru nu este
suficient: trebuie să ţineţi seama şi de configuraţia diferită a tastaturii de la o ţară la alta. Din acest
motiv, nu trebuie să încercaţi să transformaţi dumneavoastră mesajele generate de acţionarea
tastelor în mesaje caracter. Windows o poate face în locul dumneavoastră. Aţi mai văzut această
secvenţă de cod:
while (GetMessage (&msg, NULL, 0, 0))
{ TranslateMessage (&msg);
DispatchMessage (&msg);
}
Acesta este un ciclu tipic de tratare a mesajelor din funcţia WinMain. Funcţia GetMessage preia
următorul mesaj din coada de aşteptare şi completează câmpurile
structurii msg. Funcţia DispatchMessage transmite mesajul procedurii de fereastră corespunzătoare.
Între cele două funcţii este apelată funcţia TranslateMessage, care transformă mesajele generate de
acţionarea tastelor în mesaje caracter. Dacă mesajul este WM_KEYDOWN sau
WM_SYSKEYDOWN şi dacă tasta apăsată, în funcţie de starea tastelor de modificare, generează
un caracter, atunci funcţia TranslateMessage inserează un mesaj caracter în coada de aşteptare.
Acesta va fi următorul mesaj pe care îl va prelua funcţia GetMessage după mesajul generat de
acţionarea tastei. Există patru mesaje caracter:

Caractere Caractere „moarte"


Caractere non-sistem WM_CHAR WM_DEADCHAR
Caractere sistem WM_SYSCHAR WM_SYSDEADCHAR

Mesajele WM_CHAR şi WM_DEADCHAR sunt obţinute din mesaje WM_KEYDOWN. Mesajele


WM_SYSCHAR şi WM_SYSDEADCHAR sunt obţinute din mesaje WM_SYSKEYDOWN. În
majoritatea cazurilor programul poate să ignore toate celelalte mesaje în afară de WM_CHAR.
Parametrul lParam transmis procedurii de fereastră are acelaşi conţinut ca şi parametrul lParam al
mesajului generat de acţionarea tastei din care a fost obţinut mesajul caracter.
Parametrul wParam conţine codul ASCII al caracterului.
O metodă cea mai simplă de a crea o interfaţă a tastaturii este utilizarea logicii prelucrării mesajelui
WM_KEYDOWN în procedura de fereastră. De exemplu:
...
case WM_KEYDOWN:
switch(wParam)
{
case ‘x’: // При нажатии клавиши ‘x’ включается
// обработчик данного сообщения
[ Необходимые операторы обработки ]
break;
...
}
return 0;
...
Acest fragment de program este asemănător cu secvenţele de cod pentru tratarea caracterelor dintr-
un program MS-DOS obişnuit.