So Im a beginner experimenting with ImageIO. Can someone tell me why im getting this pixelated-incomplete sprite?
Here's the code
public BufferedImage getImage(String location)
{
try
{
File file = new File(location);
image = ImageIO.read(file);
}
catch (IOException e)
{
System.err.println("It don't work!!");
e.printStackTrace();
}
return image;
}
And Im using this method to display it
public void paint(Graphics g)
{
g.drawImage(getImage("Numbers/icon0.png"), 0, 0, 32, 32, null);
repaint();
}
And here's what it gives me
If you want to display the first (single) cell of your sprite, you probably meant to write:
g.drawImage(getImage("Numbers/icon0.png").getSubimage(0, 0, 32, 32), 0, 0, null);
Notice the getSubimage(x, y, w, h) part, to get a single cell.
If you wanted to draw the entire sprite sheet, you could use:
g.drawImage(getImage("Numbers/icon0.png"), 0, 0, null);
Your original code will draw the entire sprite sheet, rescaled to 32x32.
PS: You should probably not invoke repaint() from the paint method, as that will create an endless repaint loop. If you want your component to repaint, use some kind of timer that repaints your component at fixed intervals.
PPS: You should probably not do I/O (ie. read the image) inside the paint method, because any I/O operation may take time and make your UI sluggish and unresponsive. It's also unnecessary, as the sprite sheet does not change every time you repaint. Instead, read the image up front, and only draw it in the paint method.
Related
I am working on a chess game and I would like to let the player choose the board's colors. Therefore I will use this method:
static void createBoard(Graphics g) {
Color bright = new Color(255, 225, 181); //player chooses color
Color dark = new Color(188, 141, 105); //player chooses color
boolean darkTile = false;
for (int y = spaceY; y < (spaceY + BOARDHEIGHT); y += TILESIZE) {
for (int x = spaceX; x < (spaceX + BOARDWIDTH); x += TILESIZE) {
if (darkTile) {
g.setColor(dark);
} else {
g.setColor(bright);
}
g.fillRect(x, y, TILESIZE, TILESIZE);
darkTile = !darkTile;
}
darkTile = !darkTile;
}
BufferedImage overlay;
try {
overlay = ImageIO.read(new File("overlay.png"));
JLabel label = new JLabel(new ImageIcon(overlay));
g.drawImage(overlay, spaceX, spaceY, BOARDWIDTH, BOARDHEIGHT, null);
} catch (IOException e) {}
}
This I would like to save as a BufferedImage, so I don't have to run this method all the time.
So how can I save just this part of my JPanel, without the stuff outside of the chess board? (there will be more painted)
This I would like to save as a BufferedImage,
Don't know that your need to save the BufferedImage to a file. You can just create a BufferedImage to be used by the application when the application starts. You can then recreate the BufferedImage if any of the user colors change.
You can paint directly to a BufferedImage:
BufferedImage image = new BufferedImage(boardSize, boardSize, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// draw the squares onto board
g2d.dispose();
Now your createBoard() method should probably return the BufferedImage so it can be used by your application.
You put in certain efforts to put up your question, so lets honor that with some thoughts to get you going.
First of all: you have an empty catch block {}. That is bad practice. This simply eats up any error messages you get. That is not helpful. Either allow that exception to bubble up and stop your application; or at least print its contents - so that you understand what happens.
And given your comment: you never now if there will be errors. Especially when doing IO, all sorts of things can go wrong. Please believe me: empty catch blocks are bad practice; and you should not train yourself to accept them.
Second thought: don't go for that yet. As convenient as it might sound; but saving a background picture doesn't add much value at this point.
You don't need to worry about this code; it is executed once when your application comes up.
So, the real answer here: focus on the features you want to implement; and don't get distracted with pre-mature optimizations.
When drawing a BufferedImage , the transition image does not display. I am trying to create a game in which when the user wins, it displays a loading transitional screen and then proceeds to the img2 screen. I want to create a 1 or 2 second delay to make it seem as though the second level is in the process of loading however when I add thread.sleep it delays it but does not display the transition image, instead, it just jumps straight toimg2. I have tried creating separate flags,methods and increasing the time but nothing seems to work!
Below is the paintComponenetmethod (I have tried separating into separate methods):
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, 0, 0, this);
if (win1 == true) {
try {
Thread.sleep(1000);
} catch (Exception exc) {
}
g2d.drawImage(transition, 0, 0, this);
try {
Thread.sleep(1000);
} catch (Exception exc) {
}
g2d.drawImage(img2, 0, 0, this);
}
}
}
An explanation would be very helpful as well!
All painting is done on the Event Dispatch Thread.
When you invoke Thread.sleep() in the paintComponent() method the GUI can't repaint itself until all the code is finished executing which means only the last image will ultimately get painted as the two paint requests will be combined into one. Read the section from the Swing tutorial on Concurrency for more information.
Don't use Thread.sleep() in a painting method.
The better solution is to not even do custom painting. Instead you can just use a JLabel and change the Icon of the label.
Then you can use use a Swing Timer to schedule the animation. So you would set the Icon and then set the Timer to fire in 2 seconds. When the Timer fires you reset the Icon of the label.
I am currently profiling my Java-2d-Application (Game-Engine for learning purposes).
Since I cannot guarantee that each frame is overwritten completely, I have to clear the background to a solid color (i.e. Color.BLACK) each frame.
The way I do it is SLOW (about 40% of drawing-time in my environment goes to just clearing the background).
First I get a graphics-context from the bufferStrategy, then I draw a [PickYourColor]-Rectangle in full resolution on it before drawing the actual content.
// fill background with solid color
graphics.setColor(Color.BLACK);
graphics.fillRect(
0,
0,
(int) bounds.getWidth(),
(int) bounds.getHeight());
Is there a more efficient, platform-independant, way to clear the background to a solid color each frame using Java-2D (this is not a LWJGL-question)?
What I'm looking for is a graphics.clearBackgroundToSolidColor(Color color) - Method...
By request: here the full rendering method (it's not an SSCCE, but it's pretty short and self explanatory)
/**
* Create a new graphics context to draw on and
* notify all RenderListeners about rendering.
*/
public void render() {
///// abort drawing if we don't have focus /////
if (!this.windowJFrame.hasFocus()) {
return;
}
///// draw and create new graphics context /////
Graphics2D graphics = null;
do {
try {
graphics = (Graphics2D) this.bufferStrategy.getDrawGraphics();
Rectangle2 bounds = this.getBounds();
// set an inexpensive, yet pretty nice looking, rendering directives
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// fill background with solid color
graphics.setColor(Color.BLACK);
graphics.fillRect(
0,
0,
(int) bounds.getWidth(),
(int) bounds.getHeight());
// notify all listeners that they can draw now
synchronized (this.renderListeners) {
for (RenderInterface r : this.renderListeners) {
r.render(graphics, bounds);
}
}
// show buffer
graphics.dispose();
this.bufferStrategy.show();
} catch (Exception e) {
Logger.saveMessage("window", Logger.WARNING, "Caught exception while drawing frame. Exception: " + e.toString());
}
} while (this.bufferStrategy.contentsLost());
}
I can't say why the fillRect is slow, but you can try creating an Image and draw it as bg. not sure if it will be faster though.
try:
BufferedImage bi = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
int[] imageData =((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
Arrays.fill(imageData, 0);
then instead of fillRect draw the Image:
graphics.drawImage(bi, 0, 0, null);
Tell me how it went(I have my doubts about this).
If you would like to clear the entire background than try canvas.drawColor(color, PorterDuff.Mode.CLEAR). Should be a bit faster
I am currently doing a project that requires me to draw a large number (100+) images on the screen. The original resolution of each image is 20*20 and I am scaling them to 80*80 with nearest neighbor before drawing them.
I currently use AffineTransform to scale them up when the program is initialized, and redraw the enlarged images every frame in different positions.
Seeing as the target framerate is about 60 fps, I need to find a way to draw them faster, preferably so that it doesn't render them as 80*80 bitmaps. I tried Graphics2D's drawImage method with the width and height parameters, but it actually slowed my program down.
In pseudocode:
Image image; //loaded from 20*20 png file
public void init(){
image = resize(image, 80, 80);
}
public void repaint(Graphics g){
g.drawImage(image, 0, 0, null);
}
I also tried:
Image image;
public void repaint(Graphics g){
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(image, 0, 0, 80, 80, null);
}
Is there a solution to my problem?
There was a similar question about that: Load and Resize Image
Here is my answer: Load and Resize Image
I hope I could help.
I have a function
#Override
public void run() {
while(running && (!eof)){
if(surfaceHolder.getSurface().isValid()){
Canvas canvas = surfaceHolder.lockCanvas();
paint(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
thread = null;
}
where paint(canvas) calls a bunch of other functions that draw a graph and text, for example
canvas.drawText("Time="+myRecord.getMyTime(), 100, 100, paint);
The problem I'm having is that the graph and the text, both of which should be constantly changing, don't get erased but instead keep drawing over themselves. Shouldn't my entire canvas get redrawn every time because that's how double buffering works with the lock() and unlock()? Am I not understanding this correctly? How am I supposed to do this?
You need to clear the Canvas yourself after lockCanvas() using Canvas.drawColor().
This might be relevant too:
The content of the Surface is never preserved between unlockCanvas()
and lockCanvas(), for this reason, every pixel within the Surface area
must be written. The only exception to this rule is when a dirty
rectangle is specified, in which case, non-dirty pixels will be
preserved.
Source