|
Pentru a diminua unele dintre restrictiile modelului de procesare AWT original si pentru a furniza un nivel mai ridicat de abstractizare, a fost elaborata o noua specificatie denumita Java 2D API. Acest nou API extinde capacitatile AWT pentru grafica bidimensionala si pentru procesarea de imagini. In realitate, pachetul Java 2D este unificat cu AWT si este o componenta a platformei Java 2 Standard Edition.
Imaginile in Java 2D API sunt compuse din doua componente principale:
Date imagine brute care reprezinta pixeli
Informatia necesara pentru interpretarea pixelilor
In ceea ce priveste procesarea de imagini, Java 2D API retine cateva elemente din modelul AWT producator/consumator, dar adauga conceptul de date imagine persistente bazate pe memorie, set extensibil de filtre imagine 2D, o varietate mare de formate de imagine si modele de culoare si o reprezentare mai sofisticata a dispozitivelor de iesire. Java 2D introduce notiunea de reprezentare a imaginilor independenta de rezolutie prin introducerea interfetelor Renderable si Rendered, permitandu-se imaginilor sa fie trase printr-un lant de operatii filtru, cu rezolutia imaginii selectata prin contextul reprezentarii.
In Figura 1 este prezentat modelul de gestiune generala a imaginilor din Java 2D, care este controlat de catre clasa BufferedImage.
Un obiect de tip BufferedImage poate fi creat direct in memorie si utilizat pentru a pastra si manipula date imagine obtinute de la un fisier sau de la un URL. Un BufferedImage poate fi afisat folosind orice obiect Graphics2D ca dispozitiv de afisare, sau reprezentat spre orice alta destinatie folosind un context Graphics2D adecvat. Un obiect de tip BufferedImage contine doua alte obiecte: un Raster si un ColorModel.
Clasa Raster asigura gestiunea datelor imagine. Acesta reprezinta o arie dreptunghiulara dintr-o imagine care stocheaza datele imagine in memorie si de asemenea un mecanism pentru crearea a mai multor sub-imagini dintr-un singur bufer de date imagine. Aceasta clasa asigura metode pentru accesul la pixeli specifici dintr-o imagine. Un obiect Raster contine doua alte obiecte, un DataBuffer si un SampleModel.
Clasa DataBuffer pastreaza datele pixelului in memorie. Clasa SampleModel interpreteaza datele din bufer si furnizeaza pixeli individuali sau regiuni de pixeli dreptunghiulare.
Regulile pentru interpretarea pixelilor sunt incapsulate intr-un obiect ColorModel, de exemplu daca valorile trebuie interpretate in mod direct sau ca culori indexate. Astfel pentru ca un pixel sa fie afisat, acesta trebuie sa fie imperecheat cu un model de culoare.
Pachetul java.awt.image contine cateva implementari ale clasei ColorModel, incluzand acele pentru reprezentari impachetate si pe componente ale pixelului. Tot acest pachet furnizeaza clase suplimentare care definesc operatii de filtrare asupra unor obiecte BufferedImage si Raster. Fiecare operatie de procesare este concretizata printr-o clasa care implementeaza interfata BufferedImageOp, sau interfata RasterOp, sau amandoua. Clasa de operatii defineste metodele de filtrare prin care se efectueaza manipularea imaginii. In Figura 2. este ilustrat modelul de baza pentru procesarile de imagini din Java 2D API.
Operatiile suportate includ:
Transformare affine
Scalare de amplitudine
Modificare cu tabele de cautare (lookup table
Combinare liniara a benzilor
Conversie de culori
Convolutie
Elemente data sunt primitive tip utilizate ca unitati de stocare a datelor imagine. Elementele data sunt membrii individuali ai unei arii DataBuffer. Configuratia elementelor in buferul de date este independenta de interpretarea datelor ca pixeli de catre obiectul SampleModel al imaginii.
Esantioanele sunt membrii distincti de pixeli ai unei imagini. Un obiect SampleModel asigura un mecanism pentru convertirea elementelor din DataBuffer in pixeli si esantioanele sale. Esantioanele unui pixel pot sa reprezinte valori de baza intr-un model de culoare particular, de exemplu, un pixel dintr-un model de culoare RGB este compus din trei esantioane: red green si blue
Componentele sunt valori ale pixelilor independente de interpretarea culorilor. Distinctia dintre componenta si esantion este folositoare pentru IndexColorModel, unde componentele pixelului sunt indexate intr-un LookupTable.
O banda este o componenta a spatiului de culoare a unei imagini. De exemplu, componentele de rosu, verde si albastru reprezinta benzile dintr-o imagine RGB. Datele pixelilor pot fi stocate in mai multe moduri, iar Java 2D suporta doua dintre acestea: organizare pe benzi si arie regulata de pixeli.
Stocarea pe benzi a pixelilor determina ca un pixel sa fie format dintr-o data esantion de la aceeasi pozitie in fiecare banda. Stocarea pixelilor intr-o arie organizeaza datele imagine in pixeli, astfel avem o singura arie continand toti pixelii, iar benzile sunt constituite dintr-un set de esantioane la aceeasi pozitie de index pentru fiecare pixel.
Un obiect ColorSpace incapsuleaza regulile care guverneaza modul in care un set de masurari corespund unei culori anume. Implementarile clasei ColorSpace din pachetul java.awt.color reprezinta cele mai utilizate spatii de culoare, incluzand RGB sau scala de gri. Un spatiu de culoare reprezinta niste reguli pentru modul cum vor fi interpretate valorile culorilor.
Separatia spatiului de culoare de modelul de culoare asigura o flexibilitate sporita la reprezentarea si conversia culorilor dintr-un anumit spatiu al culorii in altul.
Clasa BufferedImage este clasa principala care suporta procesarea de imagini in modul imediat. Aceasta clasa gestioneaza imaginea in memorie asigurand metode de stocare, interpretare si reprezentare a datelor pixelilor spre un context Graphics sau Graphics2D. Pentru a crea un obiect BufferedImage, se apeleaza metoda Component.createImage. Aceasta returneaza un obiect de tip BufferedImage a carui caracteristici de reprezentare se potrivesc cu cele ale componentei folosite pentru creare. Imaginea creata este opaca si are culorile de suprafata si fundal ale obiectului Component, cu posibilitatea de modificare a transparentei. Acest lucru este exemplificat pe sectiunea de cod urmatoare (Exemplul 2).
Exemplul 2. Utilizarea clasei BufferedImage
BufferedImage offImg;
public Graphics2D
createMyG2D(Graphics g)
if (offImg != null)
// sterge componenta
g2.clearRect(0, 0, width, height);
return g2;
Clasa BufferedImage poate fi folosita la pregatirea elementelor grafice in afara ecranului si apoi afisarea lor pe ecran. Aceasta tehnica este folositoare atunci cand un element grafic este utilizat in mod repetat.
Facilitatile pachetului java.awt permit folosirea buferelor de pregatire in afara ecranului, astfel modificarea unei imagini se face in acelasi mod ca si cum ea ar fi afisata intr-o fereastra. Toate facilitatile de reprezentare Java 2D pot fi aplicate atunci cand se lucreaza cu imagini in afara ecranului.
Cea mai simpla metoda de a crea o imagine, care poate fi folosita ca si un bufer in afara ecranului, este prin apelul metodei Component.createImage.
Clasa BufferedImage suporta cateva tipuri de imagine predefinite:
TYPE_3BYTE_BGR - imagine cu componente de culoare RGB pe 8 biti, corespunzatoare modelului de culoare RGB din Windows, cu culorile Blue, Green si Red stocate pe 3 bytes.
TYPE_4BYTE_ABGR - imagine cu componente de culoare RGBA pe 8 biti, cu culorile Blue, Green si Red stocate pe 3 bytes si un byte pentru canalul alfa.
TYPE_4BYTE_ABGR_PRE - imagine cu componente de culoare RGBA pe 8 biti cu culorile Blue, Green si Red stocate pe 3 bytes si un byte pentru alfa
TYPE_BYTE_BINARY - imagine opaca pe 1, 2 sau 4 biti
TYPE_BYTE_GRAY - imagine scala de gri pe un unsigned byte, neindexata.
TYPE_BYTE_INDEXED - imagine pe un byte indexata.
TYPE_CUSTOM - imagine nerecunoscuta, de tip utilizator.
TYPE_INT_ARGB_PRE - imagine cu componente de culoare RGBA pe 8 biti compusa din pixeli integer.
TYPE_INT_ARGB - imagine cu componente de culoare RGBA pe 8 biti compusa din pixeli integer.
TYPE_INT_BGR - imagine cu componente de culoare RGBA pe 8 biti, corespunzator pentru modelele de culoare din Windows sau Solaris, cu culorile Blue, Green si Red impachetate in pixeli integer.
TYPE_INT_RGB - reprezinta o imagine cu componente de culoare RGB pe 8 biti impachetate in pixeli integer.
TYPE_USHORT_555_RGB - imagine cu componente de culoare 5-5-5 RGB (5-biti Red, 5-biti Green, 5-biti Blue) fara canal alfa.
TYPE_USHORT_565_RGB - imagine cu componente de culoare 5-6-5 RGB (5-biti Red, 6-biti Green, 5-biti Blue) fara canal alfa.
TYPE_INT_GRAY - imagine in scala de gri stocata pe tipul unsignrd short, neindexata.
Pentru reprezentarea intr-un obiect de tip BufferedImage, se apeleaza metoda BufferedImage.createGraphics, care returneaza un obiect Graphics2D. Cu acest obiect se pot apela toate metodele pentru reprezentarea primitivelor grafice, sau reprezentarea altor imagini in imagine. In fragmentul de cod urmator (Exemplul 3.) se ilustreaza utilizarea buferarii in afara ecranului:
Exemplul 3. Exemplu de utilizare a buferarii in afara ecranului
public void update(Graphics g)
Sterge suprafata care a fost desenata anterior
big.setColor(Color.white);
big.clearRect(0, 0, area.width, area.height);
Deseneaza si umple un nou dreptunghi in bufer.
big.setPaint(drept1);
big.draw(rect);
big.setPaint(drept2);
big.fill(rect);
Deseneaza un buffered image pe ecran.
g2.drawImage(bi, 0, 0, this);
In afara de desenarea direct intr-un obiect BufferedImage, se pot accesa si manipula datele pixelilor dintr-o imagine in mai multe moduri. Acest lucru este util daca se implementeaza interfata de filtrare BufferedImageOp.
Se poate utiliza metodele BufferedImage.setRGB pentru a modifica in mod direct valoarea unui pixel sau a unei arii de pixeli la o valoare RGB specificata. De asemenea se poate manipula datele pixelului prin intermediul unui obiect WritableRaster asociat cu un BufferedImage.
Operatia de filtrare se poate aplica la un BufferedImage utilizand un obiect care implementeaza interfata BufferedImageOp.
Pentru a reprezenta o imagine buferata intr-un context specific se apeleaza una dintre metodele drawImage din contextul obiectului Graphics. De exemplu, atunci cand se reprezinta folosind metoda Component.paint, se apeleaza drawImage asupra obiectului de afisare grafica transmis la metoda (vezi Exemplul 4).
Exemplul 4. Reprezentarea unui BufferedImage
public void paint(Graphics g)
Un obiect BufferedImage utilizeaza un obiect Raster pentru gestiunea ariilor dreptunghiulare de date pixel. Clasa Raster contine campuri pentru coordonatele sistem a imaginii - latime, inaltime si origine. Un obiect Raster utilizeaza doua obiecte pentru gestiunea datelor pixelilor, un obiect DataBuffer si un obiect SampleModel. Obiectul DataBuffer stocheaza datele pixelilor pentru rastru, iar obiectul SampleModel furnizeaza interpretarea datelor pixelilor din obiectul DataBuffer.
De cele mai multe ori, nu este nevoie sa cream un obiect Raster in mod direct deoarece este furnizat odata cu crearea obiectelor BufferedImage. Cu toate acestea, unul dintre constructorii BufferedImage permite crearea unui Raster prin trecerea printr-un obiect WritableRaster.
Clasa Raster furnizeaza un numar de metode statice pentru crearea de obiecte Raster cu obiectele de tip DataBuffer si SampleModel specificate ca parametrii. Aceste metode sunt utile atunci cand se implementeaza clase de tipul RasterOp pentru filtrare.
Clasa Raster incorporeaza conceptul de raster parinte si raster copil. Acest lucru poate sa imbunatateasca eficienta de stocare prin permiterea construirii oricarui numar de imagini buferate din acelasi parinte. Parintele si copii sai reprezinta acelasi bufer de date, iar fiecare copil are un deplasament si limite pentru asi putea identifica pozitia imaginii din bufer. Un copil isi identifica apartenenta prin intermediul metodei getParent.
Pentru a crea un subrastru, se apeleaza metoda Raster.createSubRaster. Daca se creeaza un subrastru, se poate identifica aria parintelui care-l acopera si deplasamentul de la originea parintelui.
Clasa Raster defineste mai multe metode de acces la pixeli si la datele pixelilor. Acest lucru este util atunci cand se implementeaza interfata RasterOp, care asigura filtrare la nivel de rastru si manipulare a datelor imagine sau atunci cand se implementeaza orice metoda care are nevoie sa execute manipulari de pixeli de nivel inferior.
Metodele Raster.getPixel permit preluarea unui pixel individual care este returnat ca esantion individual dintr-o arie. Metodele Raster.getDataElements returneaza o portiune specificata a imaginii neinterpretata din DataBuffer. Metoda Raster.getSample returneaza esantioane cu un pixel individual. Metoda getSamples returneaza o banda pentru o regiune specificata dintr-o imagine. Buferul de date si modelul de esantioane poate fi de asemenea accesat prin intermediul variabilelor instanta a clasei Raster. Aceste obiecte asigura mijloace suplimentare pentru accesarea si interpretarea datelor pixel a unui obiect Raster.
Subclasa WritableRaster asigura metode pentru modificarea datelor pixel si a esantioanelor. Obiectul Raster asociat cu un obiect BufferedImage formeaza un obiect WritableRaster, asigurand acces total la manipularea datelor pixel.
Obiectele de tip DataBuffer apartinand unui Raster reprezinta o arie de date imagine. La crearea unui Raster in mod direct sau prin intermediul constructorilor BufferedImage, se poate specifica latimea si inaltimea, impreuna cu un obiect SampleModel pentru datele imagine. Aceste informatii sunt folosite la crearea unui DataBuffer cu tipul de data si dimensiune corespunzatoare.
Exista patru subclase a DataBuffer, fiecare reprezentand un tip diferit de date element:
DataBufferByte - valori pe 8 biti
DataBufferInt - valori pe 32 biti
DataBufferShort - valori pe 16 biti
DataBufferUShort - reprezinta valori de tip short fara semn.
Elementele sunt membrii discreti ai unei arii din buferul de date, iar componentele sau esantioanele sunt valori discrete care impreuna formeaza un pixel. Exista diverse alocari intre un tip de element particular din DataBuffer si un tip particular de pixel reprezentat de un obiect SampleModel. Aceasta alocare este implementata de diversele subclase ale clasei SampleModel si furnizeaza o modalitate de extragere de pixeli dintr-un DataBuffer specific.
Datele imagine pot fi accesate in mod direct intr-un DataBuffer, dar este mai usor si mai convenabil sa se acceseze prin intermediul metodelor din clasele Raster si WritableRaster.
Clasele pentru reprezentarea datelor imagine sunt prezentate in Tabelul 3.
Clasa
Descrierea
DataBuffer
Acopera unul sau mai multe arii de date. Fiecare arie de date ne este referentiata ca si un grup.
Raster
Reprezinta o arie dreptunghiulara de pixeli si furnizeaza metode pentru obtinerea de date imagine.
SampleModel
Extrage esantioane de pixeli din imagini.
WriteableRaster
Furnizeaza metode pentru stocarea datelor imagine si mosteneste metode pentru obtinerea datelor imagine de la parintele ei, clasa Raster.
Tabelul 3. Clasele utilizate de Java 2D pentru
reprezentarea datelor imagine
Clasa abstracta SampleModel defineste metode pentru extragerea esantioanelor dintr-o imagine fara a cunoaste modalitatea in care datele imagine sunt stocate. Clasa contine campuri pentru monitorizarea inaltimii si latimii datelor imagine din obiectul DataBuffer asociat si pentru descrierea unui numar de benzi si a tipului de data pentru acel bufer. Metodele SampleModel asigura date imagine ca si colectie de pixeli, fiecare pixel fiind format dintr-un numar de esantioane sau componente.
Pachetul java.awt.image furnizeaza cinci tipuri de modele de esantioane:
ComponentSampleModel - utilizat pentru extragerea pixelilor din imaginile care stocheaza esantioanele in arii de elemente data separate al unui segment dintr-un DataBuffer.
BandedSampleModel - utilizat pentru extragerea pixelilor din imaginile care stocheaza fiecare esantion intr-un element data separat cu benzile stocate intr-o secventa de elemente data.
PixelInterleavedSampleModel - utilizat pentru extragerea pixelilor din imaginile care stocheaza fiecare esantion intr-un element data separat cu pixeli stocati intr-o secventa de elemente data.
MultiPixelPackedSampleModel - utilizat pentru extragerea pixelilor din imaginile cu o singura banda care stocheaza mai multi pixeli cu un singur esantion intr-un singur element data.
SinglePixelPackedSampleModel - utilizat pentru extragerea pixelilor din imaginile care stocheaza date esantion pentru un singur pixel intr-o singura arie de elemente data din primul segment al unui DataBuffer.
Datele pixel oferite de SampleModel pot fi corelate direct cu un mod de reprezentare a datelor culoare dintr-un model de culoare particular, in functie de datele sursa. De exemplu, in datele imagine ale unei fotografii esantioanele pot sa reprezinte date RGB. In datele imagine de la un dispozitiv de prelucrare a imaginilor medicale esantioanele pot sa reprezinte tipuri diferite de date ca de exemplu temperatura sau densitatea oaselor.
Exista trei categorii de metode pentru accesarea datelor imagine. Metodele getPixel returneaza un pixel intreg ca si o arie cu o singura valoare pentru fiecare esantion. Metodele getDataElement asigura accesul la datele brute, neinterpretate stocate in DataBuffer. Metodele getSample asigura accesul la componentele pixelului pentru o banda specifica.
Ca o completare la obiectul Raster in gestiunea datelor imagine, clasa BufferedImage include un obiect ColorModel pentru interpretarea acestor date ca si valori de culoare pixel. Clasa abstracta ColorModel defineste metode pentru transformarea datelor pixel dintr-o imagine intr-o valoare culoare din spatiul de culoare ColorSpace asociat.
Pachetul java.awt.image furnizeaza patru tipuri de modele de culoare:
PackedColorModel - un model abstract care reprezinta valorile pixelilor care au componente de culoare continute direct in bitii unui pixel de tip integer. Clasa DirectColorModel este o subclasa a PackedColorModel.
DirectColorModel - un model de culoare care reprezinta valorile pixelilor care au componente de culoare RGB continute direct in bitii pixelului insusi.
ComponentColorModel - un model de culoare care poate manipula un spatiu de culoare ColorSpace arbitrar si o arie de componente de culoare care sa se potriveasca spatiului de culoare.
IndexColorModel - un model de culoare care reprezinta valorile pixelilor care sunt indici intr-o harta de culori fixa din spatiul de culoare RGB.
SampleModel furnizeaza modelul de culoare ColorModel bazandu-se pe datele din DataBuffer, apoi ColorModel interpreteaza datele ca si culoare.
O tabela de cautare (lookup table) contine date pentru una sau mai multe canale sau componente imagine. De exemplu, arii separate pentru R, G si B. Pachetul java.awt.image defineste doua tipuri de tabele de cautare care extinde clasa abstracta LookupTable, unul care contine date byte si unul care contine date short si anume ByteLookupTable si ShortLookupData.
Pachetul java.awt.image furnizeaza o pereche de interfete care definesc operatii asupra obiectelor BufferedImage si Raster: BufferedImageOp si RasterOp.
Cele mai importante clase care implementeaza aceste interfete sunt: AffineTransformOp, BandCombineOp, ConvolveOp, LookupOp, RescaleOp. Aceste clase pot fi utilizate pentru transformari geometrice, blur, sharpen, imbunatatire de contrast, binarizare si modificarea culorilor imaginilor.
Fragmentul de cod urmator (Exemplul 5.) exemplifica o operatie de detectie de margini aplicata asupra unei imagini folosind o operatie de convolutie:
Exemplul 5. Detectie de margini folosind Java 2D
float[] elements = ;
BufferedImage bimg = new
BufferedImage(bw,bh,BufferedImage.TYPE_INT_RGB);
Kernel kernel = new Kernel(3, 3, elements);
ConvolveOp cop = new ConvolveOp(kernel,
ConvolveOp.EDGE_NO_OP, null);
cop.filter(bi,bimg);
Urmatorul fragment de cod (Exemplul 6.) demonstreaza o operatie de manipulare a imaginilor folosind tabele de cautare (lookup table
Exemplul 6. Manipulare de tip lookup table
byte reverse[] = new byte[256];
for (int j=0; j<200; j++)
ByteLookupTable blut=new ByteLookupTable(0, reverse);
LookupOp lop = new LookupOp(blut, null);
lop.filter(bi,bimg);O operatie de rescalare a unei imagini este prezentata in fragmentul de cod care urmeaza (Exemplul ):
Exemplul Operatie de rescalare
RescaleOp rop = new RescaleOp(1.5f, 1.0f, null);
rop.filter(bi,bimg);Urmatorul fragment de cod (Exemplul 8.) ilustreaza modul de utilizare a uneia dintre clasele de procesare a imaginilor, ConvolveOp. In acest exemplu fiecare pixel din imaginea sursa este mediat in mod egal cu cei opt pixeli inconjuratori.
Exemplul 8. Utilizarea clasei ConvolveOp
float weight = 1.0f/9.0f;
float[] elements = new float[9]; // creaza o arie 2D
// umple aria cu 9 elemente egale
for (i = 0; i < 9; i++)
// utilizeaza aria de elemente ca si argument la crearea unui Kernel
// (nucleu de convolutie)
private Kernel myKernel = new Kernel(3, 3, elements);
public ConvolveOp simpleBlur = new ConvolveOp(myKernel);
// sourceImage si destImage sunt instante a BufferedImage
simpleBlur.filter(sourceImage, destImage) // aplica blurVariabila simpleBlur din exemplul anterior contine o noua instanta a ConvolveOp care implementeaza o operatie blur asupra unui BufferedImage sau a unui Raster. Presupunand ca sourceImage si destImage sunt doua instante ale BufferedImage, atunci cand este apelata metoda filter, care este metoda principala a clasei ConvolveOp. Aceasta metoda aloca valori pentru fiecare pixel din imaginea destinatie prin medierea pixelului corespunzator din imaginea sursa cu cei opt pixeli inconjuratori.
Prin modificarea nucleului de convolutie se poate executa alte tipuri de convolutii, cum ar fi blurring (Gaussian blur, radial blur, motion blur), sharpening, operatii de netezire, etc.
Urmatorul fragment de cod (Exemplul 9.) ilustreaza sharpening folosind operatia de convolutie:
Exemplul 9. Operatie sharpening utilizand convolutia
float[] elements = ;
Kernel kernel = new Kernel(3,3,elements);
ConvolveOp cop = new ConvolveOp(kernel,
ConvolveOp.EDGE_NO_OP, null);
cop.filter(bi,bimg);Independenta reprezentarii reprezinta abilitatea de a descrie o imagine asa cum dorim noi sa apara, independent de orice parametru specific ei, cum ar fi rezolutia, dimensiunea fizica, tipul dispozitivului de iesire, calitatea culorilor, calitatea tonala si viteza de reprezentare.
Pentru o descriere a unei imagini independenta de reprezentare sunt necesare doua elemente principale:
O sursa care nu este reprezentata, numita si sursa independenta de rezolutie. Pentru o imagine statica, aceasta reprezinta conceptual, aria de captura a unei camere ideale antrenata asupra unei scene reale, aceasta lipsindu-i orice dimensiuni logice. Camera ideala are o lentila ideala care are posibilitatea de zooming infinita. Imaginea se poate considera ca este proiectata pe orice suprafata (dispozitiv de afisare), are dimensiuni, are un raport de aspect nativ (acela a dispozitivului de captura) si poate sa aiba proprietati care pot fi interogate.
Operatori pentru descrierea modului de modificare a caracterului unei imagini, independent de destinatia finala.
Sursa nereprezentata si operatorii specifica caracterul vizual al imaginii pe care ar trebui sa-l aiba atunci cand este reprezentata. Aceasta specificatie poate apoi fi asociata cu orice dispozitiv, dimensiune a dispozitivului de afisare sau calitate a reprezentarii.
Arhitectura Java AWT API integreaza un model de independenta a reprezentarii in paralel cu un model dependent de dispozitiv. Partea de independenta a reprezentarii a arhitecturii este un superset al modelului traditional de procesare dependenta de dispozitiv si nu este un inlocuitor pentru acesta.
Arhitectura Java AWT suporta adaptare dependenta de context, lucru care este potrivit pentru surse aflate in retea. Adaptarea dependenta de context este un mecanism prin care calitatea optima a unei imagini este garantata in orice context.
Java AWT este o arhitectura sincrona. Acest lucru are cateva avantaje, cum ar fi un model de programare simplificat si controale explicite asupra tipului si ordinii rezultatelor, dar are si cateva dezavantaje si anume faptul ca nu este potrivit notiunii de reprezentare progresiva sau resurse disponibile in retea.
Arhitectura Java AWT API furnizeaza doua straturi integrate de prelucrare a imaginilor: stratul Renderable si stratul Rendered.
Stratul Renderable este un strat independent de reprezentare. Stratul Renderable furnizeaza surse de imagini care pot fi optimal reutilizate de mai multe ori in contexte diferite, cum ar fi afisarea pe ecran sau imprimarea. Stratul ofera operatori de procesare de imagini care accepta parametrii independenti de reprezentare. Acesti parametri pot fi legati sub forma de lanturi. Stratul este in esenta sincron, in sensul ca "trage" imaginea prin lant oricand o reprezentare (de exemplu spre un dispozitiv de afisare sau un fisier) este solicitata. O solicitare se face la destinatia (sink) a lantului si este transmisa pe lant in sus spre sursa. Asemenea cereri sunt specifice de context (de exemplu specific dispozitivului) si lantul se adapteaza la acest context. Doar datele necesare pentru context sunt produse.
Sursele imagine si operatorii din stratul paralel Rendered sunt specifice de context. Un obiect RenderedImage este o imagine care a fost reprezentata pentru a satisface nevoile unui context. Operatorii din stratul Rendered pot fi de asemenea legati impreuna pentru a forma lanturi. Acestia primesc parametrii dependenti de context. Ca si stratul Renderable, stratul Rendered implementeaza un model sincron de tragere.
Structural, stratul Renderable este subtil, el nu se ocupa cu procesarea pixelilor; mai degraba uzeaza de obiecte operator din stratul Rendered. Acest lucru este posibil deoarece clasele operatorilor din stratul Rendered pot sa implementeze interfata ContextualRenderedImageFactory, care le permite sa se adapteze la diferite contexte. Deoarece operatorii din stratul Renderable implementeaza aceasta interfata, acestia gazduiesc in intregime operatiile specifice. Toata inteligenta necesara pentru functionarea celor doua straturi, Renderable si Rendered sunt tinute intr-o singura clasa. Acest lucru simplifica munca de a scrie noi operatori si de a face controlabila arhitectura de extensii.
In Figura 3. se ilustreaza un lant Renderable. Lantul are atasat o destinatie (un obiect Graphics2D), dar nici un pixel nu circula prin lant deocamdata.
Stratul Renderable are marele avantaj ca simplifica in mare masura munca de procesare de imagini. De exemplu, un lant de operatori Renderable raman editabili, parametrii folositi pentru constructia lantului pot fi modificati in mod repetitiv. Acest lucru nu va face sa se inceapa calcularea vreo unui pixel, in schimb pixelii sunt calculati doar atunci cand este nevoie, reprezentarea fiind obtinuta de la RenderableImage cu transmiterea definita de contextele de reprezentare.
Stratul Renderable permite constructia unui lant de operatori RenderableImageOps conectati la o sursa RenderableImage. Capatul acestui lant reprezinta o noua sursa RenderableImage. Efectul este ca RenderableImageOps trebuie sa implementeze aceeasi interfata ca si sursele (RenderableImage). Unei astfel de surse i se poate cere sa furnizeze diverse obiecte RenderedImage specifice unui context corespunzator. Dimensiunea necesara in pixeli pentru RenderedImage in spatiul dispozitivului trebuie specificata. Aceasta informatie este furnizata sub forma unei transformari din spatiul utilizator a sursei Renderable spre spatiul dispozitivului dorit.
Alte informatii pot fi date sursei sau lantului pentru a-l ajuta sa functioneze in mod optimal pentru un anume context, de exemplu o preferinta pentru viteza in detrimentul calitatii imaginii. Asemenea informatii se furnizeaza sub forma unui tabel extensibil de indicii.
Arhitectura defineste acesti parametri colectivi ca si context de reprezentare. Parametrii sunt gazduiti de o clasa RenderContext, care formeaza o legatura fundamentala intre straturile Renderable si Rendered. O sursa RenderableImage si un RenderContext, produc ca rezultat o reprezentare specifica, sau altfel spus un RenderedImage. Acest lucru este efectuat de lantul Renderable instantiind un lant de obiecte de strat Rendered. Intr-un lant de obiecte RenderedImages corespunzatoare unui context specific, obiectul RenderedImage din capat este returnat la utilizator.
Stratul Renderable ofera avantajul independentei de reprezentare, eliminand nevoia de a lucra in mod direct cu pixeli, simplificand foarte mult manipularea imaginilor. Cu toate acestea, in multe cazuri este necesar sa se lucreze direct cu pixeli si in acest caz se va folosi stratul Rendered.
Stratul Renderable este definit de catre interfata RenderableImage. Orice clasa care implementeaza aceasta interfata este o imagine sursa Renderable si este de asteptat sa se adapteze la contextul de reprezentare RenderContext. Obiectele de tip RenderContext sunt referentiate printr-un sistem de coordonate definite de utilizator. Una din functiile principale ale clasei RenderContext este sa defineasca alocarea spatiului utilizator si spatiul specific dispozitivului pentru o reprezentare dorita.
Un lant in acest strat reprezinta un lant de obiecte RenderableImage, mai precis este un lant de obiecte RenderableImageOp (clasa care implementeaza interfata RenderableImage).
Clasa RenderableImageOp preia functionalitatea unei operatii specifice prin intermediul unui parametru furnizat la momentul instantierii. Acest parametru este numele unei clase care implementeaza interfata ContextualRenderedImageFactory. Fiecare instantiere a RenderableImageOp deriva din functionalitatiile specifice claselor specificate prin nume. In acest sens, stratul Renderable este puternic dependent de stratul Rendered.
Clasele si interfetele utilizate de stratul Renderable sunt prezentate in Tabelul 4.
Clasa/Interfata
Descrierea
RenderableImage
O interfata comuna pentru imagini independente de reprezentare
ContextualRenderedImage-Factory
Furnizeaza o interfata pentru functionalitate care poate sa difere intre instante diferite de RenderableImageOp.
ParameterBlock
Incapsuleaza toate informatiile despre sursele si parametrii necesari de catre RenderableImageOp si alte clase viitoare care manipuleaza lanturi de operatori de procesare.
RenderableImageOp
Se ocupa de aspectele Renderable a unei operatii cu ajutorul unei instante asociate de ContextualRenderedImageFactory (CRIF).
RenderableImageProducer
O clasa adaptoare care implementeaza ImageProducer si permite producerea asincrona a unui RenderableImage.
RenderContext
Incapsuleaza informatia necesara pentru a produce o reprezentare specifica dintr-un RenderableImage.
Tabelul 4. Clasele si interfetele utilizate de stratul Renderable
Un alt bloc implicat in constructia unui RenderableImageOp este ParameterBlock. Clasa ParameterBlock gazduieste sursele pentru operatie si parametrii sau alte obiecte pe care acel operator le poate cere. Parametrii sunt versiuni independente de reprezentare ai parametrilor care controleaza operatorul.
Lantul Renderable este construit prin instantierea fiecarui obiect succesiv de tipul RenderableImageOp, fiind transmis in ultimul obiect RenderableImage ca si sursa in obiectul ParameterBlock. Acestui lant i se poate cere sa furnizeze un numar de reprezentari spre spatii dispozitiv specifice prin intermediul metodei getImage.
Acest lant, odata construit ramane editabil. Parametrii pentru operatiile specifice din lant si structura propriuzisa a lantului se poate schimba. Acest lucru se poate realiza prin apelul metodei setParameterBlock, modificand parametrii de control sau/si sursele. Aceste modificari afecteaza viitoarele obiecte RenderedImage derivate din punctele din lant care se gasesc in zona inferioara zonei unde sa facut modificarile. Obiectele RenderedImage care sunt obtinute mai devreme din lantul Renderable sunt fixe si complet independente de lantul din care au fost derivate.
Stratul Rendered a fost conceput sa functioneze in cooperare cu stratul Renderable. Stratul Renderable include surse si operatii pentru reprezentarea specifica unui dispozitiv a imaginilor sau a reprezentarilor. Stratul Rendered este definit de interfata RenderedImage. Aceasta interfata este implementata de clasa BufferedImage.
Operatorii din acest strat sunt obiecte RenderedImage care preiau alte obiecte RenderedImage ca sursa. Lanturile pot fi construite in aceeasi masura ca si la stratul Renderable. O secventa de obiecte RenderedImage este instantiata, fiecare preluand ultimul obiect RenderedImage ca si sursa.
In Figura 4., atunci cand utilizatorul invoca metoda Graphics-2D.drawImage, un context de reprezentare este construit si utilizat pentru a apela metoda getImage de la operatorul Renderable. Un operator Rendered, pentru a face de fapt o procesare a pixelilor este construit si atasat la sursa si la destinatia operatorului Renderable si ii este furnizata o clona a blocului de parametri a operatorului Renderable. Pixelii vor "curge" prin operatorul Rendered la obiectul Graphics2D. Lantul operatorului Renderable ramane disponibil pentru a produce mai multe reprezentari oricand metoda getImage este apelata.
In Tabelul 5. sunt prezentate clasele si interfetele utilizate de stratul Rendered.
Clasa/Interfata
Descriere
RenderedImage
O interfata comuna pentru obiecte care contin sau pot produce date imagine sub forma de obiecte Raster.
BufferedImage
O subclasa care descrie un obiect Image cu un bufer de date imagine accesibil.
WritableRenderedImage
O interfata comuna pentru obiecte care contin sau pot produce date imagine care pot fi modificate sau/si suprascrise.
Tabelul 5. Clasele si interfetele din stratul Rendered