How to increase drawing speed of JPictureBox for large image? - java

I have a JPictureBox extends from a java.awt.Component see code here http://pastebin.com/SAJc6Sht. But it only works well when there is no image stretching. Especially for the big picture, it slows program dramatically. How to increase drawing speed of JPictureBox for large image?
#Override
public void paint(Graphics g) {
super.paint(g);
int x = 0;
int y = 0;
int w = 0;
int h = 0;
if (image != null) {
switch (sizeMode) {
case AUTO_SIZE:
case NORMAL:
w = image.getWidth();
h = image.getHeight();
break;
case CENTER_IMAGE:
w = image.getWidth();
h = image.getHeight();
x = (getWidth() - w) / 2;
y = (getHeight() - h) / 2;
break;
case STRETCH_IMAGE:
w = getWidth();
h = getHeight();
break;
case ZOOM:
w = (int) Math.round(image.getWidth() * zoomFactor);
h = (int) Math.round(image.getHeight() * zoomFactor);
break;
case FIT_BOTH:
if (image.getWidth() > image.getHeight()) {
w = getWidth();
h = (int) (w / getAR());
if (h > getHeight()) {
h = getHeight();
w = (int) (h * getAR());
}
} else {
h = getHeight();
w = (int) (h * getAR());
if (w > getWidth()) {
w = getWidth();
h = (int) (w / getAR());
}
}
break;
case FIT_WIDTH:
w = getWidth();
h = (int) (w / getAR());
break;
case FIT_HEIGHT:
h = getHeight();
w = (int) (h * getAR());
break;
}
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.drawImage(image, x, y, w, h, this);
} else if (errorIcon != null) {
w = errorIcon.getIconWidth();
h = errorIcon.getIconHeight();
x = (getWidth() - w) / 2;
y = (getHeight() - h) / 2;
errorIcon.paintIcon(this, g, x, y);
}
}

Basically, you want to off load the scaling of the image to a background thread, scaling is time consuming and you don't want to do within the context of the Event Dispatching Thread.
This then raises a few more issues. You don't want to scale the image until you really have to to and you really only want the latest result.
Instead of trying to scale the image on EVERY change, you can setup a small, single repeat timer which you reset each time you want to make a change. This will consolidate the multiple resize requests down to as few requests as possible. This example uses a javax.swing.Timer set to a short 125 millisecond delay. So it will wait at least 125 milliseconds between requests for a change before actually triggering the update.
Next, it uses a ExecutorService set up with a single thread. This provides us with the means to "attempt" to cancel any pre-existing operations, as we don't want there result and start our latest request.
Next, the actual scaling operation employs a two step scale, first, it tries to do a fast, low quality scale which can be put on the screen quickly and then performs a slower, high quality scale which is updated at some time in the future...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new TestPane()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Scrollable {
private BufferedImage master;
private Image scaled;
private double zoom = 1d;
private ExecutorService service;
private List<Future> scaleTasks;
private final Timer zoomTimer;
public TestPane() {
scaleTasks = new ArrayList<>(5);
service = Executors.newSingleThreadExecutor();
try {
master = ImageIO.read(new File("Some image some where"));
scaled = master;
} catch (IOException ex) {
ex.printStackTrace();
}
zoomTimer = new Timer(125, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Update Zoom to " + getZoom());
updateToZoomFactor(getZoom());
}
});
zoomTimer.setRepeats(false);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "plus");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "minus");
am.put("plus", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
double zoom = getZoom() + 0.1;
setZoom(zoom);
}
});
am.put("minus", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
double zoom = getZoom() - 0.1;
setZoom(zoom);
}
});
}
#Override
public Dimension getPreferredSize() {
return scaled == null
? new Dimension(master.getWidth(), master.getHeight())
: new Dimension(scaled.getWidth(this), scaled.getHeight(this));
}
public BufferedImage getMaster() {
return master;
}
public void setZoom(double value) {
if (value < 0.1) {
value = 0.1;
} else if (value > 2) {
value = 2d;
}
if (value != zoom) {
zoom = value;
zoomTimer.restart();
}
}
public double getZoom() {
return zoom;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (scaled != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - scaled.getWidth(this)) / 2;
int y = (getHeight() - scaled.getHeight(this)) / 2;
g2d.drawImage(scaled, x, y, this);
g2d.dispose();
}
}
protected void setScaledResult(final Image image) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
scaled = image;
invalidate();
revalidate();
repaint();
}
});
}
protected void updateToZoomFactor(double zoom) {
Future[] tasks = scaleTasks.toArray(new Future[scaleTasks.size()]);
for (Future task : tasks) {
if (!task.isCancelled()) {
task.cancel(true);
} else {
scaleTasks.remove(task);
}
}
service.submit(new RescaleTask(zoom));
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(400, 400);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
protected class RescaleTask implements Callable<Image> {
private double zoom;
protected RescaleTask(double zoom) {
this.zoom = zoom;
}
#Override
public Image call() throws Exception {
if (zoom == 1) {
scaled = getMaster();
} else {
int width = (int) (getMaster().getWidth() * zoom);
int height = (int) (getMaster().getHeight() * zoom);
Image scaled = getMaster().getScaledInstance((int) width, (int) height, Image.SCALE_FAST);
if (!Thread.currentThread().isInterrupted()) {
setScaledResult(scaled);
if (zoom < 1) {
scaled = getScaledDownInstance(getMaster(), (int) width, (int) height);
} else {
scaled = getScaledUpInstance(getMaster(), (int) width, (int) height);
}
if (!Thread.currentThread().isInterrupted()) {
setScaledResult(scaled);
} else {
System.out.println("Was interrupted during quality scale");
}
} else {
System.out.println("Was interrupted during fast scale");
}
}
return scaled;
}
protected BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight) {
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 = img.getWidth();
int h = img.getHeight();
do {
System.out.println(w + "x" + h + " -> " + targetWidth + "x" + targetHeight);
if (w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (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, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
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) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w = img.getWidth();
int h = img.getHeight();
do {
if (w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
// createCompatibleImage(w, h, type)
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
}
}
}
nb: This is little over kill, but demonstrates some key ideas
One of the other things that might help is to convert the image to a compatible color model for the GraphicsDevice, for example...
master = ImageIO.read(new File("Some image some where"));
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage compatible = gc.createCompatibleImage(master.getWidth(), master.getHeight(), Transparency.TRANSLUCENT);
Graphics2D g2d = compatiable.createGraphics();
g2d.drawImage(master, 0, 0, this);
g2d.dispose();
master = compatible;

Related

Applying colour to the transparent area of a JButton image - but not that of its container

I have a circular JButton constructed using a circular PNG image with a transparent area.
I want to fill the transparent area of the JButton image with a given colour - but something other than the opaque background colour of the JPanel which contains the JButton. I want to do this programmatically in Java rather than providing pre-coloured images from a graphics package.
I've got as far as the code below, which simply allows the orange background of the opaque panel to colour the transparent area. I can't figure out, though, how to keep the panel background as orange but fill the image transparency with, say, blue (or another colour for rollover and pressed effects).
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.DefaultButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class App extends JFrame implements ActionListener
{
public App()
{
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(makeContentPane());
}
private final JPanel makeContentPane()
{
JPanel contentPane = new JPanel();
BufferedImage bi = null;
try
{
bi = ImageIO.read(new URL("http://features.advisorwebsites.com/sites/default/files/users/AdvisorWebsitesFeatures/icones/1384148625_twitter_circle_gray.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
ImageIcon icon = new ImageIcon(bi);
MyButton myButton = new MyButton(icon);
myButton.addActionListener(this);
contentPane.add(myButton);
contentPane.setBackground(Color.ORANGE);
contentPane.setOpaque(true);
return contentPane;
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
App app = new App();
app.pack();
app.setVisible(true);
}
});
}
class MyButton extends JButton
{
public MyButton(ImageIcon icon)
{
super(icon);
setMargin(new Insets(0, 0, 0, 0));
setFocusable(false);
setContentAreaFilled(false);
setBorderPainted(false);
setModel(new DefaultButtonModel());
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
}
#Override
public void actionPerformed(ActionEvent arg0)
{
System.out.println("You clicked me");
}
}
I'm guessing I might need to apply Graphics2D transformations to my transparent image to create a set of three new images (for the normal, rollover and pressed states of my JButton). Is this the appropriate way forward and, if so, can you provide me with a code example for the bit I'm missing ?
Thanks
try to override paintComponent() in your custom JButton
Here's what I tried
class MyButton extends JButton {
public MyButton(ImageIcon icon) {
super(icon);
setMargin(new Insets(0, 0, 0, 0));
setFocusable(false);
setContentAreaFilled(false);
setBorderPainted(false);
setModel(new DefaultButtonModel());
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
public void paintComponent(Graphics g) {
g.setColor(Color.green);
g.fillOval(0, 0, this.getWidth(), this.getHeight());
super.paintComponent(g);
}
}
And here's the result:
EDIT:
To get it to change color depending on mouse movement, you need to add a MouseListener to the JButton and add a Color attribute to the class, when a MouseEvent is fired you change the color. Also don't forget to set the graphics to that color in paintComponent()
class MyButton extends JButton {
Color color = Color.GREEN;
public MyButton(ImageIcon icon) {
super(icon);
setMargin(new Insets(0, 0, 0, 0));
setFocusable(false);
setContentAreaFilled(false);
setBorderPainted(false);
setModel(new DefaultButtonModel());
setCursor(new Cursor(Cursor.HAND_CURSOR));
this.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
color=Color.RED;
}
#Override
public void mouseReleased(MouseEvent e) {
color=Color.BLUE;
}
#Override
public void mouseEntered(MouseEvent e) {
color=Color.BLUE;
}
#Override
public void mouseExited(MouseEvent e) {
color=Color.GREEN;
}
});
}
#Override
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(0, 0, this.getWidth(), this.getHeight());
super.paintComponent(g);
}
}
Okay, this is little more then you're asking for, but it demonstrates a means by which you can generate a shape which matches the shape of a image, based on its alpha, all through the magic of AlphaComposite...
So, basically, we're going to take the original image on the left and turn into the image on the right...
First, we load the original image
Next, we create a mask of the shape we want to achieve (a circle in this case)
Next, we mask the two images together, so that the original image is cropped into the mask (the second)
Then we create a new mask, which is filled with the color we want to be used as the outline
We then mask the first masked image with the "outline" image, this basically acts as a cookie cutter, cutting the shape of the first masked imaged with the "block" image.
We take this "outlined" shape and then scale it up slightly
And finally, we combine them.
This example generates two images, a "normal" and a "roll over" which is easily applied to a JButton, for example...
Normal...
Roll over...
Now, if for some reason, you wanted to know when the button was "rolled over", you could simply add a ChangeListener to the ButtonModel, for example...
JButton btn = new JButton(new ImageIcon(normal));
btn.setRolloverIcon(new ImageIcon(rollOver));
btn.setRolloverEnabled(true);
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
System.out.println("Change: " + model.isRollover());
}
});
And the runnable example...
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class JavaApplication24 {
public static void main(String[] args) {
new JavaApplication24();
}
public JavaApplication24() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
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);
} catch (IOException ex) {
Logger.getLogger(JavaApplication24.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
public TestPane() throws IOException {
setLayout(new GridBagLayout());
BufferedImage source = ImageIO.read(...));
// This the shape we want the source to be clipped to
int size = Math.min(source.getWidth(), source.getHeight());
BufferedImage mask = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D maskg = mask.createGraphics();
applyQualityRenderingHints(maskg);
maskg.setColor(Color.WHITE);
maskg.fillOval((source.getWidth() - size) / 2, (source.getHeight() - size) / 2, size, size);
maskg.dispose();
// This will mask the source to the shape we've defined
BufferedImage masked = applyMask(source, mask, AlphaComposite.DST_ATOP);
BufferedImage normal = makeOutline(masked, Color.BLACK);
BufferedImage rollOver = makeOutline(masked, Color.RED);
JButton btn = new JButton(new ImageIcon(normal));
btn.setRolloverIcon(new ImageIcon(rollOver));
btn.setRolloverEnabled(true);
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
System.out.println("Change: " + model.isRollover());
}
});
add(btn);
}
protected BufferedImage makeOutline(BufferedImage original, Color color) {
// This generates a image which is completely filled with the provided color
BufferedImage outline = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D outlineg = outline.createGraphics();
applyQualityRenderingHints(outlineg);
outlineg.setColor(color);
outlineg.fillRect(0, 0, outline.getWidth(), outline.getHeight());
outlineg.dispose();
// This applies a AlphaComposite to mask the outline with the shape
// of the original image
outline = applyMask(original, outline, AlphaComposite.SRC_ATOP);
// Now we make it slightly larger...
double scale = 1.05;
outline = getScaledInstanceToFit(outline, scale);
// And we combine the images
outlineg = outline.createGraphics();
int x = (outline.getWidth() - original.getWidth()) / 2;
int y = (outline.getHeight() - original.getHeight()) / 2;
outlineg.drawImage(original, x, y, this);
outlineg.dispose();
return outline;
}
public BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage) {
return applyMask(sourceImage, maskImage, AlphaComposite.DST_IN);
}
public BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage, int method) {
BufferedImage maskedImage = null;
if (sourceImage != null) {
int width = maskImage.getWidth(null);
int height = maskImage.getHeight(null);
maskedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D mg = maskedImage.createGraphics();
applyQualityRenderingHints(mg);
int x = (width - sourceImage.getWidth(null)) / 2;
int y = (height - sourceImage.getHeight(null)) / 2;
mg.drawImage(sourceImage, x, y, null);
mg.setComposite(AlphaComposite.getInstance(method));
mg.drawImage(maskImage, 0, 0, null);
mg.dispose();
}
return maskedImage;
}
public 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);
// g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
public BufferedImage getScaledInstanceToFit(BufferedImage img, double scale) {
int width = (int)(img.getWidth() * scale);
int height = (int)(img.getHeight()* scale);
return getScaledInstanceToFit(img, new Dimension(width, height));
}
public BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {
double scaleFactor = getScaleFactorToFit(new Dimension(img.getWidth(), img.getHeight()), size);
return getScaledInstance(img, scaleFactor);
}
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 getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
public BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
}
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) {
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,
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;
}
}
}
This code borrows heavily from my personal library code, so it might be a little convoluted ;)

smooth animation in java for fast moving objects

I am creating simple animation of ball moving from one side of the screen to the other with different speed. The problem is that with higher speeds of the ball I can see noticeable flickering of the ball, actually it is hard to explain but something like I could see repaints when part of ball is still in previous step.
I have tried number of things including:
native swing animation using first thread/sleep/repain, then moved to timers
switched to javafx canvas/pane inside swing jframe. Tried both transitions and AnimationTimer
tinkering with CreateBufferStrategy, for 1,2,3 - to be honest haven't seen any difference (maybe I was doing something wrong...)
My question how can I improve smoothness and whether what I want to achieve is possible with native java or maybe it is better to use some external libraries ? and if so could you recommend something ?
below shown my example code for 2nd/3rd attempt.
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
import javax.swing.JFrame;
public class FXTrackerPanel extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public int crSize = 30;
public double xPos = crSize;
public double yPos = 100;
public int xSize = 100;
public int ySize = 100;
public Circle r;
int dir = 1;
public void updateScreenSize() {
int screen = 0;
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
if( screen > -1 && screen < gs.length )
{
xSize = gs[screen].getDisplayMode().getWidth();
ySize = gs[screen].getDisplayMode().getHeight();
}
else if( gs.length > 0 )
{
xSize = gs[0].getDisplayMode().getWidth();
ySize = gs[0].getDisplayMode().getHeight();
}
else
{
throw new RuntimeException( "No Screens Found" );
}
yPos = ySize / 2;
}
private void initFXPanel(JFXPanel fxPanel) {
updateScreenSize();
xPos = crSize;
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
r = new javafx.scene.shape.Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
// new AnimationTimer() {
//
// #Override
// public void handle(long now) {
// double speed = 20;
// try {
// speed = Double.valueOf(TETSimple.mp.speedSinus.getText());
// }
// catch (Exception ex) {
// speed = 20;
// }
// double xMov = (speed * 4 * Math.sin( xPos * Math.PI / xSize ) );
// if (xMov <= 0) {
// xMov = 1;
// }
// if (dir == 1) {
// if (xPos >= xSize - crSize)
// dir = 0;
// xPos += xMov;
// } else {
// if (xPos <= 1)
// dir = 1;
// xPos -= xMov;
// }
//
// r.setTranslateX(xPos);
// }
// }.start();
fxPanel.setScene(new Scene(root));
}
public FXTrackerPanel() {
updateScreenSize();
this.setSize(new Dimension(xSize, ySize));
this.setPreferredSize(new Dimension(xSize, ySize));
this.setVisible(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JFXPanel fxPanel = new JFXPanel();
this.add(fxPanel);
this.createBufferStrategy(3);
Platform.runLater(new Runnable() {
#Override
public void run() {
initFXPanel(fxPanel);
}
});
}
public static void main(String[] args)
{
new FXTrackerPanel();
}
}
And here example for swing code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.lang.Math;
import java.util.Random;
public class TrackerPanel extends JPanel implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
Shape cr;
Color c;
public int crSize = 30;
public double xPos = crSize;
public double yPos = 100;
public double xPosPrev = crSize;
public double yPosPrev = 100;
public int xSize = 100;
public int ySize = 100;
int dir = 1; // left
int timer = 50; // 50 - sins, 1500 - linear
int method = 1; // 1 - jump, 2 - sinus
int timeToChange = 1000;
int passes = 0;
Timer tt;
boolean clickedClose = false;
private int repeats = 0;
// t - timer interval, m - method of ball movement: 1 - jump, 2 - sinus
public TrackerPanel(int t, int m) {
this.setPreferredSize(new Dimension(300, 200));
this.timer = t;
this.method = m;
c = Color.red;
repaint();
this.updateScreenSize();
tt = new Timer(t, null);
tt.addActionListener(this);
tt.start();
}
public void updateScreenSize() {
int screen = TETSimple.suppMonitor;
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
if (screen > -1 && screen < gs.length) {
xSize = gs[screen].getDisplayMode().getWidth();
ySize = gs[screen].getDisplayMode().getHeight();
} else if (gs.length > 0) {
xSize = gs[0].getDisplayMode().getWidth();
ySize = gs[0].getDisplayMode().getHeight();
} else {
throw new RuntimeException("No Screens Found");
}
yPos = ySize / 2;
yPosPrev = ySize / 2;
}
public void actionPerformed(ActionEvent arg0) {
if (method == 1)
lineMovement();
else
sinusMovement();
repaint(0, ySize / 2, xSize, crSize);
}
private Double parseText2Int(String literal) {
try {
return Double.valueOf(literal);
} catch (Exception ex) {
ex.printStackTrace();
}
return 10.0;
}
private void checkFinishCondition() {
if (passes + 1 > repeats && repeats != 0) {
if (!clickedClose) {
TETSimple.mp.bStop.doClick();
clickedClose = true;
}
return;
}
}
private void sinusMovement() {
this.updateScreenSize();
this.repeats = parseText2Int(TETSimple.mp.repeatsCount.getText()).intValue();
checkFinishCondition();
double speed = parseText2Int(TETSimple.mp.speedSinus.getText());
double xMov = (speed * Math.sin(xPos * Math.PI / xSize));
if (xMov <= 0) {
xMov = 1;
}
if (dir == 1) {
if (xPos >= xSize - crSize)
dir = 0;
xPosPrev = xPos;
xPos += xMov;
} else {
if (xPos <= 1 + crSize) {
dir = 1;
passes++;
}
xPosPrev = xPos;
xPos -= xMov;
}
}
private void lineMovement() {
this.repeats = parseText2Int(TETSimple.mp.repeatsCount.getText()).intValue();
checkFinishCondition();
double left = crSize;
double center = xSize / 2 - crSize * 1.5;
double right = xSize - crSize * 2;
Random r = new Random();
if (timeToChange <= 0) {
passes++;
if (xPos == left || xPos == right) {
timeToChange = 300 + r.nextInt(12) * 100;
xPos = center;
} else if (xPos == center) {
timeToChange = 300 + r.nextInt(7) * 100;
if (r.nextBoolean())
xPos = left;
else
xPos = right;
}
} else {
timeToChange -= 100;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
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);
g2d.setColor(Color.green);
g2d.fill(new Ellipse2D.Double(xPos, yPos, crSize, crSize));
g2d.dispose();
}
}
It's difficult to know exactly what might be going wrong without a runnable example, but I would, where you can, avoid mixing JavaFX and Swing, as they have different rendering mechanisms.
The following is a VERY simple example, which simply increases the speed of the balls been animated by simply changing the amount by which they are moved on each update...
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.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class BouncyBall {
public static void main(String[] args) {
new BouncyBall();
}
public BouncyBall() {
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 ControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
private JSlider speed;
private JSlider quanity;
private BallPitPane ballPitPane;
public ControlPane() {
setLayout(new BorderLayout());
ballPitPane = new BallPitPane();
add(ballPitPane);
JPanel controls = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
speed = new JSlider(1, 100, 4);
quanity = new JSlider(1, 100, 1);
controls.add(new JLabel("Speed:"), gbc);
gbc.gridy++;
controls.add(new JLabel("Quanity:"), gbc);
gbc.gridx++;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
controls.add(speed, gbc);
gbc.gridy++;
controls.add(quanity, gbc);
add(controls, BorderLayout.SOUTH);
speed.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ballPitPane.setSpeed(speed.getValue());
}
});
quanity.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ballPitPane.setQuanity(quanity.getValue());
}
});
}
}
public class BallPitPane extends JPanel {
private List<Ball> balls;
private int speed;
public BallPitPane() {
balls = new ArrayList<>(25);
setSpeed(2);
setQuanity(1);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update(getWidth(), speed);
}
repaint();
}
});
timer.start();
}
public void setSpeed(int speed) {
this.speed = speed;
}
public void setQuanity(int quanity) {
while (balls.size() > quanity) {
balls.remove(0);
}
while (balls.size() < quanity) {
int radius = 4 + (int) (Math.random() * 48);
Ball ball = new Ball(
randomColor(),
(int) Math.abs(Math.random() * getWidth() - radius),
(int) Math.abs(Math.random() * getHeight() - radius),
radius
);
balls.add(ball);
}
}
protected Color randomColor() {
int red = (int) Math.abs(Math.random() * 255);
int green = (int) Math.abs(Math.random() * 255);
int blue = (int) Math.abs(Math.random() * 255);
return new Color(red, green, blue);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
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);
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
public class Ball {
private Color color;
private int x;
private int y;
private int radius;
private int delta;
public Ball(Color color, int x, int y, int radius) {
this.color = color;
this.x = x;
this.y = y;
this.radius = radius;
delta = Math.random() > 0.5 ? 1 : -1;
}
public void update(int width, int speed) {
x += speed * delta;
if (x + radius >= width) {
x = width - radius;
delta *= -1;
} else if (x < 0) {
x = 0;
delta *= -1;
}
}
public void paint(Graphics g) {
g.setColor(color);
g.fillOval(x, y, radius, radius);
}
}
}
}
This example works within the confines of Swing's painting process, if you need more control over the painting process you will need to use a BufferStrategy (to work within Swing)
I think the issue is caused by the rendering of the JFXPanel in AWT: there is some complex stuff happening behind the scenes to synchronize between the two different system threads (the AWT event dispatch thread and the FX Application Thread).
If you can write this as a "pure" JavaFX application (i.e. with no Swing/AWT code), it runs more smoothly:
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FXAnimationTest extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
Screen screen = Screen.getPrimary();
Rectangle2D screenBounds = screen.getBounds();
double xSize = screenBounds.getWidth();
double ySize = screenBounds.getHeight();
double crSize = 30 ;
double xPos = crSize ;
double yPos = ySize / 2 ;
Circle r = new Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
Scene scene = new Scene(root, xSize, ySize);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you have to have JavaFX embedded in a Swing application, and you're using Java 8 (I tested this on 8u20), then there is a system property that runs both UI toolkits on the same thread. I think this is still currently experimental, so use at your own risk, but try
java -Djavafx.embed.singleThread=true FXTrackerPanel
This improves things a bit, but it's still pretty flickery and not as good as the "pure JavaFX" version.

AccessControlException Applet in a html page

I wrote a game in Java and I want to put it on a HTML page. I know how, but the problem is that it gives me a AcessControlException (java.io.FilePermission "/SomePath/SomeFile.SomeExtension" "read").
Here is the Game class (loads the files at initGame method and init the JApplet):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.swing.JApplet;
public class Game extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
private Thread thread;
public static BufferedImage level;
public static BufferedImage background;
public static Image2d tileset;
public static Sprite red;
public static Sprite green;
public static Sprite player;
public static boolean mouse_left, mouse_right;
public static boolean jumping;
public static boolean play;
private boolean running = false;
private Image screen;
public static RandomLevel map;
public static Listener listener;
public static Gui gui;
public static int FALL_SPEED = 3;
public static int JUMP_SPEED = 3;
public static int JUMP_HEIGHT = 20;
public static int jump_amount = 0;
public static float START_OFFSET_SPEED = 3.0f;
public static float OFFSET_SPEED = 2.0f;
public static final int sxOffset = 0, syOffset = 0;
public static int xOffset = 0, yOffset = 0;
public static final int CURR_COLOR_SCALE = 32;
public static Sprite[] tiles = new Sprite[2];
public static int selected_color = 0;
public static int SCORE = 0;
public static final int TILE_SIZE = 16;
public static final int SCALE = TILE_SIZE * 2;
public static boolean left, right;
public static int currTime = 0;
public static final Dimension size = new Dimension(800, 600);
public static final Dimension pixel = new Dimension(size.width - SCALE, size.height - SCALE);
public void initGame() {
try {
URL levelUrl = new URL(getCodeBase(), "res/map.png");
level = new Image2d(levelUrl).getImage();
URL backgroundUrl = new URL(getCodeBase(), "res/background.png");
background = new Image2d(backgroundUrl).getImage();
URL tilesetUrl = new URL(getCodeBase(), "res/tileset.png");
tileset = new Image2d(tilesetUrl);
} catch (IOException e) {
}
red = new Sprite(new Image2d(tileset.crop(0, 0, Game.TILE_SIZE, Game.TILE_SIZE)));
green = new Sprite(new Image2d(tileset.crop(1, 0, Game.TILE_SIZE, Game.TILE_SIZE)));
player = new Sprite(new Image2d(tileset.crop(2, 0, Game.TILE_SIZE, Game.TILE_SIZE)));
tiles[0] = red;
tiles[1] = green;
gui = new Gui();
listener = new Listener();
player.setX((size.width - player.getImage().getWidth(null)) / 2);
player.setY((size.height - player.getImage().getHeight(null)) / 2 + 100);
map = new RandomLevel(10000, 20, TILE_SIZE, SCALE, size, xOffset, yOffset);
map.addCollsion(tiles[0]);
addMouseListener(listener);
addMouseMotionListener(new Mouse());
}
public static void switchCollision(Sprite tile) {
map.collision.add(0, tile);
for (int i = 0; i < map.collision.size(); i++) {
if (i != 0)
map.collision.remove(i);
}
}
public void resetGame() {
xOffset = sxOffset;
yOffset = syOffset;
OFFSET_SPEED = START_OFFSET_SPEED;
Game.SCORE = 0;
play = false;
map.generateLevel();
}
public void startGame() {
if (running)
return;
initGame();
running = true;
thread = new Thread(this);
thread.start();
}
public void stopGame() {
if (!running)
return;
running = true;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
screen = createVolatileImage(pixel.width, pixel.height);
requestFocus();
while (running) {
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
final double ns = 1000000000.0 / 60.0;
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
update();
delta--;
}
render();
if (System.currentTimeMillis() - timer > 100) {
timer += 1000;
}
}
stop();
}
}
public void update() {
if (!play && mouse_left)
play = true;
if (play) {
switchCollision(tiles[selected_color]);
if (yOffset > 180)
resetGame();
map.update();
map.setxOffset(xOffset);
map.setyOffset(yOffset);
player.update(25);
gui.update();
if (!map.getTileDownCollision(player))
yOffset += FALL_SPEED;
if (!Game.map.getTileRightCollision(player))
xOffset += Math.round(OFFSET_SPEED);
if (SCORE > 20)
OFFSET_SPEED = 4.0f;
if (SCORE > 40)
OFFSET_SPEED = 5.0f;
if (SCORE > 60)
OFFSET_SPEED = 6.0f;
if (SCORE > 80)
OFFSET_SPEED = 7.0f;
if (SCORE > 100)
OFFSET_SPEED = 8.0f;
if (SCORE > 120)
OFFSET_SPEED = 9.0f;
if (SCORE > 140)
OFFSET_SPEED = 10.0f;
if (jumping) {
FALL_SPEED = 0;
yOffset -= JUMP_SPEED;
jump_amount++;
if (jump_amount >= JUMP_HEIGHT) {
jumping = false;
FALL_SPEED = 3;
jump_amount = 0;
}
}
}
}
public void render() {
Graphics g = screen.getGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, size.width, size.height);
map.render(g);
g.drawImage(player.getImage(), Math.round(player.getX()), Math.round(player.getY()), Game.TILE_SIZE + Game.SCALE, Game.TILE_SIZE + Game.SCALE, null);
g.setFont(new Font("new Font", Font.PLAIN, 10));
g.setColor(Color.white);
g.drawString("CLICK ON THIS SIDE TO JUMP", 200 - xOffset, 240);
g.drawString("CLICK ON THIS SIDE TO SWITCH COLOR", size.width - 350 - xOffset, 240);
g.drawImage(background, 0, 0, null);
g.drawImage(map.collision.get(0).getImage(), (size.width - (Game.TILE_SIZE + CURR_COLOR_SCALE)) / 2, CURR_COLOR_SCALE / 2 - 5, Game.TILE_SIZE + CURR_COLOR_SCALE, Game.TILE_SIZE + CURR_COLOR_SCALE, null);
gui.render(g);
g.setFont(new Font("new Font", Font.BOLD, 32));
g.setColor(Color.yellow);
g.drawString("SCORE: " + SCORE, 10, 32);
g = getGraphics();
g.drawImage(screen, 0, 0, size.width, size.height, 0, 0, pixel.width, pixel.height, null);
g.dispose();
}
public void destroy() {
System.exit(0);
}
public void init() {
setSize(size);
setVisible(true);
startGame();
}
}
EDIT: Here is the Image2d class(holds ImageIO.read command)
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
public class Image2d{
private BufferedImage image;
public Image2d(URL url) {
try {
image = ImageIO.read(url);
} catch (IOException e) {
System.err.println("No file found at: " + url);
}
}
public Image2d(String path) {
try {
image = ImageIO.read(new File(path));
} catch (IOException e) {
System.err.println("No file found at: " + path);
}
}
public static BufferedImage load(URL url) {
try {
return ImageIO.read(url);
} catch (IOException e) {
System.err.println("No file found at: " + url.getPath());
return null;
}
}
public static BufferedImage load(String path) {
try {
return ImageIO.read(new File(path));
} catch (IOException e) {
System.err.println("No file found at: " + path);
return null;
}
}
public Image2d(BufferedImage image) {
this.image = image;
}
public BufferedImage crop(int x, int y, int w, int h) {
return image.getSubimage(x * w, y * h, w, h);
}
public static BufferedImage insert(Image2d target, Image2d component) {
BufferedImage tile = new BufferedImage(target.getImage().getWidth(), target.getImage().getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = tile.getGraphics();
g.drawImage(component.getImage(), 0, 0, component.getImage().getWidth(), component.getImage().getHeight(), null);
g.drawImage(target.getImage(), 0, 0, target.getImage().getWidth(), target.getImage().getHeight(), null);
return tile;
}
public static BufferedImage switchColor(BufferedImage image, Color color1, Color color2) {
Graphics g = image.getGraphics();
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y));
if (color.getRed() == color1.getRed() && color.getGreen() == color1.getGreen() && color.getBlue() == color1.getBlue() && color.getAlpha() == color1.getAlpha()) {
g.setColor(color2);
g.fillRect(x, y, 1, 1);
}
}
}
return image;
}
public static BufferedImage fill(Color color, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, width, height);
return image;
}
public static BufferedImage switchColor(BufferedImage image, Color[] color1, Color[] color2) {
Graphics g = image.getGraphics();
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y));
for (int i = 0; i < color1.length; i++)
if (color.getRed() == color1[i].getRed() && color.getGreen() == color1[i].getGreen() && color.getBlue() == color1[i].getBlue() && color.getAlpha() == color1[i].getAlpha()) {
g.setColor(color2[i]);
g.fillRect(x, y, 1, 1);
}
}
}
return image;
}
public static BufferedImage createYDropShadow(int width, int height, int startAlpha, float density, float offset, Color color) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
int alpha = startAlpha;
for (int y = 0; y < image.getHeight(); y++) {
if (alpha >= density)
alpha -= density;
for (int x = 0; x < image.getWidth(); x++) {
g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha));
g.fillRect(x + Math.round(offset), y + Math.round(offset), 1 + Math.round(offset), 1 + Math.round(offset));
}
}
return image;
}
public static BufferedImage createXDropShadow(int width, int height, int startAlpha, float density, float offset, Color color) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
int alpha = startAlpha;
for (int x = 0; x < image.getWidth(); x++) {
if (alpha >= density)
alpha -= density;
for (int y = 0; y < image.getHeight(); y++) {
g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha));
g.fillRect(x + Math.round(offset), y + Math.round(offset), 1 + Math.round(offset), 1 + Math.round(offset));
}
}
return image;
}
public BufferedImage getImage() {
return image;
}
}
And here is html code (it is inside a jar file called Blocks.jar):
<!DOCTYPE html>
<html>
<body>
<applet code="Game.class" archive="Blocks.jar" width="600" height="600"> </applet>
</body>
</html>
I saw people getting the same error but, no answer gave me a solution that worked. So what shoud I do for load the images without erros?
PS: The JApplet is running fine in eclipse, but at the html page throws me this error.
Thanks!

paint flashing java

1.The window is flashing while repainting. How can I eliminate that? Using the update method doesn't work either.
2. How do I update all the not-up-to-date classes in netbeans? It looks like my netbeans uses some old classes (before jdk 7).
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.*;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JWindow;
public class Splash extends JWindow {
private boolean mIsRunning;
private boolean mIsFadingOut;
volatile static boolean s = true;
private int mAngle;
private int mFadeCount;
private int mFadeLimit = 15;
Splash(Frame f) {
super(f);
}
public void startt() {
while (s) {
repaint();
mAngle += 3;
if (mAngle >= 360) {
mAngle = 0;
}
if (mIsFadingOut) {
if (--mFadeCount == 0) {
mIsRunning = false;
}
} else if (mFadeCount < mFadeLimit) {
mFadeCount++;
}
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Splash.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
// Paint the view.
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g.create();
float fade = (float) mFadeCount / (float) mFadeLimit;
// Gray it out.
Composite urComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .5f * fade));
g2.fillRect(0, 0, w, h);
g2.setComposite(urComposite);
// Paint the wait indicator.
int s = Math.min(w, h) / 5;
int cx = w / 2;
int cy = h / 2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(
new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.setPaint(Color.white);
g2.rotate(Math.PI * mAngle / 180, cx, cy);
for (int i = 0; i < 12; i++) {
float scale = (11.0f - (float) i) / 11.0f;
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
}
g2.dispose();
}
}
public void showSplash(){
final JFrame p = this; //parent
final Rectangle s = this.getBounds(); //parent size
new Thread(){
public void run(){
splash = new Splash(p);
splash.setBounds(s);
splash.setVisible(true);
splash.startt();
}
}.start();
}
}
Don't call Thread.sleep(...) on the EDT but rather, use a Swing Timer like the example shows. Also, you'll want to draw in the paintComponent(...) method of a JPanel that the JWindow holds to use Swing's default double buffering.
The problem comes from the fact that you override the paint method of JWindow. Override the paintComponent() of a JPanel instead, it has double buffering:
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
public class Test extends JFrame {
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JWindow window = new JWindow();
final Splash splash = new Splash();
window.add(splash);
window.setSize(100, 30);
window.setVisible(true);
new Thread() {
#Override
public void run() {
splash.startt();
}
}.start();
}
});
}
public static class Splash extends JPanel {
private boolean mIsRunning;
private boolean mIsFadingOut;
volatile static boolean s = true;
private int mAngle;
private int mFadeCount;
private int mFadeLimit = 30;
Splash() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
// Paint the view.
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g.create();
float fade = (float) mFadeCount / (float) mFadeLimit;
// Gray it out.
Composite urComposite = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade));
g2.fillRect(0, 0, w, h);
g2.setComposite(urComposite);
// Paint the wait indicator.
int s = Math.min(w, h) / 5;
int cx = w / 2;
int cy = h / 2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.setPaint(Color.white);
g2.rotate(Math.PI * mAngle / 180, cx, cy);
for (int i = 0; i < 12; i++) {
float scale = (11.0f - i) / 11.0f;
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, scale * fade));
}
g2.dispose();
}
public void startt() {
while (s) {
mAngle += 3;
if (mAngle >= 360) {
mAngle = 0;
}
if (mIsFadingOut) {
if (--mFadeCount == 0) {
mIsRunning = false;
}
} else if (mFadeCount < mFadeLimit) {
mFadeCount++;
}
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Splash.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}

Why it doesn't draw an animated curve?

This is my class which I found on the Internet. It was originally an applet but I don't want to use it as an applet so I changed some methods (such as init() to a constructor).
However, it doesn't work. Would you please help me?
SignInFrame Frame:
public class SignInFrame extends javax.swing.JFrame {
Panel panel;
/** Creates new form SignInFrame */
public SignInFrame() {
initComponents();
}
public void init() {
getContentPane().add(panel = new Panel());
}
public void start() {
panel.start();
}
public void stop() {
panel.stop();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new SignInFrame().setVisible(true);
}
});
}}
Panel Dialog:
package ClientGUI;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
/**
*
* #author ICC
*/
public class Panel extends javax.swing.JPanel implements Runnable{
private Thread thread;
private BufferedImage bimg;
private static final int NUMPTS = 6;
// solid line stoke
protected BasicStroke solid = new BasicStroke(10.0f,
BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
// dashed line stroke
protected BasicStroke dashed = new BasicStroke(10.0f,
BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10, new float[] {5}, 0);
private float animpts[] = new float[NUMPTS * 2];
private float deltas[] = new float[NUMPTS * 2];
protected Paint fillPaint, drawPaint;
// indicates whether or not to fill shape
protected boolean doFill = true;
// indicates whether or not to draw shape
protected boolean doDraw = true;
protected GradientPaint gradient;
protected BasicStroke stroke;
public Panel() {
setBackground(Color.white);
gradient = new GradientPaint(0,0,Color.red,200,200,Color.yellow);
fillPaint = gradient;
drawPaint = Color.blue;
stroke = solid;
}
// generates new points for the path
public void animate(float[] pts, float[] deltas, int i, int limit) {
float newpt = pts[i] + deltas[i];
if (newpt <= 0) {
newpt = -newpt;
deltas[i] = (float) (Math.random() * 4.0 + 2.0);
} else if (newpt >= (float) limit) {
newpt = 2.0f * limit - newpt;
deltas[i] = - (float) (Math.random() * 4.0 + 2.0);
}
pts[i] = newpt;
}
/*
* generates random points with the specified surface width
* and height for the path
*/
public void reset(int w, int h) {
for (int i = 0; i < animpts.length; i += 2) {
animpts[i + 0] = (float) (Math.random() * w);
animpts[i + 1] = (float) (Math.random() * h);
deltas[i + 0] = (float) (Math.random() * 6.0 + 4.0);
deltas[i + 1] = (float) (Math.random() * 6.0 + 4.0);
if (animpts[i + 0] > w / 2.0f) {
deltas[i + 0] = -deltas[i + 0];
}
if (animpts[i + 1] > h / 2.0f) {
deltas[i + 1] = -deltas[i + 1];
}
}
gradient = new GradientPaint(0,0,Color.red,w*.7f,h*.7f,Color.yellow);
}
// calls animate for every point in animpts
public void step(int w, int h) {
for (int i = 0; i < animpts.length; i += 2) {
animate(animpts, deltas, i + 0, w);
animate(animpts, deltas, i + 1, h);
}
}
// sets the points of the path and draws and fills the path
public void drawDemo(int w, int h, Graphics2D g2) {
float[] ctrlpts = animpts;
int len = ctrlpts.length;
float prevx = ctrlpts[len - 2];
float prevy = ctrlpts[len - 1];
float curx = ctrlpts[0];
float cury = ctrlpts[1];
float midx = (curx + prevx) / 2.0f;
float midy = (cury + prevy) / 2.0f;
GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO);
gp.moveTo(midx, midy);
for (int i = 2; i <= ctrlpts.length; i += 2) {
float x1 = (midx + curx) / 2.0f;
float y1 = (midy + cury) / 2.0f;
prevx = curx;
prevy = cury;
if (i < ctrlpts.length) {
curx = ctrlpts[i + 0];
cury = ctrlpts[i + 1];
} else {
curx = ctrlpts[0];
cury = ctrlpts[1];
}
midx = (curx + prevx) / 2.0f;
midy = (cury + prevy) / 2.0f;
float x2 = (prevx + midx) / 2.0f;
float y2 = (prevy + midy) / 2.0f;
gp.curveTo(x1, y1, x2, y2, midx, midy);
}
gp.closePath();
if (doDraw) {
g2.setPaint(drawPaint);
g2.setStroke(stroke);
g2.draw(gp);
}
if (doFill) {
if (fillPaint instanceof GradientPaint) {
fillPaint = gradient;
}
g2.setPaint(fillPaint);
g2.fill(gp);
}
}
public Graphics2D createGraphics2D(int w, int h) {
Graphics2D g2 = null;
if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
bimg = (BufferedImage) createImage(w, h);
reset(w, h);
}
g2 = bimg.createGraphics();
g2.setBackground(getBackground());
g2.clearRect(0, 0, w, h);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
return g2;
}
public void paint(Graphics g) {
Dimension d = getSize();
step(d.width, d.height);
Graphics2D g2 = createGraphics2D(d.width, d.height);
drawDemo(d.width, d.height, g2);
g2.dispose();
if (bimg != null) {
g.drawImage(bimg, 0, 0, this);
}
}
public void start() {
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
public synchronized void stop() {
thread = null;
}
public void run() {
Thread me = Thread.currentThread();
while (thread == me) {
repaint();
try {
Thread.sleep(10);
} catch (Exception e) { break; }
}
thread = null;
}
public static void main(String argv[]) {
SignInFrame n = new SignInFrame();
n.start();
}}
In your SignInFrame constructor, you call initComponents(), but that does not exist. I think you mean to call init(). Also your JFrame does not have a size set, when I ran this under linux (Java 1.6), it worked but was tiny, you should add a setSize call.
Try it with these edits:
public class SignInFrame extends javax.swing.JFrame {
Panel panel;
/** Creates new form SignInFrame */
public SignInFrame() {
setSize (600,600);
init();
}
public void init() {
getContentPane().add(panel = new Panel());
start();
}
public void start() {
panel.start();
}
public void stop() {
panel.stop();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new SignInFrame().setVisible(true);
}
});
}
}

Categories

Resources