
org.monte.media.gui.ImagePanel Maven / Gradle / Ivy
/*
* @(#)ImagePanel.java 1.6 2009-12-25
*
* Copyright (c) 1999-2009 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.gui;
import java.awt.*;
import java.awt.image.*;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
/**
* Displays images on a panel.
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland.
* @version 2009-12-25 Display multi-line messages.
*
2009-12-24 Request focus on mouse click.
*
2006-10-01 Fixed message displaying.
*
2006-07-23 Support for 16:9 anamorph pixel aspect added.
*
1.3.1 2005-01-22 Changing the pixel aspect triggers validation of
* the parent component.
*
1.3 2004-12-25 Access methods for rendering hints added. Method
* setTexture added. Underline suffixes from instance variables removed.
*
1.2 2002-04-02 Workaround for MRJ 1.3.1 Update 1 on Mac OS X.
*
1.1 2000-09-28 Does not update scale factor property during paints anymore.
*
1.0 1999-10-19
*/
public class ImagePanel
extends JComponent {
private final static RenderingHints RENDER_SPEED;
static {
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
rh.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
rh.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
rh.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
rh.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
RENDER_SPEED = rh;
}
/**
* Pixel aspect policy: Ignore pixel aspect.
*/
public final static int IGNORE_PIXEL_ASPECT = 0;
/**
* Pixel aspect policy: Preserves only extreme pixel aspects,
* where one dimension is a multiple of the other dimension.
*/
public final static int ROUNDED_PIXEL_ASPECT = 1;
/**
* Pixel aspect policy: Preserve the exact pixel aspect.
*/
public final static int EXACT_PIXEL_ASPECT = 2;
/**
* Pixel aspect policy: Use Anamoprh 16:9 pixel aspect (=16/9*4/5=1.4222).
*/
public final static int ANAMORPH_PIXEL_ASPECT = 3;
/**
* Image scaling policy: Scale as defined by #setAspectRatio
* and the image aspect.
*/
public final static int SCALE_TO_IMAGE_SIZE = 0;
/**
* Image scaling policy: Scale to panel size.
*/
public final static int SCALE_TO_VIEW_SIZE = 1;
/**
* Image scaling policy: Scale to panel but keep the
* image aspect.
*/
public final static int SCALE_TO_IMAGE_ASPECT = 2;
private Image image;
private double scaleFactor = 1d;
private double aspectRatioX = 1d;
private double aspectRatioY = 1d;
private int imageScalePolicy = SCALE_TO_VIEW_SIZE;
private int pixelAspectPolicy = EXACT_PIXEL_ASPECT;
private String message;
private BufferedImage texture;
/** Support for property change listeners. */
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private RenderingHints renderingHints = RENDER_SPEED;
public ImagePanel() {
super();
}
@Override
public void paintComponent(Graphics gr) {
Graphics2D g = (Graphics2D) gr;
Dimension size = getSize();
Rectangle clipRect = g.getClipBounds();
g.setRenderingHints(renderingHints);
g.setColor(getBackground());
g.fillRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
if (image == null || image.getWidth(this) == -1) {
} else {
final int iw = image.getWidth(this);
final int ih = image.getHeight(this);
double xAspect = getPixelAspectX();
double yAspect = getPixelAspectY();
int w, h, x, y;
switch (imageScalePolicy) {
case SCALE_TO_VIEW_SIZE: {
double factor = Math.min(size.width / (double) iw, size.height / (double) ih);
x = 0;
y = 0;
w = size.width;
h = size.height;
break;
}
case SCALE_TO_IMAGE_ASPECT: {
double factor = Math.min(size.width / (iw * xAspect), size.height / (ih * yAspect));
setScaleFactor(factor, false);
w = (int) Math.ceil(iw * xAspect * factor);
h = (int) Math.ceil(ih * yAspect * factor);
x = (size.width - w) / 2;
y = (size.height - h) / 2;
if (size.width > w) {
g.setColor(getForeground());
g.drawLine(x - 1, 0, x - 1, size.height - 1);
g.drawLine(x + w, 0, x + w, size.height - 1);
// g.setColor(getBackground());
// g.fillRect(0, 0, x-1, size.height);
// g.fillRect(x+w+1, 0, size.width, size.height);
} else if (size.height > h) {
g.setColor(getForeground());
g.drawLine(0, y - 1, size.width - 1, y - 1);
g.drawLine(0, y + h, size.width - 1, y + h);
// g.setColor(getBackground());
// g.fillRect(0, 0, size.width, y-1);
// g.fillRect(0,y+h+1, size.width, size.height);
}
break;
}
case SCALE_TO_IMAGE_SIZE:
default: {
w = (int) (iw * xAspect * getScaleFactor());
h = (int) (ih * yAspect * getScaleFactor());
x = (size.width - w) / 2;
y = (size.height - h) / 2;
g.setColor(getForeground());
g.drawRect(x - 1, y - 1, w + 1, h + 1);
break;
}
}
if (texture != null) {
g.setPaint(
new TexturePaint(
texture,
new Rectangle(x, y, texture.getWidth(), texture.getHeight())));
g.fillRect(x, y, w, h);
g.drawImage(
image,
x, y, w, h,
this);
} else {
g.drawImage(
image,
x, y, w, h,
getBackground(),
this);
}
}
if (message != null) {
int y = g.getFontMetrics().getMaxAscent();
for (String msg : message.split("\n")) {
g.setColor(getBackground());
g.drawString(msg, 1, y + 1);
g.drawString(msg, 1, y - 1);
g.drawString(msg, 0, y);
g.drawString(msg, 2, y);
g.setColor(getForeground());
g.drawString(msg, 1, y);
y += g.getFontMetrics().getMaxAscent();
}
}
}
/*
private void drawCheckerboard(Graphics g, Rectangle r) {
g.setColor(Color.white);
g.fillRect(r.x,r.y,r.width,r.height);
final int squareSize = 8;
g.setColor(Color.lightGray);
// Draw from the left to the right.
int shift = 0;
for(int x = 0; x < r.width; x+=squareSize) {
for(int y = shift*squareSize; y < r.height; y+=squareSize*2) {
g.fillRect(r.x+x,r.y+y,squareSize,squareSize);
}
shift = shift == 0 ? 1 : 0;
}
}
*/
/**
* Sets the pixel aspect policy.
*
* @param policy PixelAspectPolicy must be one of IGNORE_PIXEL_ASPECT,
* ROUNDED_PIXEL_ASPECT, EXACT_PIXEL_ASPECT.
*
* @exception IllegalArgumentException When passing invalid policy.
*/
public synchronized void setPixelAspectPolicy(int policy) {
int old = pixelAspectPolicy;
if (old != policy) {
if (policy != IGNORE_PIXEL_ASPECT
&& policy != ROUNDED_PIXEL_ASPECT
&& policy != EXACT_PIXEL_ASPECT) {
throw new IllegalArgumentException("Invalid policy:" + policy);
}
pixelAspectPolicy = policy;
propertyChangeSupport.firePropertyChange("pixelAspectPolicy", new Integer(old), new Integer(policy));
invalidate();
Component parent = getParent();
if (parent != null) {
parent.validate();
}
repaint();
}
}
/**
* Returns the pixel aspect policy.
*/
public int getPixelAspectPolicy() {
return pixelAspectPolicy;
}
/**
* Sets the image scale policy.
*/
public synchronized void setImageScalePolicy(int policy) {
int old = imageScalePolicy;
if (old != policy) {
if (policy != SCALE_TO_IMAGE_SIZE
&& policy != SCALE_TO_VIEW_SIZE
&& policy != SCALE_TO_IMAGE_ASPECT) {
throw new IllegalArgumentException("Invalid policy:" + policy);
}
imageScalePolicy = policy;
propertyChangeSupport.firePropertyChange("imageScalePolicy", new Integer(old), new Integer(policy));
invalidate();
Component parent = getParent();
if (parent != null) {
parent.validate();
}
repaint();
}
}
/**
* Returns the image scale policy.
*/
public int getImageScalePolicy() {
return imageScalePolicy;
}
/**
* Gets the horizontal pixel aspect of the image according to the
* pixel aspect policy that is in affect.
*
* @return Horizontal pixel aspect.
*/
public double getPixelAspectX() {
if (image == null) {
return 0;
}
Object property = image.getProperty("aspect", this);
if (property == null) {
return 1;
}
double ratio = (property == null || property == Image.UndefinedProperty) ? 1d : ((Double) property).doubleValue();
switch (pixelAspectPolicy) {
case IGNORE_PIXEL_ASPECT:
return 1d;
case EXACT_PIXEL_ASPECT:
return ratio >= 1d ? ratio : 1d;
case ROUNDED_PIXEL_ASPECT:
return ratio >= 1d ? Math.floor(ratio + 0.5d) : 1d;
default:
throw new InternalError("Invalid pixel aspect policy: " + pixelAspectPolicy);
}
}
/**
* Gets the vertical pixel aspect of the image according to the
* pixel aspect policy that is in effect.
*
* @return Vertical pixel aspect.
*/
public double getPixelAspectY() {
if (image == null) {
return 0;
}
Object property = image.getProperty("aspect", this);
if (property == null) {
return 1;
}
double ratio = (property == null || property == Image.UndefinedProperty) ? 1d : ((Double) property).doubleValue();
switch (pixelAspectPolicy) {
case IGNORE_PIXEL_ASPECT:
return 1d;
case EXACT_PIXEL_ASPECT:
return ratio < 1d ? 1d / ratio : 1d;
case ROUNDED_PIXEL_ASPECT:
return ratio < 1d ? Math.floor(1d / ratio + 0.5d) : 1d;
default:
throw new InternalError("Invalid pixel aspect policy: " + pixelAspectPolicy);
}
}
/**
* Gets the preferred image size.
*
* @return Image dimension after applying the
* pixel aspect policy.
*/
public Dimension getPreferredImageSize() {
if (image == null) {
return new Dimension(0, 0);
}
if (image.getWidth(this) == -1) {
try {
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image, 0);
tracker.waitForID(0);
} catch (InterruptedException e) {
}
}
return new Dimension(
(int) Math.ceil(image.getWidth(this) * getPixelAspectX()),
(int) Math.ceil(image.getHeight(this) * getPixelAspectY()));
}
/**
* Gets the scaled and pixel aspect corrected image size.
*
* @return Image dimension after scaling and applying the
* pixel aspect policy.
*/
public Dimension getScaledImageSize() {
if (image == null) {
return new Dimension(0, 0);
}
if (image.getWidth(this) == -1) {
try {
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image, 0);
tracker.waitForID(0);
} catch (InterruptedException e) {
}
}
return new Dimension(
(int) Math.ceil(image.getWidth(this) * getPixelAspectX() * getAspectRatioX() * getScaleFactor()),
(int) Math.ceil(image.getHeight(this) * getPixelAspectY() * getAspectRatioY() * getScaleFactor()));
}
/**
* Sets the image and displays it in this
* image panel.
*/
public void setImage(Image image) {
Image old = this.image;
this.image = image;
invalidate();
Component parent = getParent();
if (parent != null) {
parent.validate();
}
repaint();
propertyChangeSupport.firePropertyChange("image", old, image);
}
/**
* Gets the image that is displayed in this
* image panel.
*
* @return image.
*/
public Image getImage() {
return image;
}
/**
* Sets the texture for the backdrop paint.
*/
public void setTexture(BufferedImage newValue) {
BufferedImage old = this.texture;
this.texture = newValue;
propertyChangeSupport.firePropertyChange("texture", old, newValue);
}
/**
* Gets the backdrop paint.
*/
public BufferedImage getTexture() {
return texture;
}
/**
* Sets the scale factor.
* The scale factor scales images shown in
* this image panel.
*
* @param scaleFactor The scale factor.
*/
public void setScaleFactor(double scaleFactor) {
setScaleFactor(scaleFactor, true);
}
private synchronized void setScaleFactor(double scaleFactor, boolean repaint) {
double old = this.scaleFactor;
this.scaleFactor = scaleFactor;
if (scaleFactor != old) {
propertyChangeSupport.firePropertyChange("scaleFactor", new Double(old), new Double(scaleFactor));
if (repaint) {
Component parent = getParent();
if (parent != null) {
parent.invalidate();
parent.validate();
}
repaint();
}
}
}
/**
* Gets the scale factor.
*
* @return Scale factor.
*/
public synchronized double getScaleFactor() {
return scaleFactor;
}
/**
* Sets the rendering hints.
*
* @param newValue The new rendering hints.
*/
public void setRenderingHints(RenderingHints newValue) {
setRenderingHints(newValue, true);
}
private synchronized void setRenderingHints(RenderingHints newValue, boolean repaint) {
RenderingHints old = this.renderingHints;
this.renderingHints = newValue;
if (newValue != old) {
propertyChangeSupport.firePropertyChange("renderingHints", old, newValue);
if (repaint) {
Component parent = getParent();
if (parent != null) {
parent.invalidate();
parent.validate();
}
repaint();
}
}
}
/**
* Gets the rendering hints.
*
* @return Rendering Hints.
*/
public synchronized RenderingHints getRenderingHints() {
return renderingHints;
}
/**
* Sets the aspect ratio.
* The aspect ratio distorts images shown in this image panel.
*
* @param ratioX Horizontal scale factor.
* @param ratioY Vertical scale factor.
*/
public void setAspectRatio(double ratioX, double ratioY) {
if (aspectRatioX != ratioX || aspectRatioY != ratioY) {
setAspectRatio0(ratioX, ratioY);
Component parent = getParent();
if (parent != null) {
parent.invalidate();
parent.validate();
}
repaint();
}
}
protected void setAspectRatio0(double ratioX, double ratioY) {
double oldX = aspectRatioX;
double oldY = aspectRatioY;
aspectRatioX = ratioX;
aspectRatioY = ratioY;
propertyChangeSupport.firePropertyChange("aspectRatioX", new Double(oldX), new Double(ratioX));
propertyChangeSupport.firePropertyChange("aspectRatioY", new Double(oldY), new Double(ratioY));
}
/**
* Gets the horizontal scale factor that is
* used to scale images shown in this image panel.
*
* @return Horizontal scale factor.
*/
public double getAspectRatioX() {
return aspectRatioX;
}
/**
* Gets the vertical scale factor that is
* used to scale images shown in this image panel.
*
* @return Vertical scale factor.
*/
public double getAspectRatioY() {
return aspectRatioY;
}
/**
* Gets the preferred size of this image panel.
* The preferred size depends on the image size,
* the scale factor and the pixel aspect policy.
*/
@Override
public Dimension getPreferredSize() {
if (image == null) {
return new Dimension(50, 15);
} else {
return getScaledImageSize();
}
}
/**
* Adds a listener who is interested in changes of this object.
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
/**
* Removes a previously registered listener.
*/
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void setMessage(String message) {
String old = this.message;
// if ( ((old == null || message == null) && old != message)
// || (old != null && old.equals(this.message) == false) ) {
this.message = message;
propertyChangeSupport.firePropertyChange("message", old, message);
repaint();
// }
}
/**
* XXX Netscape gets very slow when
* painting all SOMEBITS of an image.
*/
@Override
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
if (flags == SOMEBITS) {
// suppress painting of SOMEBITS.
return true;
} else {
return super.imageUpdate(img, flags, x, y, w, h);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy