Grafik med GDI

Tags:    c++
<< < 123 > >>
Skrevet af Bruger #8985 @ 12.05.2009
Artikel filer

Virtuelle og visuelle bitmaps


Måske blev du lidt skuffet, da du fandt ud af, at forrige brug af termet "bitmap" ikke involverede en bitmap-fil, der blev læst af programmet og vist i vinduet. Det kan også være, du ikke gjorde. Uanset hvad vil jeg råde bod på det nu, ved at vise, hvordan vi kan læse data fra en BMP-fil og få en HBITMAP ud af det.

Jeg sidder inde med følgende Bitmap (jeg kan af sikkerhedsmæssige årsager ikke oplyse navnet på det program, jeg har produceret grafikken i):



Den er gemt i filen Smiley.bmp, som ligger i samme mappe som mit program. Følgende er et eksempel, der læser filen og viser resultatet i vinduets indholdområde.
Fold kodeboks ind/udKode 


Og lad os da endelig se resultatet:



Det eneste nye her er denne linje:

hSmiley = (HBITMAP)LoadImage(hInst, TEXT("Smiley.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

LoadImage
-funktionen ser således ud:

HANDLE LoadImage(
HINSTANCE hinst,
LPCTSTR lpszName,
UINT uType,
int cxDesired,
int cyDesired,
UINT fuLoad
);


hinst tager programmets "Instance Handle". På den måde kan funktionen finde ud af, hvor programmet ligger på harddisken, og altå lave den relative sti "Smiley.bmp" til en absolut sti. Hvis ikke du har en HINSTANCE ved hånden, kan du få den ved et kald til GetModuleHandle(NULL).
lpszName er stien til billedet. Den kan være relativ såvel som absolut.
uType er billedets type. LoadImage understøtter tre typer: Bitmaps (*.bmp), Ikoner (*.ico) og markører (*.cur).
cxDesired og cyDesired er de ønskede dimensioner for billedet. Hvis begge er sat til 0, bliver billedets originale størrelse brugt.
fuLoad er måden, hvorpå billedet skal indlæses. LR_LOADFROMFILE læser direkte fra filen. Her er en liste over måder, billedet kan indlæses på: http://msdn.microsoft.com/en-us/library/ms648045.aspx .

WM_PAINT er en smule anderledes denne gang. canvas-bufferen er væk, men vi behøver den heller ikke længere, for nu har vi jo allerede et bitmap – nemlig vores smiley (eller min, det er jo mig, der har tegnet den :)). Overskriften "Virtuelle og visuelle bitmaps" er også mere prangende end præcis. Der findes hverken virtuelle eller kunstige bitmaps. 'hSmiley' er nøjagtig mage til 'canvas'. Eneste forskel er, at 'hSmiley' indeholder andre bits, nemlig dem, der er gemt i bmp-filen. Som du måske har gættet, betyder det også, at vi stadig kan tegne firkanter, streger, cirkler og tekst ovenpå vores smiley. Med lidt præcision kunne vi gøre øjnene grønne. Vi kan også lave lidt spas. Jeg fortalte før lidt om raster-operationer, og med en lille ændring, kan vi få smiley'en i negative farver. Hvis vi i stedet for SRCCOPY i BitBlt bruger SRCINVERT, udebliver forskellen ikke:



Bitmapfiler har den fordel, at de understøtter et væld af farver, men de har også den ulempe, at de fylder langt mere end filer som GIF, JPEG og PNG. Hvis vi har et program med en masse billeder, ville det måske være smartere at have dem i et af de tre formater. Desværre er der ingen funktion i Win32-API'et, der kan indlæse filer i de formater. Det er dog i den grad muligt, og der findes flere online biblioteker til det formål, man frit kan downloade, men det bevæger vi os ikke ind på i denne artikel. GDI-kendere ved sikkert også, at der er noget, der hedder metafiler og DIB (Device Independent Bitmap) filer. Det vil jeg med vilje heller ikke tage med i denne artikel, da vi så bevæger os væk fra grundlæggende GDI, og det er ikke meningen. Derfor vil al billedgrafik i denne artikel være i form af bitmaps.

GetWindowDC


Indtil videre har vi arbejdet med DC'ere, der kun har berørt vinduets indholdområde. Men hvad med resten? Hvad med ikonet, det røde kryds, vinduets ramme og vinduets titel? Jo, der er naturligvis også en måde at få fat på dem. Løsningen er at bruge GetWindowDC i stedet for GetDC. Jeg vil nu prøve at opsnappe hele vinduet, og ikke kun indholdområdet.
Fold kodeboks ind/udKode 


Og resultatet:



Som du kan se, bruger dette program to BitBlt-kald. Det første overfører bittene fra hScreenDC, som, takket være GetWindowDC fremfor GetDC, er en DC, der kontrollerer grafikken for hele vinduet og ikke kun indholdområdet, over til mem, en hukommelses-DC. Det næste kald overfører bittene fra mem, vores hukommelses-DC, til ps.hdc, som er DC'en for indholdområdet. Derudover fryser jeg programmet i et sekund, efter det har opfanget vinduets position på skærmen. Det gør jeg, fordi jeg vil sikre mig, vinduet er vist på skærmen, inden vi kopierer bittene. Ellers kopierer programmet fra vinduet under vores vindue.

Vi er jo ved at blive ganske ferme. Vi kan læse data fra en bitmapfil og vise det på skærmen, vi kan tegne firkanter og cirkler og få dem til at bevæge sig, og vi kan kopiere dele af vores vindue, selv udenfor indholdområdet. I spil optræder der mange elementer på en gang. I stedet for at sidde og kalde BitBlt ved hvert eneste element, kunne vi bruge en løkke. Følgende program kopierer et område af skærmen – ikke vinduet, skærmen – og fylder indholdsområdet med det gentagne gange.
Fold kodeboks ind/udKode 


Resultat:



Tricket er at kalde GetDC med NULL som argument. Så får du en DC for hele skærmen.

Opgaver


At kigge koder igennem og læse tilhørende forklaringer er en god måde at lære på, men tingene går endnu hurtigere, når man forsøger selv. Prøv at løse nogle af disse opgaver.

  1. Lav et program, der tegner en cirkel, som følger musen.

  2. Lav et program, der udskriver musens position på skærmen.

  3. Lav et program, der tegner en firkant, hvis position man kan ændre med piltasterne.

  4. Lav et program, der tager et screenshot af skærmen og viser det i indholdområdet i negative farver.

  5. Lav et program, der trækker en streg fra midten af indholdområdet ud til musens position. Hint: MoveToEx(), LineTo().

  6. Lav et program, der udskriver tekst, hvis farve skifter hvert sekund. Hint: SetTextColor().

  7. Lav et program, der via en OPENFILENAME-struktur tillader brugeren at vælge en bitmapfil, der bliver vist i indholdområdet.

  8. Lav et program, der tillader brugeren at lave firkanter med musen, nøjagtig som i Paint.

  9. Lav et program, der udskriver R, G og B værdierne for farven på den pixel, musen hviler på. Hint: GetPixel().

  10. Lav et program, der farver den pixel af indholdområdet, man klikker på, en bestemt farve. Hint: SetPixel().



Til slut


Det er alt, jeg vil fortælle om GDI for denne gang. Hvis artiklen bliver tilpas populær, kommer der med garanti flere af slagsen. Som jeg nævnte tidligere, er der visse ting, jeg bevidst har udeladt, for GDI er faktisk temmelig omfattende, og det ville være urimeligt og endeløst at presse det hele sammen i én artikel. Så vil jeg langt hellere skrive flere artikler, der hver i sær fokuserer på et specifikt emne indenfor GDI. I kodeeksemplerne har jeg kun indsat WndProc-funktionen, for det er den eneste, der varierer fra program til program. Det betyder dog, at du ikke bare kan nøjes med at kopiere koden i en kodeboks, indsætte den i din IDE og forvente, at den kan kompileres – men igen, det er jo heller ikke meningen. Jeg håber, du kunne lide artiklen og drage nytte af den. Jeg har for så vidt muligt forsøgt at rette alle stave- og trykfejl, men hvis et par enkelte skulle forekomme, så bedes du bære over med mig.

For at 'peake' din lyst til at gå i gang med at lave fede GDI-applikationer, har jeg taget to screenshots af et lille spil, jeg har lavet i GDI.





Spillet går ud på at samle alle diamanterne ved at gå ind i dem med Smiley'en. Man bevæger sig med piltasterne, og man hopper med mellemrumstasten. Alle bevægelser starter og slutter blødt. De røde felter er trampoliner, der tillader smiley'en at hoppe dobbelt så højt, så man også kan nå de øverste diamanter. Når man har samlet alle diamanterne, kommer der en "Game Over"-boks, der fortæller en, hvor længe man var om at samle dem. Herfra kan man trykke 'J' for at spille igen, eller 'N' for at afslutte programmet.

Vedhæftede filer:


<< < 123 > >>

Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (13)

User
Bruger #10113 @ 25.05.09 16:32
Virker dette kun i Windows?
User
Bruger #8985 @ 25.05.09 17:32
Så vidt jeg ved, ja. Desværre :( Men jeg er overbevist om, at der findes alternativer til andre operativsystemer.
User
Bruger #11750 @ 02.06.09 23:04
Ikke lige mit fagområde :D Men det ser rigtig godt ud!
User
Bruger #8985 @ 04.06.09 21:17
Tak!
User
Bruger #9646 @ 23.09.09 14:07
windows.h - duer kun på windows?

Jeg har prøvet at smide ind i CodeBlocks og compiled på en Mac, og får en hel del errors :)

Hvordan kan man omskrive så den virker på mac?
User
Bruger #8985 @ 09.10.09 19:25
Jeg ville gerne hjælpe dig, men jeg har ingen idé om det. :( Jeg har aldrig skrevet programmer til andet end Windows. Faktisk har jeg aldrig ejet et operativsystem andet end Windows (eksempelvis Linux på en Virtual Machine for en kort stund). Jeg er bange for, der skal nogle helt andre biblioteker til, hvis du skal kode til Mac. Altså jeg tror ikke, der findes noget der hedder GDI ved Mac.
User
Bruger #15732 @ 04.03.10 17:09
Sakl lige teste noget håber det er ok jeg er nu på siden
Fold kodeboks ind/udKode 
#include <iostream>
std::cout << "test" << std::endl;
User
Bruger #15732 @ 04.03.10 17:10
Fold kodeboks ind/udKode 
#include <iostream>
User
Bruger #15732 @ 04.03.10 17:10
[code][#include <iostream>]
User
Bruger #15732 @ 04.03.10 17:12
Fold kodeboks ind/udKode 
[#include <iostream>]
User
Bruger #15732 @ 04.03.10 17:13
Fold kodeboks ind/udKode 
User
Bruger #15938 @ 22.06.10 15:44
Download: " Qt Creator "

- for at lave din egen Gui/ Graphical user interface med c++

Nemt og lækkert ;)
User
Bruger #14633 @ 13.09.10 15:00
Med Wine kan programmet linkes og koeres i Mac og mange POSIX-systemer.

Bemaerk at GDI er foraeldet af Microsoft til fordel for Direct2D.
Du skal være logget ind for at skrive en kommentar.
t