001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.gui.swing.image;
018:
019: // J2SE dependencies
020: import java.awt.geom.Rectangle2D;
021: import java.awt.EventQueue;
022: import java.awt.Graphics2D;
023: import java.awt.Rectangle;
024: import java.awt.image.RenderedImage;
025: import java.awt.image.renderable.ParameterBlock;
026: import java.awt.image.renderable.RenderableImage;
027:
028: // JAI dependencies
029: import javax.media.jai.JAI;
030:
031: // Geotools dependencies
032: import org.geotools.gui.swing.ZoomPane;
033:
034: /**
035: * A simple image viewer. This widget accepts either {@linkplain RenderedImage rendered} or
036: * {@linkplain RenderableImage renderable} image. Rendered image are display immediately,
037: * while renderable image will be rendered in a background thread when first requested.
038: * This widget may scale down images for faster rendering. This is convenient for image
039: * previews, but should not be used as a "real" renderer for full precision images.
040: *
041: * @since 2.3
042: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/image/ImagePane.java $
043: * @version $Id: ImagePane.java 20883 2006-08-07 13:48:09Z jgarnett $
044: * @author Martin Desruisseaux
045: */
046: public class ImagePane extends ZoomPane implements Runnable {
047: /**
048: * The default size for rendered image produced by a {@link RenderableImage}.
049: * This is also the maximum size for a {@link RenderedImage}; bigger image
050: * will be scaled down using JAI's "Scale" operation for faster rendering.
051: */
052: private final int renderedSize;
053:
054: /**
055: * The renderable image, or {@code null} if none. If non-null, then the {@link #run}
056: * method will transform this renderable image into a rendered one when first requested.
057: * Once the image is rendered, this field is set to {@code null}.
058: */
059: private RenderableImage renderable;
060:
061: /**
062: * The rendered image, or {@code null} if none. This image may be explicitly set
063: * by {@link #setImage(RenderedImage)}, or computed by {@link #run}.
064: */
065: private RenderedImage rendered;
066:
067: /**
068: * {@code true} if the {@link #run} method has been invoked for the current image.
069: * This field is used in order to avoid to start more than one thread for the same
070: * {@linkplain #renderable} image.
071: */
072: private volatile boolean running;
073:
074: /**
075: * Constructs an initially empty image pane with a default rendered image size.
076: */
077: public ImagePane() {
078: this (512);
079: }
080:
081: /**
082: * Constructs an initially empty image pane with the specified rendered image size.
083: * The {@code renderedSize} argument is the <em>maximum</em> width and height for
084: * {@linkplain RenderedImage rendered image}. Images greater than this value will be
085: * scaled down for faster rendering.
086: */
087: public ImagePane(final int renderedSize) {
088: super (UNIFORM_SCALE | TRANSLATE_X | TRANSLATE_Y | ROTATE
089: | RESET | DEFAULT_ZOOM);
090: setResetPolicy(true);
091: this .renderedSize = renderedSize;
092: }
093:
094: /**
095: * Sets the source renderable image.
096: */
097: public void setImage(final RenderableImage image) {
098: renderable = image;
099: rendered = null;
100: running = false;
101: reset();
102: repaint();
103: }
104:
105: /**
106: * Sets the source rendered image.
107: */
108: public void setImage(RenderedImage image) {
109: if (image != null) {
110: final float scale = Math.min(((float) renderedSize)
111: / image.getWidth(), ((float) renderedSize)
112: / image.getHeight());
113: if (scale < 1) {
114: final Float sc = new Float(scale);
115: image = JAI.create("Scale", new ParameterBlock()
116: .addSource(image).add(sc).add(sc));
117: }
118: }
119: renderable = null;
120: rendered = image;
121: running = false;
122: reset();
123: repaint();
124: }
125:
126: /**
127: * Reset the default zoom. This method overrides the default implementation in
128: * order to keep the <var>y</var> axis in its Java2D direction (<var>y</var>
129: * value increasing down), which is the usual direction of most image.
130: */
131: public void reset() {
132: reset(getZoomableBounds(null), false);
133: }
134:
135: /**
136: * Returns the image bounds, or {@code null} if none. This is used by
137: * {@link ZoomPane} in order to set the initial zoom.
138: */
139: public Rectangle2D getArea() {
140: final RenderedImage rendered = this .rendered; // Protect from change in an other thread
141: if (rendered != null) {
142: return new Rectangle(rendered.getMinX(),
143: rendered.getMinY(), rendered.getWidth(), rendered
144: .getHeight());
145: }
146: return null;
147: }
148:
149: /**
150: * Paint the image. If the image was a {@link RenderableImage}, then a {@link RenderedImage}
151: * will be computed in a background thread when this method is first invoked.
152: */
153: protected void paintComponent(final Graphics2D graphics) {
154: final RenderedImage rendered = this .rendered; // Protect from change in an other thread
155: if (rendered == null) {
156: if (renderable != null && !running) {
157: running = true;
158: final Thread runner = new Thread(this , "Renderer");
159: runner.setPriority(Thread.NORM_PRIORITY - 2);
160: runner.start();
161: }
162: } else {
163: graphics.drawRenderedImage(rendered, zoom);
164: }
165: }
166:
167: /**
168: * Creates a {@linkplain RenderedImage rendered} view of the {@linkplain RenderableImage
169: * renderable} image and notifies {@link ZoomPane} when the result is ready. This method
170: * is run in a background thread and should not be invoked directly, unless the user wants
171: * to trig the {@link RenderedImage} creation immediately.
172: */
173: public void run() {
174: running = true;
175: final RenderableImage producer = renderable; // Protect from change.
176: if (producer != null) {
177: final RenderedImage image = producer.createScaledRendering(
178: renderedSize, 0, null);
179: EventQueue.invokeLater(new Runnable() {
180: public void run() {
181: if (producer == renderable) {
182: setImage(image);
183: }
184: }
185: });
186: }
187: }
188: }
|