Best approach to storing image pixels in bottom-up order in Java
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/orSampleM开发者_运维技巧odel
interfaces from scratch (because of the way compatibility checking is implemented (at least in the Sun JDK), requiring specific subclasses ofSampleModel
so a genericBottomUpSampleModel
wrapper class would not work.)
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
.
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?
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.
精彩评论