I have created an applet with a red circle to move from top left to bottom right. That occurs but the whole frame is flickering even though I generate the circle as a buffered image. Here is the code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;
public class MovingCircleApplet extends JApplet implements Runnable {
private int x_pos;
private int y_pos;
private int radius;
private Thread circle;
#Override
public void init() {
x_pos = 20;
y_pos = 40;
radius = 10;
setSize(245,265);
circle = new Thread(this);
circle.start();
}
#Override
public void run()
{
while (true)
{
// redraw
repaint();
try
{
// sleep thread for 20 milliseconds
Thread.sleep(20);
}
catch (InterruptedException e)
{
System.out.println("Terminated prematurely due to interruption");
}
// move circle to lower right corner
if (x_pos < 225)
{
x_pos++;
y_pos++;
}
// restart at top right corner
else
{
x_pos = 20;
y_pos = 40;
}
}
}
#Override
public void paint(Graphics g)
{
super.paint(g);
g.drawImage(getFrame(), 0, 0, null);
}
private Image getFrame()
{
// Buffered image to prevent flickering
BufferedImage img = new BufferedImage(getWidth(),getHeight(),
BufferedImage.TYPE_3BYTE_BGR);
Graphics g = img.getGraphics();
// white background
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
// red circle
g.setColor(Color.RED);
g.fillOval(x_pos-radius, y_pos-radius, 2*radius, 2*radius);
//
g.dispose();
return img;
}
}
Related
I am making a game (like Civilization) that has different tile types that I want to render as images. I have 9 different 16x16 png images to load in (called Con1, Con2, etc.), and here is my image loading code: (img[] is my BufferedImage array)
public void loadImages(){
for(int i = 0; i < 9; i++){
try {
img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
}catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
I then paint these images with this code: (t[][] is my tile type array)
public void paint(Graphics g){
if(loop){
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
for (int x = 0; x < WIDTH; x++){
for (int y = 0; y < HEIGHT; y++){
if(i[x][y] == 0){
if (t[x][y] == 0){
g.drawImage(img[0], x, y, this);
}
else if(t[x][y] == 1){
g.drawImage(img[1], x, y, this);
}
else if(t[x][y] == 3){
g.drawImage(img[3], x, y, this);
}
else if(t[x][y] == 4){
g.drawImage(img[4], x, y, this);
}
else if(t[x][y] == 5){
g.drawImage(img[5], x, y, this);
}
}
r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
}
}
g.drawImage(B, 0, 22, this);
}
}
My problem is that it doesn't show up correctly when I run it. I get this image:
that flashes on and off in the top-left corner of the window. What I am supposed to see is an image similar to the top-left portion of the above one (landmasses surrounded by ocean) except large enough to fill the window. Here is some runnable code: (I don't think the code will run without the required images but I would appreciate some help with getting the images to you all.)
//imports
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class MCVCon extends JFrame implements KeyListener, MouseListener{
//setting up variables
public BufferedImage[] img = new BufferedImage[9];
private final int WIDTH = 50, HEIGHT = 50;
private boolean loop = false;
private int SCALE = 16;
int t[][] = new int[WIDTH][HEIGHT]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
public MCVCon(){
//creating the window
super("Conqueror");
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
setVisible(true);
requestFocusInWindow();
setDefaultCloseOperation(EXIT_ON_CLOSE);
loadImages();
loop = true;
while(true){
this.repaint();
//delay for repaint
try{
Thread.sleep(50);
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
//load images
public void loadImages(){
for(int i = 0; i < 9; i++){
try {
img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
}catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
//paint the images
public void paint(Graphics g){
if(loop){
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
for (int x = 0; x < WIDTH; x++){
for (int y = 0; y < HEIGHT; y++){
if (t[x][y] == 0){
g.drawImage(img[0], x, y, this);
}
else if(t[x][y] == 1){
g.drawImage(img[1], x, y, this);
}
r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
}
}
g.drawImage(B, 0, 22, this);
}
}
//run the program
public static void main(String[] args) {
new MCVCon();
}
//necessary overrides
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
I was wondering what the problem might be. Thanks in advance.
So, I had a look at your code, there's no easy way to say, but it's a mess, with compounding issues which would make it very difficult to isolate the origin of any one problem, other than to say, they all feed into each other.
Let's start with the painting...
You're painting directly to the frame. This is generally discouraged for a number of reasons.
Let's start with the fact that JFrame isn't a single component, it's made up of a number compounding components
This makes it inherently dangerous to paint directly to, as any one of the child components could be painted without the frame's paint method been called.
Painting directly to a frame also means you're painting without consideration to the frame's borders/decorations, which are added into the visible area of the window, but wait...
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
suggests that you've tried to compensate for this, but this is "guess" work, as the decorations could take up more or less space depending on the configuration of the system
And, finally, top level containers aren't actually double buffered.
"But I'm painting to my own buffer" you say - but you're not
You create a BufferdImage and assign it's Graphics context t r
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
But when you paint anything, you're using g, which is the Graphics context passed to the paint method
g.drawImage(img[0], x, y, this);
This is also woefully inefficient, as you're creating a new BufferedImage each time paint is called, which takes time to create, takes up memory and puts extra strain on the garbage collection process as the local object becomes eligible for disposal almost immediately
Don't even get me started on the "main paint loop"
The next problem you have, is you have no concept of virtual and real world space.
You make a virtual map of your world using t, which maintains information about which tile should be used for a given x/y coordinate, but you never map this to the real world, instead, you paint each tile exactly at the same pixel x/y position, which means they now overlap, tiles have width and height, which means they need to be offset when painted onto the real world.
And finally, which I think is your actually question, is about scaling. There are a number of ways you could apply scaling, you could pre-scale the tiles when you load them, this gives you a lot of control over "how" they are scaled, but locks you into a single scale.
You could instead maintain a list of the scaled tiles, generated from a master list, which can be updated if the scale changes
Or you could simply scale the Graphics context directly.
Now, I've create a simple example, based on your code, correcting for most of the above. It creates a series of randomly colored rectangles at 10x10 pixels, instead of tiles, but the concept is the same.
import java.awt.Color;
import java.awt.Dimension;
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.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MCVCon extends JFrame {
//setting up variables
public MCVCon() {
//creating the window
super("Conqueror");
add(new GamePane());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MCVCon frame = new MCVCon();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
//necessary overrides
public class GamePane extends JPanel {
public BufferedImage[] img = new BufferedImage[9];
private final int width = 5, height = 5;
private int scale = 16;
int t[][] = new int[width][height]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
Color[] colors = new Color[]{
Color.RED,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.YELLOW
};
int tileHeight = 10;
int tileWidth = 10;
public GamePane() {
loadImages();
Random rnd = new Random();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int value = rnd.nextInt(9);
System.out.println(value + "- " + colors[value]);
t[x][y] = value;
}
}
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width * tileWidth * scale, height * tileHeight * scale);
}
public void loadImages() {
for (int i = 0; i < 9; i++) {
try {
img[i] = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img[i].createGraphics();
g2d.setColor(colors[i]);
g2d.fill(new Rectangle(0, 0, tileWidth, tileHeight));
g2d.dispose();
} catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setTransform(AffineTransform.getScaleInstance(scale, scale));
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
g2d.drawImage(img[t[x][y]], x * tileWidth, y * tileHeight, this);
}
}
g2d.dispose();
}
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
BIG EDIT: I have now a MCVE for this
In my Super Mario 3 clone, my sprite draws correctly upon instantiating the JFrame, but when I press one of the buttons I've set to jump, it gets partially cut off. Everything on the screen is a JLabel.
Here is the code:
//for all
import java.nio.file.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.image.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
import static java.lang.invoke.MethodHandles.*;
import java.awt.event.*;
//my Mario class (cut down a lot)
class Mario {
// all numbers multiplied by 2 from OG game
protected Direction dir;
protected int x, y;
protected BufferedImage sprite;
public Mario() {
this.x = 54;
this.y = 808;
dir = Direction.RIGHT;
setSprite(MVCE.SMALLSTANDFACERIGHT);
}
public void moveRight() {
if (this.dir == Direction.LEFT) {
this.dir = Direction.RIGHT;
} else if (this.dir == Direction.RIGHT) {
this.x += 2;
}
}
public void jump() {
this.y -= 46;
}
public void setSprite(String spriteName) {
URL spriteAtLoc = MVCE.urlGenerator(spriteName);
this.sprite = MVCE.generateAndFilter(sprite, spriteAtLoc);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(sprite, 0, 0, null); // DO NOT SET x and y TO ANYTHING,
// this sets 0,0 to top left!!
}
}
// my MarioRender class:
class MarioRender extends JLabel {
protected Mario marioSprite;
public MarioRender() {
marioSprite = new Mario();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
marioSprite.paint(g2);
}
public void moveMarioRight() {
marioSprite.moveRight();
setLocation(this.marioSprite.x, this.marioSprite.y);
repaint();
}
public void jumpMario() {
marioSprite.jump();
setLocation(this.marioSprite.x, this.marioSprite.y);
repaint();
}
}
// direction class, solely for moving
enum Direction {
LEFT, RIGHT
}
// my calling class, which I called MVCE where I make the frame
public class MVCE extends JFrame {
MarioRender m = new MarioRender();
JLabel bg;
public MVCE() {
bg = new JLabel();
this.setSize(868, 915);
this.setVisible(true);
this.add(bg, BorderLayout.CENTER);
bg.setLayout(null);
bg.add(m);
m.setBounds(m.marioSprite.x, m.marioSprite.y, m.marioSprite.sprite.getWidth(),
m.marioSprite.sprite.getHeight());
KeyListener kl = new MoveListener();
this.addKeyListener(kl);
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static final String SMALLSTANDFACERIGHT = "SmallStandFaceRight.bmp"; // 30
// x
// 32
public static final String SMALLJUMPFACERIGHT = "SmallJumpFaceRight.bmp"; // 32
// x
// 32
// generate URL
public static URL urlGenerator(String name) {
URL u = lookup().lookupClass().getResource(name);
return u;
}
// return image with filtered color
public static BufferedImage generateAndFilter(BufferedImage b, URL u) {
try {
b = ImageIO.read(u);
int width = b.getWidth();
int height = b.getHeight();
int[] pixels = new int[width * height];
b.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
// System.out.println(pixels[i]);
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe;
}
}
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
b = newSprite;
} catch (IOException e) {
System.out.println("sprite not found");
e.printStackTrace();
}
return b;
}
// key listener
class MoveListener implements KeyListener {
public void keyPressed(KeyEvent k) {
if ((k.getKeyCode() == 39)) {
m.marioSprite.setSprite(SMALLSTANDFACERIGHT);
m.moveMarioRight();
}
if (k.getKeyCode() == 83) { // S key
m.marioSprite.setSprite(SMALLJUMPFACERIGHT);
m.jumpMario();
}
}
public void keyReleased(KeyEvent k) {
}
public void keyTyped(KeyEvent k) {
}
}
public static void main(String[] args) {
MVCE m = new MVCE();
}
}
sprites can be found here and here tho the downloads are in .jpg, whereas in my code, they're .bmp but you can just download, open in another app, save as bmp, or change the code
This is most likely a result of the horizontal and vertical constraints on your JLabel objects. From the look of the picture, the Mario that is jumping is slightly wider horizontally than the Mario standing on the ground.
I'm trying to draw multiple rectangles on a panel. I created an ArrayList<Shape> and initialized in my constructor. In my paintComponent method, I draw a rectangle and then add it to the ArrayList. But when I do that, the drawing outcome on my panel turns out weird. As I drag to draw my first rectangle, I get this:
Here's part of my code:
public class MyMouseListener extends MouseAdapter {
#Override
public void mousePressed(final MouseEvent theEvent) {
myStartPoint = theEvent.getPoint();
repaint();
}
#Override
public void mouseReleased(final MouseEvent theEvent) {
myEndPoint = theEvent.getPoint();
repaint();
}
}
public class MyMouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(final MouseEvent theEvent) {
myEndPoint = theEvent.getPoint();
repaint();
}
}
/**
* Paints some rectangles.
*
* #param theGraphics The graphics context to use for painting.
*/
#Override
public void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
// for better graphics display
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(new Color(51, 0, 111));
g2d.setStroke(new BasicStroke(3));
final double x = myStartPoint.getX();
final double y = myStartPoint.getY();
final double xEnd = myEndPoint.getX();
final double yEnd = myEndPoint.getY();
if (xEnd> x && yEnd > y) {
final Shape rectangle = new Rectangle2D.
Double(x, y, xEnd - x, yEnd - y);
g2d.draw(rectangle);
myDrawings.add(rectangle);
}
for (Shape s : myDrawings) {
g2d.draw(s);
}
}
Don't do any code logic within paintComponent -- that method is for drawing and drawing only, and that is the source of your bug. Add the rectangle to the ArrayList in the mouse listener, on mouse release.
When I have done a similar project, I usually have one Rectangle field that I use to draw with the mouse listener on mouse drag, and which is draw within paintComponent. Then on mouse release I place that rectangle into the ArrayList, and set the class field as null.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class RectangleDraw extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final Color TEMP_RECT_COLOR = Color.LIGHT_GRAY;
private static final Color SHAPE_COLOR = Color.RED;
private Rectangle tempRect = null;
private List<Shape> shapes = new ArrayList<>();
public RectangleDraw() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw the temporary rectangle if not null
if (tempRect != null) {
g2.setColor(TEMP_RECT_COLOR);
g2.draw(tempRect);
}
// draw all the rectangles in the list
g2.setColor(SHAPE_COLOR);
for (Shape shape : shapes) {
g2.draw(shape);
}
}
// size the GUI to my specification
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
// My mouse listener and mouse motion listener
private class MyMouse extends MouseAdapter {
private Point p1; // start point
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
// create temporary rectangle
tempRect = createRectangle(e);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
tempRect = null; // null temp rectangle and
// add rectangle to List
shapes.add(createRectangle(e));
repaint();
}
// create a rectangle from start point and current point
private Rectangle createRectangle(MouseEvent e) {
Point p2 = e.getPoint();
int x = Math.min(p1.x, p2.x);
int y = Math.min(p1.y, p2.y);
int w = Math.abs(p1.x - p2.x);
int h = Math.abs(p1.y - p2.y);
Rectangle rect = new Rectangle(x, y, w, h);
return rect;
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Rectangle Draw");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RectangleDraw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I'm trying to display a background image using g.drawImage within Canvas Jframe but i have nothing displayed and the screen still white , and when i try to fill the screen with any color it works using g.fillRect
here is the Main class :
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.ImageIcon;
public class Main extends Canvas implements Runnable, KeyListener {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 600, HEIGHT = WIDTH / 12 * 9;
// rate to convert meters to pixels in the physics engine
public static final float RATE = 30;
// counts how many loops are made in the main thread
private int counter = 49;
// image and graphics used for double buffering
private ImageIcon bgImage = new ImageIcon("res/bgImage.png");
private Image dbImage;
private Graphics dbg;
/* boolean to define when the game will be started and stopped */
private boolean running = false;
// variables for the Box2D world
Vec2 gravity = new Vec2(0.0f, 10.0f);
boolean doSleep = true;
World world = new World(gravity, doSleep);
// new array list to hold Ball references
ArrayList<Ball> balls = new ArrayList<Ball>();
// create a new player
Player character = new Player(world);
public Main() {
addKeyListener(this);
// add a ground floor to our Box2D world
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(300.0f / RATE, 400.0f / RATE);
Body groundBody = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
groundBox.setAsBox(300.0f / RATE, 0);
groundBody.createFixture(groundBox, 0.0f);
// wall fixture
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = groundBox;
fixtureDef.density = 2.0f;
fixtureDef.filter.groupIndex = -1;
// left wall
groundBodyDef.position.set(0.0f / RATE, 350.0f / RATE);
groundBody = world.createBody(groundBodyDef);
groundBox.setAsBox(0, 50.0f / RATE);
groundBody.createFixture(fixtureDef);
// right wall
groundBodyDef.position.set(600.0f / RATE, 350.0f / RATE);
groundBody = world.createBody(groundBodyDef);
groundBox.setAsBox(0, 50.0f / RATE);
groundBody.createFixture(fixtureDef);
/**
* #WIDHT : width of jpanel screen
* #HEIGHT : height of the jpanel screen
* #param : title of jpanel screen
* #this : refered to our main game instance
*/
new Window(WIDTH, HEIGHT, "Apocalypse 2D v0.0", this);
}
#Override
public int getWidth() { // Width of the CustomPanel
return WIDTH;
}
#Override
public int getHeight() { // Height of the CustomPanel
return HEIGHT;
}
#Override
public Dimension getPreferredSize() { // Dimension of the CustomPanel
return new Dimension(getWidth(), getHeight());
}
public void start() {
// starts a new thread
running = true;
Thread th = new Thread(this);
th.start();
}
public void stop() {
running = false;
}
public void destroy() {
}
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (running) {
counter++;
// Simulate the world
float timeStep = 1.0f / 60.0f;
int velocityIterations = 6;
int positionIterations = 2;
world.step(timeStep, velocityIterations, positionIterations);
// add new balls to the world every 50th loop
if (counter % 80 == 0)
balls.add(new Ball(world));
repaint();
// pause for 10 milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
// do nothing
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
}
public void paint(Graphics g) {
/*g.setColor(Color.black);*/
/*g.fillRect(0, 0, getWidth(), getHeight());*/
g.drawImage(bgImage, getWidth(), getHeight(), null);
// loop through each ball and call it's draw method
Iterator<Ball> itr = balls.iterator();
while (itr.hasNext()) {
Ball b = itr.next();
b.DrawBall(g);
// if the ball should be removed then remove it
if (b.shouldDelete())
itr.remove();
}
// draw the main character.
character.draw(g);
}
// sets up double buffering for graphics
public void update(Graphics g) {
if (dbImage == null) {
dbImage = createImage(this.getSize().width, this.getSize().height);
dbg = dbImage.getGraphics();
}
dbg.setColor(getBackground());
dbg.fillRect(0, 0, this.getSize().width, this.getSize().height);
dbg.setColor(getForeground());
paint(dbg);
g.drawImage(dbImage, 0, 0, this);
character.animplayer();
for (Ball ball : balls) {
ball.animBalls();
}
}
public void keyPressed(KeyEvent key) {
character.keyPress(key);
}
public void keyReleased(KeyEvent key) {
character.keyRelease(key);
}
public void keyTyped(KeyEvent key) {
}
public static void main(String[] args) {
new Main();
}
}
and this is the paint method where i'm trying to fill the image to the screen :
public void paint(Graphics g) {
/*g.setColor(Color.black);*/
/*g.fillRect(0, 0, getWidth(), getHeight());*/
g.drawImage(bgImage, getWidth(), getHeight(), null);
// loop through each ball and call it's draw method
Iterator<Ball> itr = balls.iterator();
while (itr.hasNext()) {
Ball b = itr.next();
b.DrawBall(g);
// if the ball should be removed then remove it
if (b.shouldDelete())
itr.remove();
}
// draw the main character.
character.draw(g);
}
The drawImage() method has several overloads, the one you are using is drawImage(BufferedImage img, int x, int y, ImageObserver observer), x and y being the top left coordinates on the canvas. Right now those are the width and the heigh of the canvas, so the image is being drawn outside of the panel. Try calling g.drawImage(dbImage, 0, 0, null).
edit:
Asuming your image is not the same size as your canvas, use g.drawImage(dbImage, 0, 0, getWidth(), getHeight(), null) to fill the canvas with your image.
I'm trying to make a paint program, and this class is the main area where you drag your mouse to paint. The problem is the clip has to be rectangular, so any other lines within that rectangle of the clip (the clip gets bigger the faster you move) will get covered by the new clip, however the new clip isn't all needed.
My ideas of solutions are:
To somehow set the clip to a line (but I think that clip would have to be set in the repaint method, not the setClip() in paint component)
To save the image currently on the paint component and set it to the backgroud
Possibly set the ocupancy of the clip lower in the areas without the line?
Thank you for looking at it, here is the code (with some parts left out for simpler reading) and if you know a solution I would love to hear it. Thanks!
public class Canvas extends JPanel implements MouseMotionListener, MouseListener{
int sizeX, sizeY;
String title;
int[] backColor = new int[3];
int brushSize=20;
Point currentP = new Point();
Point pastP = new Point();
Point paintP = new Point();
int diffX, diffY;
boolean initialize=true;
boolean initClip=true;
Canvas(){
backColor[0] = newProject.colorA;
backColor[1] = newProject.colorB;
backColor[2] = newProject.colorC;
if(backColor[0]>=255){
backColor[0]=255;
}
if(backColor[1]>=255){
backColor[1]=255;
}
if(backColor[2]>=255){
backColor[2]=255;
}
sizeX = newProject.sizeX;
sizeY = newProject.sizeY;
//System.out.println(sizeX + " " + sizeY);
setSize(sizeX,sizeY);
setBackground(Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(brushSize));
if(initialize){
g.setColor(new Color(backColor[0], backColor[1], backColor[2]));
g.fillRect(0, 0, sizeX, sizeY);
g.setColor(Color.red);
g.drawRect(0,0,50,50);
System.out.println("Initialize");
}
else{
g2.drawLine(currentP.x, currentP.y, pastP.x,pastP.y);
}
//System.out.println("Paint");
}
#Override
public void mouseDragged(MouseEvent e) {
if(initClip) //if mouse has been released since last dragged
currentP = e.getPoint(); //This causes PastP and CurrentP to be equal
initClip=false; //since pastP is set equal to CurrentP afterward
pastP = currentP;
currentP = e.getPoint();
diffX=Math.abs(currentP.x-pastP.x); //find the differences to find how big of
diffY=Math.abs(currentP.y-pastP.y); //a clip it needs
if(diffX==0){ //if no movement, set it to brush size so the
diffX=brushSize; //clip shows up
}
if(diffY==0){
diffY=brushSize;
}
initialize=false;
if(currentP.x-pastP.x>0){ //figures out which direction it moved
paintP.x=pastP.x; //sets the clip variable to the correct corner
}
else{
paintP.x=currentP.x;
}
if(currentP.y-pastP.y>0){
paintP.y=pastP.y;
}
else{
paintP.y=currentP.y;
}
System.out.println(paintP);
repaint(paintP.x, paintP.y, diffX, diffY); //repaint with point PaintP and the
//difference it moved
}
#Override
public void mouseReleased(MouseEvent arg0) {
initClip=true;
}
I'm not sure why you would bother. Each time paintComponent is called by the paint system, you are expected to repaint the entire component anyway.
Instead, simply paint what you need to paint, then paint the selection on top of it...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
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.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawSelection {
public static void main(String[] args) {
new DrawSelection();
}
public DrawSelection() {
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 background;
private Rectangle clipRect;
public TestPane() {
try {
background = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter ma = new MouseAdapter() {
private Point cp;
#Override
public void mousePressed(MouseEvent e) {
cp = e.getPoint();
clipRect = null;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
cp = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
int x = Math.min(p.x, cp.x);
int y = Math.min(p.y, cp.y);
int width = Math.max(p.x, cp.x) - x;
int height = Math.max(p.y, cp.y) - y;
clipRect = new Rectangle(x, y, width, height);
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
}
if (clipRect != null) {
g2d.setColor(UIManager.getColor("List.selectionBackground"));
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(clipRect);
}
g2d.dispose();
}
}
}
If you want to optimise the paint process, why not draw the parts of the image to a backing buffer and simply paint the buffer, then paint the selection or other dynamic parts on top of it within the paintComponent?