For some reason my image loads after 1 second even though I call render before my thread.sleep ()
public void init (){
image=new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g= (Graphics2D) image.getGraphics();
running=true;
}
public void run (){
init();
while(running){
long start=System.nanoTime();
update();
render();
drawToScreen();
try{
thread.sleep(1000);
}
catch (Exception e){
e.printStackTrace();
}
}
}
public void update (){
}
public void render (){
g.clearRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.red);
for(int r=0; r<16; r++){
for(int c=0; c<16; c++){
MazeCell m=maze[15-r][c];
int y=15-m.getRow();
int x=m.getCol();
if(m.getWallUp()) g.drawLine(x*50, y*50, (x+1)*50, y*50);
if(m.getWallDown()) g.drawLine(x*50, (y+1)*50, (x+1)*50, (y+1)*50);
if(m.getWallLeft()) g.drawLine(x*50, y*50, x*50, (y+1)*50);
if(m.getWallRight()) g.drawLine((x+1)*50, y*50, (x+1)*50, (y+1)*50);
}
}
g.drawImage(mouseImage, mouse.getCol()*50, (15-mouse.getRow())*50, null);
}
public void drawToScreen (){
Graphics g2=getGraphics();
g2.drawImage (image, 0, 0, null);
g2.dispose();
}
Everything draws but at first there is a blank gray screen and it only loads after 1 second (the time the thread sleeps) even though render and drawToScreen are called first. I don't really know the reason.
Change your:
g2.dispose();
To:
g2.finalize();
Also, the description of the Graphics.drawImage() method states:
Draws as much of the specified image as is currently available. The image is drawn with its top-left corner at (x, y) in this graphics context's coordinate space. Transparent pixels in the image do not affect whatever pixels are already there.
This method returns immediately in all cases, even if the complete image has not yet been loaded, and it has not been dithered and converted for the current output device.
If the image has completely loaded and its pixels are no longer being changed, then drawImage returns true. Otherwise, drawImage returns false and as more of the image becomes available or it is time to draw another frame of animation, the process that loads the image notifies the specified image observer.
In other words, the call returns immediately, whether or not the image drawing has completed.
You can use MediaTracker to first make sure the image is loaded before you call Graphics.drawImage():
long timeout = 60000; // 60 seconds
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image, 0);
try {
if (!tracker.waitForID( 0, timeout ) ) {
System.out.println( "Timed Out!" );
return;
}
}
catch( Exception ex) {
System.out.println( "Error waiting for the image to load." );
return;
}
boolean isFinished = g.drawImage(image, 0, 0, this);
Related
I am tryimg to set an image as background in JPanel and resize it to desired size.
This is MyPanel where i choose image and set it as backgorund:
public class MyPanel extends JPanel {
Image img;
public MyPanel(LayoutManager l) {
super(l);
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String sname = file.getAbsolutePath();
img = new ImageIcon(sname).getImage();
double xRatio = img.getWidth(null) / 400;
double yRatio = img.getHeight(null) / 400;
double ratio = (xRatio + yRatio) / 2;
img = img.getScaledInstance((int)(img.getWidth(null) / ratio), (int)(img.getHeight(null) / ratio), Image.SCALE_SMOOTH);
}
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, Color.WHITE, null);
}
}
And this is my frame:
public class MyFrame extends JFrame {
public MyFrame () {
initUI();
}
private void initUI() {
MyPanel pnl = new MyPanel(null);
add(pnl);
setSize(600, 600);
setTitle("My component");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MyFrame ex = new MyFrame ();
ex.setVisible(true);
}
});
}
}
The problem is the image doesn't show at first. It shows when i for example change frame size a little.
Like that:
You are loading your image asynchronously.
The ImageIcon(String) constructor uses Toolkit.getImage internally, which is a hold-over from the 1990s, when many home Internet connections were so slow that it made sense to always load images in a background thread.
Since the image is loading in the background, img.getWidth(null) might return the image's size, or it might return -1. The documentation explains this.
So, how do you wait until the image has been loaded in that background thread?
Normally, you would observe the image's progress, using an ImageObserver. It just so happens that all AWT Components, and by extension, all Swing JComponents, implement ImageObserver. So you have an ImageObserver object: your MyPanel instance.
So, instead of passing null to all those methods, you would pass your MyPanel instance—that is, this:
double xRatio = img.getWidth(this) / 400;
double yRatio = img.getHeight(this) / 400;
And:
g.drawImage(img, 0, 0, Color.WHITE, this);
However… this will properly track the image's loading progress, but still doesn't guarantee that img.getWidth will return a positive value at the time that you call it.
To guarantee that an image is fully loaded immediately, you can replace the use of ImageIcon with ImageIO.read:
File file = fc.getSelectedFile();
try {
img = ImageIO.read(file);
} catch (IOException e) {
throw new RuntimeException("Could not load \"" + file + "\"", e);
}
This is different from using ImageIcon and Toolkit, because it doesn't just return an Image, it returns a BufferedImage, which is a type of Image that is guaranteed to be fully loaded and present in memory—no background loading to worry about.
Since a BufferedImage is already loaded, it has some additional methods which don't need an ImageObserver, in particular getWidth and getHeight methods which don't require an argument:
BufferedImage bufferedImg = (BufferedImage) img;
double xRatio = bufferedImg.getWidth() / 400.0;
double yRatio = bufferedImg.getHeight() / 400.0;
Notice I changed 400 to 400.0. This is because dividing one int by another int results in integer arithmetic in Java. For instance, 200 / 400 returns zero, because 200 and 400 are both int values. 5 / 2 produces the int value 2.
However, if either or both numbers are doubles, Java treats the entire thing as a double expression. So, (double) 200 / 400 returns 0.5. The presence of a decimal point in a sequence of digits indicates a double value, so 200.0 / 400 and 200 / 400.0 (and, of course, 200.0 / 400.0) will also return 0.5.
Finally, there is the issue of scaling. I recommend reading MadProgrammer's answer, and in particular, the java.net article to which his answer links, The Perils of Image.getScaledInstance().
The short version is that Image.getScaledInstance is another hold-over from the 1990s, and doesn't do a very good job of scaling. There are two better options:
Draw your image into a new image, and let the drawImage method handle the scaling, using the Graphics2D object's RenderingHints.
Use an AffineTransformOp on your image to create a new, scaled image.
Method 1:
BufferedImage scaledImage = new BufferedImage(
img.getColorModel(),
img.getRaster().createCompatibleWritableRaster(newWidth, newHeight),
false, new Properties());
Graphics g = scaledImage.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(img, 0, 0, newWidth, newHeight, null);
g.dispose();
Method 2:
RenderingHints hints = new RenderingHints();
hints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
hints.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
AffineTransform transform = AffineTransform.getScaleInstance(
(double) newWidth / img.getWidth(),
(double) newHeight / img.getHeight());
BufferedImageOp op = new AffineTransformOp(transform, hints);
BufferedImage scaledImage = op.filter(img, null);
You may want to alter the RenderingHints values, based on your own preferred trade-off of speed versus quality. They're all documented in the RenderingHints class.
You have to invoke setVisible(true):
public MyFrame () {
initUI();
setVisible(true);
}
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.
I'm trying to write a JPanel Picture to a BufferedImage (later converted to Rendered Image). I am getting a stack overflow error in the AWT-EventQueue-0 thread for some reason and am not sure if there is a reason I have overlooked.
The code in question:
public BufferedImage createImage() {
int w = getWidth();
int h = getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
cp.paint(bi.getGraphics());
//debug script
File outputfile = new File("image"+index+".jpg");
try {
ImageIO.write(bi, "jpg", outputfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
index++;
return bi;
}
The JPanel paintComponent
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
r = new Random(System.nanoTime());
int maxSize = 100;
int randX = r.nextInt(getWidth());
int randY = r.nextInt(getHeight());
int randWidth = r.nextInt(maxSize);
int randHeight = r.nextInt(maxSize);
Color color = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
Graphics2D g2d = (Graphics2D) g;
ovals.add(new MyCircles(randX, randY, randWidth, randHeight, color));
for (MyCircles c : ovals) {
c.paint(g2d);
}
g2d.setColor(getForeground());
repaint();
double current = ImageComparator.calcDistance((RenderedImage)createImage());
//debugging script
System.out.println("Current: " + current);
System.out.println("Previous" + previous);
if(current > previous) {
ovals.remove(ovals.size()-1);
}
else {
previous = current;
}
}
Any insight as to how to amend this issue would be greatly appreciated!
Remove the the call to repaint in paintComponent which causes that method to be called ad infinitum
Not directly related to your problem but, you should never use the Random class in the painting method. Every time you call the method the painting will change, so the image you create and save will not be the same as the image on the panel.
Also, you should not be adding ovals in the paint method for the same reason give above.
You need to create an addOval(...) method that will set the random color of the Oval and add the oval to the List. The painting code will just iterate through the List and paint the Oval.
You also should NOT be removing ovals in the painting code. Painting code is for painting only, not manipulating the objects painted.
You can also try the Screen Image class which is basically a more flexible version of your image creation code.
Of course you have infinite loop there:
here is how you call your methods:
createImage()
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
TOY STORY 1 (Buzz) : to the infinite and beyond it :)
You never stop this cycle.
I suggest that you need to get the images and then compare them outsize of your paint. Let the paint just paint the image and do the comparison outside of it.
When redrawing circles, the window is not cleared; the new circles get added to the existing content.
The goal is to create three circle, one for each color.
The thread calls move function which draws the circles with different radii.
public void run() {
try {
while(true){
box.removeAll();
move();
box.removeAll();
sleep(500);
}
} catch (InterruptedException e) {
}
}
public synchronized void move() {
Graphics g = box.getGraphics();
g.setXORMode(box.getBackground());
x1= one.x + ofset;
y1= one.y + ofset;
System.out.println("My Point ("+ x1 + " , " + y1 +")" );
g.setColor(Color.green);
g.drawOval(pointA.x-(int)distance1, pointA.y-(int)distance1, (int)distance1*2, (int)distance1*2);
g.setColor(Color.blue);
g.drawOval(pointB.x-(int)distance2, pointB.y-(int)distance2, (int)distance2*2, (int)distance2*2);
g.setColor(Color.red);
g.drawOval(pointC.x-(int)distance3, pointC.y-(int)distance3, (int)distance3*2, (int)distance3*2);
g.dispose();
}
First of all, your approach is not recommended. But, if you only want a quick and dirty fix, you have to clear the panel before drawing the circles.
Graphics g = box.getGraphics();
g.clearRect(0, 0, box.getWidth(), box.getHeight()); // this should do it
g.setXORMode(box.getBackground());
Graphics g = box.getGraphics();
No. Don't use getGraphics(). Any painting done with that Graphics object is only temporary and will be deleted any time Swing determines a component needs to be repainted.
For custom painting override the getPreferredSize() method of a JPanel:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g); // clears the background
// add your custom painting here
}
Also, don't forget to override the getPreferredSize() method of your panel. Read the section from the Swing tutorial on Custom Painting for more information.
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