How to resize an image with JLabel resizing in an internal frame?
I tried some code that I found on the Internet like:
public static BufferedImage resize(Image image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
or
getScaledInstance(l.getWidth(), -1, Image.SCALE_SMOOTH);
the code's result
But it only makes the image smaller.
Is there a way to resize the image with the JLabel and JInternalFrame? Any help is welcome :)
Scaling an image, well, is not simple. Take a look at:
Java: maintaining aspect ratio of JPanel background image
Scale the ImageIcon automatically to label size
Quality of Image after resize very low -- Java
Scaling can also be expensive, so you'd probably want someway to reduce the number of attempts (as a window been re-resized will be bombarded with a large number of sizing events).
The overall method is the same regardless if your using a JFrame, JPanel or JInternalFrame.
You need some way to monitor for the size changes and some way to determine when it might be a suitable time to scale the image. The following example make use of a ComponentListener and non-repeating Swing Timer. This allows us the ability to schedule a task to be executed in the future, but which can be cancelled if we need to.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
BufferedImage image = ImageIO.read(getClass().getResource("/images/happy.png"));
JInternalFrame internalFrame = new JInternalFrame("Test", true, true, true, true);
internalFrame.add(new ImagePane(image));
internalFrame.pack();
internalFrame.setVisible(true);
JDesktopPane desktopPane = new JDesktopPane();
desktopPane.add(internalFrame);
JFrame frame = new JFrame();
frame.add(desktopPane);
frame.setSize(new Dimension(800, 800));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class ImagePane extends JPanel {
private Timer resizeTimer;
private BufferedImage masterImage;
private JLabel label;
private Scaler scaler;
public ImagePane(BufferedImage masterImage) {
this.masterImage = masterImage;
label = new JLabel(new ImageIcon(masterImage));
scaler = new Scaler(masterImage);
resizeTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(new ImageIcon(scaler.getScaledInstanceToFill(getSize())));
}
});
resizeTimer.setRepeats(false);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
resizeTimer.restart();
}
});
setLayout(new BorderLayout());
add(label);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class Scaler {
private BufferedImage master;
private Dimension masterSize;
private Map<RenderingHints.Key, Object> renderingHints = new HashMap<>();
public Scaler(BufferedImage master) {
this.master = master;
masterSize = new Dimension(master.getWidth(), master.getHeight());
renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
renderingHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
renderingHints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
renderingHints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
renderingHints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
public BufferedImage getScaledInstanceToFit(Dimension size) {
return getScaledInstance(getScaleFactorToFit(size));
}
public BufferedImage getScaledInstanceToFill(Dimension size) {
return getScaledInstance(getScaleFactorToFill(size));
}
protected double getScaleFactor(int masterSize, int targetSize) {
return (double) targetSize / (double) masterSize;
}
protected double getScaleFactorToFit(Dimension toFit) {
double dScaleWidth = getScaleFactor(masterSize.width, toFit.width);
double dScaleHeight = getScaleFactor(masterSize.height, toFit.height);
return Math.min(dScaleHeight, dScaleWidth);
}
protected double getScaleFactorToFill(Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
return Math.max(dScaleHeight, dScaleWidth);
}
protected BufferedImage getScaledInstance(double dScaleFactor) {
BufferedImage imgScale = master;
int targetWidth = (int) Math.round(masterSize.getWidth() * dScaleFactor);
int targetHeight = (int) Math.round(masterSize.getHeight() * dScaleFactor);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(targetWidth, targetHeight);
} else {
imgScale = getScaledUpInstance(targetWidth, targetHeight);
}
return imgScale;
}
protected BufferedImage getScaledDownInstance(
int targetWidth,
int targetHeight) {
int type = (master.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage scaled = master;
if (targetHeight > 0 || targetWidth > 0) {
int width = master.getWidth();
int height = master.getHeight();
do {
if (width > targetWidth) {
width /= 2;
if (width < targetWidth) {
width = targetWidth;
}
}
if (height > targetHeight) {
height /= 2;
if (height < targetHeight) {
height = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(width, 1), Math.max(height, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHints(renderingHints);
g2.drawImage(scaled, 0, 0, width, height, null);
g2.dispose();
scaled = tmp;
} while (width != targetWidth || height != targetHeight);
} else {
scaled = new BufferedImage(1, 1, type);
}
return scaled;
}
protected BufferedImage getScaledUpInstance(
int targetWidth,
int targetHeight) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) master;
int width = master.getWidth();
int height = master.getHeight();
do {
if (width < targetWidth) {
width *= 2;
if (width > targetWidth) {
width = targetWidth;
}
}
if (height < targetHeight) {
height *= 2;
if (height > targetHeight) {
height = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(width, height, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHints(renderingHints);
g2.drawImage(ret, 0, 0, width, height, null);
g2.dispose();
ret = tmp;
} while (width != targetWidth || height != targetHeight);
return ret;
}
}
}
Also, JLabel is actually irrelevant to your problem and I'd probably use a custom painting workflow to render the scaled image, but that's me.
Related
Hi guys I have a problem with Images drawn in a JPanel.There is no problem when the images are drawn to the panel. The Problemm occurn when I scroll up or down the image. The part of the image that was not seen in the viewport is painted white when return to that part again.
I added a BufferedImage in a JPanel. I can also resize the image using AffineTransform. The problem is when I added a JScollPane to my image, whenever I scroll up or down the scrollpane some part of the image disappears ..
I also tried using drawRenderedImage it did solve the problem about the scroll but it messed up other functions.
this is the best concise code I can make;
package convert;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class ZoomTest {
public static void main(String[] args) {
ImagePanel panel = new ImagePanel();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(panel));
f.setSize(1200, 1200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ImagePanel extends JPanel {
BufferedImage image;
double scale;
public ImagePanel() {
loadImage();
scale = .38;
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int rotation = e.getWheelRotation();
if (rotation < 0) {
scale -= .05;
} else {
scale += .05;
}
if (scale < 0) {
scale = 0;
}
revalidate();
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
double x = (getWidth() - scale * image.getWidth()) / 2;
double y = (getHeight() - scale * image.getHeight()) / 2;
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.scale(scale, scale);
g2.setTransform(at);
g2.drawImage(image, 0, 0, null);
}
public Dimension getPreferredSize() {
int w = (int) (scale * image.getWidth());
int h = (int) (scale * image.getHeight());
return new Dimension(w, h);
}
private void loadImage() {
String fileName = "c:\\users\\john ebarita\\downloads\\lorem-ipsum-1.jpg";
try {
image = ImageIO.read(new File(fileName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Take a look at Graphics2D#drawImage(Image, AffineTransform, ImageObserver). May be it helps.
package convert;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class ZoomTest2 {
public static void main(String[] args) {
ImagePanel panel = new ImagePanel();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(panel));
f.setSize(1200, 1200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ImagePanel extends JPanel {
BufferedImage image;
double scale;
public ImagePanel() {
loadImage();
scale = .38;
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int rotation = e.getWheelRotation();
if (rotation < 0) {
scale -= .05;
} else {
scale += .05;
}
if (scale < 0) {
scale = 0;
}
revalidate();
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
double x = (getWidth() - scale * image.getWidth()) / 2;
double y = (getHeight() - scale * image.getHeight()) / 2;
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.scale(scale, scale);
// g2.setTransform(at);
// g2.drawImage(image, 0, 0, this);
g2.drawImage(image, at, this);
// or:
// AffineTransform atf = g2.getTransform();
// atf.concatenate(at);
// g2.setTransform(atf);
// g2.drawImage(image, 0, 0, this);
g2.dispose();
}
public Dimension getPreferredSize() {
int w = (int)(scale * image.getWidth());
int h = (int)(scale * image.getHeight());
return new Dimension(w, h);
}
private void loadImage() {
String fileName = "aaa.png";
try {
image = ImageIO.read(new File(fileName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Here is solution for your problem. I had also a bad luck with custom painting, so my solution is directly update the image on mouse wheel and set it to a label.
import java.awt.Image;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
public class ZoomTest {
public static void main(String[] args) {
ImagePanel panel = new ImagePanel();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(panel));
f.setSize(1000, 1000);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ImagePanel extends JLabel {
BufferedImage image;
double scale;
public ImagePanel() {
setHorizontalAlignment(SwingConstants.CENTER);
loadImage();
scale = .38;
updateImage();
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int rotation = e.getWheelRotation();
if (rotation < 0) {
scale -= .05;
} else {
scale += .05;
}
if (scale < 0) {
scale = 0;
}
updateImage();
revalidate();
repaint();
}
});
}
private void updateImage() {
int w = (int) (scale * image.getWidth());
int h = (int) (scale * image.getHeight());
setIcon(new ImageIcon(image.getScaledInstance(w, h, Image.SCALE_SMOOTH)));
}
private void loadImage() {
String fileName = "c:\\users\\john ebarita\\downloads\\lorem-ipsum-1.jpg";
try {
image = ImageIO.read(new File(fileName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
In Java swing, is there any way to draw only the shape of an image, instead of the image itself? And if so, how can I do it?
I added a picture to show you what I mean:
.
My image would consist of a one-colored shape and a transparent background. My intention is to be able to change the images color.
ImageFilter is what you sought. GrayFilter could be used.
class ShapeFilter extends RGBImageFilter {
public RedBlueSwapFilter() {
// The filter's operation does not depend on the
// pixel's location, so IndexColorModels can be
// filtered directly.
canFilterIndexColorModel = true;
}
public int filterRGB(int x, int y, int rgb) {
return (rgb & 0x00_ff_ff_ff) == 0x00_ff_ff_ff // White
|| rgb & 0xff_00_00_00) == 0x00_00_00) // or transparent
? rgb : 0xff_ff_00_00; // Red opaque
}
}
Usage inside a Component (JComponent) with createImage:
Image img = ImageIO.readImage(...);
ImageFilter filter = new ShapeFilter();
ImageProducer producer = new FilteredImageSource(img.getSource(), filter);
Image img2 = createImage(producer);
You could generate a "mask" of the image
The great feature of this technique is that you can generate the mask in what ever color or opacity you want
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
public static void applyQualityRenderingHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
public static BufferedImage generateMask(BufferedImage imgSource, Color color, float alpha) {
int imgWidth = imgSource.getWidth();
int imgHeight = imgSource.getHeight();
BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
Graphics2D g2 = imgMask.createGraphics();
applyQualityRenderingHints(g2);
g2.drawImage(imgSource, 0, 0, null);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
g2.setColor(color);
g2.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
g2.dispose();
return imgMask;
}
public BufferedImage tint(BufferedImage master, BufferedImage tint) {
int imgWidth = master.getWidth();
int imgHeight = master.getHeight();
BufferedImage tinted = createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
Graphics2D g2 = tinted.createGraphics();
applyQualityRenderingHints(g2);
g2.drawImage(master, 0, 0, null);
g2.drawImage(tint, 0, 0, null);
g2.dispose();
return tinted;
}
public class TestPane extends JPanel {
private BufferedImage master;
private BufferedImage mask;
public TestPane() {
try {
master = ImageIO.read(new File(bring your own image));
mask = generateMask(master, Color.RED, 1.0f);
} catch (IOException exp) {
exp.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (master != null && mask != null) {
size = new Dimension(master.getWidth() + mask.getWidth(), Math.max(master.getHeight(), mask.getHeight()));
}
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (getWidth() - (master.getWidth() + mask.getWidth())) / 2;
int y = (getHeight() - master.getHeight()) / 2;
g.drawImage(master, x, y, this);
x += mask.getWidth();
y = (getHeight() - mask.getHeight()) / 2;
g.drawImage(mask, x, y, this);
}
}
}
I want to adjust the size of logo2.png so that it completely fits to the jFrame. Can anyone help me out?
jLabel2 = new javax.swing.JLabel();
jLabel2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Banking/logo2.png"))); // NOI18N
JDesktopPane.add(jLabel2);
jLabel2.setBounds(0, 0, 500, 290);
JLabel doesn't rescale it's content (ie the image), for that, you're going to have to get you hands a little dirty...
There are a number of ways to achieve this. Personally, I'd start with a custom component, which extends from something like JPanel which takes a base image and is capable of scaling and painting it.
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details about performing custom painting.
Scaling an image is not as straightforward as it might seem, while Java does provide some APIs to scale images, generally, they don't generate fantastic results. Take a look at Quality of Image after resize very low -- Java and The Perils of Image.getScaledInstance() for more details.
I would normally recommend taking a look at the imgscalr library, as it's results are generally quite good (better then what the base Java API provides or I've seen through most other methods), but for this example, I've included an example of a divide an conqure approach.
This example provides a "scale to fill" implementation, this ensures that the resulting image ALWAYS fills the available space while maintaining it's aspect ratio, see Java: maintaining aspect ratio of JPanel background image for more discussions on the subject
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScaledBackground {
public static void main(String[] args) {
new ScaledBackground();
}
public ScaledBackground() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
BufferedImage img = ImageIO.read(...);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ImagePane(img));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public class ImagePane extends JPanel {
private BufferedImage original;
private BufferedImage scaled;
public ImagePane(BufferedImage img) {
original = img;
scaled = original;
}
#Override
public Dimension getPreferredSize() {
return original == null ? new Dimension(200, 200) : new Dimension(original.getWidth(), original.getHeight());
}
#Override
public void invalidate() {
super.invalidate();
generateScaledInstance();
}
protected void generateScaledInstance() {
if (original != null) {
scaled = getScaledInstanceToFill(original, getSize());
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (scaled != null) {
int x = (getWidth() - scaled.getWidth()) / 2;
int y = (getHeight() - scaled.getHeight()) / 2;
g2d.drawImage(scaled, x, y, this);
}
g2d.dispose();
}
public BufferedImage getScaledInstanceToFill(BufferedImage img, Dimension size) {
float scaleFactor = getScaleFactorToFill(img, size);
return getScaledInstance(img, scaleFactor);
}
public float getScaleFactorToFill(BufferedImage img, Dimension size) {
float scale = 1f;
if (img != null) {
int imageWidth = img.getWidth();
int imageHeight = img.getHeight();
scale = getScaleFactorToFill(new Dimension(imageWidth, imageHeight), size);
}
return scale;
}
public float getScaleFactorToFill(Dimension original, Dimension toFit) {
float scale = 1f;
if (original != null && toFit != null) {
float dScaleWidth = getScaleFactor(original.width, toFit.width);
float dScaleHeight = getScaleFactor(original.height, toFit.height);
scale = Math.max(dScaleHeight, dScaleWidth);
}
return scale;
}
public float getScaleFactor(int iMasterSize, int iTargetSize) {
float scale = 1;
if (iMasterSize > iTargetSize) {
scale = (float) iTargetSize / (float) iMasterSize;
} else {
scale = (float) iTargetSize / (float) iMasterSize;
}
return scale;
}
public BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
BufferedImage imgBuffer = null;
imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
return imgBuffer;
}
protected BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean higherQuality) {
BufferedImage scaled = img;
if (dScaleFactor != 1.0) {
if (dScaleFactor > 1.0) {
scaled = getScaledUpInstance(img, dScaleFactor, hint, higherQuality);
} else if (dScaleFactor > 0.0) {
scaled = getScaledDownInstance(img, dScaleFactor, hint, higherQuality);
}
}
return scaled;
}
protected BufferedImage getScaledDownInstance(BufferedImage img, double dScaleFactor, Object hint, boolean higherQuality) {
int targetWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int targetHeight = (int) Math.round(img.getHeight() * dScaleFactor);
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) {
w = img.getWidth();
h = img.getHeight();
} else {
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,
double dScaleFactor,
Object hint,
boolean higherQuality) {
int targetWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int targetHeight = (int) Math.round(img.getHeight() * dScaleFactor);
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
w = img.getWidth();
h = img.getHeight();
} else {
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;
}
}
}
This example will grow and shrink as the image as the amount of available space for the panel changes
You can use the Stretch Icon on your JLabel.
Or you could use the Background Panel to draw the image.
In both cases the Image will shrink or grow as the frame is resized.
First get the Image, then use this method:
yourImage.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);
You can get the width and height from the JFrame. Then you can convert the Image to ImageIcon and add it.
package testIDE;
import java.awt.BorderLayout;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import Utils.MyJFrame;
public class ExampleClass {
public static void main(String[] args) {
JFrame ballRotate = new BallRotate();
}
}
class BallRotate extends MyJFrame {
ArrayList<Integer> degree = new ArrayList<Integer>();
BufferedImage backGroundImage = getBufferedImage("testIDE/buttonDefaultImage.jpg");
JLabel backGroundLabel = new JLabel(new ImageIcon(backGroundImage));
BufferedImage footballImage = getBufferedImage("testIDE/Tennis_Ball.png");
int x = 0;
public BallRotate() {
footballImage=getScaledImage(250, 250, footballImage);
BufferedImage rotatedImage = footballImage;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridBagLayout());
setLabel();
add(backGroundLabel);
pack();
centeringWindow();
setVisible(true);
setArray();
while (true) {
setDelay(60);
rotatedImage = rotateImage(rotatedImage, x);
setMyFuckingLabel(rotatedImage);
x += 10;
if (x == 10000) {
break;
}
}
}
private void setArray() {
for (int i = 0; i <= 360; i += 40) {
degree.add(i);
}
}
private void setLabel() {
JPanel footBallPanel = new JPanel(new BorderLayout());
JLabel footBallLabel = new JLabel(new ImageIcon(footballImage));
footBallPanel.add(footBallLabel);
borderingJPanel(footBallPanel, null, null);
backGroundLabel.setLayout(new GridBagLayout());
backGroundLabel.add(footBallPanel);
}
private BufferedImage rotateImage(BufferedImage buffImage, int degree) {
BufferedImage rotatedImage = null;
AffineTransform affineTransform = AffineTransform.getRotateInstance(
Math.toRadians(15*Math.PI), buffImage.getWidth() / 2,
buffImage.getHeight() / 2);
System.out.println(degree*Math.toRadians(1));
rotatedImage = new BufferedImage(buffImage.getWidth(),
buffImage.getHeight(), buffImage.getType());
Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
g.setTransform(affineTransform);
g.drawImage(buffImage, 0, 0, null);
return rotatedImage;
}
public void setMyLabel(BufferedImage rotatedBuffImage) {
JLabel backgroundlabel = (JLabel) getContentPane().getComponent(0);
JPanel footBallPanel = (JPanel) backgroundlabel.getComponent(0);
JLabel footBallLabel = (JLabel) footBallPanel.getComponent(0);
footBallLabel.setIcon(new ImageIcon(rotatedBuffImage));
}
}
As you can see, my rotating tennis ball looses his form and his colouring. It seems to be that the colours are rotating also.
Why? And is there a way to prevent this? Ive posted the code which generated the dialog above.
Thanks for any help.
So a raft of issues...
Scaling an image in a single step is never a good idea (unless you only scaling by 50%). Java also isn't particular good at it. There are tricks you can employee, like using a multi-step scale (demonstrated within the example) or use an external library like imgscalr. See The Perils of Image.getScaledInstance() for more details.
You should avoid apply effects to the same image multiply times, this simply compounds the changes and will reduce the quality of the image. Instead, maintain a master image as close to the original as you can and use it, so you're always starting from the same starting point.
Swing is not thread safe. This means three things. First, you should never do anything within the context of the Event Dispatching Thread that might block it, like infinite loops. Second, you should only ever change the state of UI components from within the context of the EDT and third, you should make sure you are creating your UIs from within the context of the EDT. See Concurrency in Swing and Initial Threads for more details...
Any thing other then a straight horizontal or vertical line is going to look pretty...ordinary under the default rendering settings. You're going to need to supply some RenderingHints to enhance the result
This then raises the question of how do you do animation in Swing? Well, you have two basic options, you can use a Thread of some kind, which requires you to manually synchronise updates back to the EDT or you can use a Swing javax.swing.Timer, which allows you to schedule call backs at regular intervals which are triggered within the context of the EDT. Take a look at How to use Swing Timers for more details...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BallRotate {
public static void main(String[] args) {
new BallRotate();
}
public BallRotate() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
private JLabel ball;
private BufferedImage rotatedImage;
private float angle = 0;
private float delta = 5;
public TestPane() {
setLayout(new GridBagLayout());
try {
master = ImageIO.read(getClass().getResource("/Ball.png"));
master = getScaledInstanceToFit(master, new Dimension(250, 250));
} catch (IOException ex) {
ex.printStackTrace();
}
ImageIcon icon = new ImageIcon(getRotatedImage(0));
ball = new JLabel(icon);
add(ball);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle += delta;
// ball.setIcon(new ImageIcon(getRotatedImage(delta)));
getRotatedImage(angle);
ball.repaint();
System.out.println(angle);
}
});
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
timer.start();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected BufferedImage getRotatedImage(float degree) {
if (rotatedImage == null) {
rotatedImage = new BufferedImage(master.getWidth(),
master.getHeight(), BufferedImage.TYPE_INT_ARGB);
}
AffineTransform affineTransform = AffineTransform.getRotateInstance(
Math.toRadians(degree), rotatedImage.getWidth() / 2,
rotatedImage.getHeight() / 2);
Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
g.setBackground(new Color(255, 255, 255, 0));
g.clearRect(0, 0, rotatedImage.getWidth(), rotatedImage.getHeight());
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g.setTransform(affineTransform);
g.drawImage(master, 0, 0, null);
g.dispose();
return rotatedImage;
}
}
public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
}
protected static 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);
// System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
if (dScaleFactor < 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
}
return imgScale;
}
protected static 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;
}
}
// if (w <= 0) w = 1;
// if (h <= 0) h = 1;
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;
}
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
public static 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 static double getScaleFactorToFit(BufferedImage img, Dimension size) {
double dScale = 1;
if (img != null) {
int imageWidth = img.getWidth();
int imageHeight = img.getHeight();
dScale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size);
}
return dScale;
}
public static BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {
double scaleFactor = getScaleFactorToFit(img, size);
return getScaledInstance(img, scaleFactor);
}
}
I'm making a fun little test screen recording program in java, and I want it to have a preview of your screen before you start recording.. but its a very slow and poor method of which I am using, involving capturing an image, saving it, then reading it in through a bufferedimage and drawing that image using Graphics. Its very slow and not useful as a "preview" is there a way to speed up and have a more efficient "previewing system".
Here is what I have so far:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class MainFrame implements ActionListener, Runnable {
//add frame components
public static JFrame frame = new JFrame("Screen Caper - v1.0.1");
JButton start = new JButton("record");
JButton close = new JButton("Exit");
JPanel preview = new JPanel();
public static boolean running = false;
public static boolean recording = false;
public static boolean paused = false;
public static String curDir = System.getProperty("user.dir");
//get the screen width
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();
Container a = new Container();
Container b = new Container();
public MainFrame() {
frame.setSize((int)(width) - 80, (int)(height) - 80);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setup the buttons and JPanel
a.setLayout(new GridLayout(1, 2));
a.add(start);
start.addActionListener(this);
a.add(close);
close.addActionListener(this);
frame.add(a, BorderLayout.NORTH);
b.setLayout(new GridLayout(1, 2));
b.add(preview);
frame.add(b, BorderLayout.CENTER);
//add anything else
running = true;
//set frame to visible
frame.setVisible(true);
run();
}
public static void main(String[] args) {
new MainFrame();
}
public void run() {
Graphics g = frame.getGraphics();
while (running) {
//draw the preview of the computer screen on the JPanel if its not recording already
if (!recording && !paused) {
drawPreview(g);
}
}
}
public void drawPreview(Graphics g) {
BufferedImage image;
try {
image = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(image, "png", new File("test.png"));
} catch (Exception ex) {
ex.printStackTrace();
}
BufferedImage prevIm;
try {
prevIm = ImageIO.read(new File("test.png"));
g.setColor(new Color(0, 0, 0));
g.fillRect(preview.getX() + 3, preview.getY() + 51, preview.getWidth(), preview.getHeight() + 1);
g.drawImage(prevIm, preview.getX() + 3, preview.getY() + 51, preview.getX() + preview.getWidth(), preview.getY() + preview.getHeight(), null);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void record(Graphics g) {
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource().equals(start)) {
if (!recording) {
//if the program isn't recording, then start recording
Graphics g = frame.getGraphics();
record(g);
start.setText("Finish");
recording = true;
System.out.println("recording...");
} else {
//else stop recording
start.setText("record");
recording = false;
System.out.println("done");
}
}
if (event.getSource().equals(close)) {
paused = true;
int ans = JOptionPane.showConfirmDialog(null, "Woah there! You're about to quit the application\nAre you sure you want to procced?", "Caution!", JOptionPane.YES_NO_OPTION);
if (ans == JOptionPane.YES_OPTION) {
System.exit(0);
} else if (ans == JOptionPane.NO_OPTION) {
paused = false;
}
}
}
}
any help is appreciated!
Don't use getGraphics, this is not how custom painting is done.
Your run method may simply be running to fast, consider using a javax.swing.Timer instead
Toolkit.getDefaultToolkit().getScreenSize() only returns the "default" screen and does not take into consideration split screens
Capturing the screen is a time consuming process and there's not much you can do about reducing it (as it's outside of your control). You "could" setup a series of Threads whose job it was to capture a given section of the desktop. You could also achieve this using SwingWorkers which would make it easier to sync the updates back to the UI...
Take a look at:
Performing Custom Painting
Concurrency in Swing
How to Use Swing Timers
Updated with example
This is a proof of concept only! You should understand what it's trying to do and borrow ideas from it...
You can see the preview window change inside the preview window...kinda freaky...
It tested this on a virtual desktop of 4480x1600.
Basically, it divides the desktop up into a 4x4 grid and starts a Thread for each section. Each thread is responsible for capturing it's own section of the screen.
It also scales that resulting image down and feeds it back to the UI.
I had started with SwingWorkers, but it seems to be hard to be limited to 10 threads. You could reduce the grid size and use SwingWorkers, they tend to be simpler to use and manage then raw Threads.
Each section is given an id, which allows me keep track of what's changed. Technically, you could just add elements to a List, but how do you determine what's new and what's old?
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class PreviewDesktop {
public static void main(String[] args) {
new PreviewDesktop();
}
public PreviewDesktop() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Puzzler {
private Rectangle virtualBounds;
private double scale;
private Map<Integer, PuzzlePiece> pieces;
public TestPane() {
virtualBounds = getVirtualBounds();
int columns = 4;
int rows = 4;
pieces = new HashMap<>(columns * rows);
int columnWidth = Math.round(virtualBounds.width / (float) columns);
int rowHeight = Math.round(virtualBounds.height / (float) rows);
int id = 0;
for (int row = 0; row < rows; row++) {
int y = virtualBounds.y + (row * rowHeight);
for (int column = 0; column < columns; column++) {
int x = virtualBounds.x + (column * columnWidth);
Rectangle bounds = new Rectangle(x, y, columnWidth, rowHeight);
GrabberWorker worker = new GrabberWorker(id, this, bounds);
System.out.println(id);
id++;
startThread(worker);
}
}
}
#Override
public double getScale() {
return scale;
}
#Override
public void invalidate() {
super.invalidate();
scale = getScaleFactorToFit(virtualBounds.getSize(), getSize());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Integer id : pieces.keySet()) {
PuzzlePiece piece = pieces.get(id);
Rectangle bounds = piece.getBounds();
BufferedImage img = piece.getImage();
g2d.drawImage(img, bounds.x, bounds.y, this);
// If you want to see each sections bounds, uncomment below...
//g2d.draw(bounds);
}
g2d.dispose();
}
#Override
public void setPiece(int id, PuzzlePiece piece) {
pieces.put(id, piece);
repaint();
}
protected void startThread(GrabberWorker worker) {
Thread thread = new Thread(worker);
thread.setDaemon(true);
thread.start();
}
}
public class PuzzlePiece {
private final Rectangle bounds;
private final BufferedImage img;
public PuzzlePiece(Rectangle bounds, BufferedImage img) {
this.bounds = bounds;
this.img = img;
}
public Rectangle getBounds() {
return bounds;
}
public BufferedImage getImage() {
return img;
}
}
public interface Puzzler {
public void setPiece(int id, PuzzlePiece piece);
public double getScale();
}
public class GrabberWorker implements Runnable {
private Rectangle bounds;
private Puzzler puzzler;
private int id;
private volatile PuzzlePiece parked;
private ReentrantLock lckParked;
public GrabberWorker(int id, Puzzler puzzler, Rectangle bounds) {
this.id = id;
this.bounds = bounds;
this.puzzler = puzzler;
lckParked = new ReentrantLock();
}
protected void process(PuzzlePiece piece) {
// puzzler.setPiece(bounds, chunks.get(chunks.size() - 1));
puzzler.setPiece(id, piece);
}
protected void publish(PuzzlePiece piece) {
lckParked.lock();
try {
parked = piece;
} finally {
lckParked.unlock();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
lckParked.lock();
try {
process(parked);
} finally {
lckParked.unlock();
}
}
});
}
#Override
public void run() {
try {
Robot bot = new Robot();
while (true) {
BufferedImage img = bot.createScreenCapture(bounds);
double scale = puzzler.getScale();
Rectangle scaled = new Rectangle(bounds);
scaled.x *= scale;
scaled.y *= scale;
scaled.width *= scale;
scaled.height *= scale;
BufferedImage imgScaled = getScaledInstance(img, scale);
publish(new PuzzlePiece(scaled, imgScaled));
Thread.sleep(500);
}
} catch (AWTException | InterruptedException exp) {
exp.printStackTrace();
}
}
}
public static Rectangle getVirtualBounds() {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
bounds.add(gd.getDefaultConfiguration().getBounds());
}
return bounds;
}
public static 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 static double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = (double) iTargetSize / (double) iMasterSize;
return dScale;
}
public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
}
protected static 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);
// System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
}
return imgScale;
}
protected static 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 static 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) {
w = img.getWidth();
h = img.getHeight();
} else {
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;
}
}
Now, if you're feeling really adventurous, you could update this idea so that if the underlying image for a section didn't change and the scale didn't change, it didn't send that to the UI, which might help reduce some of the overhead ... and no, I don't have code to do that ;)