gann-cdf /
graphics
| 1 | package org.gannacademy.cdf.graphics; |
||
| 2 | |||
| 3 | import org.gannacademy.cdf.graphics.ui.DrawingPanel; |
||
| 4 | |||
| 5 | import javax.imageio.ImageIO; |
||
| 6 | import java.awt.*; |
||
| 7 | import java.awt.geom.Rectangle2D; |
||
| 8 | import java.awt.image.BufferedImage; |
||
| 9 | import java.io.IOException; |
||
| 10 | |||
| 11 | /** |
||
| 12 | * Render an image file |
||
| 13 | * |
||
| 14 | * @author <a href="https://github.com/gann-cdf/graphics/issues" target="_blank">Seth Battis</a> |
||
| 15 | */ |
||
| 16 | public class Image extends Drawable { |
||
| 17 | private BufferedImage original, active; |
||
| 18 | private String path; |
||
| 19 | private double x, y, width, height; |
||
| 20 | |||
| 21 | /** |
||
| 22 | * <p>Construct a new image object</p> |
||
| 23 | * |
||
| 24 | * <p><img src="doc-files/Image.png" alt="Diagram of Image parameters"></p> |
||
| 25 | * |
||
| 26 | * <p>Initially, width and height of a new image object match the dimensions of the original image.</p> |
||
| 27 | * |
||
| 28 | * <p>Note that image files (as should all files used by your app internally) should be stored in a resources directory in your project. Maven creates this directory by default, but IDEs allow you to mark a subfolder of your project as the resources root. The path to the image file is relative to the root of the resources directory.</p> |
||
| 29 | * |
||
| 30 | * <p>All window coordinates are measured in pixels, with the X-axis increasing from left to right and the Y-axis |
||
| 31 | * increasing from top to bottom. All window coordinates exist in the first quadrant.</p> |
||
| 32 | * |
||
| 33 | * <p><img src="doc-files/window-coordinates.png" alt="Diagram of window coordinates"></p> |
||
| 34 | * |
||
| 35 | * @param path to the image resource |
||
| 36 | * @param x coordinate of image origin |
||
| 37 | * @param y coordinate of image origin |
||
| 38 | * @param drawingPanel on which to draw |
||
| 39 | */ |
||
| 40 | public Image(String path, double x, double y, DrawingPanel drawingPanel) { |
||
| 41 | try { |
||
| 42 | setImage(path); |
||
| 43 | setX(x); |
||
| 44 | setY(y); |
||
| 45 | setDrawingPanel(drawingPanel); |
||
| 46 | } catch (IOException e) { |
||
| 47 | System.err.println(e.getMessage()); |
||
| 48 | e.printStackTrace(); |
||
|
0 ignored issues
–
show
Best Practice
introduced
by
Loading history...
|
|||
| 49 | } |
||
| 50 | } |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Underlying image data |
||
| 54 | * |
||
| 55 | * @return Underlying {@link BufferedImage} data |
||
| 56 | */ |
||
| 57 | public BufferedImage getImage() { |
||
| 58 | return original; |
||
| 59 | } |
||
| 60 | |||
| 61 | /** |
||
| 62 | * <p>Replace the underlying {@link BufferedImage} data</p> |
||
| 63 | * |
||
| 64 | * <p>Replacing the underlying image data leaves other characteristics (fill, stroke, position) unchanged.</p> |
||
| 65 | * |
||
| 66 | * @param path to image resource |
||
| 67 | * @throws IOException if the image resource cannot be accessed |
||
| 68 | */ |
||
| 69 | public void setImage(String path) throws IOException { |
||
| 70 | this.path = path; |
||
| 71 | setImage(ImageIO.read(getClass().getResource(path))); |
||
| 72 | } |
||
| 73 | |||
| 74 | /** |
||
| 75 | * <p>Replace the underlying {@link BufferedImage} data</p> |
||
| 76 | * |
||
| 77 | * <p>Replacing the underlying image data leaves other characteristics (fill, stroke, position) unchanged.</p> |
||
| 78 | * |
||
| 79 | * @param image data |
||
| 80 | */ |
||
| 81 | public void setImage(BufferedImage image) { |
||
| 82 | original = image; |
||
| 83 | |||
| 84 | // only set width and height to match image if loading initial image |
||
| 85 | if (active == null && original != null) { |
||
| 86 | setWidth(original.getWidth()); |
||
| 87 | setHeight(original.getHeight()); |
||
| 88 | active = original; |
||
| 89 | } else { |
||
| 90 | rescaleImage(); |
||
| 91 | } |
||
| 92 | } |
||
| 93 | |||
| 94 | /** |
||
| 95 | * Path to image resource |
||
| 96 | * |
||
| 97 | * @return Path to image resource |
||
| 98 | */ |
||
| 99 | public String getPath() { |
||
| 100 | return path; |
||
| 101 | } |
||
| 102 | |||
| 103 | /** |
||
| 104 | * Rescale the underlying image data to match current width and height field values |
||
| 105 | */ |
||
| 106 | protected void rescaleImage() { |
||
| 107 | if (getWidth() > 0 && getHeight() > 0) { |
||
| 108 | active = new BufferedImage((int) getWidth(), (int) getHeight(), original.getType()); |
||
| 109 | Graphics2D graphics = active.createGraphics(); |
||
| 110 | graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||
| 111 | graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); |
||
| 112 | graphics.drawImage(original, 0, 0, (int) getWidth(), (int) getHeight(), null); |
||
| 113 | graphics.dispose(); |
||
| 114 | } |
||
| 115 | } |
||
| 116 | |||
| 117 | @Override |
||
| 118 | public Shape getShape() { |
||
| 119 | return new Rectangle2D.Double(x, y, width, height); |
||
| 120 | } |
||
| 121 | |||
| 122 | @Override |
||
| 123 | public double getX() { |
||
| 124 | return x; |
||
| 125 | } |
||
| 126 | |||
| 127 | @Override |
||
| 128 | public void setX(double x) { |
||
| 129 | this.x = x; |
||
| 130 | } |
||
| 131 | |||
| 132 | @Override |
||
| 133 | public double getY() { |
||
| 134 | return y; |
||
| 135 | } |
||
| 136 | |||
| 137 | @Override |
||
| 138 | public void setY(double y) { |
||
| 139 | this.y = y; |
||
| 140 | } |
||
| 141 | |||
| 142 | @Override |
||
| 143 | public double getWidth() { |
||
| 144 | return width; |
||
| 145 | } |
||
| 146 | |||
| 147 | public void setWidth(double width) { |
||
| 148 | this.width = width; |
||
| 149 | rescaleImage(); |
||
| 150 | } |
||
| 151 | |||
| 152 | @Override |
||
| 153 | public double getHeight() { |
||
| 154 | return height; |
||
| 155 | } |
||
| 156 | |||
| 157 | public void setHeight(double height) { |
||
| 158 | this.height = height; |
||
| 159 | rescaleImage(); |
||
| 160 | } |
||
| 161 | |||
| 162 | @Override |
||
| 163 | public void translate(double dx, double dy) { |
||
| 164 | setX(x + dx); |
||
| 165 | setY(y + dy); |
||
| 166 | } |
||
| 167 | |||
| 168 | @Override |
||
| 169 | public void setLocation(double x, double y) { |
||
| 170 | setX(x); |
||
| 171 | setY(y); |
||
| 172 | } |
||
| 173 | |||
| 174 | @Override |
||
| 175 | public void draw(Graphics2D graphics2D) { |
||
| 176 | if (getFillColor() != TRANSPARENT) { |
||
| 177 | graphics2D.setPaint(getFillColor()); |
||
| 178 | graphics2D.fill(getShape()); |
||
| 179 | } |
||
| 180 | graphics2D.drawImage(active, (int) x, (int) y, null); |
||
| 181 | if (getStroke() != NO_STROKE && getStrokeColor() != TRANSPARENT) { |
||
| 182 | graphics2D.setStroke(getStroke()); |
||
| 183 | graphics2D.setPaint(getStrokeColor()); |
||
| 184 | graphics2D.draw(getShape()); |
||
| 185 | } |
||
| 186 | } |
||
| 187 | } |
||
| 188 |