views:

201

answers:

3

I have an array of bytes representing an image in Windows BMP format and I would like my library to present it to the Java application as a BufferedImage, without copying the pixel data.

The main problem is that all implementations of Raster in the JDK store image pixels in top-down, left-to-right order whereas BMP pixel data is stored bottom-up, left-to-right. If this is not compensated for, the resulting image will be flipped vertically.

The most obvious "solution" is to set the SampleModel's scanlineStride property to a negative value and change the band offsets (or the DataBuffer's array offset) to point to the top-left pixel, i.e. the first pixel of the last line in the array. Unfortunately this does not work because all of the SampleModel constructors throw an exception if given a negative scanlineStride argument.

I am currently working around it by forcing the scanlineStride field to a negative value using reflection, but I would like to do it in a cleaner and more portable way if possible. e.g. is there another way to fool the Raster or SampleModel into arranging the pixels in bottom-up order but without breaking encapsulation? Or is there a library somewhere that will wrap the Raster and SampleModel, presenting the pixel rows in reverse order?

I would prefer to avoid the following approaches:

  • Copying the whole image (for performance reasons. The code must process hundreds of large (>= 1Mpixels) images per second and although the whole image must be available to the application, it will normally access only a tiny (but hard-to-predict) portion of the image.)
  • Modifying the DataBuffer to perform coordinate transformation (this actually works but is another "dirty" solution because the buffer should not need to know about the scanline/pixel layout.)
  • Re-implementing the Raster and/or SampleModel interfaces from scratch (because of the way compatibility checking is implemented (at least in the Sun JDK), requiring specific subclasses of SampleModel so a generic BottomUpSampleModel wrapper class would not work.)
A: 

what about having the application (or access layer) do the translation and fliping work on it's copy when accessing the tiny (but hard-to-predict) portion of the image?

Omry
Possible, but the application still needs to work with non-flipped images. I could add an "`isFlipped`" argument but that seems messy.
finnw
A: 

even better, it sounds like the application doesn't need to actually display the image? Why bother with wanting to flip it in the first place so it would look correct on screen? Just write the logic to work on the version you have? No need to have a BufferedImage that way either, work directly on the array.

jwenting
It needs to work with a variety of pixel formats (8, 16, 24 or 32bpp.) This is already implemented by the various subclasses of `ColorModel` and `SampleModel` and I would prefer not to re-implement pixel format conversion. Also the client code already operates on instances of `BufferedImage` from other sources (e.g. decoded PNG files.)
finnw
+1  A: 

I found I can implement this using only one new class, which I named BottomUpComponentSampleModel. It extends ComponentSampleModel and negates the value of the scanlineStride field (which, luckily, is protected rather than private) after calling the superclass constructor. All of the pixel address calculations work fine, although the validation in Raster.createWritableRaster does not (it can fail to detect if you give it an array that is too small), but that is not a serious problem.

This is not necessary with MultiPixelPackedSampleModel or SinglePixelPackedSampleModel, as they do accept a negative scanlineStride. They do not have band offsets, but this can be worked around by setting an offset on the DataBuffer.

finnw