12.9.1 | Farbfilter |
Eine beliebte Anwendung der Klasse ImageFilter ist die Implementierung von Farbfiltern. Dies wird an zwei verschiedenen Filtern demonstriert:
Wenn eine Schrift auf dem Bildschirm vor einem komplexen Hintergrund beliebig skaliert werden soll, ist dies bis einschließlich JDK 1.1 ohne einen ImageFilter nur durch Skalieren des Fonts möglich.
Beim JDK 1.2kann man im Grafikkontext durch Aufruf der Methode scale() ein Skalierungsfaktor für alle Zeichenoperationen einstellen. Durch Skalierung eines Fonts kann ein Schriftzug aber nicht verzerrt werden. Durch Verwendung eines ImageFilter kann man dieses Problem bis JDK 1.1 lösen.
Das Prinzip ist folgendes:
Zuerst wird ein Schriftzug in ein Offscreen-Image gezeichnet und anschließend der Hintergrundfarbe der Farbwert »transparent« zugewiesen. Hierbei wird genau so vorgegangen, wie es beim Produzieren von transparenten Bildern für HTML-Seiten gemacht wird (z. B. mit giftool): Der Alpha-Wert einer bestimmten Farbe wird einfach 0 gesetzt.
Der Filter, der verwendet wird, ist von der Klasse RGBImageFilter abgeleitet. Durch die Benutzung von RGBImageFilter kann man auf relativ einfache Weise die Farbwerte eines Bildes im RGB-Modell ändern. Hierzu ist es notwendig, in einer von RGBImageFilter abgeleiteten Klasse die Methode filterRGB() zu überschreiben.
Das Beispiel dazu besitzt folgende Implementierung:class TransparentFilter extends RGBImageFilter { private int rgb; // transparente Farbe public TransparentFilter(int rgb) { // Speichern der transparenten Farbe this.rgb = rgb; // keine positionsabhängige Filterung canFilterIndexColorModel = false; } public int filterRGB(int x, int y, int rgb) { // Ist das zu filternde Pixel // gleich der transparenten Farbe? if (rgb == this.rgb) // Wenn ja, Alpha-Wert des Pixels 0 setzen rgb &= 0x00FFFFFF; return rgb; } }Der Konstruktor bekommt in diesem Fall den Farbwert übergeben, der transparent werden soll. Bei der Filterung wird für jedes einzelne Pixel des Bildes die Methode filterRGB() aufgerufen.
Der übergebene Farbwert wird mit der Farbe verglichen, die transparent erscheinen soll. Stimmen beide überein, wird der Alphawert der Farbe auf null gesetzt, die übrigen Bits des Farbwertes bleiben unverändert. Als Ergebnis liefert die Methode das veränderte Pixel.
Die Methode filterRGB() besitzt zwei weitere Parameter, die den x- und y-Wert des gerade gefilterten Pixels innerhalb des Bildes angeben. Durch Abfrage dieses Wertes ist es möglich, ein Pixel abhängig von seiner Position innerhalb eines Bildes zu filtern.
Mit der Klasse RGBImageFilter kann man nicht nur Bilder, sondern auch ein IndexColorModel filtern. Dies geschieht durch Aufruf von filterIndexColorModel(IndexColorModel) mit einem Exemplar von IndexColorModel als Argument. Bei dieser Art der Filterung wird jede Farbe im Farbmodell durch die neue, gefilterte Farbe ersetzt. Als Ergebnis liefert filterIndexColorModel() das gefilterte Farbmodell. filterIndexColorModel() filtert die einzelnen Farben des Farbmodells durch Aufruf von filterRGB(). Doch was geschieht in diesem Fall mit den Parametern x und y?
x und y sind nur relevant, wenn positionsabhängig gefiltert wird. Da bei der Filterung eines IndexColorModels keine Bildposition geliefert werden kann, wird in diesem Fall für x und y immer -1 übergeben.
Wenn ein RGBImageFilter positionsunabhängig filtert, sollte immer canFilterIndexColorModel auf true gesetzt werden, wie es im obigen Beispiel innerhalb des Konstruktors gemacht wurde:canFilterIndexColorModel = true;Durch dieses Feld wird angezeigt, ob ein RGBImageFilter die Fähigkeit besitzt, auch ein IndexColorModel zu filtern. Ist es true, ist eine positionsabhängige Filterung überhaupt nicht möglich: x und y liefern dann auch bei Filterung von Bildern -1.
Mit einem RGBImageFilter ist es auch möglich, Arrays mit RGB-Farbwerten zu filtern, wie sie z. B. von der Klasse PixelGrabber erzeugt werden. Hierzu muss man die Methode filterRGBPixels() aufrufen. Die Parameter entsprechen denen des Konstruktors von PixelGrabber. Wenn über einen Aufruf dieser Methode gefiltert wird, erhält filterRGB() in x und y immer die Koordinaten des gerade gefilterten Pixels, unabhängig davon, ob canFilterIndexColorModel true oder false ist.
In folgendem Codeausschnitt wird die Filterung vorgenommen:Font f = new Font("SansSerif", Font.BOLD, 30); // Offscreen-Image mit der Schrift erzeugen FontMetrics fm = getFontMetrics(f); int messagewidth = fm.stringWidth(message); int messageheight = fm.getHeight(); int message_y = fm.getAscent(); Image tmp = createImage(messagewidth, messageheight); Graphics g = tmp.getGraphics(); g.setFont(f); g.setColor(Color.yellow); g.drawString(message, 0, message_y); // Hintergrund des Offscreen-Images filtern stringImage = createImage( new FilteredImageSource(tmp.getSource(), new TransparentFilter(Color.white.getRGB())));Zuerst wird der Text message in ein Offscreen-Image gezeichnet. Dieses Offscreen-Image besitzt jedoch einen weißen Hintergrund. Deshalb wird es durch den TransparentFilter gefiltert:stringImage = createImage( new FilteredImageSource(tmp.getSource(), new TransparentFilter(Color.white.getRGB())));FilteredImageSource bekommt den ImageProducer des Bildes (tmp.getSource()) und ein Exemplar des Filters übergeben.
Da in diesem Fall der weiße Hintergrund des Offscreen-Images transparent gestaltet werden soll, bekommt der Konstruktor von TransparentFilter den RGB-Farbwert von Color.white übergeben.
Diese Methode kann z. B. angewendet werden, wenn ein Text vor einem komplexen Hintergrund gezoomt werden soll. Der Text wird in ein transparentes Offscreen-Image geschrieben, das dann beliebig skaliert werden kann.
Beim JDK 1.2müssen Offscreen-Images nicht mehr gefiltert werden, um einen transparenten Hintergrund zu erhalten. Ein neu erzeugtes BufferedImage vom Typ TYPE_INT_RGB sollte per Voreinstellung einen transparenten Hintergrund besitzen.
Das komplette Listing befindet sich auf der beiliegenden CD.
Eine andere Form des RGBImageFilters kann zur Erzeugung eines Positivs aus einem Negativ (bzw. umgekehrt) eingesetzt werden:
Der Filter invertiert jeden einzelnen Farbanteil. Hierzu wird der Wert des Farbanteils vom Maximalwert 255 abgezogen und als neuer Farbwert verwendet.class NegativFilter extends RGBImageFilter { public NegativFilter() { // keine positionsabhängige Filterung canFilterIndexColorModel = true; } public int filterRGB(int x, int y, int rgb) { return (0xFF000000 | (0xFFFFFFFF - rgb)); } }Die Invertierung der einzelnen Farbanteile kann am einfachsten vorgenommen werden, wenn man den RGB-Wert des Pixels von dem Maximalwert bildet:return (0xFF000000 | (0xFFFFFFFF - rgb));Diese Differenzbildung beeinträchtigt allerdings auch die Deckung des Farbwertes. War die Deckung zuvor maximal, ist sie nach der Differenzbildung gleich 0. Das Bild wäre also in diesem Fall transparent. Deshalb wird anschließend eine ODER-Verknüpfung mit dem Wert 0xFF000000 vorgenommen. Dadurch wird die Deckung auf 255 gesetzt.
Unterschiedliche Alpha-Werte werden also bei dieser Filterung nicht unterschieden. Unabhängig vom Alpha-Wert des Originalpixels wird der Alpha-Wert des Ausgangspixels auf 255 gesetzt.
Das vollständige Listing des NegativFilter mit einem Testprogramm ist auf der CD enthalten.