Java SE 8 Standard-Bibliothek  
Professionelle Bücher. Auch für Einsteiger.
 
Inhaltsverzeichnis

Vorwort
1 Neues in Java 8 und Java 7
2 Fortgeschrittene String-Verarbeitung
3 Threads und nebenläufige Programmierung
4 Datenstrukturen und Algorithmen
5 Raum und Zeit
6 Dateien, Verzeichnisse und Dateizugriffe
7 Datenströme
8 Die eXtensible Markup Language (XML)
9 Dateiformate
10 Grafische Oberflächen mit Swing
11 Grafikprogrammierung
12 JavaFX
13 Netzwerkprogrammierung
14 Verteilte Programmierung mit RMI
15 RESTful und SOAP-Web-Services
16 Technologien für die Infrastruktur
17 Typen, Reflection und Annotationen
18 Dynamische Übersetzung und Skriptsprachen
19 Logging und Monitoring
20 Sicherheitskonzepte
21 Datenbankmanagement mit JDBC
22 Java Native Interface (JNI)
23 Dienstprogramme für die Java-Umgebung
Stichwortverzeichnis

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 11 Grafikprogrammierung
Pfeil 11.1 Grundlegendes zum Zeichnen
Pfeil 11.1.1 Die paint(Graphics)-Methode für das AWT-Frame
Pfeil 11.1.2 Die ereignisorientierte Programmierung ändert Fensterinhalte
Pfeil 11.1.3 Zeichnen von Inhalten auf ein JFrame
Pfeil 11.1.4 Auffordern zum Neuzeichnen mit repaint(…)
Pfeil 11.1.5 Java 2D-API
Pfeil 11.2 Einfache Zeichenmethoden
Pfeil 11.2.1 Linien
Pfeil 11.2.2 Rechtecke
Pfeil 11.2.3 Ovale und Kreisbögen
Pfeil 11.2.4 Polygone und Polylines
Pfeil 11.3 Zeichenketten schreiben und Fonts
Pfeil 11.3.1 Zeichenfolgen schreiben
Pfeil 11.3.2 Die Font-Klasse
Pfeil 11.3.3 Font-Metadaten durch FontMetrics *
Pfeil 11.4 Geometrische Objekte
Pfeil 11.4.1 Die Schnittstelle Shape
Pfeil 11.4.2 Pfade *
Pfeil 11.5 Das Innere und Äußere einer Form
Pfeil 11.5.1 Farben und die Paint-Schnittstelle
Pfeil 11.5.2 Farben mit der Klasse Color
Pfeil 11.5.3 Composite und XOR *
Pfeil 11.5.4 Dicke und Art der Linien von Formen bestimmen über Stroke *
Pfeil 11.6 Bilder
Pfeil 11.6.1 Eine Übersicht über die Bilder-Bibliotheken
Pfeil 11.6.2 Bilder mit ImageIO lesen
Pfeil 11.6.3 Ein Bild zeichnen
Pfeil 11.6.4 Splash-Screen *
Pfeil 11.6.5 Bilder skalieren *
Pfeil 11.6.6 Schreiben mit ImageIO
Pfeil 11.6.7 Asynchrones Laden mit getImage(…) und dem MediaTracker *
Pfeil 11.7 Weitere Eigenschaften von Graphics *
Pfeil 11.7.1 Eine Kopie von Graphics erstellen
Pfeil 11.7.2 Koordinatensystem verschieben
Pfeil 11.7.3 Beschnitt (Clipping)
Pfeil 11.7.4 Zeichenhinweise durch RenderingHints
Pfeil 11.7.5 Transformationen mit einem AffineTransform-Objekt
Pfeil 11.8 Drucken *
Pfeil 11.8.1 Drucken der Inhalte
Pfeil 11.8.2 Bekannte Drucker
Pfeil 11.9 Benutzerinteraktionen automatisieren, Robot und Screenshots *
Pfeil 11.9.1 Der Roboter
Pfeil 11.9.2 Automatisch in die Tasten hauen
Pfeil 11.9.3 Automatisierte Maus-Operationen
Pfeil 11.9.4 Methoden zur Zeitsteuerung
Pfeil 11.9.5 Bildschirmabzüge (Screenshots)
Pfeil 11.9.6 Funktionsweise und Beschränkungen
Pfeil 11.9.7 MouseInfo und PointerInfo
Pfeil 11.10 Zum Weiterlesen
 
Zum Seitenanfang

11.6Bilder Zur vorigen ÜberschriftZur nächsten Überschrift

Jede Grafik wird in AWT/Swing als Exemplar der Klasse Image repräsentiert, wobei einige Lademethoden auch Exemplare der Unterklasse BufferedImage liefern.

 
Zum Seitenanfang

11.6.1Eine Übersicht über die Bilder-Bibliotheken Zur vorigen ÜberschriftZur nächsten Überschrift

Die Java-API bietet – historisch gewachsen – mehrere Möglichkeiten zum Laden und für einige Formate auch zum Speichern von Bildern an. Zudem gibt es Zusatzbibliotheken für Spezialformate und besondere Anforderungen, wie etwa für die Verwaltung sehr großer Grafiken:

  • Die Methode getImage(…) der Klassen Toolkit (bei Applikationen) und Applet (bei Applets) liefert ein Image-Objekt.

  • Der Media-Tracker lädt Bilder und informiert über den Ladevorgang.

  • Die Klasse ImageIcon lädt für Swing Bilder, die sich direkt auf der grafischen Oberfläche auf Komponenten wie Schaltflächen platzieren lassen. Sie nutzt im Hintergrund den Media-Tracker.

  • Seit Java 1.4 gibt es das Paket javax.imageio, um das Lesen und Schreiben von Grafiken zu vereinheitlichen. Die Klasse ImageIO bietet eine einfache statische Methode read(…).

 
Zum Seitenanfang

11.6.2Bilder mit ImageIO lesen Zur vorigen ÜberschriftZur nächsten Überschrift

ImageIO ist sehr einfach zu nutzen, denn mit einer kleinen statischen Methode ImageIO.read(…) ist die Grafik geladen. Unterstützte Dateiformate sind sicher GIF, JPEG und PNG; weitere Formate können von Plattform zu Plattform unterschiedlich sein (eine präzisere Liste der angemeldeten Leser liefern ImageIO.getReaderFormatNames() und ImageIO.getReaderMIMETypes()).

final class javax.imageio.ImageIO
  • static BufferedImage read(File input) throws IOException

  • static BufferedImage read(InputStream input) throws IOException

  • static BufferedImage read(URL input) throws IOException

  • static BufferedImage read(ImageInputStream input) throws IOException
    Lädt ein Bild und liefert ein BufferedImage oder null, wenn kein Decoder das Bild lesen konnte.

[zB]Beispiel

Lesen eines Bildes aus einer Datei, Netzwerkdatenquelle und URL:

BufferedImage a = ImageIO.read(new File( "girlfriend1001.png" ));
BufferedImage b = ImageIO.read(socket.getInputStream());
BufferedImage c = ImageIO.read(new URL("http://tutego.de/images/email.gif"));

Die Bilder können auf unterschiedliche Art weiterverarbeitet werden. Sie lassen sich über drawImage(…) anzeigen und auch als Grafiken in Swing weiterverarbeiten. Zwar fordert Swing sie als ImageIcon an, doch die Klasse ist so gütig, einen Konstruktor anzubieten, der ein Image-Objekt akzeptiert.

Klassendiagramm von ImageIO

Abbildung 11.16Klassendiagramm von ImageIO

 
Zum Seitenanfang

11.6.3Ein Bild zeichnen Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Grafik zeichnet die Methode drawImage(…) der Graphics-Klasse. Die Methode ist mit unterschiedlichen Varianten überladen, um die Grafik auch in anderen Größen zu zeichnen – was sie skaliert – oder auch nur Teile zu zeichnen. Der einfachste Aufruf, der die Grafik in ihrer Originalgröße ab der Position oben links mit der Position (0,0) setzt, ist:

Image image = …
g.drawImage( image, 0, 0, this );

Die drawImage(…)-Methoden sind mehrheitlich in der Oberklasse Graphics, doch zwei zusätzliche Methoden deklariert die Graphics-Unterklasse Graphics2D. Die wichtigen Methoden sind (auf die Modifizierer abstract und die Rückgabe verzichtet die Aufzählung der Kürze halber):

abstract class java.awt.Graphics
  • drawImage(Image img, int x, int y, ImageObserver observer)

  • drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)

abstract class java.awt.Graphics2D
extends Graphics
  • drawImage(Image img, AffineTransform xform, ImageObserver obs)

  • drawImage(BufferedImage img, BufferedImageOp op, int x, int y)

[»]Hinweis

In den Methoden fällt ein besonderer Beobachter auf, der ImageObserver. Der Grund für seinen Einsatz ist die Tatsache, dass Java bei den über das Toolkit angesprochenen Grafiken das Laden so weit hinauszögert, bis eine Darstellung die Pixeldaten wirklich erforderlich macht. Damit aber nach (oder während) des Ladens die Darstellung erfolgen kann, informiert der Lader die Interessenten über den Ladezustand. Nutzen wir drawImage(…) in einer Unterklasse von Component – sie implementiert ImageObserver –, ist das Argument für den ImageObserver oft this, andernfalls null, wenn eine Ladeüberwachung nicht nötig ist.

Bildbetrachter

Das folgende Programmlisting zeigt eine einfache Applikation mit einer Menüleiste, die über einen Dateiauswahldialog eine Grafik lädt und anzeigt. Wir beginnen mit der ersten Klasse, die eine Swing-Komponente darstellt, die das Bild zeichnet:

Listing 11.10com/tutego/insel/ui/image/ImageViewer.java, ImageComponent

class ImageComponent extends JComponent {

private static final long serialVersionUID = 8055865896136562197L;

private BufferedImage image;

public void setImage( BufferedImage image ) {
this.image = image;
setPreferredSize( new Dimension(image.getWidth(), image.getHeight()) );
repaint();
invalidate();
}

@Override protected void paintComponent( Graphics g ) {
if ( image != null )
g.drawImage( image, 0, 0, this );
}
}

Da ein Dateiauswahl-Dialog gewünscht ist, der aufgrund einer Menüauswahl die Datei lädt, folgt eine Implementierung einer Swing-Aktion:

Listing 11.11com/tutego/insel/ui/image/ImageViewer.java, FileOpenAction

class FileOpenAction extends AbstractAction
{
private final ImageComponent viewComponent;

public FileOpenAction( ImageComponent viewComponent ) {
this.viewComponent = viewComponent;

putValue( NAME, "Öffnen" );
putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_O,
InputEvent.CTRL_DOWN_MASK ) );
putValue( MNEMONIC_KEY, (int) 'f' );
}

@Override public void actionPerformed( ActionEvent e ) {
JFileChooser fileDialog = new JFileChooser();
fileDialog.setFileFilter(
new FileNameExtensionFilter("*.jpg;*.gif", "jpg", "gif") );
fileDialog.showOpenDialog( viewComponent );
final File file = fileDialog.getSelectedFile();

if ( file != null ) {
new SwingWorker<BufferedImage, Void>() {
@Override protected BufferedImage doInBackground() throws IOException {
return ImageIO.read( file );
}
@Override protected void done() {
try { viewComponent.setImage( get() ); }
catch ( InterruptedException | ExecutionException e ) { }
}
}.execute();
}
}
}

Der Dialog zur Dateiauswahl ist so über einen FileFilter eingestellt, dass er nur Verzeichnisse und Dateien anzeigt, die auf .jpg oder .gif enden. Hat der Benutzer eine gültige Grafik ausgewählt, wird setImage(BufferedImage) unserer ImageComponent angewiesen, das Bild zu laden und anzuzeigen.

Den letzten Teil bildet das Hauptprogramm. Es erzeugt die Bildkomponente und das Menü, setzt den Listener und zeigt das Fenster an:

Listing 11.12com/tutego/insel/ui/image/ImageViewer.java, ImageViewer

public class ImageViewer {
public static void main( String[] args ) {
JFrame f = new JFrame( "Bildbetrachter" );

ImageComponent imageComponent = new ImageComponent();
f.add( new JScrollPane(imageComponent) );
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu( "Datei" );
menu.setMnemonic( 'D' );
menu.add( new JMenuItem( new FileOpenAction(imageComponent) ) );
menuBar.add( menu );
f.setJMenuBar( menuBar );

f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setSize( 600, 400 );
f.setVisible( true );
}
}
Ein einfacher Bildbetrachter mit Dateiauswahldialog

Abbildung 11.17Ein einfacher Bildbetrachter mit Dateiauswahldialog

 
Zum Seitenanfang

11.6.4Splash-Screen * Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Splash-Screen ist ein Willkommensbildschirm mit Grafik, der noch vor dem eigentlichen Programmstart über die JVM erscheint und dem Benutzer Informationen über Version und Autor übermittelt. Java kann in zwei Varianten einen Startschirm mit einer beliebigen Grafik – nennen wir sie beispielsweise splash.png – anzeigen:

  • über den Schalter -splash beim Start der JVM; etwa java -splash:splash.png Main

  • mit einem Eintrag Splashscreen-Image: splash.png in der Manifest-Datei

Öffnet unser Java-Programm das erste Fenster, schließt sich der Splash-Screen automatisch. Dennoch gibt es Möglichkeiten, auf den Splash-Screen aus dem Java-Programm zuzugreifen:

SplashScreen splash = SplashScreen.getSplashScreen();
Graphics2D g2 = splash.createGraphics();
// Zeichenoperationen
splash.update();

Die Methode close() schließt den Splash-Screen manuell und wartet nicht auf das erste eigene Fenster, das den Splash-Screen automatisch schließt.

 
Zum Seitenanfang

11.6.5Bilder skalieren * Zur vorigen ÜberschriftZur nächsten Überschrift

Die Methode getScaledInstance(int width, int height, int hints) der Klasse Image gibt ein neues Image-Objekt mit größeren oder kleineren Maßen zurück. Das neue Bild wird wieder nur dann berechnet, wenn es auch benötigt wird – das Verhalten ist also ebenso asynchron wie bei der gesamten Bildverwaltung über die Image-Klasse. Beim Vergrößern oder Verkleinern kommt es zu Pixelfehlern, und das Vergrößern der Pixel beeinflusst das Endergebnis und die Geschwindigkeit. Stellen wir uns vor, ein Bild der Größe 100 × 100 Pixel soll um das Doppelte vergrößert werden. Das Resultat ist ein Bild mit 200 × 200 Pixeln, doch muss aus einem Bildpunkt nun die Information für drei weitere Punkte abgeleitet werden. Eine Lösung bestünde darin, die Farbwerte der Punkte einfach zu duplizieren, dann bleibt die Schärfe, aber das Bild wirkt wie aus groben Blöcken zusammengesetzt. Eine andere Möglichkeit wäre, die Farbinformationen für die neuen Punkte aus den Informationen der Nachbarpunkte zu errechnen. Das Bild wirkt glatter, aber auch unschärfer bei hoher Skalierung. Und ebenso wie beim Vergrößern der Bilder sollten auch beim Verkleinern die Bildinformationen nicht einfach wegfallen, sondern, wenn möglich, zu neuen Farbwerten zusammengefasst werden. So erwarten wir von einem Algorithmus, dass dieser bei einer Schrumpfung von drei Farbwerten zu einem Farbwert die drei Informationen zu einem neuen Wert zusammenlegt.

Damit diese Anforderungen erfüllt werden können, verlangt getScaledInstance(…) nicht nur die neue Breite und Höhe, sondern auch eine Konstante für die Art der Skalierung. Der Parameter bestimmt den Algorithmus – mögliche Konstanten sind SCALE_DEFAULT, SCALE_FAST, SCALE_SMOOTH, SCALE_REPLICATE und SCALE_AREA_AVERAGING.

Skalierungsparameter

Bedeutung

SCALE_DEFAULT

Verwendet einen Standard-Skalierungsalgorithmus.

SCALE_FAST

Verwendet einen Skalierungsalgorithmus, der mehr Wert auf Geschwindigkeit als auf die Glätte des Bildes legt.

SCALE_SMOOTH

Verwendet einen Algorithmus mit guter Bildqualität und legt weniger Wert auf Geschwindigkeit.

SCALE_REPLICATE

Benutzt für den Skalierungsalgorithmus den ReplicateScaleFilter.

SCALE_AREA_AVERAGING

Verwendet den AreaAveragingScaleFilter.

Tabelle 11.1Argumente für getScaledImage(…)

Mithilfe dieser Konstanten lässt sich die Methode aufrufen:

abstract class java.awt.Image
  • Image getScaledInstance(int width, int height, int hints)
    Liefert ein skaliertes Bild mit den neuen Maßen width und height. Das neue Bild kann asynchron geholt werden. hints gibt den Skalierungsalgorithmus als Konstante an. Ist die Höhe oder Breite negativ, so berechnet sich der Wert aus dem anderen, um das Seitenverhältnis beizubehalten.

[zB]Beispiel

Eine Grafik soll geladen und zwei skalierte neue Image-Exemplare sollen abgeleitet werden. Die erste Skalierung soll das Original um einen Prozentwert verändern, und die zweite Skalierung soll – unabhängig von der korrekten Wiedergabe der Seitenverhältnisse – das Bild auf die Größe des Bildschirms bringen. Wir wollen es mit Image.SCALE_SMOOTH skaliert haben:

Image image = ImageIO.read( "ottosHaus.jpg" );
int percent = 175;
Image scaled1 = image.getScaledInstance(
(image.getWidth() * percent) / 100,
(image.getHeight() * percent) / 100,
Image.SCALE_SMOOTH );
Image scaled2 = image.getScaledInstance(
Toolkit.getDefaultToolkit().getScreenSize().width,
Toolkit.getDefaultToolkit().getScreenSize().height,
Image.SCALE_SMOOTH );

Hinter den Kulissen

Was auf den ersten Blick wie die Wahl zwischen unglaublich vielen Varianten aussieht, entpuppt sich als typische Informatiker-Lösung: entweder schnell und schmutzig oder schön und gemächlich. Aber so ist nun mal das Leben. Der Quelltext macht dies deutlich:

Listing 11.13java/awt/Image.java, getScaledInstance()

public Image getScaledInstance(int width, int height, int hints) {
ImageFilter filter;
if ((hints & (SCALE_SMOOTH | SCALE_AREA_AVERAGING)) != 0)
filter = new AreaAveragingScaleFilter(width, height);
else
filter = new ReplicateScaleFilter(width, height);
ImageProducer prod;
prod = new FilteredImageSource(getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(prod);
}

Bei der Wahl zwischen sanftem Bild und schnellem Algorithmus greift getScaledInstance(int, int, int) auf die beiden Filterklassen AreaAveragingScaleFilter und ReplicateScaleFilter zurück. Sie berechnen jeweils das neue Bild über einen Bildproduzenten. ReplicateScaleFilter ist der einfachere von beiden. Bei der Vergrößerung werden die Pixel einer Zeile oder Spalte einfach verdoppelt. Wird verkleinert, so werden einfach Reihen oder Spalten weggelassen. Mit einem AreaAveragingScaleFilter bekommen wir die besseren Resultate, da Pixel nicht einfach kopiert werden, sondern weil wir eingefügte Pixel aus einer Mittelwertberechnung erhalten. Der Algorithmus heißt im Englischen auch nearest neighbor algorithm.

 
Zum Seitenanfang

11.6.6Schreiben mit ImageIO Zur vorigen ÜberschriftZur nächsten Überschrift

ImageIO ist eine Utility-Klasse mit statischen Methoden zum Lesen und Schreiben von Grafiken und um einen passenden Bildleser/-schreiber herauszusuchen.

So wie die statische Methode ImageIO.read(…) eine Grafik liest, schreibt ImageIO.write(…) sie zum Beispiel im PNG- oder JPG-Format. Voraussetzung ist eine Grafik, die als RenderedImage vorliegt. Die Schnittstelle wird beispielsweise von BufferedImage implementiert, der wichtigsten Klasse für Bildinformationen. Gilt es, die Grafik abzuspeichern, wird die Methode ImageIO. write(…) mit einem Verweis auf das RenderedImage sowie das Datenformat und ein File-Objekt oder den Ausgabestrom aufgerufen.

[zB]Beispiel

Speichere als PNG-Datei ein Bild mit den Maßen 100 × 100 und einem gefüllten Kreis:

Listing 11.14com/tutego/insel/ui/image/SaveImage.java, main()

GraphicsConfiguration gfxConf = GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice()
.getDefaultConfiguration();
BufferedImage image = gfxConf.createCompatibleImage( 100, 100 );
image.createGraphics().fillOval( 0, 0, 100, 100 );
ImageIO.write( image, "png", new File( "c:/circle.png" ) );

Kann ImageIO ein Format behandeln? *

ImageIO erlaubt standardmäßig das Speichern in JPG, PNG und GIF. Eine Liste der unterstützten Formate liefern ImageIO.getWriterFormatNames() bzw. ImageIO.getWriterMIMETypes():

String[] types = ImageIO.getWriterMIMETypes();
System.out.println( Arrays.toString(types) );

Die Ausgabe sieht etwa so aus:

[image/jpeg, image/png, image/x-png, image/vnd.wap.wbmp, image/gif, image/bmp]

Ob ImageIO ein Bild mit einem bestimmten Grafikformat lesen kann, bestimmt im Grunde die statische Methode ImageIO.getImageReadersByFormatName(String formatName) – sie liefert eine Liste von ImageReader-Objekten, die das Format übernehmen. Da die Liste über einen Iterator gegeben ist, lässt sich die Frage, ob ImageIO ein bestimmtes Format lesen kann, über das Ergebnis von ImageIO.getImageReadersByFormatName().hasNext() beantworten.

[zB]Beispiel

Soll für eine Endung die Möglichkeit des Lesens erfragt werden, liefert eine eigene Methode canReadExtension(String) die Antwort – wieder über einen Iterator:

public static boolean canReadExtension( String ext ) {
return ImageIO.getImageReadersBySuffix(ext).iter.hasNext();
}

ImageIO.getImageReadersByMIMEType(String) liefert einen Iterator der MIME-Typen für Grafik-Leser.

Die Anfragemöglichkeit gibt es natürlich nicht nur für die Leser, sondern äquivalent auch für die Schreiber. Hier erfüllen drei statische Methoden ihren Zweck:

  • getImageWritersByFormatName(String formatName)

  • getImageWritersBySuffix(String fileSuffix)

  • getImageWritersByMIMEType(String MIMEType)

Die Anfragetypen richten sich bisher nach den Dateiendungen oder MIME-Typen. Diese Aussagen erfordern aber Unterstützung vom Dateisystem oder vom Server. Was ist, wenn eine Grafik über das Netzwerk übertragen wird, die Typinformationen aber fehlen? Dann helfen statische Methoden wie getImageReadersBySuffix(String) nicht, sondern eine inhaltliche Analyse muss her. Hilfreich ist die Methode ImageIO. createImageInputStream(Object), die drei Datengeber analysieren kann: File-Objekte, lesbare RandomAccessFile-Objekte und InputStream-Objekte. Weil die Entwickler nun aber nicht drei unterschiedliche statische Methoden mit unterschiedlichen Parametern für createImageInputStream(…) vorsehen wollten, nahmen sie die Oberklasse – nämlich Object (das glänzt nicht gerade vor gutem Bibliotheksdesign).

ImageInputStream iis = ImageIO.createImageInputStream( object );

Die Rückgabe ist ein ImageInputStream, der, obwohl er InputStream im Namen trägt, kein Eingabestrom im klassischen Sinne ist. ImageInputStream erlaubt einen Datenzugriff mit wahlfreier Positionierung, und createImageInputStream(Object) ist eine Methode, die den ImageInputStream für eine Datenquelle liefert. Für Benutzer ist ImageInputStream aber immer noch nicht gedacht; Benutzer arbeiten mit ImageReader-Objekten. Ein passendes ImageReader-Objekt für die Bytes liefert getImageReaders(Object):

Iterator = ImageIO.getImageReaders( iis );

Der Iterator liefert alle ImageReader, die das Datenformat für den Binärstrom verarbeiten können. Uns reicht der erste:

if ( iter.hasNext() )
ImageReader reader = (ImageReader) iter.next();
else
// Kein Reader, der das Format versteht.

Komprimieren mit ImageIO *

Die statischen Methoden ImageIO.write(…) und ImageIO.read(…) sind nur Hilfsmethoden, die im Hintergrund einen passenden ImageWriter und ImageReader suchen und ihm die Arbeit überlassen. Während der Kontakt zum tatsächlichen ImageReader eher selten ist, gibt es einen guten Grund, sich mit dem schreibenden ImageWriter näher zu beschäftigen – ihm können über ein ImageWriteParam-Objekt zusätzliche Parameter übertragen werden, etwa der Kompressionsgrad, der sich zwischen 0 und 1 bewegt. JPEG-Bilder sind im Gegensatz zu GIF-Bildern mit Verlust komprimiert, doch lassen sich diese Verluste klein halten. Über eine diskrete Kosinustransformation werden 8 × 8 große Pixelblöcke vereinfacht. Die Komprimierung nutzt die Unfähigkeit des Auges aus, Farbunterschiede so stark wahrzunehmen wie Helligkeitsunterschiede. So können Punkte mit einer ähnlichen Helligkeit, doch einer anderen Farbe zu einem Wert werden. Bei einer hohen Kompression treten jedoch so genannte Artefakte auf, die unschön wirken. Bei einer sehr hohen Kompression ist die Bildgröße sehr klein (und die Bilder sind hässlich). Der Qualitätsfaktor ist vom Typ float und bewegt sich zwischen 0,0 und 1,0. Der Wert 1 bedeutet im Prinzip keine Kompression und somit höchste Qualität. Ein Wert um 0,75 ist ein hoher Wert für Qualitätsbilder, der Wert 0,5 liefert Bilder mittlerer Qualität, und 0,25 sorgt für stärkere Artefakte und hohe Kompression.

Ein Programm, das ein Bild im JPG-Format in eine Datei schreibt, muss zunächst ein Objekt vom Typ ImageWriter erfragen und anschließend den ImageOutputStream aufbauen, um die Daten schreiben zu können. Nach dem Aufbau eines ImageWriteParam-Objekts für die Parameter lässt sich das Bild speichern. Zwar verfügt ImageWriter über eine Methode write(RenderedImage), um zum Beispiel ein BufferedImage zu schreiben – RenderedImage ist eine Schnittstelle, die BufferedImage implementiert –, doch im Fall der Parameter muss das Bild als IIOImage vorliegen. IIOImage versammelt die Bildinformationen (RenderedImage oder Raster), zusammen mit Vorschaubild und Metadaten.

Bilder in verschiedenen Kompressionsstufen speichern *

Wir wollen nun ein Programm entwickeln, das einen Screenshot nimmt und ihn in den Qualitätsstufen 1,0 bis 0,0 in 0,25er-Schritten komprimiert und das Ergebnis auf dem Bildschirm ausgibt:

Listing 11.15com/tutego/insel/ui/image/ImageWriterDemo.java

package com.tutego.insel.ui.image;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Locale;
import javax.imageio.*;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

class ImageWriterDemo {
public static void main( String[] args ) throws Exception {
BufferedImage img = new Robot().createScreenCapture(
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );

int size = 0;

for ( float quality = 1f; quality >= 0; quality -= 0.25 ) {
ByteArrayOutputStream out = new ByteArrayOutputStream( 0xfff );

writeImage( img, out, quality );

if ( size == 0 ) size = out.size();

System.out.printf( "Qualität: %.1f – Größe: %,.0f k – Verhältnis: %.2f%n",
quality, (double) out.size() / 1024,
(double) out.size() / size );
}
}

private static void writeImage( BufferedImage img,
ByteArrayOutputStream out,
float quality ) throws IOException {
ImageWriter writer = ImageIO.getImageWritersByFormatName( "jpg" ).next();
try ( ImageOutputStream ios = ImageIO.createImageOutputStream( out ) ) {
writer.setOutput( ios );
ImageWriteParam iwparam = new JPEGImageWriteParam( Locale.getDefault() );
iwparam.setCompressionMode( ImageWriteParam.MODE_EXPLICIT ) ;
iwparam.setCompressionQuality( quality );
writer.write( null, new IIOImage(img, null, null), iwparam );
ios.flush();
writer.dispose();
}
}
}

Die Ausgabe des Programms für ein Bild ist in etwa die folgende:

Qualität: 1,0 – Größe: 1.005 k – Verhältnis: 1,00
Qualität: 0,8 – Größe: 339 k – Verhältnis: 0,34
Qualität: 0,5 – Größe: 253 k – Verhältnis: 0,25
Qualität: 0,3 – Größe: 182 k – Verhältnis: 0,18
Qualität: 0,0 – Größe: 77 k – Verhältnis: 0,08

Da der Bildschirminhalt durch die Konsolenausgabe immer etwas anders aussieht, werden natürlich auch die Dateigrößen immer anders aussehen.

 
Zum Seitenanfang

11.6.7Asynchrones Laden mit getImage(…) und dem MediaTracker * Zur vorigen ÜberschriftZur nächsten Überschrift

Das Laden von Bildern mittels getImage(…) der Klassen Toolkit oder Applet wird dann vom System angeregt, wenn das Bild zum ersten Mal benötigt wird. Diese Technik ist zwar recht nett und entzerrt den Netzwerktransfer, eignet sich aber nicht für bestimmte grafische Einsätze. Nehmen wir zum Beispiel eine Animation: Wir können nicht erwarten, die Animation erst dann im vollen Ablauf zu sehen, wenn wir nacheinander alle Bilder im Aufbauprozess gesehen haben. Daher ist zu wünschen, dass zunächst alle Bilder geladen werden können, bevor sie angezeigt werden.

Die Klasse MediaTracker ist eine Hilfsklasse, mit der wir den Ladeprozess von Media-Objekten – bisher sind es nur Bilder – beobachten können. Um den Überwachungsprozess zu starten, werden die Media-Objekte dem MediaTracker zur Beobachtung übergeben.

Neben diesem besitzt die Klasse gegenüber der herkömmlichen Methode noch weitere Vorteile:

  • Bilder lassen sich in Gruppen organisieren.

  • Bilder können synchron oder asynchron geladen werden.

  • Die Bildergruppen können unabhängig geladen werden.

Bilder dem Cache entnehmen

Eine Webcam erzeugt kontinuierlich neue Bilder. Sollen diese in einem Applet präsentiert werden, so ergibt sich das Problem, dass ein erneuter Aufruf von getImage(…) lediglich das alte Bild liefert. Dies liegt an der Verwaltung der Image-Objekte, da sie in einem Cache gehalten werden. Für sie gibt es keine automatische Speicherbereinigung, die die Entscheidung fällt: »Das Bild ist alt.« Hier hilft die Methode flush() der Image-Klasse weiter. Sie löscht das Bild aus der internen Liste. Eine erneute Aufforderung zum Laden bringt also das gewünschte Ergebnis.

abstract class java.awt.Image
  • abstract void flush()
    Gibt die für das Image belegten Ressourcen frei.

[»]Hinweis

Image-Objekte werden nicht automatisch freigegeben. flush() entsorgt diese Bilder, macht den Speicher frei und den Rechner wieder schneller.

Klassendiagramm von Image mit Eigenschaften

Abbildung 11.18Klassendiagramm von Image mit Eigenschaften

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2018. Original - https://www.rheinwerk-verlag.de/openbook/
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

 
 


21.12.2024 - Sitemap.xml