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".

Reply via email to