Klassi java.awt.Graphics eksemplari kutsutakse graafiliseks kontekstiks Ta esitab teatavat joonistatavat ala (näiteks komponendi ekraanipind või nn eeljoonistuspuhver(off-screen buffer). Graafikakontekst pakub meetodeid kõigi põhiliste graafikaoperatsioonide täitmiseks oma pinnal. Sinna kuuluvad ka pildi andmete joonistamiseks vajalikud meetodid. Kontekstiks annab teda alust kutsuda see, et ta sisaldab endas ka selliseid kontekstuaalseid andmeid nagu tekstifont, aktiivne värv, edastuse tüüp.
Üldjoontes on neli põhilist võimalust Graphics objekti kasutamiseks Need on:
Tegeldes mingi komponendi update() meetodiga võime eeldada, et pilt ekraanil püsib muutmatuna ning me saame seda vaid mõningate täiendustega ajakohastada. Sellise tegevuse juures aitab sageli joonistamistegevust optimiseerida nn. väljalõigatava (Clipping) ala määramine. paint() meetodi puhul joonistatakse kogu ala uuesti üle.
Kuna antud koordinaatide süsteem on vaid vaikimisi ning pole kindlasti mitte kohustuslik, siis saab koordinaate translate() meetodiga ka nihutada andes talle argumendiks uue null-punkti koordinaadid.
clipRect() meetodiga saab joonistatavast alast teha väljalõike. Töötamaks mitme väljalõigatud alaga tuleks luua originaalsest graafika kontekstist create() meetodiga koopia.
Kui olete teinud mingitki graafikat sisaldavat programmeerimist, tulevad nii joonistusmeetodid kindlasti tuttavad ette. Järgnev näide TestPatterndemonstreerib peamisi kujundi joomistamise meetodeid.
Kõik eelpoolmainitud näites toodud meetodid välja arvatud fillArc() ja fillPolygon() vajavad oma tööks ülemise vasaku punkti koordinaate ning kõrgust ning laiust.
Uue ja huvitava kujundina antud näites kasutasime hulknurka (Polygon). Hulknurga joonistamiseks on vaja kahte massiivi, kus oleksid siis vastavalt iga hulknurga tipu x ja y koordinaadid. Ehkki Graphics klassis on ka lihtsad meetodid kahe massiivi järgi täidetud hulknurga joonistamiseks, lõime me näites Polygon objekti, kuna tal on mitmeid päris kasulikke omadusi, nagu näiteks see, et ta võib teatada oma nn. "raamkasti" ning ka seda, kas mingi asub hulknurga sees.
fillArc() meetod vajab oma argumentideks kuut täisarvu. Neli esimest määravad sektori "raamkasti" ning kaks viimast stardinurga ning kaare suuruse (mõlemad kraadides).
Tabelis 11-1 näidatakse kujundijoonistamise meetodeid Graphics klassist. Nagu näha, on pea iga fill() meetodi kõrval ka vastav draw() meetod, mis renderdab kujundit tühjana.
Meetod | Kirjeldus |
---|---|
draw3DRect() | 3D ristkülik |
drawArc() | kaar |
drawLine() | joon |
drawOval() | ovaal |
drawPolygon() | hulknurk |
drawRect() | ristkülik |
drawRoundRect() | ümarate nurkadega ristkülik |
fill3DRect() | täidetud 3D ristkülik |
fillArc() | täidetud kaar |
fillOval() | täidetud ovaal |
fillPolygon() | täidetud hulknurk |
fillRect() | täidetud ristkülik |
Värvidega tegeleb klass java.awt.Color. Objekt Color kirjeldab üksikut värvi. Uue värvi loomiseks tuleb määrata tema punase-, rohelise- ja sinise (RGB) väärtus kas kolme täisarvuna, või ujukomaarvudega vahemikus 0.0 ... 1.0. Samuti saab kasutada getColor() meetodit saamaks süsteemset värvi nime. getColor() võtab String värvi omaduse, teisendab selle täisarvuks omaduste listist ning tagastab Color objekti, mis sellele numbrile vastab.
Color klassis defineeritakse ka mitmed static final tüüpi värviväärtusi, mida me kasutasime TestPattern näites.
Font timesFont = new Font("TimesRoman", Font.PLAIN, 12);
Font courierFont = new Font("Courier", Font.BOLD, 18);
Fondi nimi on seotud tegeliku fondiga kohalikul platvormil. Töö fontidega on siiski veel seotud mõningaste platvormist sõltuvate raskustega, aga eelpoolmainitud nimede kasutamisel peaks tulemus olema igati platvormist sõltumatu.
Samuti on võimalik kasutada static meetodit Font.getFont() saamaks fondi nime süsteemi atribuutide listist. Sarnaselt getColor()'ile teisendab getFont() fondi String omaduse vastavaks fondinimeks Properties tabelist ning väljastab siis vastava Font objekti.
Klass Font defineerib kolm static tüüpi identifikaatorit: PLAIN, BOLD ja ITALIC. Neid väärtusi võib kasutada iga fondiga. Fondi suurus punktides määrab tema suuruse ekraanil. Kui vastava suurusega fonti süsteemis pole, omandab font vaikimisi suuruse.
Font'i kohta saab infot mitmete erinevate meetoditega. Näiteks olgu toodud getName(), getFamily(), getSize() ning getStyle
Font objekti kasutatakse argumendina setFont() meetodis, määrates kas komponendi või Graphics objekti kirjastiili.
getFontMetrics()'i abil saab küsida FontMetrics objekti nii mingi kindla fondi kohta, kui ka Graphics objekti vaikimisi fondi kohta (nt. g.getFontMetrics() ). Järgnev näide näitab ekraanil sõna, ning joonistab mõningad abijooni. HiireVajutusega vahetatakse suurt ja väikest fonti.
Järgnev tabel näitab mõningaid fondi atribuute käsitlevaid meeetodeid:
Meetod | Kirjeldus |
---|---|
getFont() | objekt Font koos kirjeldusega |
getAscent() | kõrgus ülalpool baasjoont |
getDescent() | sügavus allpool baasjoont |
getLeading() | standardne vertikaalne ruum ridade vahel |
getHeight() | täielik rea kõrgus (ascent + descent + leading) |
getWidth( char ch ) | tähe laius |
stringWidth( String str ) | stringi laius |
getWidths() | antud stiili esimese 256 sümboli laiused, väljastab int()'i |
getMaxAdvance() | kõigi märkide laiuste maksimum |
Nimetatud probleemidega tegelevadki pildivaatlejad - spetsiaalsed objektid,
mis implementeerivad ImageObserver liidest. Kõik pildi
joonistamise või uurimise meetodid lõpetavad alati viivitamatult,
aga võtavad pildivaatleja objekti omale parameetriks.
ImageObserver valdab pildi staatuse kohta käivat infot
ning teeb pildi info ülejäänud rakendusele kättesaadavaks.
Pildivaatleja saab informeeritud nii pildi lugemise infost (kas pildi andmed
on kohal, kas on veel lisapunkte saabumas, kas kogu pildi raam on valmis,
kas pildi lugemisel saadi viga) kui ka pildi karakteristikutest (mõõdud,
muud omadused) niipea, kui need osutuvad määratuteks.
drawImage() meetod, nagu ka teised pildioperatsioonid võtab parameetriks ImageObserver objekti ning tagastab tõeväärtuse selle kohta, kas pilt joonistati oma täies terviklikkuses või mitte. Kui kogu pilti ei saa joonistada, siis kuvab drawImage() pildi kõik osad, mida ta kuvada suudab ning lõpetab. Pildivaatleja tegeleb edaspidisega juba iseseisvalt. Kui terve pilt on kohal võib pildivaatleja teha sellega mida iganes. Kõige tuihemini kutsub ta välja repaint() meetodi.
Detailsemalt puudutame pildivaatlejat pisut hiljem. praegu püüame kasutada klassi Component pakutavaid olemasolevaid vahendeid. Nagu sellest aru võib saada, saab iga komponent kasutada oma isiklikku pildivaatlejat.
class MyApplet extends java.applet.Applet; ... public void paint(Graphics g) { drawImage( myImage, x, y, this ); ...
drawImage( myImage, x, y, x2, y2, this );
Pildi suuruse teada saamiseks on meetodid getWidth() ning getHeight() et pildi mõõtmeid ei pruugi tema lugemise ajal veel teada olla on ka nende meetodite parameetriks pildivaatleja objekt. Kui pildi suurust ei saa hetkel määrata tagastavad antud meetodid -1 ja informeerivad vaatlejat.
Kuna joonistamine võtab aega, ning ajakulu viib viivituste ning ebatäiusliku tulemuseni, siis on meie eesmärk seda joonistamiseks kuluvat aega minimiseerida. Nagu eespool juba veendusime oli TestPattern'il probleeme vilkumisega. See tulenes sellest, et me joonistasime üle suuri värvitud alasid iga kord, kui paint() meetod välja kutsuti. Antud juhul saab vilkumise eemaldada, kui pilt joonistada pildipuhvrisse ning seejärel kopeerida puhver ekraanile. Lähemalt sellest mõni aeg hiljem.
Järgmine näide illustreerib mõningaid pildi uuendamisel üles kerkivaid probleeme. Nagu paljudes animatsioonides, on ka siin tagapõhi ning liigutatavaks objektiks on skaleeritud pilt.
Kui me hiirt vedasime, muutusid kaks muutujat (CurrentX ja CurrentY) ning repaint() kutsuti välja pildi uuendamiseks. Selle peale käivitatakse paint() ning tema kuvabki kogu pildi üle.
Meie esimene viga on see, et me küll uuendasime ekraanipilti, kuid me jätsime hooletusse rakendi update() meetodi. update() meetodi vaikimisi sisu on järgmine:
// default implementation of applet.update public void update( Graphics g) { setColor( BackgroundColor ); fillrect( 0, 0, size().width, size()height ); paint( g ); }See meetod lihtsalt kustutab kogu pildi ja joonistab selle siis uuesti. See pole peaaegu kunagi parim lahendus, aga ta on ainus variant uuendada pilti teadmata, kui palju meil on tegelikult vaja uuendusi teha.
Me võime ära jätta selle tagapõhja ülejoonistamise, sest meie rakend teeb seda niikuinii. Uus update() näeks siis välja järgmine:
public void update( Graphics g) { paint( g ); }See teeb meie rakendi töö tunduvalt sujuvamaks, aga siiski teeme me veel ülipalju tarbetut tööd.
Miks me peaksime joonistatavat ala piirama?. Aga selleks, et uuendada vaid antud (kitsamat) ala. Ko~iki joonistamisi, mis antud alast va"ljapoole ja"a"vad, ignoreeritakse. Teise hea omadusena saab hea realiseerimise korral graafilist konteksti panna a"ra tundma ko~iki va"ljaspool seda ala toimuvaid joonistamisi ning neid siis ignoreerida. Sellisest tegevusest saab keerukate joonistusoperatsioonide korral korraliku aja kokkuhoiu. Meie na"ite korralikuks toimimiseks peame piirama joonistatava ala pisimaks ristkülikuks, mis sisaldab nii "vana" kui ka "uut" pilti.
Meie uus ja "targem" update() peaks hoidma oluliselt kokku aega uuendades vaid vajalikku "valja lõigatud" ala. Kuna antud meetodi realiseerimiseks on vaja teha vaid va"heseid muudatusei meie eelmise na"ite koodis, siis olgu uus na"ide eelmise alamklass.
Pole just ta"htsusetu lisada, et va"ljakutsed repaint() poole mouseDrag() meetodis pole u"ks-u"heselt seotud AWT poolt tehtavate va"ljakutsetega update() poole. See pole siiski mitte puudus, vaid positiivne joon, mis aitab AWT'l planeerida ning koordineerida joonistusva"ljakutseid.
Saamaks iga hiirekursori koordinadimuutuse peale ka kohe muudetud pilti, on meil kaks vo~imalust. Me kas teeme natuke u"mber mouseDrag() meetodit vo~i paigutame oma su"ndmused teatavasse ja"rjekorda. Esimest neist vo~imalusist ka"sitleme me DoodlePad na"ites. Teise variandi puhul peaksime me mo"o"da hiilima AWT enda su"ndmuseplaneeringu vo~imalustest ning asendama need meie meie enda omadega, ga seda vastutust me siinkohal endale ei vo~ta.
Topeltpuhverdus tähendab, et pilt joonistatakse enne näitamist nn. eeljoonistuspuhvrisse ning siis kopeeritakse valmis töö u"he joonistuska"suga ekraanile.
Topeltpuhverduse efekti saame me eelnevas näites vaid pisikesi muutusi tehes.
Antud na"ites oli muutuja offScrImg see eeljoonistuspuhver. Ta on tavaline joonistatav Image objekt, mille saamiseks kasutatakse createImage() meetodit. createImage() on sarnane getImage()'le, kuid tekitab ette antud mo~o~tmetega tu"hja pildiala. Edasi saame me oma eeljoonistuspilti, kui tavalist ekraani ala kasutada, ku"sides neilt na"iteks getGraphics() meetodiga graafilist konteksti. Pa"rast seda, kui oleme eeljoonistuspildi valmis saanud saame selle ekraanile kopeerida drawImage() meetodiga.
Iga uue update() puhul tekitame me uue eeljoonistuspuhvri, sest iga kord on meil vaja uut va"ljalo~igatavat ala.
Graphics klassi dispose() meetod vo~imaldab meil graafilise konteksti vabastada, kui me teda enam ei vaja. See on vaid optimiseerimine, millest on kasu, kui teeme keerukaid joonistamisi. Suuremate u"lejoonistamiste ning keerukmate arvutuste puhul ei ole lihtsalt otstarbekas prahikoristaja (carbage collection) peale loota.
Lipp | Kirjeldus |
---|---|
HEIGHT | pildi pikkus on valmis |
WIDTH | pildi laius on valmis |
FRAMEBITS | raam on valmis |
SOMEBITS | suvaline hulk punkte on kohal |
ALLBITS | pilt on valmis |
ABORT | pildi lugemine katkestati |
ERROR | pildi töötlemisel saadi viga,pildi kuvamise katse ebaõnnestus |