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();
}
}
}
Related
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
class DrawIma extends JPanel{
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<20;i++){
for (int j=0;j<20;j++) {
g.drawImage(BuArr[i*20+j], 20*i, 20*j, 20, 20, null);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
In this part, BuArr are the 400 blocks divided from a BufferedImage, now i want them to be draw one by one, but the method can not draw the blocks separately, how can i do this?
Swing is single thread and not thread safe.
This means that you should not perform any long running or blocking (Thread.sleep) operations within the IU thread (the Event Dispatching Thread). It also means that you can not update, modify or create UI elements outside of the EDT context.
Instead, use a Swing Timer to generate a repeated callback at a specified interval and render the portions of the image to something like a BufferedImage, which you can the paint to the component via its paintComponent method...
See Concurrency in Swing and How to use Swing Timers for more details
Because it was a good time waster
This generates a List of Rectangles which represent the individual blocks I want to paint, I then randomise the List and run the Timer, picking the top most Rectangle off the List and using BufferedImage#getSubImage to draw it from the master to the buffer, which gets painted to the screen...
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.Rectangle;
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.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestImage {
public static void main(String[] args) {
new TestImage();
}
public TestImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
private BufferedImage copy;
private List<Rectangle> blocks;
public TestPane() {
setBackground(Color.BLACK);
try {
master = ImageIO.read(new File("..."));
copy = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = copy.createGraphics();
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, master.getWidth(), master.getHeight());
g2d.dispose();
int blockSize = 40;
int width = master.getWidth();
int height = master.getHeight();
float aspect = Math.min(width, height) / (float) Math.max(width, height);
int blockHeight = blockSize;
blocks = new ArrayList<>(blockSize * 2);
for (int y = 0; y < master.getHeight(); y += blockHeight) {
if (y + blockHeight > master.getHeight()) {
blockHeight = master.getHeight() - y;
}
int blockWidth = blockSize;
for (int x = 0; x < master.getWidth(); x += blockWidth) {
if (x + blockWidth > master.getWidth()) {
blockWidth = master.getWidth() - x;
}
Rectangle block = new Rectangle(x, y, blockWidth, blockHeight);
blocks.add(block);
}
}
Collections.shuffle(blocks);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (blocks.isEmpty()) {
((Timer) e.getSource()).stop();
} else {
Graphics2D g2d = copy.createGraphics();
Rectangle block = blocks.remove(0);
g2d.drawImage(master.getSubimage(block.x, block.y, block.width, block.height), block.x, block.y, TestPane.this);
g2d.dispose();
repaint();
}
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return master == null ? new Dimension(200, 200) : new Dimension(master.getWidth(), master.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (copy != null) {
int x = (getWidth() - copy.getWidth()) / 2;
int y = (getHeight() - copy.getHeight()) / 2;
g2d.drawImage(copy, x, y, this);
}
g2d.dispose();
}
}
}
I am currently working on a simple top down shooter. The object is a ball that slides around the screen, and I am trying to make a sort of wet-dragging affect.
I am using Java Swing and just the default Graphics2d lib inside.
This is what I have:
and this is my goal:
I need to know how I can make a curved line that has the ability to change alpha at the trailing end. I have searched online but I can only find non-dynamic solutions. (The tail needs to update as the player moves across the screen.)
A simple solution might be to simple add each point to a List of Points which before the player is moved.
You would simply then need to iterate this list and either simple use something like Graphics#drawLine or even GeneralPath to render the "drag" line, for example...
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.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.List;
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 Drag {
public static void main(String[] args) {
new Drag();
}
public Drag() {
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 List<Point> points;
private Point pos;
private int diametere = 10;
public TestPane() {
points = new ArrayList<>(25);
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");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
ActionMap am = getActionMap();
am.put("left", new MoveAction(-5, 0));
am.put("right", new MoveAction(5, 0));
am.put("up", new MoveAction(0, -5));
am.put("down", new MoveAction(0, 5));
pos = new Point(100 - (diametere / 2), 100 - (diametere / 2));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (points.size() > 1) {
g2d.setColor(Color.RED);
GeneralPath path = new GeneralPath();
boolean started = false;
System.out.println("----");
for (Point p : points) {
if (started) {
System.out.println(p);
path.lineTo(p.x, p.y);
} else {
path.moveTo(p.x, p.y);
started = true;
}
}
g2d.draw(path);
}
int radius = (int) (diametere / 2d);
g2d.setColor(Color.GREEN);
g2d.draw(new Ellipse2D.Double(pos.x - radius, pos.y - radius, diametere, diametere));
g2d.dispose();
}
protected void moveBy(int xDelta, int yDelta) {
if (pos.x + xDelta < 0) {
xDelta = 0;
pos.x = 0;
} else if (pos.x + xDelta + diametere > getWidth()) {
xDelta = 0;
pos.x = getWidth() - diametere;
}
if (pos.y + yDelta < 0) {
yDelta = 0;
pos.y = 0;
} else if (pos.y + yDelta + diametere > getHeight()) {
yDelta = 0;
pos.y = getWidth() - diametere;
}
points.add(new Point(pos));
pos.x += xDelta;
pos.y += yDelta;
repaint();
}
public class MoveAction extends AbstractAction {
private int xDelta;
private int yDelta;
public MoveAction(int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
}
#Override
public void actionPerformed(ActionEvent e) {
moveBy(xDelta, yDelta);
}
}
}
}
Hmm.. maybe you need something like this:
public class BallArea extends JComponent {
static final int MAX_SIZE = 63;
static final BasicStroke stroke = new BasicStroke(5);
final Queue<Point> points = new LinkedList();
public BallArea() {
setSize(400, 400);
setBackground(Color.BLACK);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
if (points.size() >= MAX_SIZE) {
points.poll();
}
points.add(e.getPoint());
repaint();
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(stroke);
int i = 1;
Point prev = null;
for (Point p : points) {
if (prev == null) {
prev = p;
continue;
}
g2.setColor(new Color(255, 0, 0, i*4));
g.drawLine(prev.x, prev.y, p.x, p.y);
i++;
prev = p;
}
}
}
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);
}
});
}
}
I'm trying to draw Images with Graphics2D on JFrame.
But this code only displays blank background.
How to do that?
Java Version: SE-1.6
IDE: Eclipse
My code looks like this:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.geom.Line2D;
import java.util.TimerTask;
import javax.swing.JFrame;
public class GraphicTest extends JFrame{
public static void main(String[] args) {
GraphicTest gt = new GraphicTest();
gt.start();
}
JFrame frame;
BufferStrategy strategy;
GraphicTest(){
int width = 320;
int height = 240;
this.frame = new JFrame("test");
this.frame.setSize(width, height);
this.frame.setLocationRelativeTo(null);
this.frame.setLocation(576, 336);
this.frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
this.frame.setUndecorated(true);
this.frame.setBackground(new Color(0, 0, 0, 50));
this.frame.setVisible(true);
this.frame.setIgnoreRepaint(true);
this.frame.createBufferStrategy(2);
this.strategy = this.frame.getBufferStrategy();
}
public void onExit(){
System.exit(0);
}
void start(){
java.util.Timer timer = new java.util.Timer();
timer.schedule(new RenderTask(), 0, 16);
}
class RenderTask extends TimerTask{
int count = 0;
#Override
public void run() {
GraphicTest.this.render();
}
}
void render() {
// Some moving images
Graphics2D g2 = (Graphics2D)this.strategy.getDrawGraphics();
g2.setStroke(new BasicStroke(5.0f));
Line2D line = new Line2D.Double(20, 40, 120, 140);
g2.draw(line);
this.strategy.show();
}
}
Thank you for any help you can provide.
BufferStrategy is normally associated with heavy weight components, which don't have any concept of transparency.
Transparent and translucent (per alpha pixeling) is not "officially" supported under Java 6
Making a window translucent effects anything else painted to it...this very annoying, regardless if you are using Java 6 or 7
The secret is to make the Window transparent to begin with, then overlay a transparent component that has a special "translucent" paint effect.
Under Java 6 (update 10 I think), there became available a private API called AWTUtilities which provide the ability to make a window transparent or translucent, the following example is based on that API.
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.Window;
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.lang.reflect.Method;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransparentWindowAnimation {
public static void main(String[] args) {
new TransparentWindowAnimation();
}
public TransparentWindowAnimation() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
if (supportsPerAlphaPixel()) {
try {
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
setOpaque(frame, false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
} else {
System.err.println("Per pixel alphering is not supported");
}
}
});
}
public static boolean supportsPerAlphaPixel() {
boolean support = false;
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
support = true;
} catch (Exception exp) {
}
return support;
}
public static void setOpaque(Window window, boolean opaque) throws Exception {
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
method.invoke(null, window, opaque);
}
} catch (Exception exp) {
throw new Exception("Window opacity not supported");
}
}
public class PaintPane extends JPanel {
private BufferedImage img;
private int xPos, yPos = 100;
private int xDelta = 0;
private int yDelta = 0;
public PaintPane() {
while (xDelta == 0) {
xDelta = (int)((Math.random() * 8)) - 4;
}
while (yDelta == 0) {
yDelta = (int)((Math.random() * 8)) - 4;
}
setOpaque(false);
try {
img = ImageIO.read(new File("AngryBird.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
yPos += yDelta;
if (xPos - (img.getWidth() / 2) <= 0) {
xPos = img.getWidth() / 2;
xDelta *= -1;
}
if (xPos + (img.getWidth() / 2) >= getWidth()) {
xPos = getWidth() - (img.getWidth() / 2);
xDelta *= -1;
}
if (yPos - (img.getHeight() / 2) <= 0) {
yPos = img.getHeight() / 2;
yDelta *= -1;
}
if (yPos + (img.getHeight() / 2) >= getHeight()) {
yPos = getHeight() - (img.getHeight() / 2);
yDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(128, 128, 128, 128));
g2d.fillRect(0, 0, getWidth(), getHeight());
int x = xPos - (img.getWidth() / 2);
int y = yPos - (img.getHeight()/ 2);
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
}
Another way can be seen here. It can be accomplished by
frame.setBackground(new Color(0, 0, 0, 0));
....
setOpaque(false); //for the JPanel being painted on.