How much does Java Graphics drawImage consume? - java

I was making an application in Java, and noticed after doing this:
g.drawImage(ImageIO.read(...)....);
that it consumed a lot more CPU and frames, so I started to wonder
how much memory does this take up? and if there were any other better ways to draw an image.
Thanks!

It's not the drawImage that's taking up a lot of CPU, it's the ImageIO.read(…)! Opening streams is expensive and thus should only be done once; when you first need to load the images. The best way to draw an image is to pre-load it into a BufferedImage (or if you manipulate the pixels a VolatileImage).
BufferedImage imageToDraw = ImageIO.read(getClass().getResource("path/To/Image.png"));
That path starts in the directory of your .java files. So, you'll want to add a folder containing your image(s). There are many different ways to load images, more of which are discussed here.
Then you can draw it just like you did before.
g.drawImage(imageToDraw, x, y, null);
This should be much faster. If you are still having problems with speed then this question might be useful.

One possibility is using visualvm: http://visualvm.java.net. I've used it with some success.
Quick steps:
start your app locally
start visualvm
Tools > Options > Profiling > Misc (bottom) > Reset calibration data: Ok
in VVM, connect to your app i.e., in left-hand frame, double-click on pkg.YourMainClass (pid 2112)
Profiler tab, then click on Memory.
after initial baseline profiling is complete, you see a histogram of live objects.
You can take snapshots and compare them, set filters on packages, etc. See http://visualvm.java.net/docindex.html

If you're reading the image rather than holding it as an instance of Image, you will be taking way too much memory up.
I highly, HIGHLY suggest to store the image as an instance of Image with ImageIO.read(params) rather than drawing it and reading it every single loop pass-through.
You'd most likely do it like this in your Canvas/JPanel/Swing Component class:
private Image myImage;
//Assume other code here
public void loadImages() {
myImage = ImageIO.read(params);
//load any other images
}
public void paint(Graphics g) {
g.drawImage(myImage, x, y, null);
}
Just like any other application, resources (images, data, etc) are all loaded at the start-up.

Related

Copying a snapshot of a Canvas to a BufferedImage

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.

WorldWind complicated updating 2D interface

I use using WorldWind to plot data, and I also require a 2D interface as an overlay. I have been creating a new BufferedImage a few times per second to update the data, but this requires a lot of overhead. I'd like to redraw on an existing image to decrease the overall usage, both in terms of CPU and memory. I'm using this code before redrawing:
BufferedImage img = TrackingService.img.get(width).get(height);
g = img.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g.fillRect(0,0,img.getWidth(),img.getHeight());
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
image = img;
ach element which can add to the UI returns a HashMap of BufferedImage to Point. Each BufferedImage and point is transformed into a ButtonAnnotation and added to an AnnotationLayer. Each x milliseconds, the system will:
layer.removeAllAnnotations();
layer.addAnnotations(buttonAnnotations);
redraw()
on the AWT Event queue. This works fine for markers, etc. but these images will never change from the first image I use. I've tried replacing the layer, disposing it, etc. I've tried writing the images to a debug file and noted that the BufferedImage is changing as expected. The problem is that WorldWind must be caching the images at some level. Since I am providing the same instance of BufferedImage to each new ButtonAnnotation, with a few modifications made using Graphics2D, it appears to be assuming that no changes have been made, when, in fact, there have been. This works perfectly fine if I use a new BufferedImage each time I want the data to change.
I have also tried the suggestions in This Question, and they did not work for me.

Which Image to Use

I've read the java api, but I still do not understand the main difference between:
1) ImageIcon
2) BufferedImage
3) VolatileImage
4) Image
Can someone tell me when each one is used?
I wouldn't call this explanation below the absolute standard of using Java Image types, but these are the rules of thumb that I follow:
1. ImageIcon
This is typically used when you have small images that you want to add to buttons or to use as window icons. These may be created directly from anything that implements the Image interface.
2. BufferedImage
Typically used when you need to manipulate individual pixels within an image, or when you want to double-buffer a custom paint(Graphics g) method. The image resides in RAM, so it can take up a lot of space, and modifications to BufferedImage instances are not typically hardware-accelerated.
3. VolatileImage
Hardware-accelerated image, so it's fast, but you run the risk of the hardware-backed buffer being overwritten before you're done painting (although this rarely happens and according to Oracle, is only an issue for Windows-based machines). It's a little more difficult to use than BufferedImage for double-buffering custom paint(Graphics g) methods, but is well worth it if you find yourself doing a lot of pre-processing before rendering to screen.
4. Image
This is basically just an interface that defines some bare-bones functionality that every Image should have. You should use this when you don't need to modify an image's contents and/or want to make methods that handle read-only-image data most flexible.
Also, ImageIcon implements serializable so you can send it through java sockets. If you have Image objects, you have to convert them to ImageIcon and send. When the client side took the ImageIcons, it can convert them to Images again.

Java Hardware Acceleration

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.

SWT Image concatenation or tiling / mosaic

I have an Eclipse RCP application that displays a lot (10k+) of small images next to each other, like a film strip. For each image, I am using a SWT Image object. This uses an excessive amount of memory and resources. I am looking for a more efficient way. I thought of taking all of these images and concatenating them by creating an ImageData object of the proper total, concatenated width (with a constant height) and using setPixel() for the rest of the pixels. However, the Palette used in the ImageData constructor I can't figure out.
I also searched for SWT tiling or mosaic functionality to create one image from a group of images, but found nothing.
Any ideas how I can display thousands of small images next to each other efficiently? Please note that once the images are displayed, they are not manipulated, so this is a one-time cost.
You can draw directly on the GC (graphics context) of a new (big) image. Having one big Image should result in much less resource usage than thousands of smaller images (each image in SWT keeps some OS graphics object handle)
What you can try is something like this:
final List<Image> images;
final Image bigImage = new Image(Display.getCurrent(), combinedWidth, height);
final GC gc = new GC(bigImage);
//loop thru all the images while increasing x as necessary:
int x = 0;
int y = 0;
for (Image curImage : images) {
gc.drawImage(curImage, x, y);
x += curImage.getBounds().width;
}
//very important to dispose GC!!!
gc.dispose();
//now you can use bigImage
Presumably not every image is visible on screen at any one time? Perhaps a better solution would be to only load the images when they become (or are about to become) visible, disposing of them when they have been scrolled off the screen. Obviously you'd want to keep a few in memory on either side of the current viewport in order to make a smooth transition for the user.
I previously worked with a Java application to create photomosaics, and found it very difficult to achieve adequate performance and memory usage using the java imaging (JAI) libraries and SWT. Although we weren't using nearly as many images as you mention, one route was to rely on a utilities outside of java. In particular, you could use ImageMagick command-line utilities to stitch together your mosaic, and the load the completed memory from disk. If you want to get fancy, there is also a C++ API for ImageMagick, which is very efficient in memory.

Categories

Resources