I have a JFrame in which I am using Graphics2D to draw a VolatileImage using this tutorial. I have mainly copied the code to see how it works, but have slightly edited it for my game. I am running my computer with two screens.
The problem arises when I drag the window of the game onto the other screen which the window did not originally appear on. The window goes grey and no graphics are shown on screen, even the simple rectangles I have drawn with the Graphics2D. This only happens when I call for the draw method of the volatileimage as shown in the tutorial.
I believe it may have something to do with this...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
...but I am not sure.
Any help would be greatly appreciated. It would also be interesting to know if VolatileImage is the way I should be going for my game or if BufferedImage or something else is a better method for performance and frame rate.
Yes, you are correct. VolatileImage is device-specific. From "The VolatileImage API User Guide" you can read:
A VolatileImage is device-specific: if you created a VolatileImage with one
GraphicsDevice, you might not be able to copy that VolatileImage to another
GraphicsDevice. For this reason, you need to call validate before attempting to copy the
VolatileImage.
and
If the code is IMAGE_INCOMPATIBLE then the VolatileImage is not
compatible with the current GraphicsConfiguration. This
incompatibility can occur if the image was created with one
GraphicsConfiguration and then drawn into another. For example,
in a multiple-monitor situation, the VolatileImage exists is
associated with a particular GraphicsConfiguration. Copying that
image onto a different GraphicsConfiguration could cause
unpredictable results. To correct this problem, you need to create a
new VolatileImage that is compatible with the current
GraphicsConfiguration
When dragging your frame to another screen device you need to check the result from the VolatileImage.validate(gc) method of your and recreate the image to the new device. Note that there are cases when you cannot create a VolatileImage, in those cases you need to fall back on another Image implementation like BufferedImage.
Related
I'm currently using a Canvas with an associated BufferStrategy. How can I copy the contents (image) of the Canvas and save it to a BufferedImage. So far I've only tried
BufferedImage image = ...
g = image.createGraphics();
canvas.printAll(g); //or paintAll
However, it appears that only a 'blank' (filled with the background color) screen is being drawn; I it's assume because that's the default behavior of Canvas.paint(). Is there as way for me to retrieve the image currently on screen (the last one shown with BufferStrategy.show()), or do I need to draw to a BufferedImage and copy the image to the BufferStrategy's graphics. It seems like that would/could be a big slow down; would it?
Why (because I know someone wants to ask): I already have a set of classes set up as a framework, using Canvas, for building games that can benefit from rapidly refreshing screens, and I'm looking to make an addon framework to have one of these canvases tied into a remote server, sending it's displayed image to other connected clients. (Along the lines of remotely hosting a game.) I'm sure there are better ways to do this in given particular cases (e.g. why does the server need to display the game at all?) But I've already put in a lot of work and am wondering if this method is salvageable without modifying (to much of) the underlying Game framework code I already have.
I got two Graphics objects, is it possible to combine them into one?
The answer of Hovercraft Full of Eels is useful, but not what I meant. Say I have two Graphics objects, private Graphics gr1 and private Graphics gr2. Now, how should I merge them in the paintComponent(Graphics) method to draw them to my (for example JPanel)?
The question is not at all about images, but about mere Graphics objects. Sorry, the first paragraph was misleading.
Edit:
In response to Hovercraft Full of Eels:
If you draw in the paintComponent() method, I find it annoying everything is gone if you redraw your screen, and if you want to move something you have to save the coordinates and dimensions of everything, adjust those and get them someway in your Graphics object.
The question I asked myself was accely: What object is best suited or saving a Graphics object. A Graphics object, I thought. but the problem is that if you have (for example) two rectangles, and one moves to the left and one moves to the right you can't if you have 1 Graphics object.
My solution was multiple graphics objects, which can be transformed to simulate movement and then drawn to the screen.
#Hovercraft Full of Eels I think you (and most SO'ers) think this is not a good solution, looking at your answer.
But, an answer saying You're doing it all wrong, you'd better stop programming doesn't help me at all, so please give me an alternative.
You can draw any image into a BufferedImage by getting its Graphics context, drawing in it, and then disposing of the Graphics object. For example
// get a Graphics object from BufferedImage, img1
Graphics g = img1.getGraphics();
// use that g to draw another BufferedImage starting at x, y of 0, 0
g.drawImage(img2, 0, 0, null);
// clean up resources so as not to run out.
g.dispose();
Now img1 shows old img1 with img2 overlying it. If you want the images to be transparent, you'll need to read up on alpha composite and how to use it
Edit
Regarding your edited question:
I got two Graphics objects, is it possible to combine them into one?
The answer of Hovercraft Full of Eels is useful, but not what I meant. Say I have two Graphics objects, private Graphics gr1 and private Graphics gr2. Now, how should I merge them in the paintComponent(Graphics) method to draw it to my (for example JPanel)?
The question is not at all about images, but about mere Graphics objects. Sorry, the first paragraph was misleading.
This request is somewhat confusing to me since I think of Graphics objects as pens or paintbrushes that are used to draw something to an image or the screen. So I don't think that you draw a Graphics object to anything, but rather that you use a Graphics object as a tool to draw to something else that can display or store the graphics. You are going to have to put a lot more effort into asking your question, supplying us with enough details so that we don't have to keep guessing what it is you are trying to do. Please tell us the whole story.
Edit 2
In response to your latest edit to your question:
If you draw in the paintComponent() method, I find it annoying everything is gone if you redraw your screen, and if you want to move something you have to save the coordinates and dimensions of everything, adjust those and get them someway in your Graphics object.
The question I asked myself was accely: What object is best suited or saving a Graphics object. A Graphics object, I thought. but the problem is that if you have (for example) two rectangles, and one moves to the left and one moves to the right you can't if you have 1 Graphics object.
No, a better solution I think is to use a BufferedImage to save that which should persist. When ever you wanted to add an image to the background BufferedImage, you'd obtain the BufferedImage's Graphics object by calling getGraphics() on it, then you'd draw your image to it by using the Graphics object's drawImage(...) method, then you'd call dispose() on the BufferedImage's Graphics object so as not to lose resources.
To draw the background BufferedImage in the background, you'd call it at the top of the paintComponent(...) method, usually just after calling the super.paintComponent(...) method.
My solution was multiple graphics objects, which can be transformed to simulate movement and then drawn to the screen.
#Hovercraft Full of Eels I think you (and most SO'ers) think this is not a good solution, looking at your answer.
Yep, you guess correctly -- I believe that there are better solutions than the one you suggest.
An example of just what I'm getting at can be found in my code post in my answer to this question: changing-jpanel-graphics-g-color-drawing-line.
If you run my code, you'd see this:
To impose images, use an appropriate layout, as shown here.
To compose images, use an appropriate composite rule, as shown here.
Addendum: #HFOE is correct. An instance of Graphics or Graphics2D, sometimes called a graphics context, is a transitory means to apply rendering methods; the target device or off-screen image must exist independently. Classes implementing the Shape interface are good candidates for encapsulating information about what to render in a given context. AffineTest and GraphPanel are examples.
I have been spending some time looking into the hardware acceleration features of Java, and I am still a bit confused as none of the sites that I found online directly and clearly answered some of the questions I have. So here are the questions I have for hardware acceleration in Java:
1) In Eclipse version 3.6.0, with the most recent Java update for Mac OS X (1.6u10 I think), is hardware acceleration enabled by default? I read somewhere that
someCanvas.getGraphicsConfiguration().getBufferCapabilities().isPageFlipping()
is supposed to give an indication of whether or not hardware acceleration is enabled, and my program reports back true when that is run on my main Canvas instance for drawing to. If my hardware acceleration is not enabled now, or by default, what would I have to do to enable it?
2) I have seen a couple articles here and there about the difference between a BufferedImage and VolatileImage, mainly saying that VolatileImage is the hardware accelerated image and is stored in VRAM for fast copy-from operations. However, I have also found some instances where BufferedImage is said to be hardware accelerated as well. Is BufferedImage hardware accelerated as well in my environment? What would be the advantage of using a VolatileImage if both types are hardware accelerated? My main assumption for the advantage of having a VolatileImage in the case of both having acceleration is that VolatileImage is able to detect when its VRAM has been dumped. But if BufferedImage also support acceleration now, would it not have the same kind of detection built into it as well, just hidden from the user, in case that the memory is dumped?
3) Is there any advantage to using
someGraphicsConfiguration.getCompatibleImage/getCompatibleVolatileImage()
as opposed to
ImageIO.read()
In a tutorial I have been reading for some general concepts about setting up the rendering window properly (tutorial) it uses the getCompatibleImage method, which I believe returns a BufferedImage, to get their "hardware accelerated" images for fast drawing, which ties into question 2 about if it is hardware accelerated.
4) This is less hardware acceleration, but it is something I have been curious about: do I need to order which graphics get drawn? I know that when using OpenGL via C/C++ it is best to make sure that the same graphic is drawn in all the locations it needs to be drawn at once to reduce the number of times the current texture needs to be switch. From what I have read, it seems as if Java will take care of this for me and make sure things are drawn in the most optimal fashion, but again, nothing has ever said anything like this clearly.
5) What AWT/Swing classes support hardware acceleration, and which ones should be used? I am currently using a class that extends JFrame to create a window, and adding a Canvas to it from which I create a BufferStrategy. Is this good practice, or is there some other type of way I should be implementing this?
Thank you very much for your time, and I hope I provided clear questions and enough information for you to answer my several questions.
1)
So far hardware acceleration is never enabled by default, and to my knowledge it has not changed yet. To activate rendering acceleration pass this arg (-Dsun.java2d.opengl=true) to the Java launcher at program start up, or set it before using any rendering libraries. System.setProperty("sun.java2d.opengl", "true"); It is an optional parameter.
2)
Yes BufferedImage encapsulates some of the details of managing the Volatile Memory because, when the BufferdImage is accelerated a copy of it is stored in V-Ram as a VolatileImage.
The upside to a BufferedImage is as long as you are not messing with the pixels it contains, just copying them like a call to graphics.drawImage(), then the BufferedImage will be accelerated after a certain non specified number of copies and it will manage the VolatileImage for you.
The downside to a BufferedImage is if you are doing image editing, changing the pixels in the BufferedImage, in some cases it will give up trying to accelerate it, at that point if you are looking for performant rendering for your editing you need to consider managing your own VolatileImage. I do not know which operations make the BufferedImage give up on trying to accelerate rendering for you.
3)
The advantage of using the createCompatibleImage()/createCompatibleVolatileImage()
is that ImageIO.read() does not do any conversion to a default supported Image Data Model.
So if you import a PNG it will represent it in the format built by the PNG reader. This means that every time it is rendered by a GraphicsDevice it must first be converted to a compatible Image Data Model.
BufferedImage image = ImageIO.read ( url );
BufferedImage convertedImage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
GraphicsDevice gd = ge.getDefaultScreenDevice ();
GraphicsConfiguration gc = gd.getDefaultConfiguration ();
convertedImage = gc.createCompatibleImage (image.getWidth (),
image.getHeight (),
image.getTransparency () );
Graphics2D g2d = convertedImage.createGraphics ();
g2d.drawImage ( image, 0, 0, image.getWidth (), image.getHeight (), null );
g2d.dispose()
The above process will convert an image read in with the image io api to a BufferedImage that has a Image Data Model compatible with the default screen device so that conversion does not need to take place when ever it is rendered. The times when this is most advantageous is when you will be rendering the image very frequently.
4)
You do not need to make an effort to batch your image rendering because for the most part Java will attempt to do this for you. There is no reason why you cant attempt to do this but in general it is better to profile your applications and confirm that there is a bottleneck at the image rendering code before you attempt to carry out a performance optimization such as this. The main disadvantage is that it my be implemented slightly differently in each JVM and then the enhancements might be worthless.
5)
To the best of my knowledge the design you have outlined is one of the better strategies out there when doing Double Buffering manually and actively rendering an application.
http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html
At this link you will find a description of the BufferStrategy. In the description it shows a code snippet that is the recommended way to do active rendering with a BufferStrategy object. I use this particular technique for my active rendering code. The only major difference is that in my code. like you, I have created the BufferStrategy on an instance of a Canvas which I put on a JFrame.
Judging from some older documentation, you can tell on Sun JVMs whether hardware acceleration is on or not by checking the sun.java2d.opengl property.
Unfortunately, I do not know if this applies to the Apple JVM.
You can check if an individual image is hardware accelerated using Image's getCapabilities(GraphicsConfiguration).isAccelerated()
Having said all this, all the documentation I've seen (including this one) imply that BufferedImage is not hardware accelerated. Swing has also been changed to use VolatileImages for its double-buffering for this very reason.
We have an old Java Swing application. we need to display thousands, hundreds of thousands small circle spots on the canvas based on the real data. Right now we have an image file of a small circle spot. When we need it, we draw that image onto the canvas, thousands, hundreds of thousands times.
Now I am think it may be better (better performance and memory usage) to just draw a filled circle each time instead of load the image and draw it.
how about your opinion?
thanks,
You only need to load the template image once and hold it in memory and copy it to the canvas as needed using Graphics2D drawImage function. Drawing multiple filled circles may become expensive due to calls to the Flood-fill/Scan-fill algorithm as well as Bresenham to draw the circle. To optimize the rendering you can also decimate the rendered result or perform clustering, since the user will not really appreciate dense overlapping circles anyway.
To reduce render calls test the pixel where your template is going and pass a render if it is already coloured.
Here is a nice benchmarking applet.
It is almost certainly much faster to hold a single image and draw it many times than to make a call to draw a filled circle. Here is a recent presentation on the subject, showing that it is faster to draw an image than even a simple horizontal cross. http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-4170&yr=2009&track=javase
Time your code
It is most definitely faster to draw an image lots of times than drawing a circle or String lots of times and it's very easy to test. At the beginning of your paintComponent() method add the line:
paintComponent(){
long start = System.currentTimeMillis();
...
// draw 100,000 circles as images or circles
...
System.out.println("Rendering time: " +
(start - System.currentTimeMillis()) + " ms");
}
If the times turn out to be zero all the time, you can instead use System.nanoTime().
Paint to Cached Image
Another thing you can do is to paint these circles onto an image and only recreate the image when the content changes. If nothing has changed just draw that image onto the Graphics2D object instead of redrawing all of the circles. This is commonly called double buffering. You also can use Volatile Images to take advantage of hardware acceleration.
Create Compatible Images
You should also make sure you use images that are compatible with the user's monitor by using createCompatibleImage() as shown below:
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
// Create an image that does not support transparency
BufferedImage bimage = gc.createCompatibleImage(width, height, Transparency.OPAQUE);
// Create an image that supports transparent pixels
bimage = gc.createCompatibleImage(width, height, Transparency.BITMASK);
// Create an image that supports arbitrary levels of transparency
bimage = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
More Tips
I'd recommend the book Filthy Rich Clients. It has lots of great tips for speeding up swing. Especially look at chapters 4 and 5 about images and performance.
I don't now if this would be helpful but you can test which one works for you by testing worst case . But I think filled circle would be best .
A third way to do it is to use the unicode char for filled circle, ●, since you can bet that rendering thousands of chars (as in: a piece of text) is the most normal thing for any graphics engine.
It's hard to predict which is faster, because certain operations under certain circumstances are accelerated by the GPU hardware of the video card.
If the GPU is used to make the circle, then that would be much faster than the cpu copying pixels of a buffered circle as an image.
There is VolatileImage as well. Perhaps it's possible to make the image blits so that they end up being accelerated.
The only way to find out is to benchmark it yourself.
I know how to draw a rectangle onto a JPanel, but how can I paint a rectangle to the screen so that the rectangle appears to be floating? More specifically, a non-filled rectangle. My thought is to use a transparent JFrame with a rectangle drawn on it; however, this makes all of the content in the JFrame transparent.
My Solution
So I think there are many ways of going about this, some more complex than others, some more practical than others.
I tried the splash screen. The problem with that is you need to pass VM parameters "-splash " when you run. I created a manifest file to automate this/put the parameters into eclipse; but then the code is dependent on the .gif file and I can't change the size/position of the rectangle easily. Similar problems occur while faking it via screen screenshot. Good suggestions though, I learned some pretty cool stuff.
So, back to what I did. I used a JFrame, got the content pane and set the background to red (what ever color you want), then set the frame undecorated which removes the titlebar and border of the window. This created a floating rectangle which I could easily change the size and location of (frame.setSize, .setLocation). I have yet to make this a non filled rectangle, I tried internal frames and layeredpanes, but no success.
JFrame is a heavyweight component, and those were always opaque for the longest time. However, since Java 6u10, there is an inofficial API for transparent windows in the class com.sun.awt.AWTUtilities, which will most likely become official in Java 7. In earlier versions, the only way to simulate this kind of thing was to fake it via screenshots taken with java.awt.Robot
You would probably have to have parts of the window transparent while the actual drawn rectangle is not. I doubt there is a platform-agnostic solution for this so you would need to resort to JNI for this. Depending on what you need to do it might also be nice to make the window invisible for clicks which would need other tricks as well.
https://github.com/twall/jna/
That project offers a library and has examples of a clock and info balloons that are semi-transparent and transcend even what you're trying to do. The demos work well on Windows but I couldn't speak to how cross platform they are.
You might want to look at JLayeredPane. The demo on this page shows partially what you want, however when painting your rectangle you'll need to set your paint to transparent:
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
g2d.setComposite(ac);
g2d.drawImage(image, x, y, this);