Java Servlets – grafické efekty a cache
V predchádzajúcich článkoch sme vytvorili servlety, ktoré vytvárajú alebo navzájom kombinujú grafické prvky. V tomto článku sa pozrieme na to, ako servlet môže pridávať k obrázkom rôzne grafické efekty. Napríklad môže zmenšiť čas potrebný na prenos dát tým, že zmenší veľkosť obrázku pred jeho odoslaním klientovi. Alebo môže k existujúcemu obrázku pridať tieň, imitujúc tak tlačidlo. Vytvoríme si tiež príklad, v ktorom servlet použijeme na konverziu farebného obrázku na obrázok v 256 stupňoch šedej farby.
Konverzia s využitím šedej škály
Nasledujúci príklad ukazuje servlet, ktorý pred odoslaním skonvertuje obrázok použijúc 256 stupňov šedej farby. Rozdiel v porovnaní s predchádzajúcimi servletmi je v tom, že servlet nevytvára konvertovaný obrázok pomocou externého grafického kontextu. Namiesto toho použije špeciálny filter.
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import Acme.JPM.Encoders.*;
public class GreyScaleServlet extends HttpServlet {
public void doGet
(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType(„image/gif“);
ServletOutputStream out = res.getOutputStream();
// získame info o umiestnení obrázku
String source = req.getPathTranslated();
if (source == null) {
throw new ServletException(„Chyba v umiestnení obrázku“);
}
/* vytvoríme viacnásobne použiteľný frame, pričom
tentokrát nepotrebujeme volať metódu addNotify() */
Frame frame = new Frame();
// natiahneme obrázok do pamäte
Image image = Toolkit.getDefaultToolkit().getImage(source);
MediaTracker mt = new MediaTracker(frame);
mt.addImage(image, 0);
try {
mt.waitForAll();
}
catch (InterruptedException e) {
getServletContext().log(„Chyba pri načítaní obrázka“, e);
throw new ServletException(e.getMessage());
}
// zistíme rozmery obrázku
int width = image.getWidth(frame);
int height = image.getHeight(frame);
// obrázok preženieme cez filter
Image filtered = frame.createImage(
new FilteredImageSource(image.getSource(),
new GreyScaleFilter()));
// obrázok zakódujeme a pošleme
GifEncoder encoder = new GifEncoder(filtered, out);
encoder.encode();
}
}
Tento servlet nevyužíva metódu createImage(int,int) triedy Component tak, ako sme boli zvyknutý. Namiesto toho však používa metódu createImage(ImageProducer). Tento „producent obrázkov“ je vytvorený prostredníctvom triedy FilteredImageSource, ktorej predáme odkaz na obrázok a vlastnú implementáciu filtra GreySscaleFilter. Tento filter konvertuje každý jeden pixel na jeho náprotivok použitím prevodného algoritmu a 256-ovej stupnice.
import java.awt.*;
import java.awt.image.*;
public class GreyScaleFilter extends RGBImageFilter {
public GreyScaleFilter() {
canFilterIndexColorModel = true;
}
/* skonvertujeme farebné pixle pomocou
algoritmu odpovedajúcemu RGB špecifikácii */
public int filterRGB(int x, int y, int pixel) {
// získame priemernú RGB intenzitu
int red = (pixel & 0x00ff0000) >> 16;
int green = (pixel & 0x0000ff00) >> 8;
int blue = pixel & 0x000000ff;
// aplikujeme vážený priemer
int rgb = (int) (0.299*red + 0.587*green + 0.114*blue);
// vrátime hodnotu pre každý RGB komponent
return (0xff << 24) | (rgb << 16) | (rgb << 8) | rgb;
}
}
Tento filter obdrží RGB hodnotu každého pixla a vráti novú prefiltrovanú hodnotu. Nastavením canFilterIndexColorModel na true určíme, že filter resp. metóda filterRGB(), bude operovať nad tabuľkou farebných indexov objektu IndexColorModel. V opačnom prípade bude postupovať pixel za pixlom a vykonávať konverziu.
RGB hodnota pixla je daná 32 bitovým číslom, kde prvý oktet reprezentuje hodnotu alfa kanálu (transparentnosť), druhý oktet je intenzita červenej, tretí oktet je intenzita zelenej a štvrtý oktet je intenzita modrej. Ak chcete skonvertovať pixel použitím šedej stupnice a zároveň poznáte jeho hodnoty RGB, je nutné tieto hodnoty nastaviť na identické, to znamená spriemerovať ich.
Avšak vzhľadom na skutočnosť ako ľudské oko vníma farby a ich intenzitu (spolu s inými faktormi), nestačí vykonať jednoduchý aritmetický priemer jednotlivých farebných zložiek, ale odporúča sa následne vykonať vážený priemer v definovanom pomere (Red)0,299 : (Green)0,587 : (Blue)0,114. Nasleduje ukážka práce nášho príkladu:
Originálny obrázok
Obrázok prehnaný cez filter
Cache-ovanie konvertovaného obrázku
Tvorba a kódovanie grafiky môžu byť z hľadiska náročnosti na zdroje (pamäť a CPU) veľmi drahé. Zvlášť ak ide o servery s veľkou návštevnosťou. Určitým riešením (a tiež dobrým mravom), je nevykonávať všetku prácu pri každej požiadavke, ale vytvoriť všetko potrebné len prvý krát a pri ďalších požiadavkách poskytnúť už hotový výsledok. Riešením teda môže byť uložiť obrázok dočasne do pamäte (cache).
Upravme si teda predchádzajúci príklad tak, aby pred prvým odoslaním uložil konvertovaný obrázok do cache. Urobíme to tak, aby sa každý konvertovaný obrázok uložil do hash tabuľky pod svojím menom. Ako prvé je nutné vytvoriť inštanciu Hashtable, mimo metódy doGet(). Aby sme mohli hash tabuľku naplniť, musíme z konvertovaného obrázka vytvoriť ByteArrayOutputStream, ten následne zakódovať a uložiť do tabuľky. Potom môžeme zakódovaný prúd bajtov zapísať do objektu ServletOutputStream a tým ho poslať klientovi. Nasleduje upravený príklad.
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import Acme.JPM.Encoders.*;
public class GreyScaleServletCached extends HttpServlet {
// vytvoríme hash tabuľku
Hashtable gifs = new Hashtable();
public void doGet
(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType(„image/gif“);
ServletOutputStream out = res.getOutputStream();
// získame info o umiestnení obrázku
String source = req.getPathTranslated();
if (source == null) {
throw new ServletException(„Chyba v umiestnení obrázku“);
}
// ak skonvertovaný obrázok existuje, pošleme ho klientovi
if (gifs.containsKey(source)) {
ByteArrayOutputStream baos = (ByteArrayOutputStream)
gifs.get(source);
baos.writeTo(out);
return;
}
// vytvoríme viacnásobne použiteľný frame
Frame frame = new Frame();
// natiahneme obrázok do pamäte
Image image = Toolkit.getDefaultToolkit().getImage(source);
MediaTracker mt = new MediaTracker(frame);
mt.addImage(image, 0);
try {
mt.waitForAll();
}
catch (InterruptedException e) {
getServletContext().log(„Chyba pri načítaní obrázka“, e);
throw new ServletException(e.getMessage());
}
// zistíme rozmery obrázku
int width = image.getWidth(frame);
int height = image.getHeight(frame);
// obrázok preženieme cez filter
Image filtered = frame.createImage(
new FilteredImageSource(image.getSource(),
new GreyScaleFilter()));
// obrázok zakódujeme, uložíme do Hashtable a pošleme
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GifEncoder encoder = new GifEncoder(filtered, baos);
encoder.encode();
gifs.put(source, baos);
baos.writeTo(out);
}
}
Použitím týchto modifikácií je akýkoľvek obrázok, uložený v pamäti, vrátený klientovi veľmi rýchlo. Treba však myslieť na to, že cache-ovanie veľkého množstva obrázkov skonzumuje aj veľa pamäte. V prípade, že môže táto alternatíva nastať, je rozumné do kódu zapracovať algoritmus, ktorý bude do pamäte ukladať len niekoľko najčastejšie žiadaných obrázkov.
Na záver vám ponúkam použité súbory na stiahnutie.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Jak chránit webové stránky před Web/AI Scrapingem
27. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
4 tipy, jak na efektivní úsporu při rozjezdu podnikání
3. ledna 2023 -
Jak si vyzkoušet Apple Intelligence v EU
2. srpna 2024
Nejnovější
-
Jak rozšířit úložiště Macu za pětinovou cenu?
16. prosince 2024 -
Nové trendy v doménách pro osobní projekty – DIY, LIVING a LIFESTYLE
9. prosince 2024 -
Jak chránit webové stránky před Web/AI Scrapingem
27. listopadu 2024 -
Jaký monitor je nejlepší k novému Macu Mini?
25. listopadu 2024