Let's say I have a grid with images in Java.
I now draw the images in the Graphics2D component g as follows:
g.drawImage(image, 50 * cellWidth, 50 * cellHeight, cellWidth, cellHeight, Color.WHITE, null)
I'm now interested in rotating the image (while staying in the same grid row and column) 90 degrees in a given direction.
Could someone help me accomplish this?
First, you need a Graphics2D context. In most cases when supplied with a Graphics it's actually an instance of Graphics2D so you can simply cast it.
Having said that though, when perform transformations, it's always useful to create a new context (this copies the state only)...
Graphics2D g2d = (Graphics2D) g.create();
Next, you want to translate the origin point. This makes it a lot easier to do things like rotation.....
g2d.translate(50 * cellWidth, 50 * cellHeight);
Then you can rotate the context around the centre point of the cell (remember, 0x0 is now our cell offset)...
g2d.rotate(Math.toRadians(90), cellWidth / 2, cellWidth / 2);
And then we can simply draw the image...
g2d.drawImage(image, 0, 0, cellWidth, cellHeight, Color.WHITE, null);
And don't forget to dispose of the copy when you're done
g2d.dispose();
You might also want to take a look at The 2D Graphics trail, as you could use a AffineTransformation instead, but it'd be accomplishing the same thing, more or less
Is there a way to actually see the rotating happening (so see the rotation "live")?
Animation is a complex subject, add in the fact that Swing is single threaded and not thread safe and you need to think carefully about it.
Have a look at Concurrency in Swing and How to Use Swing Timers for more details.
Simple animation
The following example makes use of simple Swing Timer to rotate a image when it's clicked. The example makes use of time based approach (ie the animation runs over a fixed period of time). This produces a better result then a linear/delta approach.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Simple {
public static void main(String[] args) throws IOException {
new Simple();
}
public Simple() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Advanced.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private List<BufferedImage> images;
private BufferedImage selectedImage;
public TestPane() throws IOException {
images = new ArrayList<>(9);
for (int index = 0; index < 9; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/p" + (index + 1) + ".png"));
images.add(img);
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (selectedImage != null) {
return;
}
int col = (e.getX() - 32) / 210;
int row = (e.getY() - 32) / 210;
int index = (row * 3) + col;
selectedImage = images.get(index);
startTimer();
}
});
}
private Timer timer;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(1);
private double maxAngle = 1440;
private double currentAngle = 0;
protected void startTimer() {
if (timer != null) {
return;
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double)duration.toMillis();
if (progress >= 1.0) {
progress = 1.0;
selectedImage = null;
startedAt = null;
stopTimer();
}
currentAngle = maxAngle * progress;
repaint();;
}
});
timer.start();
}
protected void stopTimer() {
if (timer == null) {
return;
}
timer.stop();
timer = null;
}
#Override
public Dimension getPreferredSize() {
return new Dimension((210 * 3) + 64, (210 * 3) + 64);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(32, 32);
int row = 0;
int col = 0;
for (BufferedImage img : images) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
if (selectedImage == img) {
gc.rotate(Math.toRadians(currentAngle), 210 / 2, 210 / 2);
}
gc.drawImage(img, 0, 0, this);
gc.dispose();
col++;
if (col >= 3) {
col = 0;
row++;
}
}
g2d.dispose();
}
}
}
nb: My images are 210x210 in size and I'm been naughty with not using the actual sizes of the images, and using fixed values instead
Advanced animation
While the above example "works", it becomes much more complicated the more you add it. For example, if you want to have multiple images rotate. Towards that end, you will need to keep track of some kind of model for each image which contains the required information to calculate the current rotation value.
Another issue is, what happens if you want to compound the animation? That is, scale and rotate the animation at the same time.
Towards this end, I'd lean towards using concepts like "time lines" and "key frames"
The following example is based on my personal library Super Simple Swing Animation Framework. This is bit more of a playground for me then a fully fledged animation framework, but it embodies many of the core concepts which help make animating in Swing simpler and help produce a much nicer result
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.kaizen.animation.Animatable;
import org.kaizen.animation.AnimatableAdapter;
import org.kaizen.animation.AnimatableDuration;
import org.kaizen.animation.DefaultAnimatableDuration;
import org.kaizen.animation.curves.Curves;
import org.kaizen.animation.timeline.BlendingTimeLine;
import org.kaizen.animation.timeline.DoubleBlender;
public class Advanced {
public static void main(String[] args) throws IOException {
new Advanced();
}
public Advanced() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Advanced.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private List<BufferedImage> images;
private Map<BufferedImage, Double> imageZoom = new HashMap<>();
private Map<BufferedImage, Double> imageRotate = new HashMap<>();
private BlendingTimeLine<Double> zoomTimeLine;
private BlendingTimeLine<Double> rotateTimeLine;
public TestPane() throws IOException {
zoomTimeLine = new BlendingTimeLine<>(new DoubleBlender());
zoomTimeLine.addKeyFrame(0, 1.0);
zoomTimeLine.addKeyFrame(0.25, 1.5);
zoomTimeLine.addKeyFrame(0.75, 1.5);
zoomTimeLine.addKeyFrame(1.0, 1.0);
rotateTimeLine = new BlendingTimeLine<>(new DoubleBlender());
rotateTimeLine.addKeyFrame(0d, 0d);
rotateTimeLine.addKeyFrame(0.1, 0d);
// rotateTimeLine.addKeyFrame(0.85, 360.0 * 4d);
rotateTimeLine.addKeyFrame(1.0, 360.0 * 4d);
images = new ArrayList<>(9);
for (int index = 0; index < 9; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/p" + (index + 1) + ".png"));
images.add(img);
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int col = (e.getX() - 32) / 210;
int row = (e.getY() - 32) / 210;
int index = (row * 3) + col;
BufferedImage selectedImage = images.get(index);
if (imageZoom.containsKey(selectedImage)) {
return;
}
animate(selectedImage);
}
});
}
protected void animate(BufferedImage img) {
Animatable animatable = new DefaultAnimatableDuration(Duration.ofSeconds(1), Curves.CUBIC_IN_OUT.getCurve(), new AnimatableAdapter<Double>() {
#Override
public void animationTimeChanged(AnimatableDuration animatable) {
double progress = animatable.getProgress();
Double desiredZoom = zoomTimeLine.getValueAt(progress);
imageZoom.put(img, desiredZoom);
double desiredAngle = rotateTimeLine.getValueAt(progress);
imageRotate.put(img, desiredAngle);
repaint();
}
#Override
public void animationStopped(Animatable animator) {
imageZoom.remove(img);
imageRotate.remove(img);
repaint();
}
});
animatable.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension((210 * 3) + 64, (210 * 3) + 64);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(32, 32);
int row = 0;
int col = 0;
for (BufferedImage img : images) {
if (!(imageZoom.containsKey(img) || imageRotate.containsKey(img))) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
gc.drawImage(img, 0, 0, this);
gc.dispose();
}
col++;
if (col >= 3) {
col = 0;
row++;
}
}
row = 0;
col = 0;
for (BufferedImage img : images) {
if (imageZoom.containsKey(img) || imageRotate.containsKey(img)) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
double width = img.getWidth();
double height = img.getHeight();
double zoom = 1;
if (imageZoom.containsKey(img)) {
zoom = imageZoom.get(img);
width = (img.getWidth() * zoom);
height = (img.getHeight() * zoom);
double xPos = (width - img.getWidth()) / 2d;
double yPos = (height - img.getHeight()) / 2d;
gc.translate(-xPos, -yPos);
}
if (imageRotate.containsKey(img)) {
double angle = imageRotate.get(img);
gc.rotate(Math.toRadians(angle), width / 2, height / 2);
}
gc.scale(zoom, zoom);
gc.drawImage(img, 0, 0, this);
gc.dispose();
}
col++;
if (col >= 3) {
col = 0;
row++;
}
}
g2d.dispose();
}
}
}
nb: The paint workflow is a little more complicated (and could be optimised more) as it focuses on painting the images which are been animated onto of the others, which results in a much nicer result
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
As i've declared in title of my question, I'm about to make a sort of editor of particular areas of a given png image to change colours pixel by pixel by clicking on it, maybe helping myself magnifying the area...
I'm mainly stuck because I don't know, ad I didn't find so far a solution to display a png which has a "grid" that divides every pixel.
I mean, a sort of thin line that like crosswords could "highlight" every pixel.
Pls point me in the right direction!
thanks!
Okay, so basically, what this is does is a very "simple" scaling process. Each pixel in the image is represented by a "cell" which has a size. Each cell is filled with the color of the pixel. A simple grid is then overlaid on top.
You can use the slider to change the scaling (making the grid larger or smaller).
The example also makes use of the tool tip support to show the pixel color
This example doesn't providing editing though. It would be a trival matter to add a MouseListener to the EditorPane and using the same algorithm as the getToolTipText method, find the pixel which needs to be updated.
My example was using a large sprite (177x345) and is intended to provide for a variable sized sprite. Smaller or fixed sized sprites will provide better performance.
import java.awt.Color;
import java.awt.Container;
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.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
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.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 SpriteEditorSpane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class SpriteEditorSpane extends JPanel {
private JLabel sprite;
private JSlider zoom;
private EditorPane editorPane;
public SpriteEditorSpane() throws IOException {
setLayout(new GridBagLayout());
BufferedImage source = ImageIO.read(new File("sprites/Doctor-01.png"));
sprite = new JLabel(new ImageIcon(source));
editorPane = new EditorPane();
editorPane.setSource(source);
zoom = new JSlider(2, 10);
zoom.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
editorPane.setGridSize(zoom.getValue());
}
});
zoom.setValue(2);
zoom.setPaintTicks(true);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridheight = GridBagConstraints.REMAINDER;
add(sprite, gbc);
gbc.gridx++;
gbc.gridheight = 1;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 1;
add(new JScrollPane(editorPane), gbc);
gbc.gridy++;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
gbc.weighty = 0;
add(zoom, gbc);
}
}
public class EditorPane extends JPanel implements Scrollable {
private BufferedImage source;
private BufferedImage gridBuffer;
private int gridSize = 2;
private Color gridColor;
private Timer updateTimer;
public EditorPane() {
updateTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doBufferUpdate();
revalidate();
repaint();
}
});
updateTimer.setRepeats(false);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
updateBuffer();
}
});
setGridColor(new Color(128, 128, 128, 128));
setToolTipText("Sprite");
}
#Override
public Dimension getPreferredSize() {
return source == null ? new Dimension(200, 200)
: new Dimension(source.getWidth() * gridSize, source.getHeight() * gridSize);
}
public void setGridColor(Color color) {
if (color != gridColor) {
this.gridColor = color;
updateBuffer();
}
}
public Color getGridColor() {
return gridColor;
}
public void setSource(BufferedImage image) {
if (image != source) {
this.source = image;
updateBuffer();
}
}
public void setGridSize(int size) {
if (size != gridSize) {
this.gridSize = size;
updateBuffer();
}
}
public BufferedImage getSource() {
return source;
}
public int getGridSize() {
return gridSize;
}
#Override
public String getToolTipText(MouseEvent event) {
Point p = event.getPoint();
int x = p.x / getGridSize();
int y = p.y / getGridSize();
BufferedImage source = getSource();
String tip = null;
if (x < source.getWidth() && y < source.getHeight()) {
Color pixel = new Color(source.getRGB(x, y), true);
StringBuilder sb = new StringBuilder(128);
sb.append("<html><table><tr><td>");
sb.append("R:").append(pixel.getRed());
sb.append(" G:").append(pixel.getGreen());
sb.append(" B:").append(pixel.getBlue());
sb.append(" A:").append(pixel.getAlpha());
String hex = String.format("#%02x%02x%02x%02x", pixel.getRed(), pixel.getGreen(), pixel.getBlue(), pixel.getAlpha());
sb.append("</td></tr><tr><td bgcolor=").append(hex);
sb.append("width=20 height=20> </td></tr></table>");
tip = sb.toString();
}
return tip;
}
#Override
public Point getToolTipLocation(MouseEvent event) {
Point p = new Point(event.getPoint());
p.x += 8;
p.y += 8;
return p;
}
protected void doBufferUpdate() {
BufferedImage source = getSource();
int gridSize = getGridSize();
gridBuffer = null;
if (source != null) {
gridBuffer = new BufferedImage(source.getWidth() * gridSize, source.getHeight() * gridSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = gridBuffer.createGraphics();
for (int row = 0; row < source.getHeight(); row++) {
for (int col = 0; col < source.getWidth(); col++) {
int xPos = col * gridSize;
int yPos = row * gridSize;
Color pixel = new Color(source.getRGB(col, row), true);
g2d.setColor(pixel);
g2d.fillRect(xPos, yPos, gridSize, gridSize);
g2d.setColor(getGridColor());
g2d.drawRect(xPos, yPos, gridSize, gridSize);
}
}
g2d.dispose();
} else if (getWidth() > 0 && getHeight() > 0) {
gridBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = gridBuffer.createGraphics();
g2d.setColor(gridColor);
for (int xPos = 0; xPos < getWidth(); xPos += gridSize) {
g2d.drawLine(xPos, 0, xPos, getHeight());
}
for (int yPos = 0; yPos < getHeight(); yPos += gridSize) {
g2d.drawLine(0, yPos, getWidth(), yPos);
}
g2d.dispose();
}
}
protected void updateBuffer() {
updateTimer.restart();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (gridBuffer != null) {
g2d.drawImage(gridBuffer, 0, 0, this);
}
g2d.dispose();
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 200);
}
#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() {
Container parent = getParent();
return parent instanceof JViewport
&& parent.getWidth() > getPreferredSize().width;
}
#Override
public boolean getScrollableTracksViewportHeight() {
Container parent = getParent();
return parent instanceof JViewport
&& parent.getHeight() > getPreferredSize().height;
}
}
}
The overall performance is pretty slow when generating the "grid", you might be able to use byte[] bytes = ((DataBufferByte)gridBuffer.getRaster().getDataBuffer()).getData() which will give you a byte array of the pixels, but in my testing, it didn't make that big a difference.
You might also like to have a look at Zoom box for area around mouse location on screen
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.
I'm fairly new to programming with graphics and I'm attempting to code a side scrolling 2D game. At the moment, I'm trying to figure out how to approach redrawing a scrolling image as it appears in the JFrame. I'm using 8x8 pixel blocks as images. One possible issue I thought about concerns moving a sprite just 1 or 2 pixels and still rendering each image as it appears pixel by pixel on/off of the screen. How do I go about rendering the image/blocks pixel by pixel instead of whole images should the sprite barely move? Any feedback is much appreciated!
This is a proof of concept only! I randomly generate the tiles that get painted, I hope you have some kind of virtual map setup so you know which tiles to paint at any given virtual point!
Basically, what this does, is when the screen is moved left or right, it shifts the "master" image left or right and stitches new tiles onto new edge
My test was using a style sheet of 31x31 cells (don't ask, I just grab it off the net)
This is VERY scaled down example of the output, it was running at 1100x700+
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
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.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Scroll {
public static void main(String[] args) {
new Scroll();
}
public Scroll() {
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 {
private BufferedImage screen;
private BufferedImage styleSheet;
public TestPane() {
try {
styleSheet = ImageIO.read(getClass().getResource("/StyleSheet.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
ActionMap am = getActionMap();
am.put("left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
stitch(-31);
}
});
am.put("right", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
stitch(31);
}
});
}
#Override
public void invalidate() {
screen = null;
super.invalidate();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void stitch(int direction) {
if (screen == null) {
prepareScreen();
}
Random r = new Random();
BufferedImage update = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = update.createGraphics();
g2d.drawImage(screen, direction, 0, this);
int gap = direction < 0 ? (direction * -1) : direction;
int xOffset = 0;
if (direction < 0) {
xOffset = getWidth() - gap;
}
for (int x = 0; x < gap; x += 31) {
for (int y = 0; y < getHeight(); y += 31) {
xOffset += x;
int cellx = 2;
int celly = 2;
if (r.nextBoolean()) {
cellx = 7;
celly = 5;
}
BufferedImage tile = styleSheet.getSubimage((cellx * 33) + 1, (celly * 33) + 1, 31, 31);
g2d.drawImage(tile, xOffset, y, this);
}
}
g2d.dispose();
screen = update;
repaint();
}
protected void prepareScreen() {
if (screen == null) {
screen = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Random r = new Random();
Graphics2D g2d = screen.createGraphics();
for (int x = 0; x < getWidth(); x += 31) {
for (int y = 0; y < getHeight(); y += 31) {
int cellx = 2;
int celly = 2;
if (r.nextBoolean()) {
cellx = 7;
celly = 5;
}
BufferedImage tile = styleSheet.getSubimage((cellx * 33) + 1, (celly * 33) + 1, 31, 31);
g2d.drawImage(tile, x, y, this);
}
}
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (screen == null) {
prepareScreen();
}
g2d.drawImage(screen, 0, 0, this);
g2d.dispose();
}
}
}
What I have implemented till now in java is ask the user to upload an image from the directory. My next step is that when the image is loaded a grid is placed above that image just for visual purpose so that the image gets divided in a, say 10 x 10 grids. How do I implement this stuff? Here's what I have implemented till now.
JFileChooser choose=new JFileChooser();
choose.showOpenDialog(null);
File f=choose.getSelectedFile();
String filename=f.getAbsolutePath();
path.setText(filename);
BufferedImage img;
try {
img=ImageIO.read(f);
Image dimg = img.getScaledInstance(500,500,Image.SCALE_SMOOTH);
ImageIcon imageIcon = new ImageIcon(dimg);
image_label.setIcon(imageIcon);
}
catch(Exception e) {
System.out.println(e);
}
paint the image in a panel
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
Then based on the the number of cells you want, say 10x10, just draw 100 cells (drawRect()) over the image. Something like
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
int cellHeight = (int)(getHeight() / 10);
int cellWidth = (int)(getWidth() / 10);
for (int y = 0; y < getWidth(); y += cellHeight) {
for (int x = 0; x < getHeight(); x += cellWidth){
g.drawRect(x, y, cellWidth, cellHeight);
}
}
}
I haven't test it, but the basic concept is there. You may also want to use variables (a constant probably) for the 10.
UPDATE 1
You can see the precision's a little off because I used int, but you can use doubles and draw by using Grapchics2D Rectangle2D.Double. I'm too lazy to change it
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageGrid extends JPanel {
private static final int CELLS = 10;
BufferedImage img;
public ImageGrid() {
try {
img = ImageIO.read(getClass().getResource("/resources/stackoverflow5.png"));
} catch (IOException ex) {
Logger.getLogger(ImageGrid.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
int cellHeight = (int) (getHeight() / CELLS);
int cellWidth = (int) (getWidth() / CELLS);
for (int y = 0; y < getHeight(); y += cellHeight) {
for (int x = 0; x < getWidth(); x += cellWidth) {
g.drawRect(x, y, cellWidth, cellHeight);
}
}
}
}
#Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(300, 300)
: new Dimension(img.getWidth(), img.getHeight());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
JPanel wrapperPanel = new JPanel(new GridBagLayout());
wrapperPanel.add(new ImageGrid());
frame.add(wrapperPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
UPDATE 2 With JLabel
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageGrid extends JLabel {
private static final int CELLS = 10;
BufferedImage img;
public ImageGrid() {
try {
img = ImageIO.read(getClass().getResource("/resources/stackoverflow5.png"));
setIcon(new ImageIcon(img));
} catch (IOException ex) {
Logger.getLogger(ImageGrid.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
int cellHeight = (int) (getHeight() / CELLS);
int cellWidth = (int) (getWidth() / CELLS);
for (int y = 0; y < getHeight(); y += cellHeight) {
for (int x = 0; x < getWidth(); x += cellWidth) {
g.drawRect(x, y, cellWidth, cellHeight);
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
JPanel wrapperPanel = new JPanel(new GridBagLayout());
wrapperPanel.add(new ImageGrid());
frame.add(wrapperPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Let's say I have a BufferedImage of type TYPE_4BYTE_ABGR in Swing and I want to draw only a part of it. For example I would like to draw the left half only or some triangular shape or something more complicated.
Reason is that the final image shall be composed from subparts of individual images I have.
What's the best way to do that?
I would prefer to define a polygon and then use this shape as a mask for drawing, if this is possible.
My current idea: make a copy of the individual image and set all pixels outside the wished shape to transparent, then draw the whole image. I think this might work but might be too slow with the copying and all.
edit:
I tested the solution of Guillaume and found that it works and does not extremely slow down the painting. Using a clip resulted in an increase of drawing time from 14ms to 35ms but these times are very inaccurate. I used profiling the EDT from here. Here is the code.
import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
*
*/
public class ClipTilesTest {
// tile size and number of tiles in each row/column
private static int TILE_SIZE = 100;
private static int TILE_NUM = 6;
// taken from https://stackoverflow.com/questions/5541493/how-do-i-profile-the-edt-in-java-swing
public static class TimedEventQueue extends EventQueue {
#Override
protected void dispatchEvent(AWTEvent event) {
long startNano = System.nanoTime();
super.dispatchEvent(event);
long endNano = System.nanoTime();
if (endNano - startNano > 5000000) {
System.out.println(((endNano - startNano) / 1000000) + "ms : " + event);
}
}
}
private static void initUI() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new TimedEventQueue());
// download image
BufferedImage image;
try {
image = ImageIO.read(new URL("http://download.chip.eu//ii/163859211_4b28e1e687.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
return;
}
// take out small chunk
final BufferedImage tile = image.getSubimage(0, 0, TILE_SIZE, TILE_SIZE);
JFrame frame = new JFrame();
frame.setTitle(ClipTilesTest.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// the panel containing some tiles
JPanel view = new JPanel() {
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < TILE_NUM; i++) {
for (int j = 0; j < TILE_NUM; j++) {
// version 1
/*
g2d.setClip(i * TILE_SIZE, j * TILE_SIZE , (i+1)*TILE_SIZE, (j+1)*TILE_SIZE);
g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
*/
// version 2
g2d.setClip(i * TILE_SIZE, j * TILE_SIZE , i*TILE_SIZE + TILE_SIZE/2, (j+1)*TILE_SIZE);
g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
g2d.setClip(i * TILE_SIZE + TILE_SIZE/2, j * TILE_SIZE , (i+1)*TILE_SIZE , (j+1)*TILE_SIZE);
g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
}
}
}
};
view.setPreferredSize(new Dimension(TILE_SIZE * TILE_NUM, TILE_SIZE * TILE_NUM));
// add, pack, set visible
frame.add(view);
frame.pack();
frame.setVisible(true);
// now make a repaint event, so we can start measuring
view.repaint();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ClipTilesTest.initUI();
}
});
}
}
One easy way to achieve this effect, is to modify the "clip" of the Graphics object and to set it to the shape you want to draw.
I don't know how efficient this is, but you could consider caching the clipped image and then draw the entire cached image.
Here is a small demo code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestClippedPanel {
private static class ClippedPanel extends JPanel {
private ImageIcon image;
private List<Shape> shapes;
public ClippedPanel() throws MalformedURLException {
shapes = new ArrayList<Shape>();
image = new ImageIcon(new URL("http://download.chip.eu//ii/163859211_4b28e1e687.jpg"));
Random random = new Random();
for (int i = 0; i < 10; i++) {
int x = random.nextInt(image.getIconWidth() - 1);
int y = random.nextInt(image.getIconHeight() - 1);
int w = random.nextInt(image.getIconWidth() - x) + 1;
int h = random.nextInt(image.getIconHeight() - y) + 1;
shapes.add(new Rectangle(x, y, w, h));
}
for (int i = 0; i < 10; i++) {
int x = random.nextInt(image.getIconWidth() - 1);
int y = random.nextInt(image.getIconHeight() - 1);
int w = random.nextInt(image.getIconWidth() - x) + 1;
int h = random.nextInt(image.getIconHeight() - y) + 1;
shapes.add(new Ellipse2D.Double(x, y, w, h));
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Image img = image.getImage();
for (Shape shape : shapes) {
((Graphics2D) g).setClip(shape);
g.drawImage(img, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getIconWidth(), image.getIconHeight());
}
}
protected void initUI() throws MalformedURLException {
final JFrame frame = new JFrame(TestClippedPanel.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final ClippedPanel panel = new ClippedPanel();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new TestClippedPanel().initUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}