jav...@javadesktop.org wrote:
Thank you Dmitri for your answer.
Let me just say a bit more of the problem and why i go to this method:
My goal is to find the faster way to load an image from the disk (a tif file,
either binary or colored) and display it on the screen in a compatible way
(e.g. fast to redraw, pan, zoom, etc).
From all the reading i have done, i have concluded that i need to:
1. Load the image with ImageIO
2. Convert it to compatible
3. Display.
1. Converting an image to a compatible one is always faster with a ColorConverOp than with a g.drawImage. At times the speed is the same, but in general, the ColorConverOp is always a bit faster.
Both ColorConvertOp and drawImage have a set of image formats that they
handle directly with optimized loops (all native code), and other
formats that they use generalized code that may be as slow as making a
method call per pixel. You seem to be running into cases where CCO has
optimized loops, but drawImage does not. We tend to look to add loops
for drawImage to handle common image formats that come up for our
developers, but we need to avoid adding too many special case loops and
bloating the JDK.
What is really interesting is this: if an images already uses a
SinglePixelPackedSampleModel then the ColorConvertOp is significantly faster
(more than 50%).
What is the image format you are using in the TIFF file and what are the
parameters of the *Models it uses to represent it in memory? (Before
you start converting it.)
2. So the question remains:
a. Is it faster to convert directly to compatible using g.drawImage, or
b. is it faster to create an unmanageable image with a
SinglePixelPackedSampleModel and then use the ColorConvertOp to make a
compatible out of it?
g.drawImage() has optimized loops to convert from some common image
formats to destination images in some other common image formats. If
you fall into one of those categories then it makes sense to use that.
ColorConvertOp also has its own set of loops as well.
With respect to SPPSM, it isn't so much about that particular sample
model than about which parameters you use with it. When we classify an
image internally we examine not just the type of the SampleModel and
ColorModel, but what masks, offsets, and other parameters they use.
g.drawImage() is one of the simplest techniques, and for occasional use,
or for use at application startup for most image sizes any potential
performance issues with that technique are probably not noticeable.
But, if you are doing this to a lot of images on the fly, then it is
worth optimizing this step some more (and pushing to have the internal
drawImage() code support it more directly as well).
This is why i use it.
Empirical results are often the best guide here, but let me present some
rules of thumb which might help make the choices easier (to make, or at
least easier to understand) and point out some potential other alternatives.
First, some details about the implementation:
- If you grab the DataBuffer from an image, it becomes unmanageable in
JDK 6 and prior. This would recommend using the various WritableRaster
methods to write data to the images if you want to remain manageable, or
using a conversion process. This is also true if you have a DataBuffer
and make a BufferedImage out of it.
- In JDK 7 and beyond we now track the pixels at the DataBuffer level so
you can grab the DataBuffer and use its methods to modify or access an
image, as long as you don't grab the array itself from the DataBuffer.
For instance, creating a DataBuffer using your own array automatically
disqualifies the image from being manageable. This gets you much closer
to the raw data without disrupting the cache management, but only works
on JDK 7 and forward.
- Manageable images, as you seem to already be aware, can be hardware
accelerated via cached copies kept in VRAM on platforms that support it,
so it is good to use techniques that maintain that property if they work
well enough, otherwise doing what works best even if it defeats image
management and using the old "convert to a compatible image after the
fact" technique becomes necessary.
- As I stated above, drawImage has a matrix of optimized from/to image
format pairs it supports well for doing software copies and conversions.
These copies are going to be slower than a hardware copy, but much
faster than the code that deals with arbitrary from/to images which
often breaks down to a per-pixel method call.
- Also, as stated earlier, ColorConvertOp also has a matrix of from/to
formats, but it is implemented using an entirely different system than
drawImage and so the matrix is unrelated. This Op may also need to
resort to per-pixel method calls if you fall outside its matrix.
- The most likely candidates for support in the matrices above would be
the formats created using the "new BufferedImage(w,h,TYPE_FOO)"
constants. It looks like you are already targetting one of those
formats with your construction of the BufferedImage, but I would
recommend just using "new BI(..., TYPE_INT_RGB)" instead of creating it
manually from the bottom up as you are doing because the potential of
getting one of the parameters wrong is always a possibility. If you use
the BI(TYPE) constructor then you can be sure we got all of the *Models
constructed with all of the right values. You can then dig out the
appropriate Raster, or DataBuffer, or raw Java array from that image for
more direct access if you need it. For example:
BufferedImage img = new BufferedImage(w,h,TYPE_INT_RGB);
WritableRaster wr = img.getRaster();
// These maintain manageability on all releases that
// support managed images, listed in rough order of
// probably fastest to probably slowest (though it can
// still depend on format - YMMV):
wr.setDataElements(...);
wr.setPixels(...);
wr.setSamples(...);
// or, this defeats manageable on JDK 6 and prior:
DataBufferInt dbi = wr.getDataBuffer();
dbi.setElem(...);
// or, this defeats manageable on JDK 7 and prior:
// (and probably future VMs as well)
int buf[] = dbi.getData();
buf[...] = ...;
Another thing to consider is that you use getPixels(...) to get an
integer array from the first image and then try to turn that into the
DataBuffer array for the result image. While I think this is
commendable for "saving an extra array allocation", it is one source of
potential performance problems down the road in terms of resulting in an
unmanageable image, and probably ends up requiring an additional copy to
a compatible image so that it can be managed. I would suggest the
following:
- Use getDataElements() instead to get the source pixels and unpack the
pixels yourself into the destination image using one of the above
techniques to preserve the manageable state of the result image.
- Use getPixels(), but do it a row at a time rather than the entire
image. Be sure to reuse the buffer for subsequent rows by passing it in
as the array parameter to subsequent calls. Use one of the above
techniques to create a manageable image and store the converted data
into it at a high enough level to avoid tainting its buffer. Note that
getPixels() is just doing getDataElements() and manually exploding the
pixels under the covers for you so you may end up allocating less of a
temporary buffer to use the first technique, but you'd have to write the
pixel-exploding code yourself.
And finally:
- let us know what image formats you are seeing in the loaded image and
which Reader is loading it so we can consider adding loops that deal
with that format more directly (or modifying our "format detection" code
to recognize it if there is something amiss with our classifications).
We do already have support for a fair number of "binary" image formats
so I'm curious as to what the exact specifics of the format are and why
they fail to trigger our existing optimized binary format loops. This
should probably be done through the bug tracking system to keep records
of the issue...
...jim
===========================================================================
To unsubscribe, send email to lists...@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
lists...@java.sun.com and include in the body of the message "help".