Noţiuni Elementare de Winapi: 1 Structura de Bază A Unui Program Windows
Noţiuni Elementare de Winapi: 1 Structura de Bază A Unui Program Windows
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ă:
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ă.
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.
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.
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.