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
DataBufferto 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
Rasterand/orSampleModelinterfaces from scratch (because of the way compatibility checking is implemented (at least in the Sun JDK), requiring specific subclasses ofSampleModelso a genericBottomUpSampleModelwrapper class would not work.)
I found I can implement this using only one new class, which I named
BottomUpComponentSampleModel. It extendsComponentSampleModeland negates the value of thescanlineStridefield (which, luckily, isprotectedrather thanprivate) after calling the superclass constructor. All of the pixel address calculations work fine, although the validation inRaster.createWritableRasterdoes 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
MultiPixelPackedSampleModelorSinglePixelPackedSampleModel, as they do accept a negativescanlineStride. They do not have band offsets, but this can be worked around by setting an offset on theDataBuffer.