001 /*
002 * $Id: ImagePainter.java,v 1.5 2006/04/26 02:52:48 joshy Exp $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
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 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021
022 package org.jdesktop.swingx.painter;
023
024 import java.awt.Graphics2D;
025 import java.awt.Image;
026 import java.awt.Rectangle;
027 import java.awt.TexturePaint;
028 import java.awt.geom.Point2D;
029 import java.awt.geom.Rectangle2D;
030 import java.awt.image.BufferedImage;
031 import java.util.logging.Logger;
032 import javax.swing.JComponent;
033 import javax.swing.SwingUtilities;
034
035 /**
036 * <p>A Painter instance that paints an image. Any Image is acceptable. This
037 * Painter also allows the developer to specify a "Style" -- CENTERED, TILED,
038 * SCALED, POSITIONED, and CSS_POSITIONED; with the following meanings:</p>
039 *
040 * <ul>
041 * <li><b>CENTERED</b>: draws the image unscaled and positioned in the center of
042 * the component</li>
043 * <li><b>TILED</b>: draws the image repeatedly across the component, filling the
044 * entire background.</li>
045 * <li><b>SCALED</b>: draws the image stretched large enough (or small enough) to
046 * cover the entire component. The stretch may not preserve the aspect ratio of the
047 * original image.</li>
048 * <li><b>POSITIONED</b>: draws the image at the location specified by the imageLocation
049 * property. This style of drawing will respect the imageScale property.</li>
050 * <li><b>CSS_POSITIONED</b>: draws the image using CSS style background positioning.
051 *It will use the location specified by the imageLocation property. This property should
052 *contain a point with the x and y values between 0 and 1. 0,0 will put the image in the
053 *upper left hand corner, 1,1 in the lower right, and 0.5,0.5 in the center. All other values
054 *will be interpolated accordingly. For a more
055 * complete defintion of the positioning algorithm see the
056 * <a href="http://www.w3.org/TR/CSS21/colors.html#propdef-background-position">CSS 2.1 spec</a>.
057 * </li>
058 * </ul>
059 *
060 * @author Richard
061 */
062 public class ImagePainter extends AbstractPainter {
063 /**
064 * Logger to use
065 */
066 private static final Logger LOG = Logger.getLogger(ImagePainter.class.getName());
067
068 /**
069 * <p>An enumeration of Styles supported by the ImagePainter. CENTERED indicates
070 * that the image should be centered. If the image is too large for the
071 * canvas area, it will be clipped, but remain centered</p>
072 *
073 * <p>TILED indicates that the image should be painted multiple times, as
074 * a series of tiles</p>
075 *
076 * <p>SCALED indicates that the image should be scaled to fit the canvas area.
077 * The smallest dimension (Math.min(width, height)) will be used to constrain
078 * the image.
079 */
080 public static enum Style {CENTERED, TILED, SCALED, POSITIONED, CSS_POSITIONED};
081
082 /**
083 * The image to draw
084 */
085 private Image img;
086
087 /**
088 * Specifies how to draw the image, i.e. what kind of Style to use
089 * when drawing
090 */
091 private Style style = Style.CENTERED;
092
093 /**
094 * Create a new ImagePainter. By default there is no image, and the Style
095 * is set to Style.CENTERED
096 */
097 public ImagePainter() {
098 super();
099 }
100
101 /**
102 * Create a new ImagePainter with the specified image and the Style
103 * Style.CENTERED
104 *
105 * @param image the image to be painted
106 */
107 public ImagePainter(Image image) {
108 super();
109 this.img = image;
110 }
111
112 /**
113 * Create a new ImagePainter with the specified image and style.
114 *
115 * @param image the image to be painted
116 * @param style the style of the image
117 */
118 public ImagePainter(Image image, Style style) {
119 super();
120 this.img = image;
121 this.style = style;
122 }
123
124 /**
125 * Sets the image to use for the background of this panel. This image is
126 * painted whether the panel is opaque or translucent.
127 *
128 * @param image if null, clears the image. Otherwise, this will set the
129 * image to be painted. If the preferred size has not been explicitly set,
130 * then the image dimensions will alter the preferred size of the panel.
131 */
132 public void setImage(Image image) {
133 if (image != img) {
134 Image oldImage = img;
135 img = image;
136 firePropertyChange("image", oldImage, img);
137 }
138 }
139
140 /**
141 * @return the image used for painting the background of this panel
142 */
143 public Image getImage() {
144 return img;
145 }
146
147 /**
148 * Sets what style to use when painting the image
149 *
150 * @param s The style constant to apply to the image.
151 */
152 public void setStyle(Style s) {
153 if (style != s) {
154 Style oldStyle = style;
155 style = s;
156 firePropertyChange("style", oldStyle, s);
157 }
158 }
159
160 /**
161 * @return the Style used for drawing the image (CENTERED, TILED, etc).
162 */
163 public Style getStyle() {
164 return style;
165 }
166
167 /**
168 * @inheritDoc
169 */
170 public void paintBackground(Graphics2D g, JComponent component) {
171 if (img != null) {
172 int imgWidth = img.getWidth(null);
173 int imgHeight = img.getHeight(null);
174 if (imgWidth == -1 || imgHeight == -1) {
175 //image hasn't completed loading, do nothing
176 } else {
177 switch (style) {
178 case CENTERED:
179 Rectangle clipRect = g.getClipBounds();
180 int imageX = (component.getWidth() - imgWidth) / 2;
181 int imageY = (component.getHeight() - imgHeight) / 2;
182 Rectangle r = SwingUtilities.computeIntersection(imageX, imageY, imgWidth, imgHeight, clipRect);
183 if (r.x == 0 && r.y == 0 && (r.width == 0 || r.height == 0)) {
184 return;
185 }
186 //I have my new clipping rectangle "r" in clipRect space.
187 //It is therefore the new clipRect.
188 clipRect = r;
189 //since I have the intersection, all I need to do is adjust the
190 //x & y values for the image
191 int txClipX = clipRect.x - imageX;
192 int txClipY = clipRect.y - imageY;
193 int txClipW = clipRect.width;
194 int txClipH = clipRect.height;
195
196 g.drawImage(img, clipRect.x, clipRect.y, clipRect.x + clipRect.width, clipRect.y + clipRect.height,
197 txClipX, txClipY, txClipX + txClipW, txClipY + txClipH, null);
198 break;
199 case TILED:
200 if (img instanceof BufferedImage) {
201 BufferedImage b = (BufferedImage)img;
202 TexturePaint paint = new TexturePaint(b,
203 new Rectangle2D.Double(0, 0, b.getWidth(), b.getHeight()));
204 g.setPaint(paint);
205 g.fillRect(0, 0, component.getWidth(), component.getHeight());
206 } else {
207 //TODO!
208 LOG.fine("unimplemented");
209 }
210 case SCALED:
211 g.drawImage(img, 0, 0, component.getWidth(), component.getHeight(), null);
212 break;
213 case POSITIONED:
214 g.drawImage(img, (int)imagePosition.getX(), (int)imagePosition.getY(),
215 (int)(((double)img.getWidth(null))*imageScale),
216 (int)(((double)img.getHeight(null))*imageScale),
217 null);
218 break;
219 case CSS_POSITIONED:
220 double x = imagePosition.getX() * (component.getWidth()-img.getWidth(null));
221 double y = imagePosition.getY() * (component.getHeight()-img.getHeight(null));
222 g.drawImage(img,(int)x,(int)y,null);
223 break;
224 default:
225 LOG.fine("unimplemented");
226 g.drawImage(img, 0, 0, null);
227 break;
228 }
229 }
230 }
231 }
232
233 private Point2D imagePosition = new Point2D.Double(0,0);
234 public void setImagePosition(Point2D imagePosition) {
235 this.imagePosition = imagePosition;
236 }
237 public Point2D getImagePosition() {
238 return imagePosition;
239 }
240
241 private double imageScale = 1.0;
242 public void setImageScale(double imageScale) {
243 this.imageScale = imageScale;
244 }
245
246 }