|
Quick Lists
|
|
Bug ID:
|
6196792
|
|
Votes
|
4
|
|
Synopsis
|
Update Image.getScaledInstance with acceleration or documentation
|
|
Category
|
java:classes_2d
|
|
Reported Against
|
|
|
Release Fixed
|
|
|
State
|
6-Fix Understood,
bug
|
|
Priority:
|
4-Low
|
|
Related Bugs
|
6574349
|
|
Submit Date
|
17-NOV-2004
|
|
Description
|
Some time ago, when I found that my image scaling was incredibly slow, I was told by the 2D team that using Image.getScaledInstance() was not recommended any more. The preferred approach is to create a new Image and paint to it, scaling on the fly, to take advantage of faster loops.
I am still seeing people using getScaledInstance() and I've been trying to correct them. But we need to fix this in our API. Either a) by deprecating the old method, b) documenting the preferred approach, c) changing the implementation to use the new faster loops.
I suspect there may be similar situations with other methods - but I can't confirm.
xxxxx@xxxxx 2004-11-17 20:58:35 GMT
|
|
Work Around
|
Until now, this bug report did not explicitly mention the proper way of resizing
an image, which is covered in the Java 2D FAQ:
http://java.sun.com/products/java-media/2D/reference/faqs/index.html#Q_How_do_I_create_a_resized_copy
I'll paste that full entry here, in case developers trip across this bug report
and are wondering about the correct/best way to resize an image:
----
Create a BufferedImage of the desired size and draw the original image into it, scaling on the fly. Note that depending on whether your original image is opaque or non-opaque (that is, if it's translucent or transparent), you may need to create an image with an alpha channel.
BufferedImage createResizedCopy(Image originalImage,
int scaledWidth, int scaledHeight,
boolean preserveAlpha)
{
int imageType = preserveAlpha ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, imageType);
Graphics2D g = scaledBI.createGraphics();
if (preserveAlpha) {
g.setComposite(AlphaComposite.Src);
}
g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
return scaledBI;
}
You can control the quality of the scaled copy with the Graphics2D.setRenderingHint() method.
See http://java.sun.com/j2se/1.5.0/docs/api/java/awt/RenderingHints.html#KEY_INTERPOLATION
Add the following before the drawImage() call:
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_[NEAREST_NEIGHBOR
BILINEAR
BICUBIC]);
----
|
|
Evaluation
|
We've discussed this bug internally for what seems like years, but we haven't
yet done anything to resolve it (other than tell developers not to use
getScaledInstance()). It's unlikely that we will deprecate the method at
this point. At the very least we could provide some documentation about how
to use the more optimal alternatives (i.e. Graphics.drawImage()).
It would be relatively simple to change the implementation of getScaledInstance()
so that if the hint is SCALE_REPLICATE (synonymous with SCALE_FAST), then we
could ensure that the image is fully loaded and then use the scaling drawImage()
variant to produce a scaled copy of the original image. That part is not very
controversial. The real problem comes when we talk about about the
SCALE_AREA_AVERAGING hint (synonymous with SCALE_SMOOTH). Over the years
developers have come to appreciate the quality of that filtering algorithm,
but unfortunately we don't have an equivalent RenderingHint (along the lines
of BILINEAR or BICUBIC), so it wouldn't be as simple as the case I've descibed
above.
We have a couple options on how to deal with SCALE_AREA_AVERAGING. We could
potentially offer a new RenderingHint called AREA_AVERAGING that would be
"smooth filtering" alternative to BILINEAR/BICUBIC. We could then update
the implementation of getScaledInstance() as suggested above and use this
new AREA_AVERAGING hint. This would require quite a lot of work (our mediaLib
layer is only prepared to handle NEAREST_NEIGHBOR, BILINEAR, and BICUBIC,
as are our TransformHelper loops), not to mention that we'd need to add new
API, which is tough at this stage of the release.
Perhaps as a middle ground we could update the implementation of
getScaledInstance() to use drawImage(), except we would mimic the filtering
quality of AREA_AVERAGING by instead using a multistep BILINEAR approach,
which would produce similar results, but probably with much better performance
than what we have currently. In the unlikely chance that a developer is not
satisfied with the filtering quality of this approach, they could always use
the AreaAveragingScaleFilter, which would still offer the historical
AREA_AVERAGING algorithm that they are used to.
Posted Date : 2005-11-10 01:41:00.0
In response to the JDC comment, I've added code to the workaround section
demonstrating the appropriate way of resizing an image without using
Image.getScaledInstance().
We are still planning to fix this bug at some point either by putting
similar workaround code in the javadocs for Image.getScaledInstance(), or by
updating the implementation of that method with something that performs better.
In the meantime (and regardless of whether this bug is fixed at all), we still
recommend that developers use the suggested workaround code instead of
Image.getScaledInstance(), not only because of the performance benefits, but
also because the suggested code allows for more flexibility and control.
Just to demonstrate the performance difference between Image.getScaledInstance()
and the preferred approach of using the scaling variant of Graphics.drawImage(),
I wrote a small testcase that measures the time taken for each. I think the
numbers speak for themselves:
bash-2.05$ j160 ScaledInstancePerf
Testing upscale performance (200x200 to 500x500)...
getScaledInstance(): avg scale=2663ms, avg draw=1972ms
drawImage(): avg scale=340ms, avg draw=310ms
getScaledInstance(): avg scale=3236ms, avg draw=2449ms
drawImage(): avg scale=521ms, avg draw=489ms
getScaledInstance(): avg scale=2296ms, avg draw=1657ms
drawImage(): avg scale=391ms, avg draw=364ms
Testing downscale performance (200x200 to 80x80)...
getScaledInstance(): avg scale=130ms, avg draw=88ms
drawImage(): avg scale=4ms, avg draw=2ms
getScaledInstance(): avg scale=117ms, avg draw=61ms
drawImage(): avg scale=4ms, avg draw=2ms
getScaledInstance(): avg scale=96ms, avg draw=59ms
drawImage(): avg scale=3ms, avg draw=2ms
Posted Date : 2006-11-01 23:23:12.0
|
|
Comments
|
Submitted On 31-OCT-2006
robertabbe
This is a huge deal for document imaging applications that need to reduce the size of scanned or faxed documents.
Customers expect the performance to be on par with native code applications that accomplish the same effect.
Submitted On 08-FEB-2007
RLN
the suggestions here do not do much good, since the code given here always returns very poor quality, regardless of the different setRenderingHint() changes I attempted (which were many of them)
Submitted On 20-SEP-2008
stolsvik
Read this:
http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
The algorithm is improved in the Filthy Rich Clients book, where only one intermediate image is used (apparently it is possible to scale an image into itself).
However, It is important that the fix doesn't end up making intermediate BufferedImage instances that the user cannot control the creation of. If an intermediate image is needed, there should be a possibility for the user to supply it, either directly (one half-by-half BI instance is needed, in addition to the final sized BI instance), or by use of a factory instance that the user provides.
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |