Java paint speed relative to color model - java

I have a BufferedImage with an IndexColorModel. I need to paint that image onto the screen, but I've noticed that this is slow when using an IndexColorModel. However, if I run the BufferedImage through an identity affine transform it creates an image with a DirectColorModel and the painting is significantly faster. Here's the code I'm using
AffineTransformOp identityOp = new AffineTransformOp(new AffineTransform(), AffineTransformOp.TYPE_BILINEAR);
displayImage = identityOp.filter(displayImage, null);
I have three questions
1. Why is painting the slower on an IndexColorModel?
2. Is there any way to speed up the painting of an IndexColorModel?
3. If the answer to 2. is no, is this the most efficient way to convert from an IndexColorModel to a DirectColorModel? I've noticed that this conversion is dependent on the size of the image, and I'd like to remove that dependency.
Thanks for the help

This is too long for a comment...
Are you sure that the BufferedImage you're creating are the best depending on the OS you're on? You should always create a "compatible" BufferedImage. The fastest on, say, Windows, may be TYPE_INT_ARGB but this is not true on OS X and vice-versa.
Something like this (ouch, the Law of Demeter hurts ;) :
GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration()
.createCompatibleImage(width, height,Transparency.TRANSLUCENT)

Related

How do you efficiently scale a JFrame's content Java?

I'm searching for a method to properly scale (almost) all of a JFrame's content. All the solutions I have tried so far had a huge lack in rendering speed. What I am looking for is a speed similar to what you have when scaling content on your smartphone.
The JFrame's content should be rescalable quickly and stay scaled even if you overdraw the JFrame with new content. It should also be flexible enough so it let's you choose which BufferedImage's (which is essentially the only type I'm drawing, I don't draw any other "shapes") to redraw. I'm drawing using an ordinary Graphics, resp. Graphics2D object.
What I've tried before is the Graphic2D's scale-method and using an AffineTransformat object to scale each BufferedImage individually:
g.scale(scalingFactorX, scalingFactorY);
or alternatively:
BufferedImage img = someImageToScale();
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scalingFactorX, scalingFactorY);
AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
return bilinearScaleOp.filter(img, new BufferedImage(targetWidth, targetHeight,
BufferedImage.TYPE_INT_ARGB));
where scalingFactorX/Y are the factors the content should be scaled by and targetWidth, resp. targetHeight denote the resulting (scaled) dimensions of the BufferedImage.
Both approaches are rather slow which seems to be because in both cases, each frame, the scaled version of the contents have to be recalculated. I feel like I'm missing something very obvious here.
While drawImage() can resample an image, it must must do so each time paintComponent() is called. As suggested here, AffineTransformOp lets you choose the interpolation type; use the fastest one that meets your image quality requirements. Once you've satisfactorily resampled an image, ensure that you do not inadvertently resample the image a second time when rendering in a particular Graphics context. Override getPreferredSize() to make the destination component the same size as the resampled image, as shown in these examples.
My images are only 32x32, but there are a large amount of them (~500).
Consider using the flyweight pattern to render only visible images. JTable and JList are examples, as outlined here.
Are there performance benefits or other advantages to using separate components?
You may need to prototype and profile to be sure, perhaps by comparing representative examples:
Typical component-based examples include the button-based game cited here, or the chess games examined here, here and here.
The tile-based game cited here illustrates a single tile panel and multiple accessory panels that all listen to a common game model.

How to resize a BufferedImage in Java

I am looking for the simplest (and still non-problematic) way to resize a BufferedImage in Java.
In some answer to a question, the user coobird suggested the following solution, in his words (very slightly changed by me):
**
The Graphics object has a method to draw an Image while also performing a resize operation:
Graphics.drawImage(Image, int, int, int, int, ImageObserver)
method can be used to specify the location along with the size of the image when drawing.
So, we could use a piece of code like this:
BufferedImage originalImage = // .. created somehow
BufferedImage newImage = new BufferedImage(SMALL_SIZE, SMALL_SIZE, BufferedImage.TYPE_INT_RGB);
Graphics g = newImage.createGraphics();
g.drawImage(originalImage, 0, 0, SMALL_SIZE, SMALL_SIZE, null);
g.dispose();
This will take originalImage and draw it on the newImage with the width and height of SMALL_SIZE.
**
This solution seems rather simple. I have two questions about it:
Will it also work (using the exact same code), if I want to resize an image to a larger size, not only a smaller one?
Are there any problems with this solution?
If there is a better way to do this, please suggest it.
Thanks
The major problem with single step scaling is they don't generally produce quality output, as they focus on taking the original and squeezing into a smaller space, usually by dropping out a lot of pixel information (different algorithms do different things, so I'm generalizing)
Will drawGraphics scale up and down, yes, will it do it efficiently or produce a quality output? These will come down to implementation, generally speaking, most of the scaling algorithms used by default are focused on speed. You can effect these in a little way, but generally, unless you're scaling over a small range, the quality generally suffers (from my experience).
You can take a look at The Perils of Image.getScaledInstance() for more details and discussions on the topic.
Generally, what is generally recommend is to either use a dedicated library, like imgscalr, which, from the ten minutes I've played with it, does a pretty good job or perform a stepped scale.
A stepped scale basically steps the image up or down by the power of 2 until it reaches it's desired size. Remember, scaling up is nothing more then taking a pixel and enlarging it a little, so quality will always be an issue if you scale up to a very large size.
For example...
Quality of Image after resize very low -- Java
Scale the ImageIcon automatically to label size
Java: JPanel background not scaling
Remember, any scaling is generally an expensive operation (based on the original and target size of the image), so it is generally best to try and do those operations out side of the paint process and in the background where possible.
There is also the question whether you want to maintain the aspect ratio of the image? Based on you example, the image would be scaled in a square manner (stretched to meet to the requirements of the target size), this is generally not desired. You can pass -1 to either the width or height parameter and the underlying algorithm will maintain the aspect ratio of the original image or you could simply take control and make more determinations over whether you want to fill or fit the image to a target area, for example...
Java: maintaining aspect ratio of JPanel background image
In general, I avoid using drawImage or getScaledInstance most of the time (if your scaling only over a small range or want to do a low quality, fast scale, these can work) and rely more on things like fit/fill a target area and stepped scaling. The reason for using my own methods simply comes down to not always being allowed to use outside libraries. Nice not to have to re-invent the wheel where you can
It will enlarge the original if you set the parameters so. But: you should use some smart algorithm which preserves edges because simply enlarging an image will make it blurry and will result in worse perceived quality.
No problems. Theoretically this can even be hardware-accelerated on certain platforms.

Blitting in Java

I do not know java (usually write in c)
How can I do efficiently some way of blitting
pixel array content onto a window in java?
I need (in loop) blit pixels[][] onto a window
I could use something like
pixels[][] -> MemoryImageSource -> Image -> drawImage
but creating and deleting MemoryImageSource and Image
in every frame seems strange to me - how it can be
done simply and reasonably efficiently? Could someone
give a code example, tnx
Normally in Java it's easier to work with the native Image types and use their derived graphics. Behind the scenes Java uses blits as well, so the higher level abstractions is made to easen the workload.
But if there's no way to abstract on the pixel data you can use Raster and WritableRaster (where you can replace portions of the array) as an alternative to your solution. These rasters can be used with a BufferedImage which then can be drawn using the drawImage method you mentioned. I found one way of doing it here which basically creates the Image and then retrieves the raster for future manipulation.
int x, y = 100;
BufferedImage image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
WritableRaster raster = image.getRaster();
That raster (or just small areas of it) can then be manipulated and repainted.
This might improve performance slightly since the distance from you pixel-array to the screen is shorter. But I think very few people fully understands the entire depths of the AWT api - and it all depends on the native implementations of course - so my idea contains a healthy part of speculation ;-)
But I hope it helped..
For speed, you can pre-compute variations of the ColorModel, as shown in this example.

How can you produce sharp paint results when rotating a BufferedImage?

One attempted approach was to use TexturePaint and g.fillRect() to paint the image. This however requires you to create a new TexturePaint and Rectangle2D object each time you paint an image, which isn't ideal - and doesn't help anyway.
When I use g.drawImage(BufferedImage,...), the rotated images appear to be blurred/soft.
I'm familiar with RenderingHints and double-buffering (which is what I'm doing, I think), I just find it difficult to believe that you can't easily and efficiently rotate an image in Java that produces sharp results.
Code for using TexturePaint looks something like this.
Grahics2D g2d = (Graphics2D)g;
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);
I'm using AffineTransform to rotate a hand of cards into a fan.
What would be the best approach to paint good-looking images quickly?
Here is a screenshot:
The 9 is crisp but the rest of the cards are definitely not as sharp.
It could be possible that the problem lies in when I create each card image and store it in an array.
Here's how I'm doing it at the moment:
// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);
Graphics2D g = img.createGraphics();
setRenderingHints(g);
g.drawImage(shadow, 0, 0, 86, 126, null);
g.drawImage(white, 3, 3, 80, 120, null);
g.drawImage(suit, 3, 3, 80, 120, null);
g.drawImage(value, 3, 3, 80, 120, null);
g.dispose();
cardImages[i] = img;
}
private void setRenderingHints(Graphics2D g){
g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}
How should I approach this differently?
Thanks.
Edit:
Without RenderingHints
Setting AA hints made no difference. Also, setting RenderingHints when creating the images makes no difference either. It's only when they are being rotated with AffineTransform and painted using g.drawImage(...) that they seem to blur. The image above shows the difference between default (nearest neighbor) and bilinear interpolation.
Here is how I'm currently painting them (much faster than TexturePaint):
// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
}
// ImageLoader.java
public BufferedImage getCard(int code){
return cardImages[code];
}
All my cards are 80x120 and the shadow .png is 86x126, so as to leave 3px semi-transparent shadow around the card. It's not a realistic shadow I know, but it looks okay.
And so the question becomes... How can you produce sharp paint results when rotating a BufferedImage?
Reference to a previous question also regarding a fanned card hand:
How can you detect a mouse-click event on an Image object in Java?
Bounty-Edit:
Okay so after much discussion I made a few test .svg cards to see how SVG Salamander would go about rendering them. Unfortunately, the performance is terrible. My implementation is clean enough, seeing as with double-buffered BufferedImage's the painting was incredibly fast. Which means I have come full circle and I'm back to my original problem.
I'll give the 50 bounty to whoever can give me a solution to get sharp BufferedImage rotations. Suggestions have been to make the images bigger than they need to be and downscale before painting, and to use bicubic interpolation. If these are the only possible solutions, then I really don't know where to go from here and I may just have to deal with the blurred rotations - because both of those impose performance setbacks.
I can finish my game if I can find a way to do this well.
Thanks to everyone. :)
When you rotate a rasterized image (such as a BufferedImage), you lose data. The best solution is to save your images larger than you'll need them, and downscale on the fly when you paint them. I've found that 1.5x the size you need is a good starting point.
Then, when you're painting the image, resize on the fly:
g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
Rotations using bilinear interpolation is recommended.
Credit for suggestion goes to guido.
This advice is probably a little late in your design, but may be worth mentioning.
Rasterized images is probably the wrong technology to use if a lot of rotations and animations are a part of your UI; especially with complicated images with lots of curves. Just wait until you try and scale your canvass. I might suggest looking at a vector based graphical library. They will render the sorts of effects you want with less potential for artifacts.
http://xmlgraphics.apache.org/batik/using/swing.htm
Setting the interpolation type, as well as anti-aliasing value, in an AffineTransformOp may offer some improvement. Type TYPE_BICUBIC, while slower, is typically the best quality; an example is outlined here. Note that you can supply multiple RenderingHints. Another pitfall arises from failing to apply the hints each time the image is rendered. You may also need to adjust the transparency of the background, as suggested here. Finally, consider creating an sscce that includes one of your actual images.

draw image or draw filled circle?

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, &#x25CF, 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.

Categories

Resources