/**
*
*/
package xy.plot;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.UIManager;
import xy.plot.tools.ImagePanel;
/**
* Rasterbilder in einem Fenster (Frame) darstellen.
* <p>
* Dieser Frame (<code>java.swing.JFrame</code>) dient als eigenständiges Fenster (<code>ContentPane</code>) zum
* Darstellen und Bearbeiten von Rasterbildern. Es kann auf einzelne Pixel des Bildrasters lesend oder schreibend
* zugegriffen werden (setRGB()/getRGB()). Die Auswahl eines bestimmten Pixels erfolgt durch die Angabe eines
* xy-Koordinatenpaares, wobei die linke obere Ecke des Bildes der Koordinatenursprung (0,0) ist.
* Die Größe des Bildrasters in Breite und Höhe (jeweils in Pixel) wird im Konstruktor festgelegt
* und kann nachträglich nicht mehr verändert werden (non mutable). Es kann ein Raster jeder gewünschten Größe
* angelegt werden, welches mit der Standard-Farbe schwarz initialisiert wird. Oder es kann eine Bild-Datei vom Typ
* .gif, .jpg, .bmp, ... geladen werden, wobei die Größe des Rasters mit der Größe der Bild-Datei
* initialisiert wird. Es existiert auch ein Konstruktor, der eine angegebene Bild-Datei in eine gewünschte
* Größe umsetzt (siehe Konstruktoren).
* <p>
* Nach der Erzeugung (Konstruktion) einer neuen Instanz der Klasse <code>ImageFrame</code> bleibt das Fenster
* zunächst unsichtbar. Deshalb kann der Bildinhalt zu nächst nicht sichtbar bearbeitet werden. Mit der Methode
* <code>setVisible(true)</code> und dem Parameter <code>true</code> wird das Fenster und der Bildinhalt sichtbar.
* Die Größe des Fensters wird an die Größe des Bildrasters angepasst; kann aber durch Ziehen verändert werden.
* <p>
* Nachdem ein Fenster sichtbar wurde, wird der Bildinhalt bei Veränderung nicht automatisch aktualisiert, d.h.
* die Veränderungen am Bildinhalt werden nicht sichtbar. Mit Hilfe der Methode <code>repaint()</code> und deren
* Varianten kann eine Aktualisierung des Fensters angefordert werden.
* <p>
* <b>Beispiel-Code:</b>
* <p>
* <code>
* // Erstellen eines neuen Fensters mit schwarzem Bildinhalt der Groesse 800x600 Pixel <br>
* ImageFrame frame = new ImageFrame(800,600); <br>
* // Verändern der Farbe eines einzelnen Pixels zu gelb, Position x=100, y=33 <br>
* frame.setRGB(100,33,0xFFFF00); <br>
* // Sichtbar machen des Fensters <br>
* frame.setVisible(true); <br>
* </code>
* @author MaBe
* @version 1.2
* @see java.swing.JFrame
*/
public class ImageFrame extends JFrame implements Printable
{
private static final long serialVersionUID = 4953658294908426545L;
// Content-Pane
private ImagePanel pane;
/**
* Einen neuen, noch unsichtbaren Frame mit einem Bild-Raster der angegebenen Größe und dem angegebenen Titel erzeugen.
* Der angegebene Titel wir in der Titelleiste des Fensters angezeigt.
* Mit setVisible(true) sichtbar machen.
*
* @param width Breite des Raster-ImagePanels in Pixel
* @param height Höhe des Raster-ImagePanels in Pixel
* @param titel Titel in der Fensterleiste
*/
public ImageFrame(int width, int height, String titel)
{
this(titel, new ImagePanel(width, height));
}
/**
* Einen neuen, noch unsichtbaren Frame mit einem Bild-Raster der angegebenen Größe
* als ContentPane erzeugen.
* In der Titelleiste des Fensters wird ein vorgegebener Titel gezeigt.
* Mit setVisible(true) sichtbar machen.
*
* @param width Breite des Raster-ImagePanels in Pixel
* @param height Höhe des Raster-ImagePanels in Pixel
*/
public ImageFrame(int width, int height)
{
this("Raster-Image Viewer", new ImagePanel(width, height));
}
/**
* Einen neuen, noch unsichtbaren Frame mit einem Rasterbild aus einer Bilddatei erzeugen.
* In der Titelleiste des Fensters wird ein vorgegebener Titel gezeigt.
* Die angegebene Datei wird als Bildinhalt geladen. Die Größe des Bild-Rasters entspricht
* der Größe der geladenen Bild-Datei.
* Mit setVisible(true) sichtbar machen.
*
* @param file Bild-Datei als <code>File</code> Objekt
*/
public ImageFrame(File file)
{
this("Raster-Image Viewer", new ImagePanel(file, null));
}
/**
* Einen neuen, noch unsichtbaren Frame mit einem Rasterbild aus einer Bilddatei und dem angegebenen Titel erzeugen.
* In der Titelleiste des Fensters wird der angegebene Titel gezeigt.
* Die angegebene Datei wird als Bildinhalt geladen. Die Größe des Bild-Rasters entspricht
* der Größe der geladenen Bild-Datei.
* Mit setVisible(true) sichtbar machen.
*
* @param file Bild-Datei als <code>File</code> Objekt
* @param titel Titel in der Fensterleiste
*/
public ImageFrame(File file, String titel)
{
this(titel, new ImagePanel(file, null));
}
/**
* Einen neuen, noch unsichtbaren Frame mit einem Rasterbild aus einer Bilddatei
* und dem angegebenen Titel erzeugen. Der Bildinhalt wird mit der angegebenen Bild-Datei initialisiert.
* Dabei wird das angegebene Bild auf die gewünschte Rastergröße skaliert.
* In der Titelleiste des Fensters wird der angegebene Titel gezeigt.
* Mit <code>setVisible(true)</code> sichtbar machen.
*
* @param width Breite des Raster-ImagePanels in Pixel
* @param height Höhe des Raster-ImagePanels in Pixel
* @param file Bild-Datei als <code>File</code> Objekt, wird geladen und skaliert
* @param titel Titel in der Fensterleiste
*/
public ImageFrame(int width, int height, File file, String titel)
{
this(titel, new ImagePanel(width, height, file, null));
}
/**
* Einen neuen, noch unsichtbaren Frame mit einem <code>ImagePanel</code> als ContentPane erzeugen.
* Der angegebene Name erscheint in der Titelleiste des Fensters.
* Mit setVisible(true) sichtbar machen.
* <p>
* Für interne Verwendung!
*
* @param titel Titel des Fensters in der Titelleiste
* @param p zugehöriges ContentPane (<code>ImagePanel</code>)
*/
public ImageFrame(String titel, ImagePanel p)
{
setSystemLookAndFeel();
JFrame.setDefaultLookAndFeelDecorated(true);
pane = p;
this.setDefaultCloseOperation(ImageFrame.EXIT_ON_CLOSE);
this.setContentPane(pane);
this.setTitle(titel);
this.addWindowListener(p);
this.pack();
// this.g2d = pane.getGraphics2D();
}
/**
* Einen neuen, noch unsichtbaren Frame mit einem <code>ImagePanel</code> als ContentPane erzeugen.
* Der angegebene Name erscheint in der Titelleiste des Fensters. Der Rahmen des darstellenden Fensters
* kann ausgeblendet werden (<code>undecorated = true</code>).
* Mit setVisible(true) sichtbar machen.
*
* @param titel Titel des Fensters in der Titelleiste
* @param p zugehöriges ContentPane (<code>ImagePanel</code>)
* @param undecorated true falls kein Fensterrahmen gezeigt werden soll
*/
public ImageFrame(String titel, ImagePanel p, boolean undecorated)
{
super(titel);
setSystemLookAndFeel();
JFrame.setDefaultLookAndFeelDecorated(true);
this.pane = p;
this.setUndecorated(undecorated);
this.setDefaultCloseOperation(ImageFrame.EXIT_ON_CLOSE);
this.setContentPane(pane);
// this.setTitle(titel);
this.addWindowListener(p);
pack();
}
/**
* Einstellen des jeweiligen system-spezifischen GUI-Aussehens (Look-and-Feel).
* Diese Methode sollte vor dem ersten Erstellen eines Fensters einmalig aufgerufen werden,
* um das jeweils systemspezifische Aussehen eines Fensters zu erhalten.
*
*/
public static void setSystemLookAndFeel()
{
try
{
// set up system look-and-feel
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Access to the destination image's buffer <code>BufferedImage</code>
* @return the buffered image
*/
protected BufferedImage getBufferedImage()
{
return pane.getBufferedImage();
}
/**
* Liefert die Breite des Frame-Inhalts in Pixel
*
* @return die Rasterbreite in Pixel
*/
public int getImageWidth()
{
return pane.getImageWidth();
}
/**
* Liefert die Höhe des Frame-Inhalts in Pixel
*
* @return die Rasterhöhe in Pixel
*/
public int getImageHeight()
{
return pane.getImageHeight();
}
/**
* Setzen der Farbe eines Pixels im Bildraster.
* <p>
* Die angegebenen Pixelkoordinaten (x,y) müssen
* im Bereich des Rasters (x = 0...width-1, y = 0...height-1) liegen. Die angegebene Farbe ist
* eine 32bit Farbangabe mit 0xAARRGGBB (hexadezimal jeweils 8 bit für Alpha, Rot, Grün, Blau).
* Der Alpha-Wert findet derzeit keine Verwendung, da nur ein Bild deckend dargestellt wird.
*
* @param x, x-Koordinate des Pixels
* @param y, y-Koordinate des Pixels
* @param rgb, Farbcodierung 32 bit, jeweils 8-bit für Alpha, Rot, Grün, Blau
*/
public void setRGB(int x, int y, int rgb)
{
if (x >= 0 && x < pane.getImageWidth() && y >= 0 && y < pane.getImageHeight())
pane.setRGB(x, y, rgb);
}
/**
* Auslesen der Farbe eines Pixels im Bildraster.
* <p>
* Die angegebenen Pixelkoordinaten (x,y) müssen
* im Bereich des Rasters (x = 0...width-1, y = 0...height-1) liegen. Der zurückgelieferte Farbcode ist
* eine 24bit Farbangabe mit 0x00RRGGBB (hex).
*
* @param x x-Koordinate des Pixels
* @param y yKoordinate des Pixels
* @return RGB-Farbcodierung 24 bit, jeweils 8-bit für Rot, Grün, Blau
*/
public int getRGB(int x, int y)
{
if (x >= 0 && x < pane.getImageWidth() && y >= 0 && y < pane.getImageHeight())
return pane.getRGB(x, y);
else
return 0;
}
/**
* Liefert die aktuell eingestellte Füll- und Stiftfarbe für alle elementaren
* Zeichenfunktionen.
*
* @return RGB Farbcodierung 32 bit, jeweils 8-bit für Rot, Grün, Blau, Alpha wird nicht benützt
*/
public int getColor()
{
return pane.getColor();
}
/**
* Einstellen der aktuellen Füll- und Stiftfarbe für alle elementaren
* Zeichenfunktionen.
*
* @param color Farbcodierung 32 bit, jeweils 8-bit für Alpha, Rot, Grün, Blau
*/
public void setColor(int color)
{
pane.setColor(color);
}
/**
* Füllen des gesamten Bildrasters mit der aktuell eingestellten Füll- und Stiftfarbe
* (Löschen) und aktualisieren des Fensters.
*/
public void clear()
{
pane.clear();
pane.repaint();
}
/**
* Füllen eines rechteckigen Bereichs mit der aktuell eingestellten Füll- und Stiftfarbe.
* Die angegebenen Koordinaten entsprechen der linken oberen Ecke des Rechtecks; die Größe
* wird mit width und height festgelegt.
*
* @param x
* @param y
* @param width
* @param height
* @see java.awt.Graphics#clearRect(int, int, int, int)
*/
public void clearRect(int x, int y, int width, int height)
{
pane.getGraphics2D().clearRect(x, y, width, height);
}
/**
* Zeichnen der Umrisse einer beliebigen geometrischen Figur (<code>Shape</code>) unter
* Verwendung des aktuell eingestellten Stifts (<code>Stroke</code>).
*
* @param s eine geometrische Figur
* @see java.awt.Graphics2D#draw(java.awt.Shape)
*/
public void draw(Shape s)
{
pane.getGraphics2D().draw(s);
}
/**
* Zeichnen eines Kreis-/Ellipsensegments.
*
* @param x
* @param y
* @param width
* @param height
* @param startAngle
* @param arcAngle
* @see java.awt.Graphics#drawArc(int, int, int, int, int, int)
*/
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
{
pane.getGraphics2D().drawArc(x, y, width, height, startAngle, arcAngle);
}
/**
* Zeichnen einer Linie zwischen zwei Punkten P1(x1,y1) und P2(x2,y2)
*
* @param x1 x-Koordinate des ersten Punkts
* @param y1 y-Koordinate des ersten Punkts
* @param x2 x-Koordinate des zweiten Punkts
* @param y2 y-Koordinate des zweiten Punkts
* @see java.awt.Graphics#drawLine(int, int, int, int)
*/
public void drawLine(int x1, int y1, int x2, int y2)
{
pane.getGraphics2D().drawLine(x1, y1, x2, y2);
}
/**
* Zeichnen der Umrisse eine Ellipse, die in dem angegebenen Rechteck einbeschrieben ist.
*
* @param x
* @param y
* @param width
* @param height
* @see java.awt.Graphics#drawOval(int, int, int, int)
*/
public void drawOval(int x, int y, int width, int height)
{
pane.getGraphics2D().drawOval(x, y, width, height);
}
/**
* Zeichnen der Umrisse eines Polygones mit den angegebenen Eckpunkten.
*
* @param xPoints x-Koordinaten aller Eckpunkte
* @param yPoints y-Koordinaten aller Eckpunkte
* @param nPoints Anzahl der Eckpunkte
* @see java.awt.Graphics#drawPolygon(int[], int[], int)
*/
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
{
pane.getGraphics2D().drawPolygon(xPoints, yPoints, nPoints);
}
/**
* Zeichnen einer Verbindungslinie von mehreren gegebenen Punkten.
* @param xPoints x-Koordinaten aller Eckpunkte
* @param yPoints y-Koordinaten aller Eckpunkte
* @param nPoints Anzahl der Eckpunkte
* @see java.awt.Graphics#drawPolyline(int[], int[], int)
*/
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
{
pane.getGraphics2D().drawPolyline(xPoints, yPoints, nPoints);
}
/**
* Zeichnen der Umrisse eines Rechtecks.
*
* @param x x-Koordinate der linken, oberen Ecke des Rechtecks
* @param y y-Koordinate der linken, oberen Ecke des Rechtecks
* @param width Breite des Rechtecks in Pixel
* @param height Höhe des Rechtecks in Pixel
* @see java.awt.Graphics#drawRect(int, int, int, int)
*/
public void drawRect(int x, int y, int width, int height)
{
pane.getGraphics2D().drawRect(x, y, width, height);
}
/**
* Darstellen eines Textes als Grafik.
*
* @param str der darzustellende Text
* @param x x-Koordinate der Textposition
* @param y y-Koordinate der Textposition
* @see java.awt.Graphics2D#drawString(java.lang.String, int, int)
*/
public void drawString(String str, int x, int y)
{
pane.getGraphics2D().drawString(str, x, y);
}
/**
* Darstellen eines Bildes an der angegebenen Position in Originalgröße (keine Skalierung).
*
* @param img Bild Objekt
* @param x x-Koordinate der linken, oberen Ecke des Bildes
* @param y y-Koordinate der linken, oberen Ecke des Bildes
* @return
*/
public boolean drawImage(Image img, int x, int y)
{
return pane.getGraphics2D().drawImage(img, x, y, pane);
}
/**
* Darstellen eines Bildes an der angegebenen Position und in der angegebenen Größe (Skalierung).
*
* @param img Bild Objekt
* @param x x-Koordinate der linken, oberen Ecke des Bildes
* @param y y-Koordinate der linken, oberen Ecke des Bildes
* @param width Breite der Darstellung in Pixel
* @param height Höhe der Darstellung in Pixel
* @return
* @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver)
*/
public boolean drawImage(Image img, int x, int y, int width, int height)
{
return pane.getGraphics2D().drawImage(img, x, y, width, height, pane);
}
/**
* @param img
* @param dx1
* @param dy1
* @param dx2
* @param dy2
* @param sx1
* @param sy1
* @param sx2
* @param sy2
* @return
* @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, int, int, int, int, java.awt.image.ImageObserver)
*/
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2)
{
return pane.getGraphics2D().drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, pane);
}
/**
* Füllen einer beliebigen geometrischen Figur (<code>Shape</code>) unter
* Verwendung des aktuell eingestellten Füllmusters (<code>Paint</code>).
*
* @param s eine geometrische Figur
* @see java.awt.Graphics2D#draw(java.awt.Shape)
*/
public void fill(Shape s)
{
pane.getGraphics2D().fill(s);
}
/**
* @param x
* @param y
* @param width
* @param height
* @param startAngle
* @param arcAngle
* @see java.awt.Graphics#fillArc(int, int, int, int, int, int)
*/
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
{
pane.getGraphics2D().fillArc(x, y, width, height, startAngle, arcAngle);
}
/**
* @param x
* @param y
* @param width
* @param height
* @see java.awt.Graphics#fillOval(int, int, int, int)
*/
public void fillOval(int x, int y, int width, int height)
{
pane.getGraphics2D().fillOval(x, y, width, height);
}
/**
* @param xPoints
* @param yPoints
* @param nPoints
* @see java.awt.Graphics#fillPolygon(int[], int[], int)
*/
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
{
pane.getGraphics2D().fillPolygon(xPoints, yPoints, nPoints);
}
/**
* @param x
* @param y
* @param width
* @param height
* @see java.awt.Graphics#fillRect(int, int, int, int)
*/
public void fillRect(int x, int y, int width, int height)
{
pane.getGraphics2D().fillRect(x, y, width, height);
}
/**
* @return
* @see java.awt.Graphics2D#getPaint()
*/
public Paint getPaint()
{
return pane.getGraphics2D().getPaint();
}
/**
* @return
* @see java.awt.Graphics2D#getStroke()
*/
public Stroke getStroke()
{
return pane.getGraphics2D().getStroke();
}
/**
* @return
* @see java.awt.Graphics2D#getTransform()
*/
public AffineTransform getTransform()
{
return pane.getGraphics2D().getTransform();
}
/**
* @param paint
* @see java.awt.Graphics2D#setPaint(java.awt.Paint)
*/
public void setPaint(Paint paint)
{
pane.getGraphics2D().setPaint(paint);
}
/**
* @param hints
* @see java.awt.Graphics2D#setRenderingHints(java.util.Map)
*/
public void setRenderingHints(Map<?, ?> hints)
{
pane.getGraphics2D().setRenderingHints(hints);
}
/**
* @param s
* @see java.awt.Graphics2D#setStroke(java.awt.Stroke)
*/
public void setStroke(Stroke s)
{
pane.getGraphics2D().setStroke(s);
}
/**
* @param Tx
* @see java.awt.Graphics2D#setTransform(java.awt.geom.AffineTransform)
*/
public void setTransform(AffineTransform Tx)
{
pane.getGraphics2D().setTransform(Tx);
}
/**
* @param Tx
* @see java.awt.Graphics2D#transform(java.awt.geom.AffineTransform)
*/
public void transform(AffineTransform Tx)
{
pane.getGraphics2D().transform(Tx);
}
/**
* Access to the Graphics2D object.
*
* @return the corresponding Graphics2D object
*/
public Graphics2D getGraphics2D()
{
return pane.getGraphics2D();
}
// Baustelle !
public void print()
{
// Get a PrinterJob
PrinterJob job = PrinterJob.getPrinterJob();
// Specify the Printable is an instance of SimplePrint2D
job.setPrintable(null);
// Put up the dialog box
if (job.printDialog())
{
// Print the job if the user didn't cancel printing
try { job.print(); }
catch (Exception e) { /* handle exception */ }
}
}
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException
{
if (pageIndex > 0)
return Printable.NO_SUCH_PAGE;
Graphics2D g = (Graphics2D)graphics;
Dimension d = pane.getSize();
Dimension dprev = d; // this.getPreferredSize();
pane.setSize(dprev);
// g.setClip((int)pageFormat.getImageableX(), (int)pageFormat.getImageableY(), (int)pageFormat.getImageableWidth(), (int)pageFormat.getImageableHeight());
g.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
double scale = Math.min(pageFormat.getImageableWidth() / dprev.width, pageFormat.getImageableHeight() / dprev.height);
g.scale(scale, scale);
pane.paint(g);
// this.setSize(d);
return Printable.PAGE_EXISTS;
}
/**
* 1. Beispiel-Applikation.
* Zeichnen eines Musters (zufällig und sinnlos)
*
* @param args not used
*/
public static void main(String args[])
{
// Zeichnen der Plots
final int width = 700, height = 480;
ImageFrame f = new ImageFrame(width, height);
f.setVisible(true);
for (int x=0; x<width; x++)
for (int y=0; y<height; y++)
{
int r = (int)(256 * Math.sqrt((double)(x*x + y*y) / (width*width + height*height))) & 0xFF;
int g = (x ^ y) & 0xFF;
int b = (int)(Math.random()*256.0);
int a = (x>>>3)%2 == 0 ? 128 : 255;
int argb = (a<<24)|(r<<16)|(g<<8)|b;
f.setRGB(x, y, argb);
}
f.repaint();
f.setColor(0xFFFFD020);
f.drawLine(0, 0, width-1, height-1);
f.repaint();
}
/**
* 2.Beispiel-Applikation.
* Plotten der Höhenwerte (y-Werte) einer Funktion zweier Veränderlicher.
*
* @param args not used
*/
public static void main1(String args[])
{
// Zeichnen der Plots
final int width = 800, height = 600;
ImageFrame f = new ImageFrame(width, height);
for (int x=0; x<width; x++)
for (int y=0; y<height; y++)
{
// Koordinaten-Range
double xo = 2.0*(double)x/width - 1.0; // -1 ... 1
double yo = - (2.0*(double)y/height - 1.0); // -1 ... 1
// Funktion
double z = xo*yo/(xo*xo+yo*yo);
// Farbe generieren
int h = (int)Math.round(255*2*z);
int r = h > 0? h : 0;
int g = h > 0? 255-h : 255 + h;
int b = h > 0? 0 : -h;
int a = 0xFF;
int argb = (a<<24)|(r<<16)|(g<<8)|b;
f.setRGB(x, y, argb);
}
f.setVisible(true);
}
/**
* 3.Beispiel-Applikation.
* Laden einer existierenden Bilddatei.
*
* @param args not used
*/
public static void main3(String args[])
{
// Darstellen einer Bilddatei
ImageFrame f = new ImageFrame(new File("src/xy/plot/images/maske.gif"));
f.setVisible(true);
}
}