I am creating a program where I have to reflect an image horizontally and vertically. I have created a geometric shape image, but I am having a hard time figuring out how to flip my picture. I was wondering if someone could help me and tell me what to do to flip a picture. Thanks
My code so far is :
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
class DrawingDemoV3
{
Picture canvas = null;
Graphics g = null;
Graphics2D g2 = null;
DrawingDemoV3(int length, int height, Color color)
{
canvas = new Picture(length, height);
canvas.setAllPixelsToAColor(color);
g = canvas.getGraphics();
g2 = (Graphics2D)g;
g2.setColor(color);
}
public void drawAFilledOval(Color color, int x1, int y1, int width, int height)
{
g2.setColor(color);
g2.fillOval(x1, y1, width, height);
}
public void drawARectangle(Color color, int x1, int y1, int width, int height)
{
g2.setColor(color);
g2.drawRect(x1, y1, width, height);
}
public void drawAFilledRectangle(Color color, int x1, int y1, int width, int height)
{
g2.setColor(color);
g2.fillRect(x1,y1, width, height);
}
public void drawALine(Color color, int x1, int y1, int x2, int y2)
{
g2.setColor(color);
g2.drawLine(x1,y1,x2,y2);
}
public Picture getDrawing()
{
return canvas;
}
}
public class DrawingDemoTesterV3
{
public static void main(String[] args)
{
DrawingDemoV3 drawing1 = new DrawingDemoV3(200, 200, Color.BLACK);
drawing1.drawAFilledRectangle(Color.PINK, 90, 0, 20, 200);
drawing1.drawAFilledRectangle(Color.PINK, 0, 90, 200, 20);
drawing1.drawARectangle(Color.CYAN, 40, 40, 120, 120);
drawing1.drawALine(Color.ORANGE, 0,0, 200, 200);
drawing1.drawALine(Color.ORANGE, 200,0, 0, 200);
drawing1.drawAFilledOval(Color.YELLOW, 80, 80, 38, 36);
Picture picture1 = drawing1.getDrawing();
picture1.show();
}
}
Flipping/Reflection on an image.
The goal is to use Affine Transform for sequences of translations, scales, flips, rotations, and shears applied to 2D points and mapping.
While link only answers are typically frowned on, I don't want to just copy paste and rip off a perfectly explained answer and fill in his words with mine.
Related
I have been searching all over the internet for a simple way to just rotate a sprite following an angle.
The objective is to have a weapon sprite following the mouse by rotating at the centre of the screen (Top-down shooter in Java). I have tried different things:
NB: The render(Graphics g) function is inside my Gun.java class and uses g, the Graphics element I use to paint on the canvas of the game. The image is the BufferedImage containing the original sprite. And reticle.getAngle() is giving the angle made by the mouse considering the centre of the screen as the origin of the frame.
Attempt 1
public void render(Graphics g) {
// create a new BufferedImage with the image of the gun on it
BufferedImage rotatedImage = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics gRotatedImage = rotatedImage.getGraphics();
gRotatedImage.drawImage(image, 0, 0, null);
// rotate this gun image in the direction of shoot
private AffineTransform at = new AffineTransform();
at.rotate(reticle.getAngle() + Math.PI,
rotatedImage.getWidth()/2, rotatedImage.getHeight()/2);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
rotatedImage = op.filter(rotatedImage, null);
// finally display the rotated version of the gun image
g.drawImage(rotatedImage,
(int)(handler.getDisplay().getWidth()/2 - rotatedImage.getWidth()/2),
(int)(handler.getDisplay().getHeight()/2 - rotatedImage.getHeight()/2),
rotatedImage.getWidth(), rotatedImage.getHeight(), null);
}
With this solution, from java2s.com, I end up with the sprite being displayed at the centre and rotating but more like a helicopter... It keeps rotating not following the mouse.
I also tested all the solutions from the related StackOverflow question. This time, I get the weapon being displayed at the top left corner following the mouse, but I can't find a way to place it at the centre of the screen. I tried translations but then the sprite image rotates considering the top left corner as the centre.
Attempt 2
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
double rotation = 0f;
int width = image.getWidth() - 1;
int height = image.getHeight() - 1;
rotation = reticle.getAngle();
rotation = Math.toDegrees(rotation) + 180;
g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
// g2d.translate(handler.getDisplay().getWidth()/2, handler.getDisplay().getHeight()/2);
g2d.drawImage(image, 0, 0, null);
int x = width / 2;
int y = height / 2;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.drawLine(x, y, x, y - height / 4);
g2d.dispose();
}
I just would like to rotate my sprite every tick of the game by the angle provided by reticle.getAngle() which I know is good. I feel really lost on how to use Graphics2D or AffineTransform to perform rotation. Can someone provide an example on how to rotate a sprite following the mouse and then display it at the centre of the screen?
What is the best way to rotate an image which we then want to display at the centre of the screen?
I don't know if this helps, but I wrote this example program, which has a rectangle (works the same as an image really...) at the center of the screen which rotates following your mouse. The important stuff is in the paintComponent method, pretty much everything else is setup.
package main;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
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 javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class GPanel extends JPanel{
private int width, height;
private Timer timer;
private int mouseX, mouseY;
public static void main(String[] args) {
JFrame f = new JFrame();
GPanel gp = new GPanel(400, 400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(gp,BorderLayout.CENTER);
f.pack();
f.setVisible(true);
}
public GPanel(int width, int height) {
mouseX = 0;
mouseY = 0;
this.width = width;
this.height = height;
this.setPreferredSize(new Dimension(width, height));
timer = new Timer(17 , new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TO DO
repaint();
}
});
addMouseMotionListener();
timer.start();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g.setColor(new Color(255, 255, 255));
g2.fillRect(0, 0, width, height);
g2.setColor(new Color(0, 0, 0));
g2.translate(width / 2, height / 2);
double x = mouseX - width / 2d;
double y = mouseY - height / 2d;
double theta = Math.atan2(x, y);
g2.rotate(-theta);
g2.translate(-20, 0);
g2.fillRect(0, 0, 40, 100);
}
private void addMouseMotionListener() {
this.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
}
#Override
public void mouseDragged(MouseEvent e) {
if(SwingUtilities.isLeftMouseButton(e)) {
//TO DO
} else if(SwingUtilities.isRightMouseButton(e)) {
}
repaint();
}
});
}
}
What I would do is create an affine transformation for the sprite. I imagine a sprite, img should be at (cx, cy) and the angle it is rotated should be theta.
Graphics2D g2d = (Graphics2D)g;
AffineTransform at = new AffineTransform();
//translate the center to be at cx, cy.
at.translate(cx - img.getWidth()/2.0, cy - img.getHeight()/2.0);
//rotate about the center of the sprite.
at.rotate(theta, img.getWidth()/2.0, img.getHeight()/2.0);
g2d.drawImage(img, at, this);
I tested it out with this example, where you can click on the JPanel and center the sprite. The sprite follows the mouse cursor around when you move it.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
public class RotateArrow{
int cx = 0;
int cy = 0;
double theta = 0;
public void startGui(){
JFrame frame = new JFrame("arrow");
BufferedImage img = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
g.setColor(Color.RED);
g.drawLine(0, 32, 64, 32);
g.drawLine(48, 0, 48, 64);
g.dispose();
JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
AffineTransform at = new AffineTransform();
//rotate about center of image.
at.translate(cx - img.getWidth()/2.0, cy - img.getHeight()/2.0);
at.rotate(theta, img.getWidth()/2.0, img.getHeight()/2.0);
g2d.drawImage(img, at, this);
}
};
panel.addMouseListener( new MouseAdapter(){
#Override
public void mousePressed(MouseEvent evt){
cx = evt.getX();
cy = evt.getY();
panel.repaint();
}
} );
panel.addMouseMotionListener( new MouseAdapter(){
#Override
public void mouseMoved(MouseEvent evt){
double dx = evt.getX() - cx;
double dy = evt.getY() - cy;
if(dx != 0 || cy != 0){
theta = Math.atan2(dy, dx);
panel.repaint();
}
}
} );
frame.setContentPane(panel);
frame.setSize(512, 512);
frame.setVisible(true);
}
public static void main(String[] args){
EventQueue.invokeLater( ()->new RotateArrow().startGui() );
}
}
when i use doublebuffering, painted image is expanded with white background.
Is there something wrong?
enter code here
private Image image_buffer;
private Graphics graphics_buffer
public void paint(Graphics g) {
super.paint(g);
buffering(img1, x1, 40, g);
}
public void buffering(Image img, int x, int y, Graphics g){
image_buffer = createImage(100,100);
graphics_buffer = image_buffer.getGraphics();
buffer.drawImage(img, x, y, this);
g.drawImage(image_buffer, x, y, this);
}
Why not replace your code with just:-
public void paint(Graphics g) {
super.paint(g);
g.drawImage(img1, x1, 40, this);
}
The reason you have a white background is because you're creating a 100x100 "canvas", and drawing on that.
I am trying to create a grid in a JPanel with lines, and to do this, I draw evenly spaced horizontal and vertical lines until I reach the end of the JPanel. I use a class called Drawing which extends JPanel and is the object I add to the window. Below is my code.
public final class Drawing extends JPanel {
public Drawing() {
super();
setBackground(new Color(255, 255, 255));
setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
GroupLayout workspacePanelLayout = new GroupLayout(this);
setLayout(workspacePanelLayout);
workspacePanelLayout.setHorizontalGroup(workspacePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGap(0, 343, Short.MAX_VALUE));
workspacePanelLayout.setVerticalGroup(workspacePanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGap(0, 400, Short.MAX_VALUE));
initWorkSpace();
}
private static class Line {
final int x1;
final int y1;
final int x2;
final int y2;
final Color color;
public Line(int x1, int y1, int x2, int y2, Color color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
}
}
private final LinkedList<Line> lines = new LinkedList<>();
public void addLine(int x1, int x2, int x3, int x4) {
addLine(x1, x2, x3, x4, Color.black);
}
public void addLine(int x1, int x2, int x3, int x4, Color color) {
lines.add(new Line(x1, x2, x3, x4, color));
repaint();
}
public void clearLines() {
lines.clear();
repaint();
}
#Override
private void paintComponent(Graphics g) {
super.paintComponent(g);
for (Line line : lines) {
g.setColor(line.color);
g.drawLine(line.x1, line.y1, line.x2, line.y2);
}
}
public void initWorkSpace() {
int x = 0;
int y = 0;
while (x < this.getWidth()) {
addLine(x, 0, x, this.getHeight(), new Color(153, 153, 153));
x += getSpacing();
}
while (y < this.getHeight()) {
addLine(0, y, this.getWidth(), y, new Color(153, 153, 153));
y += getSpacing();
}
}
}
The problem is that 'this.getHeight()' and 'this.getWidth()' both return 0 so the grid doesn't get drawn. drawing the lines works fine, its just that the panel apparently has no dimension. How do I solve this.
Not the main problem but you need to override the getPreferredSize() method of your class to return the size.
Each component is responsible for determining its own preferred size.
Then the layout manager can use this information when the panel is added to a parent panel.
Of course this assumes the parent panel is using a layout manager which you should be doing.
For more information and working examples read the section from the Swing tutorial on Custom Painting
The real problem is when you invoke the following method:
initWorkSpace();
All components have a zero size when the component is created. So when the above method is invoked from the constructor the size will always be zero.
If your painting code is dynamic which means it changes as the frame is resized, then you need to invoke that logic inside the paintComponent() method.
Or if your logic is too complex to execute every time the component is repainted, you can add a ComponentListener to the panel and handle the componentResized method and invoke that method.
Also, I'm not sure why you are using a GroupLayout when you are doing custom painting.
I've made a Button class which allows me to have buttons (Kind of obvious). But in my button class, I'm using an image to display the button on the screen. I got that to work, but I want to resize the image to the size of the button.
My "Image Resizer" works flawlessly, but when I try to resize the button, the button doesn't show up. I don't get any errors.
Here's my Button class:
private String text;
private int size = 0;
private BufferedImage buttonHD;
public Button(int x, int y, int width, int height, int size) {
super(x, y, width, height);
this.size = size;
buttonHD = Renderer.resizeImage(Images.button, x, y, width, height);
}
public Button setText(String text) {
this.text = text;
return this;
}
public void drawButton(Graphics g, int xoffset, int yoffset) {
int xx = x + xoffset;
int yy = y + yoffset;
if(!MouseInput.MOUSE.intersects(this)) {
g.drawImage(buttonHD, x, y, width, height, null);
} else if(MouseInput.MOUSE.intersects(this)){
g.setColor(Color.DARK_GRAY);
g.fillRect(x, y, width, height);
}
Renderer.drawText(text, g, xoffset, yoffset, size);//Draws button text
}
The original image that I'm resizing is stored into my Images class as:
public static BufferedImage button;
Here's my "Button Resizer" method:
public static BufferedImage resizeImage(BufferedImage origImg, int x, int y, int initWidth, int initHeight) {
BufferedImage resizedImg = new BufferedImage(initWidth, initHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = resizedImg.createGraphics();
g2d.drawImage(origImg, x, y, initWidth, initHeight, null);
g2d.dispose();
return resizedImg;
}
The way I'm using these buttons are in ScreenState classes. Each class representing as each state. The buttons are set in there and are loaded up by the class's constructor.
The buttons do work as they should, but the images just don't show up. If more code is needed, just let me know and I'll provide you with it.
I've been trying to fix this problem, but had no luck. If someone could just hint out as to where my problem is or maybe have a solution, that'd be great. Thanks!
This function resizes the BufferedImage to the given width and height:
public static BufferedImage resizeImage(BufferedImage image, int width, int height) {
// calculate the scale factor
double xScale = width / (double) image.getWidth();
double yScale = height / (double) image.getHeight();
// create the object that will contain the resized image
BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// resize the image
Graphics2D g = (Graphics2D) resizedImage.getGraphics();
g.scale(xScale, yScale);
g.drawImage(image, 0, 0, null);
g.dispose();
// return the resized image
return resizedImage;
}
Then simply use it:
public class MyButton extends JButton
{
private BufferedImage image;
public MyButton() {
image = resizeImage(ImageIO.read(IMAGE_PATH), BUTTON_WIDTH, BUTTON_HEIGHT);
}
#Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
}
IMAGE_PATH is the File where your image is located, BUTTON_WIDTH and BUTTON_HEIGHT is your button dimension.
I have a 10000x10000 BufferedImage and I'm looking to draw only part of it to a Canvas, is there a way to do this using args such as:
x, y, width, height ?
So for example, drawImage(img, x, y, width, height) would draw a rectangle from the image starting at (x, y) and having (width, height) as the dimensions?
EDIT:
I'm going to re- word this question:
I have a 10000x10000 image and I only want to display a portion of it on the screen, the problem with just offsetting it by x and y is that this still causes lag as the entire image is being rendered, just most of it off canvas. How can I basically make it so that the entire image is rendered but I can scroll around it without causing the canvas to lag?
I have a 10000x10000 BufferedImage and I'm looking to draw only part
of it to a Canvas, is there a way to do this using args such as:
Don't use canvas for custom painting in java. use JComponent or JPanel instead. It has a nice function paintComponent(Graphics g), override it and paint your image inside with g.drawImage(x, y, width, height, observer);
Swing graphics has Graphics.clipRect(int x, int y, int width, int height) to bound the area rectangle to which you wish to draw prior to drawing the image.
Edit (In response to your edited question):
First approach is to use BufferedImage..getSubimage(x, y, width, height) to get a sub image with specified rectangle region. It is faster.
BufferedImage img = ImageIO.read(new File("file"));
img = img.getSubimage(50, 50, 500, 500); // 500 x 500
This function will give you a new image cropped with the rectangle(x, y, width, height) of your original image you specified. Use the returned image to draw on your component.
Tutorial resource: Clipping the Drawing Region
Demo: Demonstrating clipping Image with Animation:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.logging.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.Timer;
class MyCanvas extends JPanel implements ActionListener
{
public BufferedImage buffImg;
public Rectangle rectangle;
Random random;
long lastTimeChanged;
int dirX = 1, dirY = 1;
volatile static boolean imageLoading = true;
public MyCanvas() {
random = new Random();
rectangle = new Rectangle(50, 50, 250, 250);
lastTimeChanged = System.currentTimeMillis();
setBackground(Color.WHITE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(imageLoading)
{
showWaitForLoading(g);
return;
}
g.clipRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
g.drawImage(buffImg, 0, 0, getWidth(), getHeight(), this);
}
public void showWaitForLoading(Graphics g)
{
Graphics2D g2d = (Graphics2D)g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.DARK_GRAY);
g2d.fillRoundRect(getWidth()/2-100, getHeight()/2-15, 200, 30, 30, 30);
g2d.setColor(Color.WHITE);
g2d.drawString("Loading image...", getWidth()/2 - 45, getHeight()/2 + 3 );
g2d.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
long endTime = System.currentTimeMillis();
if(endTime - lastTimeChanged > 500)
{
dirX = random.nextInt(2) == 0 ? -1 : 1;
dirY = random.nextInt(2) == 0 ? -1 : 1;
lastTimeChanged = endTime;
}
if(rectangle.x < 0)dirX = 1;
else if(rectangle.x + rectangle.width > getWidth())dirX = -1;
if(rectangle.y < 0)dirY = 1;
else if(rectangle.y + rectangle.height > getHeight())dirY = -1;
rectangle.x = rectangle.x + dirX * 10;
rectangle.y = rectangle.y + dirY * 10;;
repaint();
}
}
public class CustomPainting {
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final MyCanvas canvas = new MyCanvas();
JFrame frame = new JFrame();
frame.setSize(new Dimension(500, 500));
frame.add(canvas);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Timer timer = new Timer(200, canvas);
timer.start();
new Thread()
{
public void run()
{
try {
canvas.buffImg = ImageIO.read(new URL("http://images6.fanpop.com/image/photos/33400000/Cute-Panda-beautiful-pictures-33434826-500-500.jpg"));
MyCanvas.imageLoading = false;
} catch (IOException ex) {
Logger.getLogger(CustomPainting.class.getName()).log(Level.SEVERE, null, ex);
}
}
}.start();
}
});
}
}
You can scale or draw a part of an image using Graphics.drawImage as mentioned another answer and according to Java documentation, ImageObserver argument is not needed for BufferedImage so you can just pass null.
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html
However, my choice would be the clipping drawing region of image instead.
Here is an example you can try:
Graphics2D g = BufferedImage.getGraphics;
g.setClip(x, y, width, height);
g.drawImage(sx, sy, x - sx, y - sy, null );
Yes there is: drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)