Piirtopinnalle piirtäminen



Samankaltaiset tiedostot
Hiirisanomiin vastaaminen

Visual C++ -ohjelman tekeminen ja suunnittelu

Tulostaminen ja esikatselu

Fonttien käyttö LUKU. Tekstin esittäminen värein ja tyylein. Fonttien käyttö sovelluksen viimeistelyyn OSA IV LUKU 17.

Tekstikontrollit LUKU. Tekstin ja sanomien esittäminen valintaikkunoissa. Tekstin muokkaaminen suorituksen aikana. Tiedon tarkistaminen syötön aikana

Muita kuvankäsittelyohjelmia on mm. Paint Shop Pro, Photoshop Elements, Microsoft Office Picture Manager

Kynien ja siveltimien käyttö

MDI-sovellusten kehittäminen

Luettelo-, puu-, parannetun tekstiruutu- sekä HTML-näkymän käyttö

ActiveX-kontrollien käyttö

Sovelluksen toimintojen toteuttaminen

Jypelin käyttöohjeet» Ruutukentän luominen

Usean näkymän luominen

Kuvan pienentäminen Paint.NET-kuvankäsittelyohjelmalla

XNA grafiikka laajennus opas. Paavo Räisänen. Tämän oppaan lähdekoodit ovat ladattavissa näiden sivujen Ladattavat osiossa.

1. HARJOITUS harjoitus3_korjaus.doc

Sivu 1 / Viikin kirjasto / Roni Rauramo

UpdateIT 2010: Editorin käyttöohje

HARJOITUKSIA ios-ohjelmointiin LIITTYEN

TALLENNETAAN MUISTITIKULLE JA MUISTIKORTILLE

Kehitysympäristö LUKU. Developer Studio kehitysympäristön alkeet. Liikkuminen projektin työtilassa ja sen eri näkymissä

Dokumentit, näkymät ja kehykset

Muistitikun liittäminen tietokoneeseen

ITKP102 Ohjelmointi 1 (6 op)

6. Harjoitusjakso II. Vinkkejä ja ohjeita

Kylänetti projektin sivustojen käyttöohjeita Dokumentin versio 2.10 Historia : 1.0, 1.2, 1.6 Tero Liljamo / Deserthouse, päivitetty 25.8.

GeoGebra-harjoituksia malu-opettajille

Sisältö. 22. Taulukot. Yleistä. Yleistä

Ohjelmoinnin perusteet Y Python

ASENNUS- JA KÄYTTÖOHJE

SSH Secure Shell & SSH File Transfer

Graafisen käyttöliittymän ohjelmointi Syksy 2013

Kirjaudu sisään palveluun klikkaamalla Omat kartat -painiketta.

KUVANKÄSITTELY THE GIMP FOR WINDOWS OHJELMASSA

Ohjelmoinnin perusteet Y Python

Sisältö. 2. Taulukot. Yleistä. Yleistä

TAULUKKO, KAAVIO, SMARTART-KUVIOT

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Tapahtumapohjainen ohjelmointi. Juha Järvensivu 2007

Ohjelmoinnin perusteet Y Python

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

ActiveX-kontrollien luominen

Valintaikkunoiden luonti ja suunnittelu

Winapi. Juha Järvensivu 2007

Sovellusten ohjelmointi Microsoft Foundation Classes -luokkien avulla

Ohjeita. Datan lukeminen

Lyhyt kertaus osoittimista

6.1 Tekstialueiden valinta eli maalaaminen (tulee tehdä ennen jokaista muokkausta ym.)

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Aloita uusi kartoitus -painikkeesta käynnistyy uuden kartoituksen tekeminen

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

OpenOffice.org Impress 3.1.0

TIEP114 Tietokoneen rakenne ja arkkitehtuuri, 3 op. Assembly ja konekieli

JWT 2016 luento 11. to klo Aulikki Hyrskykari. PinniB Aulikki Hyrskykari

2.1 Yksinkertaisen geometrian luonti

Outlook Web App ver 1.2

KÄYTTÖOHJE. Servia. S solutions

Pong-peli, vaihe Koordinaatistosta. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 2/7. Tämän vaiheen aikana

Kameran käyttö Excel 2003 ja 2007

Kyläsivujen InfoWeb-ohje

Verkkosivut perinteisesti. Tanja Välisalo

KÄYTTÖÖN. Koulukirjat tietokoneelle PIKAOHJEET PAPERPORT -OHJELMAN. Sisällysluettelo

Harjoitus 2: Oppijan aktivointi ( )

Racket ohjelmointia osa 2. Tiina Partanen Lielahden koulu 2014

1 ClipArt -kuvan käyttö Paint-ohjelmassa

Web Services tietokantaohjelmoinnin perusteet

Kopioi cd-levyt kiintolevylle, niin fyysiset levyt joutavat eläkkeelle.

BlueJ ohjelman pitäisi löytyä Development valikon alta mikroluokkien koneista. Muissa koneissa BlueJ voi löytyä esim. omana ikonina työpöydältä

Tilastokeskuksen rajapintapalveluiden käyttöönotto ArcGISohjelmistossa

Näkymien vierittäminen ja koon muuttaminen

OPINNÄYTETYÖ MALLIPOHJAN KÄYTTÖOHJE

KUVAN TUOMINEN, MUOKKAAMINEN, KOON MUUTTAMINEN JA TALLENTAMINEN PAINTISSA

Versio 1.1 P/N Copyright 2002, ATI Technologies Inc. Kaikki oikeudet pidätetään.

LIITE 1 1. Tehtävänä on mallintaa kitara ohjeiden mukaan käyttäen Edit Poly-tekniikkaa.

PageMaker 6.5 -moniste

ASCII-taidetta. Intro: Python

PowerPoint -esitysgrafiikka

Kuukauden kuvat kerhon galleriaan lähtien kuukaudenkuvaajan kuvagalleria on siirretty uudelle palvelimelle osoitteeseen:

Kansion asetusten muuttaminen Windows 2000 käyttöjärjestelmässä Resurssienhallinnan kautta

Ohjelmoinnin perusteet Y Python

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

Osoitin ja viittaus C++:ssa

3.3 Kurssin palauttaminen

Väitöskirja -mallipohja

KESKUSTANUORTEN NETTISIVUT- OHJEITA PIIRIYLLÄPITÄJÄLLE 1. KIRJAUTUMINEN

Asteri Laskutus (Dos)

MAANMITTAUSLAITOKSEN ILMAISTEN KARTTOJEN TULOSTAMINEN QUANTUM GIS -OHJELMALLA

Esimerkkiprojekti. Mallivastauksen löydät Wroxin www-sivuilta. Kenttä Tyyppi Max.pituus Rajoitukset/Kommentit

Fiery Driver Configurator

Taulukot Päivi Vartiainen 1

Uutiskirjesovelluksen käyttöohje

3. Vasemman reunan resurssiselaimen Omiin resursseihin luodaan uusi Handmade -niminen kansio.

Muuttujien määrittely

Siirtyminen Outlook versioon

Ohjelmoinnin perusteet Y Python

Peilaus pisteen ja suoran suhteen Pythonin Turtle moduulilla

JAVA on ohjelmointikieli, mikä on kieliopiltaan hyvin samankaltainen, jopa identtinen mm. C++

Park systems XE-100 atomivoimamikroskoopin käyttöohje

Ohjelmistopohjaisen lisenssin käyttö

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

Transkriptio:

OSA IV 333 LUKU 15 15 LUKU Erilaiset piirtopinnat ja niiden käyttö Piirtotilan ja piirtopinnan asetukset ja käyttö Koordinaatistojen kohdistustapojen käyttö piirrettäessä tuumina tai sentteinä

334 Piirtopintatietorakenteet Pohjimmiltaan piirtopinta on Windowsin tietorakenne, jossa on piirtotoimintojen, kuten viivan piirtämisen, käyttämät oletusasetukset. Useimmista muista Windowsin tietorakenteista poiketen piirtopintatietorakennetta ei pääse käsittelemään koskaan suoraan sovelluksesta; ainoastaan sen asetuksia voidaan muuttaa vakiosaantifunktioilla. Windowsin piirtokirjastot Kaikki käyttöjärjestelmän piirtokoodi on toteutettu kahden DLL-kirjaston välisenä vuorovaikutuksena. Ensimmäinen näistä on aina GDI.DLL, joka tarjoaa sovellukselle laiteriippumattomat piirtofunktiot. Toinen DLL on laiteriippuva ja se on eri kirjasto eri näytöille, tulostimille tai piirtureille. Esimerkiksi tavalliselle VGAnäytölle piirrettäessä VGA.DLL tarjoaa piirtofunktioiden toteutuskoodin kyseiselle laitteelle. Grafiikan piirtäminen Piirtopinta Piirtopinta-termiä (device context) ei kannata säikähtää. Tämä hankala termi viittaa kuitenkin johonkin hyvin yksinkertaiseen, Windowsin perustana olevaan asiaan. Millaiselta Windows näyttäisi ilman piirtopintaa? Ei miltään. Piirtopinta on kirjaimellisesti pinta, jolle kaikki graafiset elementit - pisteet, viivat, fontit, värit ja kaikki muu piirretään. Tämä pinta voi olla näyttö, mutta se voi olla myös tulostin, piirturi, VR-kypärä tai joku muu kaksiulotteinen laite, eikä ohjelmoijan tarvitse tuntea tällaista laitetta yksityiskohtaisesti sitä käyttääkseen. Eräs Microsoftin innovaatioista oli piilottaa kaikki erilaiset piirtotavat ja laitteet Windows-käyttöjärjestelmän sisään. Tästä on ollut seurauksena graafisten sovellusten huima yleistyminen. Siinä oli ehkä Windowsin kaikkein tärkein ja aliarvostetuin vaikutus. Tänään voin ostaa paikallisesta tietokoneliikkeestäni uusinta mallia olevan tulostimen, asentaa yhden, valmistajan toimittaman, Windowsohjaimen ja jokainen Windows-ohjelma osaa käyttää uutta tulostinta välittömästi. Nämä yhdistää keskenään piirtotuen tarjoava piirtopinta. KATSO MYÖS Piirtopinnalle piirtämisestä luvusta 16. Tulostinpiirtopinnan käyttö luvussa 22. Erilaiset piirtopinnat Piirtopintoja on tavallisia ja on erikoisia, tiettyyn tarkoitukseen käytettäviä. Itse piirtopinnat ovat GDI-olioita (graphics device interface, piirtolaiteliittymä). GDI tarkoittaa oliojoukkoa, joka elää Windowsin ytimessä olevassa DLL-kirjastossa. Nämä funktiot ovat linkki piirtofunktiokutsun ja kaiken toteuttavan kovon kanssa keskustelevan laiteohjaimen välillä. Microsoft Foundation Classes (MFC) kirjaston luokat piilottavat piirtopinnat ja yksinkertaistavat ohjelman toimintaa GDI-olioiden kanssa. Tavalliset piirtopinnat kuuluvat CDC-luokkaan, joka sisältää lukuisia näyttämiseen tarkoitettuja piirto-, koordinaatistomuunnossekä rajausfunktioita. Myös erikoistuneemmat piirtopinnat on luotu tätä luokkaa laajentamalla. Seuraavissa kappaleissa on kerrottu näistä luokista tarkemmin.

OSA IV LUKU 15 335 CDC-luokan käyttö Käyttämäsi piirtopintaluokka periytyy aina CDC-kantaluokasta. Tässä luokassa on kaksi alla olevaan GDI-olioon liittyvää kahvaa: m_hdckahva ja m_hattribdc-kahva. Näistä m_hdc on varsinainen piirtopinnan kahva, joka liittää luokan GDI-olioon käsittelemään kaiken piirtofunktioiden tulostuksen. m_hattribdc on puolestaan se piirtopinnan kahva, jonka kautta voidaan käsitellä piirtopinnan ominaisuuksia, kuten värien määrää ja piirtotiloja. Näistä jäsenattribuuteista et joudu juuri huolehtimaan, mutta muista, että käyttäessäsi GetDC():n ja ReleaseDC():n kaltaisia funktioita (jotka varaavat ja vapauttavat piirtopinnan ikkunasta) nämä GDI-kahvat liitetään CDC-luokkaasi ja irrotetaan siitä. CDC-piirtofunktiot CDC-luokka toteuttaa kaikki Windowsissa saatavilla olevat piirtofunktiot. Kukin näistä kutsuu yksinkertaisesti vastaavaa Win32- piirtofunktiota (GDI.DLL:stä) välittäen sille CDC-olioon upotetun piirtopintakahvan (m_hdc ja m_hattribdc). CDC-luokan kykyä kiinnittyä piirtopintaan ja piirtää siihen voidaan havainnollistaa hyvin yksinkertaisella esimerkkiohjelmalla. Jokaisessa ikkunassa on koko ikkunan kattava piirtopinta, näin myös koko näytön peittävällä työpöydän ikkunalla. Toistaiseksi olet nähnyt varsin vastuuntuntoisia sovelluksia: piirtopinnan helppoutta ja tehokkuutta esittääkseni aion tarttua kiinni suoraan työpöydän piirtopintaan (DC) ja piirtää siihen. Tämä tosin sekoittaa tilapäisesti Windowsin näytön, mutta ohjelmasta poistuttaessa se saadaan päivitettyä ennalleen. Seuraavien vaiheiden avulla saat tehtyä valintaikkunasovelluksen, joka käyttää piirtopintaa suoraan. Piirtopintaan tarttuvan valintaikkunasovelluksen luominen 1. Valitse File-valikosta New. 2. Valitse New-ikkunassa Projects-välilehti ja saat esiin luettelon erilaisista projekteista. 3. Valitse luettelosta MFC AppWizard (exe) ja anna projektin nimeksi Project Name ruutuun DCDraw. AppWizardin tekemä ReadMe.txt-tiedosto AppWizard tekee projektiesi tiedostojen hakemistoon readme.txt-tiedoston, jossa on selitetty luotujen tiedostojen merkitys sovellukselle. 4. Napauta OK ja saat esiin AppWizardin vaiheen 1. 5. Valitse sovelluksen tyypiksi Dialog Based Application. 6. Napauta Finish-painiketta ja saat erittelyn projektin rungosta. 7. Napauta OK, niin AppWizard tekee projektin tiedostot. Nyt sinulla on valintaikkunasovelluksen runko. Lisätään aluksi yksinkertainen painike sekä käsittelijä sen napautukselle, jotta voisimme toteuttaa piirtopintakoodimme.

336 Grafiikan piirtäminen Painikkeen luominen 1. Napauta projektin työtilaikkunassa ResourceView-välilehteä. 2. Napauta plusmerkkiä, jotta näkisit DCDraw-projektin resurssit. 3. Napauta Dialog-kansion viereistä plusmerkkiä ja saat esiin projektin valintaikkunaresurssit. 4. Kaksoisnapauta IDD_DCDRAW_DIALOG-alkiota ja pääset muokkaamaan sovelluksen pääikkunaa. 5. Napauta kerran TODO: Place Dialog Controls Here tekstiä ja paina Delete-näppäintä tekstin poistamiseksi. 6. Valitse painike Controls-työkalupalkista, raahaa se valintaikkunaan ja pudota. Suurenna painiketta huomattavasti. 7. Näppäile Alt+Enter ja pääset muokkaamaan painikkeen ominaisuuksia Push Button Properties ikkunassa. Anna painikkeen tekstiksi Caption-ruutuun Draw!. 8. Muuta painikkeen ID-tunnukseksi IDC_DRAWIT. 9. Näppäile Enter sulkeaksesi Push Button Properties ikkunan. Valintaikkunassa on nyt painike, joten voit lisätä sille käsittelijän napautusta varten. BN_CLICKED-käsittelijän lisääminen pikavalinnalla Helpoin tapa lisätä painikkeelle käsittelijä on kaksoisnapauttaa kyseistä kontrolia resurssieditorissa. Tämä lisää samalla tavoin rivin sanomakarttaan ja luo käsittelijäfunktion koodirungon, mutta ohittaa ClassWizardin vaiheet, jolloin pääset suoraan vaiheeseen 5 eli käsiksi koodiin. BN_CLICKED-käsittelijän lisääminen ClassWizardilla 1. Valitse uusi valintaikkunan painike (tässä Draw!) sitä napauttamalla. 2. Käynnistä MFC ClassWizard näppäilemällä Crtl+W. 3. MFC ClassWizard Message Maps sivun pitäisi tulla esiin. Uuden IDC_DRAWIT-painikkeen tulisi olla valmiiksi valittuna ellei, valitse se Object IDs luettelosta. 4. Kaksoisnapauta Messages-luettelossa BN_CLICKED-sanomaa lisätäksesi uuden käsittelijäfunktion. 5. Napauta Add Member Function ikkunassa OK ja käsittelijäfunktion nimeksi annetaan oletusnimi OnDrawit. 6. Napauta Edit Code painiketta aloittaaksesi käsittelijäfunktion muokkaamisen. Nyt voit toteuttaa piirtopintaan tarttumisen ja siihen piirtämisen painikkeen napautuksen käsittelijäfunktiossa listauksen 15.1 mukaisesti.

LISTAUS 15.1 LST15_1.CPP Työpöydän piirtopinnalle piirtäminen OSA IV LUKU 15 337 1 void CDCDrawDlg::OnDrawit() 2 { 3 // TODO: Add your control notification handler code here 4 5 // ** Get a pointer to the desktop window 6 CWnd* pdesktop = GetDesktopWindow(); 1 7 // ** Get a pointer to it's device context 8 CDC* pdc = pdesktop->getwindowdc(); 2 9 // ** loop through 300 pixels horizontally 10 for(int x=0;x<300;x++) 11 { 12 // ** loop through 300 pixels vertically 13 for(int y=0;y<300;y++) 14 { 15 // ** set each pixel to a different color 16 pdc->setpixel(x,y,x*y); 3 17 } 18 } 19 pdesktop->releasedc(pdc); 20 } 1 Tämä rivi hakee työpöytäikkunan (koko näytön). 2 Tässä haetaan työpöytäikkunan pääpiirtopinta. 3 Kukin suorakulmion kuvapiste asetetaan sen koordinaateista lasketuksi väriksi. Tässä listauksessa olen hankkinut osoittimen Windowsin työpöydälle rivin 6 GetDesktopWindow()-funktiolla. Tästä CWnd-osoittimella voin tarttua suoraan Windowsin pääikkunan piirtopintaan rivin 8 GetWindowDC()-funktiolla. Riveillä 10-18 piirretään kuvan 15.1 esittämä hauska kuvio. Rivit 10-13 käyvät läpi 300 x 300 kuvapisteen alueen kahdessa sisäkkäisessä for-silmukassa. Kunkin kuvapisteen kohdalla kutsutaan SetPixel()-funktiota, jonka ensimmäiset parametrit määrittävät kuvapisteen vaaka- ja pystykoordinaatit (0,0 on vasemmalla ylhäällä). Kolmas parametri on värin numero (tästä tarkemmin luvussa 16). Tässä väri lasketaan x- ja y-koordinaattien tulona, joka saa aikaan upean ja värikkään moiré-kuvien. Lopuksi joudut vapauttamaan piirtopinnan rivin 19 ReleaseDC()-funktiolla. Moirén renkaat Moirén renkaat ovat kahden samanlaisen osittain läpinäkyvän mallin muodostama interferenssikuvio, joka ensimmäisen kerran havaittiin läikesilkkikankaassa (ransk. moiré). Sama ilmiö saadaan aikaan ohuissa verkkoverhoissa, kun kangaskerrosta kierretään hitaasti toisen yllä. Tämä sama interferenssikuvio on myös hologrammitekniikan perusteena.

338 Grafiikan piirtäminen KUVA 15.1 Työpöytään tarttuminen ja siihen piirtäminen Koska DCDraw-sovellus käyttäytyy vastuuttomasti ja sotkee työpöydän, joudut siivoamaan jälkesi. Voit käsitellä WM_DESTROY-sanoman OnDestroy()-käsittelijässä. Tätä kutsutaan valintaikkunaa tuhottaessa, joten siivouskoodi sopii sinne oikein hyvin. WM_DESTROY-sanoman käsittelijän lisääminen 1. Valitse projektin työtilaikkunassa ClassView-sivu. 2. Avaa DCDraw-luokkien luettelo plusmerkistä. 3. Avaa CDCDrawDlg-luokan pikavalikko napauttamalla sitä hiiren oikealla painikkeella. 4. Valitse pikavalikosta Add Windows Message Handler komento. 5. Valitse New Windows Messages/Events luettelosta WM_DESTROY-sanoma. 6. Napauta Add and Edit painiketta ja saat lisättyä sanomalle käsittelijän CDCDrawDlg-luokkaan ja voit aloittaa funktion koodaamisen.

Nyt voit lisätä siivouskoodin OnDestroy()-käsittelijään. Saat siivottua työpöydän pyytämällä työpöytäikkunaa piirtämään itsensä uudelleen ja pyytämään vielä lapsi-ikkunoitaankin tekemään samoin (sovelluksesi siis). Tämä tehdään listauksessa 15.2 esitetyssä koodissa. Rivillä 8 työpöytäikkunan (löytyy GetDesktopWindow()-funktiolla) jäsenfunktio RedrawWindow() pakottaa työpöytäikkunan päivityksen. RedrawWindow()-funktiolle syötetään kolme parametriä; kaksi ensimmäistä ovat piirtosuorakulmio ja piirtoalue. Syöttämällä molempien parametrina NULL RedrawWindow() olettaa täysin oikein, että koko ikkuna halutaan päivittää. Kolmas parametri sisältää joukon lippuja: DRW_ERASE pyyhkii kaiken, RDW_INVALIDATE pyytää uudelleenpiirron jälkeenpäin, RDW_ALLCHILDREN pyytää lapsiikkunoitakin piirtämään itsensä uudelleen ja DRW_ERASENOW pakottaa toimenpiteen heti suoritettavaksi. LISTAUS 15.2 LST15_2.CPP Työpöydän ja sovellusikkunoiden piirtäminen uudelleen RedrawWindow()-funktiolla OSA IV LUKU 15 339 1 void CDCDrawDlg::OnDestroy() 2 { 3 CDialog::OnDestroy(); 4 5 // TODO: Add your message handler code here 6 7 // ** Redraw the desktop and all child windows 8 GetDesktopWindow()->RedrawWindow(NULL,NULL, 1 9 RDW_ERASE+RDW_INVALIDATE+ 10 RDW_ALLCHILDREN+RDW_ERASENOW); B } 1 Pakottaa koko Windowsin työpöydän ja kaikkien sovellusikkunoiden uudelleenpiirtämisen. Käännettyäsi ja ajettuasi ohjelman muutokset tehtyäsi pitäisi ohjelman piirtää kuvan 15.1 mukainen värikäs suorakulmio. Toki piirtopintaluokassa (DC) on paljon SetPixel()-funktiota monipuolisempiakin piirtofunktioita, mutta emme käsittele niitä vielä, vaan luvuissa 16 ja 17. KATSO MYÖS Kynillä ja siveltimillä piirtäminen luvussa 16. Tekstin kirjoittaminen piirtopinnalle luvussa 17.

340 Työaluepiirtopintaan liittyvän ikkunankahvan käsitteleminen Saatat tarvita pääsyä työaluepiirtopintaan liittyvään ikkunaan. CClientDC-luokka tallentaa siihen liittyvän ikkunan kahvan m_hwnd-jäsenmuuttujaan. Voit välittää m_hwnd-ikkunakahvan (HWND) CWnd-olion Attach()- funktiolle käsitelläksesi ikkunafunktioita. 1 Muodosta piirtopinta valintaikkunan työalueen oikein käyttämiseksi. Grafiikan piirtäminen Ikkunan työalue piirtopintana CClientDC-luokka huolehtii automaattisesti GetDC()- ja ReleaseDC()-toiminnoista puolestasi. Muodostaessasi CClientDColion annat parametrinä sille ikkunaosoittimen ja luokka hoitaa puolestasi kiinnittymisen annetun ikkunan piirtopintaan. Luokkaa tuhottaessa kutsutaan automaattisesti ReleaseDC()-funktiota, joten et joudu huolehtimaan siitäkään. Client-termi tarkoittaa tavallista ikkunan työaluetta, mutta ikkunalle löytyy myös harvinaisempi CWindowDC-luokka, jonka kautta piirtopintana voidaan käyttää ikkunan otsikkopalkkia ja reunoja. Tähän ei kuitenkaan usein ole tarvetta, koska hyvin käyttäytyvä sovellus piirtää normaalisti vain ikkunan työalueelle. Saat oletusprojektisi käyttämään CClientDC-oliota CDC-osoittimen sijaan. Jos kaksoisnapautat ClassView-ikkunassa OnDrawit()-funktiota, pääset suoraan muokkaamaan painikkeen käsittelijäkoodia. Tee muutokset OnDrawit()-funktioon listauksen 15.3 mukaan. Työpöytäikkunan sijaan piirrät nyt valintaikkunan piirtopinnan päälle. Tätä varten CClientDC-oliolle dlgdc välitetään itse valintaikkunaan osoittava this-osoitin (osoitin omaan luokkaan). Koska valintaikkuna on CWnd-luokan tyyppinen olio, tämä ei aiheuta ongelmia ja dlgdc-olio saa kahvan itse valintaikkunan piirtopintaan. Voit piirtää entiseen tapaan riveillä 8-16, mutta muista, että rivillä 14 dlgdc käyttää SetPixel()-funktiota olion, ei osoittimen kautta. Lopuksi huomaa, että ReleaseDC()-funktiota ei kutsuta OnDrawit()-funktion lopussa, sen sijaan piirtopintakahva vapautetaan automaattisesti CClientDC:n tuhoajafunktiossa, kun dlgdc-olio tuhotaan funktion lopussa. LISTAUS 15.3 LST15_3.CPP CClientDC-luokan käyttö valintaikkunan piirtopinnan kaappaamiseen 1 void CDCDrawDlg::OnDrawit() 2 { 3 // TODO: Add your control notification handler code here 4 5 // ** Construct a client DC from the dialog window 6 CClientDC dlgdc(this); 1 7 // loop through 300 pixels horizontally 8 for(int x=0;x<300;x++) 9 { 10 // loop through 300 pixels vertically 11 for(int y=0;y<300;y++)

OSA IV LUKU 15 341 12 { 13 // ** set each pixel to a different color 14 dlgdc.setpixel(x,y,x*y); 15 } 16 } 17 } Käännettyäsi ja ajettuasi sovelluksen uudelleen näiden muutosten tekemisen jälkeen pitäisi piirron kohdistua ainoastaan valintaikkunaan DrawIt-painiketta painettaessa (kuva 15.2). Paint-piirtopinnan käyttö CPaintDC on piirtopinnan erikoistapaus, joka paketoi Windowsin WM_PAINT-sanoman käsittelyn. Ikkunalle lähetetään WM_PAINT-sanoma, kun koko ikkuna tai osa siitä paljastuu esiin toisen ikkunan takaa tällöin sovelluksesi pitää piirtää ikkunan sisältö uudelleen. Ikkunan uudelleenpiirtämisen pakottaminen Tavallisesti WM_PAINT-sanomia lähetetään, kun tietty ikkunan osa tulee piirtää uudelleen. Voit myös pakottaa WM_PAINT-sanoman lähettämisen ikkunalle CWndluokan RedrawWindow()- funktiolla. Tämän funktion parametreillä voit määrittää ikkunan uudelleenpiirrettävän alueen ja sen, pyyhitäänkö sen yhteydessä myös ikkunan tausta. KUVA 15.2 Valintaikkunan piirtopinnan käyttö CClientDC:n avulla Koko ikkunan uudelleenpiirtämisen sijaan pienen osan siitä paljastuessa voit hyödyntää Windowsin välittämää tietoa paljastuneen ikkunan osan koordinaateista. Näin voit piirtää uudelleen ainoastaan paljastuneen osan ja välttää vielä piilossa olevan ikkunan osan piirtämisen. Tähän Windowsilta saatuun alueeseen ei kuitenkaan tarvitse kiinnittää liiemmin huomiota: jos yrität piirtää muuttuneen alueen ulkopuolelle, mitään ei tapahdu. Tämä johtuu siitä, että piirtopinta on asetettu myös rajaamaan piirtoalue muuttuneeseen alueeseen. Tämän rajaamiseen ei välttämättä kannata luottaa, koska kaikki piirtokoodi suoritettaisiin kuitenkin ja nopeutusetu menetettäisiin. Joskus tämä on kuitenkin välttämätöntä, koska mutkikkaiden kuvioiden ja tekstin kohdalla on usein vaikeaa rajata piirrettävä alue. Voit siis antaa Windowsin huolehtia rajaamisesta (clipping). Tekemäni pisteen piirtäminen on tehty hyödyntämään piirtotietoa. Voit muuttaa esimerkkiohjelman piirtämään OnPaint()-käsittelijästä WM_PAINT-sanoma saataessa ja piirtämään ainoastaan paljastuneeseen ikkunan osaan. Kaksoisnapauta ClassView-ikkunassa OnPaint()-

342 Grafiikan piirtäminen käsittelijäfunktiota ja pääset muokkaamaan sitä suoraan. Et tällä kertaa tarvitse ClassWizardia, koska AppWizard luo OnPaint()-funktion automaattisesti sovellusrunkoon. Koko OnPaint()-funktio on esitetty listauksessa 15.4. Rivillä 21 on rivi, joka on jouduttu lisäämään OnDrawit()-funktiosi kutsumiseksi. Toinen kiinnostava AppWizardin tuotos näkyy myös tässä listauksessa. Rivillä 3 kutsutaan IsIconic()-funktiota ja sen palauttaessa TRUE ikkuna pienennetään (pois työpöydältä tehtäväpalkkiin). Näin tapahtuessa sovelluksen kuvake (icon) piirretään pienennetyn ikkunan keskelle rivillä 16. IsIconic()-funktion FALSE-paluuarvoon voit sitten toteuttaa oman OnPaint()-koodisi ja rivillä 21 kutsua omaa OnDrawit()-funktiotasi. LISTAUS 15.4 LST15_4.CPP OnPaint()-käsittelijän muuttaminen valintaikkunasovellukselle 1 Pyyhkii pienennetyn ikkunan taustan. 2 Piirtää kuvakkeen pienennetyn ikkunan keskelle. 3 Tällä lisärivillä kutsutaan hauskaa väritysfunktiota. 1 void CDCDrawDlg::OnPaint() 2 { 3 if (IsIconic()) 4 { 5 CPaintDC dc(this); // device context for painting 6 SendMessage(WM_ICONERASEBKGND, 7 (WPARAM) dc.getsafehdc(), 0); 1 8 // Center icon in client rectangle 9 int cxicon = GetSystemMetrics(SM_CXICON); 10 int cyicon = GetSystemMetrics(SM_CYICON); 11 CRect rect; 12 GetClientRect(&rect); 13 int x = (rect.width() - cxicon + 1) / 2; 14 int y = (rect.height() - cyicon + 1) / 2; 15 // Draw the icon 16 dc.drawicon(x, y, m_hicon); 2 17 } 18 else 19 { 20 // ** kutsu omaa piirtofunktiota 21 OnDrawit(); 3 22 } 23 } Muuta nyt OnDrawit()-funktio käyttämään CPaintDC:tä. Nämä muutokset on esitetty listauksessa 15.5. Huomaa, että rivillä 6 muodostetaan CPaintDC-luokasta paintdc-olio valintaikkunasta. Tämä toimii kunnolla ainoastaan, kun ikkuna käsittelee WM_PAINT-sanoman.

Tässä kutsut funktiota OnPaint()-käsittelijästä ja piirto toimii. Tämä on yhä myös painikkeen käsittelijäkoodi, joten voit kutsua sitä painiketta napauttamalla. Mitään ei tosin tapahdu, koska ikkunassa ei ole päivittämättömiä alueita, joten piirto rajataan kokonaan pois. Seuraava muutos on rivillä 8, jossa määritellään RECT-osoitin ja se asetetaan paintdc-olion m_ps-jäsenessä olevan rcpaint-suorakulmion osoitteeksi. Tässä suorakulmiossa on uudelleenpiirrettävä alue, jota hyödynnetään riveillä 10 ja 13, jotta silmukassa käytettäisiin ainoastaan alueen sisällä olevia koordinaatteja. Tämä nopeuttaa piirtämistä huomattavasti, koska vain pieni osa ikkunasta joutuu uudelleenpiirrettäväksi. SetPixel() osoittautuu varsin hitaaksi suuremmilla alueilla; alkuperäisessä versiossa sitä kutsuttiin 300 x 300 kuvapisteen piirtämiseen eli 90 000 kertaa. Jos paljastunut alue on ikkunasta ainoastaan 50 x 30 kuvapistettä, ei piirrettäviä pisteitä ole kuin 1500, joka nopeuttaa piirtämisen 60-kertaiseksi. Kannattaa! Huomaa vielä yksi muutos: SetPixel()-funktiota kutsutaan nyt paintdc-oliosta rivillä 16. SetPixel() on toteutettu CDC-luokkaan, joten kaikki sen piirtofunktiot ovat edelleen käytettävissä siitä periytyvissä erikoistuneemmissa piirtopintaluokissa. LISTAUS 15.5 LST15_5.CPP CPaintDC-luokan hyödyntäminen WM_PAINT-pyyntöjen käsittelyssä OSA IV LUKU 15 343 1 void CDCDrawDlg::OnDrawit() 2 { 3 // TODO: Add your control notification handler code here 4 5 // ** Construct a paint DC from the dialog window 6 CPaintDC paintdc(this); 1 Huomaa, että ainoastaan tällä kertaa paint-suorakulmiota käytetään piirtokohteena. 7 // ** Make a short cut poiner to the paint rectangle 8 RECT* prect = &paintdc.m_ps.rcpaint; 1 9 // ** loop through the horizontal paint rectangle 10 for(int x=prect->left; x < prect->right ;x++) 11 { 12 // ** loop through the vertical paint rectangle 13 for(int y=prect->top; y < prect->bottom ;y++) 14 { 15 // ** set each pixel to a different color 16 paintdc.setpixel(x,y,x*y); 17 } 18 } 19 }

344 Grafiikan piirtäminen Käännä ja aja sovellus näiden muutosten tekemisen jälkeen ja pääsemme tutkimaan paint-piirtopinnan käyttäytymistä. Aluksi valintaikkunalla pitäisi olla värikäs tausta ja painikkeet näkyvissä: ikkunaa avattaessa annetaan ensimmäinen WM_PAINT-sanoma, jolla koko ikkuna piirretään ja painikkeet piirretään sen päälle. Näet WM_PAINT-sanoman toiminnassa peittäessäsi ikkunan puoliksi toisella (esimerkiksi Laskinohjelmalla). Sen saat käynnistettyä napauttamalla tehtäväpalkin Käynnistä-painiketta, valitsemalla Ohjelmat ja Apuohjelmatvalikosta Laskin (Start/Programs/Accessories/Calculator). Järjestely näkyy kuvassa 15.3. KUVA 15.3 WM_PAINT-sanoman kokeileminen paljastamalla ikkuna toisen alta. Kun asetat Laskimen puoliksi DCDraw-ikkunan päälle, napauta DCDraw-ikkunaa, jotta se tulisi etualalle. Uusi paljastuva ikkunan osa piirretään uudelleen. Piirtäminen kestää hetken (vanhalla P166:llani noin puoli sekuntia). Valitse nyt Laskin-ikkuna ja siirrä sitä hieman vasemmalle ja ylös. Lopeta raahaaminen ja sovellusikkunasi paljastunut alue tulee uudelleenpiirretyksi hieman nopeammin. Kun siirrät päällimmäistä ikkunaa lisää, piirto nopeutuu joka kerralla. Kun jäljellä on ainoastaan pieni paljastumaton osa vasenta yläkulmaa, valitse sovellusikkunasi ja tutki piirtoaikaa se käy paljon ensimmäistä kertaa nopeammin. Syy tähän on, että paljastumaton alue oli pieni ja ainoastaan se piti päivittää. Näin ollen tarvitaan vähemmän SetPixel()- kutsuja ja piirtäminen käy nopeammin. Näitä kahta ikkunaa siirtelemällä saat tuntuman WM_PAINT-sanoman vaikutukseen. Kaikki Windows-sovellukset joutuvat vastaamaan tähän sanomaan, vaikka sen käsittely usein piilotetaankin kontrolliin tai näkymäluokkaan, jolloin siitä ei tarvitse itse huolehtia. Muistanet vielä CPaintDC-luokan m_ps-jäsenen. Kyseessä on PAINTSTRUCT-tietorakenne, jossa on määritelty muun muassa piirrettä-

vän alueen määrittävä rcpaint-suorakulmio. Tietorakenteeseen kuuluu myös muita hyödyllisiä jäseniä. PAINTSTRUCT-tietorakenne määritellään seuraavasti: OSA IV LUKU 15 345 typedef struct tagpaintstruct { HDC hdc; BOOL ferase; RECT rcpaint; BOOL frestore; BOOL fincupdate; BYTE rgbreserved[32]; } PAINTSTRUCT; Jäsen hdc on kahva luokkaan piiloutuvaan GDI-piirtopintaan. Mukana on ferase-lippu, jonka Windows asettaa arvoon TRUE, mikäli tausta halutaan pyyhkiä ennen piirtämistä. Näit jo rcpaint-jäseneen talletettavan piirrettävän suorakulmion koordinaatit listauksessa 15.5. Kolme viimeistä jäsentä on varattu Microsoftin dokumentoinnin mukaan Windowsille, joten niihin on parempi olla koskematta. Saattaa ihmetyttää, mistä kaikki nämä tiedot saadaan CPaintDColiollesi. Vastaus on CWnd-luokan BeginPaint()-funktiossa. Muistathan, että CDialog-luokka on muiden kontrollien tavoin peritetty CWnd-luokasta, joka myös hoitaa valintaikkunankin näyttämisen. Näin kaikilla tästä luokasta periytyvillä luokilla on kaikki ikkunan perustoiminnat ja ne kaikki alustavat CPaintDC-oliota määriteltäessä PAINTSTRUCT-tietorakenteen BeginPaint()-funktiolla. Kun CPaintDC-olio tuhotaan, kutsutaan vastaavaa CWnd:n EndPaint()- funktiota ilmoittamaan Windowsille, että WM_PAINT-sanoman käsittely on päättynyt ja olet saanut uudelleenpiirrettyä sovellusikkunassasi piirrettäväksi tarkoitetut osat. WM_ERASEBKGNDsanoman käsitteleminen Toinen tapa selvittää, tarvitseeko tausta pyyhkiä, on lisätä käsittelijäfunktio Windowsin WM_ERASEBKGND-sanomalle. Tällöin uutta käsittelijäfunktiotasi OnEraseBkGnd() kutsutaan aina taustan pyyhkimistä tarvittaessa. OnEraseBkGnd()-funktio saa ensimmäisenä parametrinään osoittimen kyseiseen piirtopintaolioon (CDC-olio). Muistipiirtopintojen käyttö Muistipiirtopinta (memory device context) on piirtopinta ilman siihen liittyvää laitetta. Tämä saattaa aluksi tuntua oudolta, mutta kyseessä on itse asiassa varsin käyttökelpoinen järjestely. Tavallisesti näitä käytetään tavallisen piirtopinnan kanssa kuva-alueiden kopiointiin ja liittämiseen eri osiin näyttöä. Voit luoda näyttöpiirtopinnan kanssa yhteensopivan muistipiirtopinnan. Tämän jälkeen voit kopioida kuvia muistiin ilman niiden näyttämistä ja takaisin varsinaiselle piirtopinnalle kuvien näyttämistä varten. Muistipiirtopinta ei piiloudu omaan MFC:n luokkaan, koska CDC

346 Bittikarttojen koot muistipiirtopinnoilla Bittikartta voi muistipiirtopinnalla olla selvästi vastaavaa näyttöpiirtopintaa suurempi tai pienempi. Luomalla suuren bittikartan voit luoda paljon tavallista näyttöä erottelukyisemmän piirtopinnan. Muista kuitenkin, että suuri bittikartta vaatii enemmän muistia. Jos muistia tarvitaan enemmän kuin koneeseen on asennettu RAMia, otetaan käyttöön virtuaalimuisti, mikä hidastaa selvästi koneen toimintaa. Hitauden syy on jatkuva muistilohkojen vaihtaminen kovalevyltä RAMiin ja päinvastoin. Yhteensopivien muistipiirtopintojen ja kuvalohkokopioinnin (bit blitting) käyttö Jos haluat kopioida näytön väritettyjä lohkoja muistipiirtopinnalle tai muistipiirtopinnalta näytölle, joudut aina luomaan ja valitsemaan käyttöön näyttöpiirtopinnan kanssa yhteensopivan bittikartan. Grafiikan piirtäminen kelpaa tarkoitukseen varsin hyvin. Koska kyseessä on tavallinen CDCpiirtopintaluokka, voit käyttää sitä piirtämiseen tavalliseen tapaan tosin piirto menee muistiin, eikä käyttäjä näe sitä, ellet kopioi sitä edelleen näyttöpiirtopinnalle. Voit siis luoda muistiyhteensopivan piirtopinnan ja muuttaa piirtokoodisi piirtämään muistiin näytön sijaan. Kuvan muistiin piirtämisen jälkeen se saadaan kopioitua näytölle BitBlt()-funktiolla. Listaukseen 15.6 on tehty varsin paljon aiemmista versioista, koska muistipiirtopinnan luomisen lisäksi joudut luomaan myös bittikartan muistipiirtopinnalle. Ikkunaan liitetyistä näyttöpiirtopinnoista poiketen muistipiirtopinnalla ei ole automaattisesti näyttöyhteensopivaa bittikarttaa. Paint-piirtopinta on muutettu takaisin tavalliseksi työalueen piirtopinnaksi ja muistipiirtopinta on määritelty listauksen 15.6 rivillä 9 memdc-olioksi. Piirtopinnan CreateCompatibleDC()-jäsenfunktiota kutsutaan rivillä 10. Tälle funktiolle syötetään clientdc-olio, jonka avulla lasketaan piirtopinnan koko ja muut tiedot, jotta muistipiirtopinta saataisiin yhteensopivaksi näyttöikkunan kanssa. Rivillä 17 määritellään bittikartta, jota muistipiirtopinta käyttää piirtämiseen. Tämä bittikartta luodaan rivin 18 CreateCompatibleBitmap()-funktiolla. Funktio saa clientdcviittauksen bittikartalle käytettävän piirtopinnan selvittämistä varten tästä saadaan tiedot bittikartan värimäärästä. Työalueen leveys ja korkeus selvitetään rivillä 14 ja nämä syötetään parametrinä bittikartan kooksi. Rivillä 22 bittikartta lisätään piirtopintaan SelectObject()- funktiolla. Kaikki muistipiirtopintaa käyttävät piirtofunktiot käyttävät nyt annettua bittikarttaa. Tämän jälkeen voit piirtää aiemman värikkään bittikartan tavalliseen tapaan, tällä kertaa kuvio vain päätyy muistiin eikä suoraan näytölle. Rivillä 36 BitBlt()-funktiolla kopioidaan bittikartta takaisin näyttöpiirtopinnalle. BitBlt() on kuvankopiointifunktio (nimi tulee termistä bit blitting tai bit block transfer, joka tarkoittaa nopeaa kovon tukemaa muistilohkon siirtoa paikasta toiseen). Yhä useammin näytönohjain tekee tämän, tosin kovotuen puuttuessa prosessori hoitaa kopioinnin. BitBlt() kopioi tiedot memdc:stä clientdc:lle, kunhan syötät parametreinä kuva-alueen leveyden (width), korkeuden (height) sekä aloituskoordinaatit. Tämän funktion kiinnostavin parametri on SRCINVERT-lippu, joka määrää BitBlt():n kopioimaan kuvan käänteisvärein. Tällä saadaan aikaan kohdeikkunassa erilaiset värit kuin alkuperäisessä, mikä todista-

nee muistipiirtopinnan käytön ja sieltä kopioinnin valintaikkunan piirtopinnalle. Jos haluat kopioida kuvan sellaisenaan, SRCCOPY-lippu takaa sen. Tee listauksessa 15.6 esitetyt muutokset ja poista OnPaint()- käsittelijästä OnDrawit()-kutsu (muuten seuraukset ovat odottamattomat). Käännä ja aja sovelluksesi. DrawIt-painiketta napautettaessa huomaat pienen viiveen kuvaa muistiin piirrettäessä. Tämän jälkeen vastavärikuva kopioidaan koko valintaikkunan piirtopinnalle. LISTAUS 15.6 LST15_6.CPP Muistipiirtopintaan piirtäminen OSA IV LUKU 15 347 1 void CDCDrawDlg::OnDrawit() 2 { 3 // TODO: Add your control notification handler code here 4 5 // ** Construct a paint DC from the dialog window 6 CClientDC clientdc(this); 7 8 // ** Create a compatible memory device context 9 CDC memdc; 10 memdc.createcompatibledc(&clientdc); 1 11 12 // ** Find the Client Rect 13 CRect rcclient; 14 GetClientRect(&rcClient); 15 16 // ** Create a compatible bitmaps 17 CBitmap membitmap; 18 membitmap.createcompatiblebitmap(&clientdc, 19 rcclient.width(),rcclient.height()); 2 20 21 // ** Select it into our memory device context 22 memdc.selectobject(&membitmap); 23 24 // ** loop through the horizontal client rectangle 25 for(int x=0; x < rcclient.width() ;x++) 26 { 27 // ** loop through the vertical client rectangle 28 for(int y=0; y < rcclient.height() ;y++) 29 { 30 // ** set each pixel to a different color 31 memdc.setpixel(x,y,x*y); 3 32 } 33 } 34 35 // ** Copy the memory image back to the client device context 36 clientdc.bitblt(0,0, rcclient.width(), 37 rcclient.height(), &memdc,0,0,srcinvert); 4 38 } 1 Näyttöpiirtopinnan kanssa yhteensopiva mustipiirtopinta luodaan.tietokoneen nimi haetaan ja tallennetaan tekstimuuttujaan. 2 Myös bittikartan tulee olla yhteensopiva näyttöpiirtopinnan ominaisuuksien kanssa. 3 Kaikki piirtäminen tehdään muistiin, näytöllä ei näy siitä vielä mitään. 4 Lopuksi koko kuva kopioidaan näytölle käyttäjän nähtäväksi.

348 Koordinaattiavaruus ja pystyakseli Kaikki piirtopinnan piirtofunktiot tarvitsevat parametrit, jotka määrittävät paikan koko koordinaattiavaruudessa. Koordinaattiavaruus on kaksiulotteinen pinta, jonka akselit ovat suorassa kulmassa keskenään. Kunkin pisteen pystyja vaakakomponentti syötetään omana parametrinään piirtofunktioille. Kun käytetään MM_TEXTkohdistustapaa, pystyakselilla pienemmät arvot sijoittuvat ikkunassa ylemmäksi ja pienemmät vaakasuuntaiset arvot vasemmalle. Muissa koordinaatiston kohdistustavoissa pystyakselin arvot kasvavat ylöspäin, mutta vaaka-akselin pienimmät arvot ovat yhä vasemmalla. Grafiikan piirtäminen Koordinaatiston kohdistustapojen käyttö Piirtopinnat tarjoavat hyvin hyödyllisen työkalun koordinaatistojen kohdistustapojen muodossa. Koordinaatiston kohdistustapa (mapping mode) tarkoittaa periaatteessa mahdollisuutta piirtää näytölle tai tulostimelle kuvapisteiden sijaan tuuma- tai senttiyksiköin ja vieläpä saada mittakaavakin miltei oikein. Koordinaatiston kohdistustavoista puhuttaessa törmäät loogisiin yksiköihin ja fyysisiin yksiköihin (device units). Yksinkertaistaen: fyysinen yksikkö on kuvapiste, pikseli, joka on pienin laitteen erottama kuvan mitta. Looginen yksikkö voi olla esitetty amerikkalaisin/ englantilaisin mitoin, metrijärjestelmässä tai suhteessa fonttiin. Oletuskohdistustapa piirtopinnoilla on MM_TEXT. Outo nimi viittaa yhtenevyyteen loogisten ja fyysisten yksiköiden välillä: yksi looginen on yksi fyysinen yksikkö, joten mitään ei muunneta ja ohjelma antaa piirtokomennot suoraan kuvapisteinä. Muitakin kohdistustapoja on, kuten taulukko 15.1 esittää. TAULUKKO 15.1 Tuetut koordinaatistojen kohdistustavat Lippu MM_TEXT MM_LOMETRIC MM_HIMETRIC MM_LOENGLISH MM_HIENGLISH MM_TWIPS MM_ISOTROPIC MM_ANISOTROPIC Yksi looginen yksikkö on Yksi kuvapiste 0,1 mm 0,01 mm 0,01 tuumaa 0,001 tuumaa 1/1440 tuumaa tai 1/20 (tulostus)pistettä (fonteissa) Käyttäjän määräämä arvo, kunhan X ja Y ovat yhtä suuret Käyttäjän määräämä arvo Koordinaatiston kohdistustavan asettaa piirtopinnan SetMapMode()- funktio, jonka ainoa parametri on joku taulukon lipuista. Asettamisen jälkeen piirtofunktion saama GDI muuntaa koordinaatit fyysisiksi yksiköiksi (kuvapisteiksi). Joten jos asetat kohdistustavaksi MM_LOMETRIC ja annat piirtofunktiolle arvon 100, kohdistustapa tietää tarkoitettavan 100 x 0,1 mm eli yhtä senttimetriä. Tämän jälkeen lasketaan vielä, kuinka monta kuvapistettä näytöllä tai tulostimella

voidaan esittää ja muunnos tehdään tällä perusteella. MM_ISOTROPIC ja MM_ANISOTROPIC tapoja käytettäessä käyttäjä voi itse määrittää kuvapisteen ja mittayksikön suhteen SetWindowExt()- ja SetViewPortExt()-funktioilla. Lisäksi koordinaatiston nollapisteen paikkaa voidaan muuttaa SetWindowOrg()- ja SetViewPortOrg()- funktioilla. Kannattaa myös muistaa, että kaikki koordinaatiston kohdistustavat MM_TEXT:iä lukuun ottamatta kasvattavat y- koordinaattia ylöspäin. Tämän huomiotta jättämisestä saattaa seurata ylösalaisin olevia kuvia tai kuva saattaa joutua jopa pois näyttöalueelta. Negatiiviset koordinaatitkin ovat varsin tavallisia, etenkin, jos koordinaatiston nollapistettä on siirretty. Kokeilemme tässä mainittuja tietoja pienessä SDI-sovelluksen näkymää käyttävässä ohjelmassa. Luo aluksi SDI-sovellus luvun 12 ohjeiden mukaan. Ensimmäisessä AppWizardin ikkunassa (Step 1) joudut muuttamaan sovelluksen SDI-tyyppiseksi, mutta muut asetukset voit säilyttää oletettuina. Nimeä projekti MapMode:ksi. MapModen näkymäluokassa on OnDraw()-jäsenfunktio, johon piirtämiskoodi tavallisesti sijoitetaan. Pääset käsiksi OnDraw()- funktioon seuraavien ohjeiden mukaan. OSA IV LUKU 15 349 Näkymän OnDraw()-funktion hakeminen ja muokkaaminen 1. Valitse projektin työtilaikkunassa ClassView-sivu. 2. Napauta plusmerkkiä avataksesi projektin luokkien luettelon. 3. Napauta MapModeView-luokan viereistä plusmerkkiä. 4. Kaksoisnapauta näkymäluokan (view) OnDraw()-funktiota ja pääset muokkaamaan sitä. Voit lisätä listauksessa 15.7 esitetyt uudet rivit OnDraw()-funktioon. Aluksi koordinaatiston kohdistustavaksi asetetaan MM_LOMETRIC (rivillä 9), eli käytettävä yksikkö on 0,1 mm. Rivillä 10 luetaan työalueen koordinaatit funktiolla GetClientRect(). Kohdistustavasta riippumatta saat tältä funktiolta koordinaatit aina kuvapisteinä, joten joudut muuttamaan ne fyysisistä koordinaateista loogisiksi rivin 16 funktiolla DPtoLP(). DPtoLP() tulee sanoista Device Points to Logical Points (fyysinen piste loogiseksi). Funktio käsittelee osoittimena annetun pisteen (CPoint) tai suorakulmion (CRect) koodinaatteja. Saman kääntäen tekee LPtoDP()-

350 Grafiikan piirtäminen funktio, eli muuttaa loogisen pisteen koordinaatit fyysisiksi. Rivien 19-20 silmukassa x-muuttuja käy työalueen vasemmasta reunasta oikeaan loogisiksi koordinaateiksi muuttamisen jälkeen. Silmukassa siirretään x- koordinaattia 100 loogista yksikköä kerrallaan eli MM_LOMETRIC-tavalla yksi senttimetri. Kolme SetPixel()-funktiota merkkaavat ikkunan keskiosan sentin välein. LISTAUS 15.7 LST15_7.CPP MM_LOMETRIC-kohdistustavan käyttö 1 Tässä asetetaan koordinaatiston kohdistustavaksi MM_LOMETRIC-tila, jolloin yksi yksikkö vastaa 0,1 millimetriä. 2 DPtoLP muuttaa laitteen pisteet loogisiksi pisteiksi (kukin 0,1 mm). 3 Kolmella SetPixel()-kutsulla piirretään pieni pystysuuntainen merkki. 1 void CMapModeView::OnDraw(CDC* pdc) 2 { 3 CMapModeDoc* pdoc = GetDocument(); 4 ASSERT_VALID(pDoc); 5 6 // TODO: add draw code for native data here 7 8 // ** Set the mapping mode to 0.1 mm 9 pdc->setmapmode(mm_lometric); 1 10 11 // ** Find the client rectangle (always in device coords) 12 CRect rcclient; 13 GetClientRect(&rcClient); 14 15 // ** Convert Device Coordinates to logical coordinates 16 pdc->dptolp(&rcclient); 2 17 18 // ** Loop from left to right for every 100 logical units 19 for(int x=rcclient.topleft().x; 20 x<rcclient.bottomright().x;x+=100) 21 { 22 // ** Set threes pixels at the y coordinate 23 pdc->setpixel(x,rcclient.centerpoint().y-1,0);3 24 pdc->setpixel(x,rcclient.centerpoint().y,0); 25 pdc->setpixel(x,rcclient.centerpoint().y+1,0); 26 } 27 } Huomaa, että OnDraw() saa osoittimen piirtämisessä käytettyyn piirtopintaan (DC). Sovellusrunko on tehnyt tämän osan koodia automaattisesti. Luvussa 22 opit, kuinka tämä piirtopinta saadaan osoittamaan näytön sijasta tulostimelle. Kääntäessäsi ja ajaessasi tämän sovelluksen pitäisi ikkunaan piirtyä samalle vaakaviivalle merkkejä tasaisin välimatkoin. Viivoittimella mitaten huomaat, että merkit ovat näytöllä sentin välein.

OSA IV LUKU 15 351 Muuta koordinaatiston kohdistustapa MM_LOMETRIC muotoon MM_LOENGLISH. Ohjelmaa ajettaessa merkkien tulisi olla harvemmassa, tuuman välein. Vapaasti skaalattavat kohdistustavat Voit asettaa koordinaatistolle myös itse valitsemasi mittakaavat, mikä helpottaa huomattavasti zoomaustoimintojen toteutusta. Käytettävä kohdistustapa on tällöin MM_ANISOTROPIC terminä hieman huono, koska se tarkoittaa eri kokoista eri suunnasta. Tämä on osin totta, tässä kohdistustavassa mittakaava on erilainen x- ja y-suunnissa, mutta voisin sanoa koiraanikin anisotrooppiseksi, koska se on pitkä, mutta matala. No, nimestä huolimatta tämä kohdistustapa on varsin tehokas, koska voit itse säätää, kuinka muunnos loogisesta yksiköstä tehdään fyysiseksi. Listauksessa 15.8 on esitetty tämä muunnos asettamalla laiteyksiköt SetViewPortExt()-funktiolla työalueen levyiseksi ja 500 kuvapistettä korkeaksi. Rivillä 19 asetetaan looginen leveys sitten 10000 yksiköksi SetWindowExt()-funktiolla pitäen yhä korkeuden 500 loogisessa pisteessä. Näin ollen ikkunan leveys on täsmälleen 10000 yksikköä. Rivien 23-30 silmukassa piirretään tasan 100 vaakasuuntaista 100 pistettä leveää merkkiä sivun reunasta reunaan. Kunkin loogisen yksikön pystyakseli on kuitenkin täsmälleen yksi kuvapiste (kuten MM_TEXT). Jos käännät ja ajat sovelluksen tehtyäsi nämä listauksen 15.8 mukaiset muutoksen OnDraw()-funktioon, pitäisi ikkunassa näkyä 100 tasavälein piirrettyä merkintää ikkunan reunasta reunaan. Jos muutat ikkunan kokoa, merkintöjen koko muuttuu myös ja niitä on yhä 100 yhtä tasaisin välein. LISTAUS 15.8 LST15_8.CPP MM_ANISOTROPIC-- kohdistustavan käyttö 1 void CMapModeView::OnDraw(CDC* pdc) 2 { 3 CChap16P2Doc* pdoc = GetDocument(); 4 ASSERT_VALID(pDoc); 5 6 // TODO: add draw code for native data here 7 8 pdc->setmapmode(mm_anisotropic); 1 9 10 CRect rcclient; 11 GetClientRect(&rcClient); Kohdistustarkkuus näytöllä Älä usko näytöllä esitettyjen mittojen tarkkuuteen - monitorit ovat eri kokoisia ja monitorien kuvapisteetkin. Tarkkuus tulostimella on selvästi parempi. 1 MM_ANISOTROPIC mahdollistaa eri yksiköt eri akseleilla.

352 Grafiikan piirtäminen 2 SetViewportExt() funktio asettaa laitteen leveyden ja korkeuden kuvapistekoordinaatein. 3 SetWindowExt()-funktio asettaa ikkunan loogisen leveyden ja korkeuden loogisin koordinaatein. 12 13 // ** Set the device extent to the client extent across 14 // ** and 500 pixels down 15 pdc->setviewportext(csize(rcclient.bottomright().x,500)); 2 16 17 // ** Set the logical extent to the 10,000 units across 18 // ** and 500 pixels down 19 pdc->setwindowext(csize(10000,500)); 3 20 21 pdc->dptolp(&rcclient); 22 23 for(int x=rcclient.topleft().x; 24 x<rcclient.bottomright().x; 25 x+=100) 26 { 27 pdc->setpixel(x,rcclient.centerpoint().y-1,0); 28 pdc->setpixel(x,rcclient.centerpoint().y,0); 29 pdc->setpixel(x,rcclient.centerpoint().y+1,0); 30 } 31 } SetWindowExt()- ja SetViewportExt()- funktioiden käyttö SetWindowExt()- ja SetViewportExt()-funktioita käytetään ainoastaan MM_ISOTROPIC ja MM_ANISOTROPICkohdistustavoissa; muissa niistä ei välitetä. MM_ISOTROPIC-kohdistustapa on nimetty varmaankin samalla logiikalla kuin MM_ANISOTROPIC:kin, termi isotrooppinenhan tarkoittaa samojen ominaisuuksien omaamista eri suunnissa. Voit käyttää tätä kohdistustapaa aivan edellisen tavoin SetWindowExt() ja SetViewPortExt()-funktioilla, mutta muista, että GDI skaalaa eri suuruiset mittakaavat siten, että vaaka-akselin yksi looginen yksikkö on aina myös yksi pystyakselin looginen yksikkö. Tämä kohdistustapa on hyödyllinen, jos haluat säilyttää kuvan sivusuhteen ennallaan. KATSO MYÖS Koordinaatiston kohdistustapojen käyttö tulostuksessa luvusta 22. Piirtopinnan ominaisuuksien lukeminen Piirtopinnan avulla saadaan suoraan tutkittua, mitä ominaisuuksia kyseinen laite tukee. Yleisimmässä tapauksessa tuki löytyy suunnilleen kaikenlaiseen piirtämiseen ja kaikille fonteille. Osa laitteista kuten piirturit kykenevät kuitenkin vain viivanpiirtoon. Tulostimet saattavat olla mustavalkoisia tai vähävärisiä. Saat tämän kaiken selville yhdellä funktiolla, joka on GetDeviceCaps(). Kuten arvata saattaa, jotta funktio saataisiin kertomaan nämä kaikki tiedot, sille voidaan syöttää useita erilaisia lippuja. Piirtopinnan GetDeviceCaps()- jäsenfunktio tarvitsee ainoastaan yhden parametrin, joka kertoo

lipputietona halutun tiedon tyypin. Nämä liput on ryhmitelty tarvittavan tiedon mukaan. TECHNOLOGY-lippu kertoo tutkittavan laitteen tyypin, mahdolliset paluuarvot on esitetty taulukossa 15.2. TAULUKKO 15.2 GetDeviceCaps()-funktion paluuarvot TECHNOLOGY-syötteellä OSA IV LUKU 15 353 Paluuarvo DT_RASDISPLAY DT_RASPRINTER DT_PLOTTER DT_RASCAMERA DT_CHARSTREAM DT_METAFILE DT_DISPFILE Kuvaus Tavallinen näyttökortti ja näyttö Tavallinen bittikarttatulostin Viivapiirturi Bittikartan syöttölaite Merkkisyöttölaite, kuten näppäimistö Tiedosto, jossa on piirto-ohjeet Näyttötiedosto Laiteohjaimen versio saadaan DRIVERVERSION-lipulla. Laitteen mitat saadaan useaa, taulukossa 15.3 esitettyä lippua käyttäen. TAULUKKO 15.3 GetDeviceCaps()-funktion liput fyysisten mittojen lukemista varten Syötetty lippu HORZRES VERTRES HORZSIZE VERTSIZE ASPECTX ASPECTY ASPECTXY Kuvaus Laitteen leveys kuvapisteinä Laitteen korkeus kuvapisteinä Laitteen leveys millimetreinä Laitteen korkeus millimetreinä Laitteen korkeuden ja leveyden suhde kuvapisteinä Laitteen leveyden ja korkeuden suhde kuvapisteinä Laitteen kulmasta kulman halkaisijan pituus kuvapisteinä Myös värin ja piirto-ominaisuuksien hakemiseen on useita lippuja, jotka on esitetty taulukossa 15.4.

354 Laitekohtaiset ominaisuudet Funktiolle on olemassa useita kaarenpiirto- (CURVECAPS), viivanpiirto- (LINECAPS), monikulmion piirto (POLYGONCAPS) ja tekstinpiirtoominaisuuslippuja (TEXTCAPS). Näillä on merkitystä erityisesti piirturilaitteilla, jotka eivät välttämättä tue kaikkia piirtotoimintoja. Grafiikan piirtäminen TAULUKKO 15.4 GetDeviceCaps()-funktion liput värien ja piirto-ominaisuuksien lukemista varten Syötetty lippu NUMCOLORS PLANES BITSPIXEL NUMPENS NUMBRUSHES NUMFONTS COLORRES SIZEPALETTE Kuvaus Laitteen tukemien yksittäisten värien määrä Laitteen tukemien väritasojen määrä Yhden kuvapisteen esittämiseen käytettyjen bittien määrä Laitteen tukemien kynien määrä Laitteen tukemien siveltimien määrä Laitteen tukemien fonttien määrä Laitteen väriresoluutio bitteinä kuvapistettä kohden, mikäli aiheellinen Järjestelmäpaletissa olevien alkioiden määrä, mikäli aiheellinen GetDeviceCaps()-funktion parametrit Funktiolle voidan syöttää myös monia tässä esittelemättömiä lippuja. Näiden avulla saadaan selville, tukeeko laite tietttyä piirtotoimintoa. Kaikki piirtotoiminnot ovat käytettävissä ja toimivat hyvin useimmilla tavallisimmilla näytöilla ja tulostimilla. Yritämme nyt selvittää GetDeviceCaps()-funktion avulla näyttösi ominaisuudet. Nämä tiedot voitaisiin esittää luetteloruudussa MapMode-sovelluksen About-ikkunassa. Luetteloruutukontrollin lisääminen About-ikkunaan 1. Valitse projektin työtilaikkunassa ResourceView-sivu. 2. Avaa kansiot ja löydät IDD_ABOUTBOX-valintaikkunan. 3. Kaksoisnapauta IDD_ABOUTBOX:ia päästäksesi muokkaamaan sitä. 4. Muuta valintaikkunan koko sopivaksi ja lisää siihen luetteloruutu (Listbox). 5. Näppäile Alt+Enter ja pääset muokkaamaan luetteloruudun asetuksia. 6. Aseta List Box Properties ikkunassa General-välilehdellä kontrollin ID-tunnukseksi IDC_DEVCAPS. 7. Napauta Styles-välilehteä ja poista Sort-valinta. 8. Sulje Properties-ikkuna Enterillä. 9. Lisää luetteloruudun yläpuolelle Static-tekstikontrolli ja syötä sen otsikoksi (caption) Device Capabilities. 10. About-ikkunan pitäisi nyt olla kuvassa 15.4 esitetyn kaltainen.

OSA IV LUKU 15 355 KUVA 15.4 MapMode-projektin Aboutikkunan muokkaaminen. Nyt joudut vielä liittämään kontrollimuuttujan luetteloruutuusi, jotta voisit lisätä siihen helposti tekstiä. Muuttujan liittäminen luetteloruutukontrolliin ClassWizardilla 1. Valitse luetteloruutu napauttamalla sitä resurssieditorissa. 2. Käynnistä ClassWizard näppäilemällä Ctrl+W. ClassWizardikkunan Member Variables välilehdellä tulisi nyt olla Control IDs luettelossa IDC_DEVCAPS valittuna. 3. Avaa Add Member Variable ikkuna napauttamalla Add Variable painiketta. 4. Avaa Category-yhdistelmäruutu ja vaihda Value-valinta Control:iksi. 5. Syötä jäsenmuuttujan nimeksi Member Variable ruutuun m_listdevcaps. 6. Sulje valintaikkuna OK-painikkeella.

356 Grafiikan piirtäminen 7. Sulje ClassWizard OK-painikkeella. Nyt olet liittänyt CListBox-muuttujan luetteloruutukontrolliin CAboutDlg-luokkaan. Nyt joudut syöttämään vielä sopivan koodin GetDeviceCaps()-funktiolle piirtopinnan ominaisuuksien lukemista varten ja näyttämään tulokset luetteloruudussa. Tätä varten tarvitset CAboutDlg-luokkaan OnInitDialog()-käsittelijän luetteloruudun alustamiseksi ikkunaa avattaessa. OnInitDialog()-käsittelijäfunktion lisääminen 1. Valitse projektin työtilaikkunassa ClassView-sivu. 2. Avaa pikavalikko napauttamalla CAboutDlg-alkiota hiiren oikealla painikkeella. 3. Valitse Add Windows Message Handler komento. 4. Valitse New Windows Messages/Events luettelosta WM_INITDIALOG-sanoma. 5. Lisää käsittelijäfunktio Add and Edit painikkeella ja pääset samalla myös muokkaamaan sitä. Nyt voit lisätä listauksessa 15.9 esitetyn koodin lisätäksesi GetDeviceCaps()-funktion palauttamat arvot About-ikkunan luetteloruutuun. Tässä listauksessa CClientDC:tä käytetään rivillä 7 About-ikkunan piirtopinnan osoittamiseen. Tässä piirtopinnassa on kaikki näytön tiedot. Riveillä 12 ja 13 HORZRES-vaakaresoluutiolippuun liittyvä arvo muotoillaan luettavaksi tekstiksi. Rivillä 16 tämä merkkijono lisätään luetteloruutuun näyttämistä varten. Samat toimenpiteet tehdään myös muille luettaville ominaisuuksille. LISTAUS 15.9 LST15_9.CPP GetDeviceCaps()-funktion palauttamien arvojen näyttäminen About-ikkunassa 1 BOOL CAboutDlg::OnInitDialog() 2 { 3 CDialog::OnInitDialog(); 4 5 // TODO: Add extra initialization here 6 // ** Capture the about dialog's device context 7 CClientDC dcdev(this); 8 // ** Declare a string 9 CString strcap;

10 11 // ** Capture and format the device cap results 12 strcap.format("horizontal Resolution = %d pixels", 13 dcdev.getdevicecaps(horzres)); 14 m_listdevcaps.addstring(strcap); 15 strcap.format("vertical Resolution = %d pixels", 16 dcdev.getdevicecaps(vertres)); 17 m_listdevcaps.addstring(strcap); 18 strcap.format("horizontal Size = %d cm", 19 dcdev.getdevicecaps(horzsize) / 10); 20 m_listdevcaps.addstring(strcap); 21 strcap.format("vertical Size = %d cm", 22 dcdev.getdevicecaps(vertsize) / 10); 23 m_listdevcaps.addstring(strcap); 24 strcap.format("supported Colors = %d colors", 25 dcdev.getdevicecaps(numcolors)); 26 m_listdevcaps.addstring(strcap); 27 return TRUE; // return TRUE unless you set the // focus to a control 28 // EXCEPTION: OCX Property Pages should // return FALSE 29 } OSA IV LUKU 15 357 Käännettyäsi sovelluksen ja käynnistettyäsi sen näiden muutosten tekemisen jälkeen valitse Help-valikosta About Map Mode ja näet näyttösi tekniset tiedot kuvan 15.5 mukaisesti. Oma näyttösi saattaa ehkä tukea useampia värejä kuin mitä luetteloruudussa esitetään, koska piirtopinnan kautta esitetään ainoastaan järjestelmäpaletin värien määrä, joita oletuksena on 20. Määrää saadaan kasvatettua liittämällä piirtopintaan suurempi väripaletti näytön sallimissa puitteissa.

358 Grafiikan piirtäminen KUVA 15.5 GetDeviceCaps()-funktion palauttamien laitteen ominaisuuksien näyttäminen. KATSO MYÖS Tulostinlaitteen ominaisuuksien selvittäminen luvussa 22.