static class ScalablePane extends JPanel { private Image master; private boolean toFit; private Image scaled; public ScalablePane(Image master) { this(master, true); } public ScalablePane(Image master, boolean toFit) { this.master = master; setToFit(toFit); } @Override public Dimension getPreferredSize() { return master == null ? super.getPreferredSize() : new Dimension(master.getWidth(this), master.getHeight(this)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Image toDraw = null; if (scaled != null) { toDraw = scaled; } else if (master != null) { toDraw = master; } if (toDraw != null) { int x = (getWidth() - toDraw.getWidth(this)) / 2; int y = (getHeight() - toDraw.getHeight(this)) / 2; g.drawImage(toDraw, x, y, this); } } @Override public void invalidate() { generateScaledInstance(); super.invalidate(); } public boolean isToFit() { return toFit; } public void setToFit(boolean value) { if (value != toFit) { toFit = value; invalidate(); } } protected void generateScaledInstance() { scaled = null; if (isToFit()) { scaled = getScaledInstanceToFit(master, getSize()); } else { scaled = getScaledInstanceToFill(master, getSize()); } } protected BufferedImage toBufferedImage(Image master) { Dimension masterSize = new Dimension(master.getWidth(this), master.getHeight(this)); BufferedImage image = createCompatibleImage(masterSize); Graphics2D g2d = image.createGraphics(); g2d.drawImage(master, 0, 0, this); g2d.dispose(); return image; } public Image getScaledInstanceToFit(Image master, Dimension size) { Dimension masterSize = new Dimension(master.getWidth(this), master.getHeight(this)); return getScaledInstance( toBufferedImage(master), getScaleFactorToFit(masterSize, size), RenderingHints.VALUE_INTERPOLATION_BILINEAR, true); } public Image getScaledInstanceToFill(Image master, Dimension size) { Dimension masterSize = new Dimension(master.getWidth(this), master.getHeight(this)); return getScaledInstance( toBufferedImage(master), getScaleFactorToFill(masterSize, size), RenderingHints.VALUE_INTERPOLATION_BILINEAR, true); } public Dimension getSizeToFit(Dimension original, Dimension toFit) { double factor = getScaleFactorToFit(original, toFit); Dimension size = new Dimension(original); size.width *= factor; size.height *= factor; return size; } public Dimension getSizeToFill(Dimension original, Dimension toFit) { double factor = getScaleFactorToFill(original, toFit); Dimension size = new Dimension(original); size.width *= factor; size.height *= factor; return size; } public double getScaleFactor(int iMasterSize, int iTargetSize) { return (double) iTargetSize / (double) iMasterSize; } public double getScaleFactorToFit(Dimension original, Dimension toFit) { double dScale = 1d; if (original != null && toFit != null) { double dScaleWidth = getScaleFactor(original.width, toFit.width); double dScaleHeight = getScaleFactor(original.height, toFit.height); dScale = Math.min(dScaleHeight, dScaleWidth); } return dScale; } public double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) { double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width); double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height); return Math.max(dScaleHeight, dScaleWidth); } public BufferedImage createCompatibleImage(Dimension size) { return createCompatibleImage(size.width, size.height); } public BufferedImage createCompatibleImage(int width, int height) { GraphicsConfiguration gc = getGraphicsConfiguration(); if (gc == null) { gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } BufferedImage image = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT); image.coerceData(true); return image; } protected BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) { BufferedImage imgScale = img; int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor); int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor); if (dScaleFactor <= 1.0d) { imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality); } else { imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality); } return imgScale; } protected BufferedImage getScaledDownInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint, boolean higherQuality) { int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; BufferedImage _ret = (BufferedImage) img; if (targetHeight > 0 || targetWidth > 0) { int w, h; if (higherQuality) { // Use multi-step technique: start with original size, then // scale down in multiple passes with drawImage() // until the target size is reached w = img.getWidth(); h = img.getHeight(); } else { // Use one-step technique: scale directly from original // size to target size with a single drawImage() call w = targetWidth; h = targetHeight; } do { if (higherQuality && w > targetWidth) { w /= 2; if (w < targetWidth) { w = targetWidth; } } if (higherQuality && h > targetHeight) { h /= 2; if (h < targetHeight) { h = targetHeight; } } BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type); Graphics2D g2 = tmp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); g2.drawImage(_ret, 0, 0, w, h, null); g2.dispose(); _ret = tmp; } while (w != targetWidth || h != targetHeight); } else { _ret = new BufferedImage(1, 1, type); } return _ret; } protected BufferedImage getScaledUpInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint, boolean higherQuality) { int type = BufferedImage.TYPE_INT_ARGB; BufferedImage _ret = (BufferedImage) img; int w, h; if (higherQuality) { // Use multi-step technique: start with original size, then // scale down in multiple passes with drawImage() // until the target size is reached w = img.getWidth(); h = img.getHeight(); } else { // Use one-step technique: scale directly from original // size to target size with a single drawImage() call w = targetWidth; h = targetHeight; } do { if (higherQuality && w < targetWidth) { w *= 2; if (w > targetWidth) { w = targetWidth; } } if (higherQuality && h < targetHeight) { h *= 2; if (h > targetHeight) { h = targetHeight; } } BufferedImage tmp = new BufferedImage(w, h, type); Graphics2D g2 = tmp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); g2.drawImage(_ret, 0, 0, w, h, null); g2.dispose(); _ret = tmp; tmp = null; } while (w != targetWidth || h != targetHeight); return _ret; } }