001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------------- 028 * ImageTitle.java 029 * --------------- 030 * (C) Copyright 2000-2021, by David Berry and Contributors; 031 * 032 * Original Author: David Berry; 033 * Contributor(s): David Gilbert; 034 * 035 */ 036 037package org.jfree.chart.title; 038 039import java.awt.Graphics2D; 040import java.awt.Image; 041import java.awt.geom.Rectangle2D; 042import java.util.Objects; 043 044import org.jfree.chart.block.RectangleConstraint; 045import org.jfree.chart.event.TitleChangeEvent; 046import org.jfree.chart.api.HorizontalAlignment; 047import org.jfree.chart.api.RectangleEdge; 048import org.jfree.chart.api.RectangleInsets; 049import org.jfree.chart.block.Size2D; 050import org.jfree.chart.api.VerticalAlignment; 051 052/** 053 * A chart title that displays an image. This is useful, for example, if you 054 * have an image of your corporate logo and want to use as a footnote or part 055 * of a title in a chart you create. 056 * <P> 057 * ImageTitle needs an image passed to it in the constructor. For ImageTitle 058 * to work, you must have already loaded this image from its source (disk or 059 * URL). It is recommended you use something like 060 * Toolkit.getDefaultToolkit().getImage() to get the image. Then, use 061 * MediaTracker or some other message to make sure the image is fully loaded 062 * from disk. 063 * <P> 064 * SPECIAL NOTE: this class fails to serialize, so if you are 065 * relying on your charts to be serializable, please avoid using this class. 066 */ 067public class ImageTitle extends Title { 068 069 /** The title image. */ 070 private Image image; 071 072 /** 073 * Creates a new image title. 074 * 075 * @param image the image ({@code null} not permitted). 076 */ 077 public ImageTitle(Image image) { 078 this(image, image.getHeight(null), image.getWidth(null), 079 Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, 080 Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); 081 } 082 083 /** 084 * Creates a new image title. 085 * 086 * @param image the image ({@code null} not permitted). 087 * @param position the title position. 088 * @param horizontalAlignment the horizontal alignment. 089 * @param verticalAlignment the vertical alignment. 090 */ 091 public ImageTitle(Image image, RectangleEdge position, 092 HorizontalAlignment horizontalAlignment, 093 VerticalAlignment verticalAlignment) { 094 095 this(image, image.getHeight(null), image.getWidth(null), 096 position, horizontalAlignment, verticalAlignment, 097 Title.DEFAULT_PADDING); 098 } 099 100 /** 101 * Creates a new image title with the given image scaled to the given 102 * width and height in the given location. 103 * 104 * @param image the image ({@code null} not permitted). 105 * @param height the height used to draw the image. 106 * @param width the width used to draw the image. 107 * @param position the title position. 108 * @param horizontalAlignment the horizontal alignment. 109 * @param verticalAlignment the vertical alignment. 110 * @param padding the amount of space to leave around the outside of the 111 * title. 112 */ 113 public ImageTitle(Image image, int height, int width, 114 RectangleEdge position, 115 HorizontalAlignment horizontalAlignment, 116 VerticalAlignment verticalAlignment, 117 RectangleInsets padding) { 118 119 super(position, horizontalAlignment, verticalAlignment, padding); 120 if (image == null) { 121 throw new NullPointerException("Null 'image' argument."); 122 } 123 this.image = image; 124 setHeight(height); 125 setWidth(width); 126 127 } 128 129 /** 130 * Returns the image for the title. 131 * 132 * @return The image for the title (never {@code null}). 133 */ 134 public Image getImage() { 135 return this.image; 136 } 137 138 /** 139 * Sets the image for the title and notifies registered listeners that the 140 * title has been modified. 141 * 142 * @param image the new image ({@code null} not permitted). 143 */ 144 public void setImage(Image image) { 145 if (image == null) { 146 throw new NullPointerException("Null 'image' argument."); 147 } 148 this.image = image; 149 notifyListeners(new TitleChangeEvent(this)); 150 } 151 152 /** 153 * Arranges the contents of the block, within the given constraints, and 154 * returns the block size. 155 * 156 * @param g2 the graphics device. 157 * @param constraint the constraint ({@code null} not permitted). 158 * 159 * @return The block size (in Java2D units, never {@code null}). 160 */ 161 @Override 162 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 163 Size2D s = new Size2D(this.image.getWidth(null), 164 this.image.getHeight(null)); 165 return new Size2D(calculateTotalWidth(s.getWidth()), 166 calculateTotalHeight(s.getHeight())); 167 } 168 169 /** 170 * Draws the title on a Java 2D graphics device (such as the screen or a 171 * printer). 172 * 173 * @param g2 the graphics device. 174 * @param area the area allocated for the title. 175 */ 176 @Override 177 public void draw(Graphics2D g2, Rectangle2D area) { 178 RectangleEdge position = getPosition(); 179 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { 180 drawHorizontal(g2, area); 181 } 182 else if (position == RectangleEdge.LEFT 183 || position == RectangleEdge.RIGHT) { 184 drawVertical(g2, area); 185 } 186 else { 187 throw new RuntimeException("Invalid title position."); 188 } 189 } 190 191 /** 192 * Draws the title on a Java 2D graphics device (such as the screen or a 193 * printer). 194 * 195 * @param g2 the graphics device. 196 * @param chartArea the area within which the title (and plot) should be 197 * drawn. 198 * 199 * @return The size of the area used by the title. 200 */ 201 protected Size2D drawHorizontal(Graphics2D g2, Rectangle2D chartArea) { 202 double startY; 203 double topSpace; 204 double bottomSpace; 205 double leftSpace; 206 double rightSpace; 207 208 double w = getWidth(); 209 double h = getHeight(); 210 RectangleInsets padding = getPadding(); 211 topSpace = padding.calculateTopOutset(h); 212 bottomSpace = padding.calculateBottomOutset(h); 213 leftSpace = padding.calculateLeftOutset(w); 214 rightSpace = padding.calculateRightOutset(w); 215 216 if (getPosition() == RectangleEdge.TOP) { 217 startY = chartArea.getY() + topSpace; 218 } 219 else { 220 startY = chartArea.getY() + chartArea.getHeight() - bottomSpace - h; 221 } 222 223 // what is our alignment? 224 double startX = 0.0; 225 switch (getHorizontalAlignment()) { 226 case CENTER: 227 startX = chartArea.getX() + leftSpace + chartArea.getWidth() / 2.0 228 - w / 2.0; 229 break; 230 case LEFT: 231 startX = chartArea.getX() + leftSpace; 232 break; 233 case RIGHT: 234 startX = chartArea.getX() + chartArea.getWidth() - rightSpace - w; 235 break; 236 default: 237 throw new IllegalStateException("Unexpected horizontal alignment."); 238 } 239 g2.drawImage(this.image, (int) startX, (int) startY, (int) w, (int) h, 240 null); 241 242 return new Size2D(chartArea.getWidth() + leftSpace + rightSpace, 243 h + topSpace + bottomSpace); 244 245 } 246 247 /** 248 * Draws the title on a Java 2D graphics device (such as the screen or a 249 * printer). 250 * 251 * @param g2 the graphics device. 252 * @param chartArea the area within which the title (and plot) should be 253 * drawn. 254 * 255 * @return The size of the area used by the title. 256 */ 257 protected Size2D drawVertical(Graphics2D g2, Rectangle2D chartArea) { 258 259 double startX; 260 double topSpace = 0.0; 261 double bottomSpace = 0.0; 262 double leftSpace = 0.0; 263 double rightSpace = 0.0; 264 265 double w = getWidth(); 266 double h = getHeight(); 267 268 RectangleInsets padding = getPadding(); 269 if (padding != null) { 270 topSpace = padding.calculateTopOutset(h); 271 bottomSpace = padding.calculateBottomOutset(h); 272 leftSpace = padding.calculateLeftOutset(w); 273 rightSpace = padding.calculateRightOutset(w); 274 } 275 276 if (getPosition() == RectangleEdge.LEFT) { 277 startX = chartArea.getX() + leftSpace; 278 } 279 else { 280 startX = chartArea.getMaxX() - rightSpace - w; 281 } 282 283 // what is our alignment? 284 double startY = 0.0; 285 switch (getVerticalAlignment()) { 286 case CENTER: 287 startY = chartArea.getMinY() + topSpace 288 + chartArea.getHeight() / 2.0 - h / 2.0; 289 break; 290 case TOP: 291 startY = chartArea.getMinY() + topSpace; 292 break; 293 case BOTTOM: 294 startY = chartArea.getMaxY() - bottomSpace - h; 295 break; 296 default: 297 throw new IllegalStateException("Unexpected vertical alignment."); 298 } 299 300 g2.drawImage(this.image, (int) startX, (int) startY, (int) w, (int) h, 301 null); 302 303 return new Size2D(chartArea.getWidth() + leftSpace + rightSpace, 304 h + topSpace + bottomSpace); 305 306 } 307 308 /** 309 * Draws the block within the specified area. 310 * 311 * @param g2 the graphics device. 312 * @param area the area. 313 * @param params ignored ({@code null} permitted). 314 * 315 * @return Always {@code null}. 316 */ 317 @Override 318 public Object draw(Graphics2D g2, Rectangle2D area, Object params) { 319 draw(g2, area); 320 return null; 321 } 322 323 /** 324 * Tests this {@code ImageTitle} for equality with an arbitrary 325 * object. Returns {@code true} if: 326 * <ul> 327 * <li>{@code obj} is an instance of {@code ImageTitle}; 328 * <li>{@code obj} references the same image as this 329 * {@code ImageTitle}; 330 * <li>{@code super.equals(obj)} returns {@code true}; 331 * </ul> 332 * 333 * @param obj the object ({@code null} permitted). 334 * 335 * @return A boolean. 336 */ 337 @Override 338 public boolean equals(Object obj) { 339 if (obj == this) { 340 return true; 341 } 342 if (!(obj instanceof ImageTitle)) { 343 return false; 344 } 345 ImageTitle that = (ImageTitle) obj; 346 if (!Objects.equals(this.image, that.image)) { 347 return false; 348 } 349 return super.equals(obj); 350 } 351 352 /** 353 * Returns a hash code for this instance. 354 * 355 * @return A has code. 356 */ 357 @Override 358 public int hashCode() { 359 int hash = super.hashCode(); 360 hash = 83 * hash + Objects.hashCode(this.image); 361 return hash; 362 } 363 364}