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 }