Question

I am using code similar to Java - get pixel array from image to get low-level access to pixel data of a BMP image, along the lines of:

BufferedImage image = ImageIO.read(is);
DataBuffer buffer = image.getRaster().getDataBuffer();
byte[] rawPixels = ((DataBufferByte) buffer).getData();

The resulting array is laid bottom to top (ie. its first bytes are the beginning of the last image line), which makes sense considering that BMP files usually have the same layout.

I would like to hide this low-level detail from callers by flipping the lines in this situation. Is there a way I can query the pixels orientation/layout of the loaded BufferedImage?

Was it helpful?

Solution

I have checked the source code of the Java 7 BMPImageReader, and it does translate from bottom-up to top-down order while reading, as I expected it to do. The DataBuffers backing array will thus be in the normal top-down order. I cannot reproduce this behavior using Oracle Java 7 JRE on Windows.

The OP has verified that the problem was indeed in another part of the code, not posted as part of the question.

I think what is described just might be possible, using a special subclass of SampleModel that translates all incoming y-coordinates, but there's no standard method to query for orientation (all Rasters are assumed to be top-down).

Anyway, just for fun, I created some code, to test if it is at all possible. Below is a fully runnable example.

public class SampleModelOrientationTest {
    public static void main(String[] args) {
        BufferedImage image = new BufferedImage(16, 9, BufferedImage.TYPE_3BYTE_BGR);
        WritableRaster raster = image.getRaster();
        DataBuffer dataBuffer = raster.getDataBuffer();

        SampleModel sampleModel = image.getSampleModel();

        QueryingDataBuffer queryBuffer = new QueryingDataBuffer(dataBuffer, sampleModel.getWidth(), sampleModel.getNumDataElements());
        sampleModel.getDataElements(0, 0, null, queryBuffer);
        System.out.println(queryBuffer.getOrientation());

        queryBuffer.resetOrientation();
        SampleModel bottomUpSampleModel = new BottomUpSampleModel(sampleModel);
        bottomUpSampleModel.getDataElements(0, 0, null, queryBuffer);
        System.out.println(queryBuffer.getOrientation());
    }

    private static class QueryingDataBuffer extends DataBuffer {
        enum Orientation {
            Undefined,
            TopDown,
            BottomUp,
            Unsupported
        }

        private final int width;
        private final int numDataElements;

        private Orientation orientation = Orientation.Undefined;

        public QueryingDataBuffer(final DataBuffer dataBuffer, final int width, final int numDataElements) {
            super(dataBuffer.getDataType(), dataBuffer.getSize());
            this.width = width;
            this.numDataElements = numDataElements;
        }

        @Override public int getElem(final int bank, final int i) {
            if (bank == 0 && i < numDataElements && isOrientationUndefinedOrEqualTo(Orientation.TopDown)) {
                orientation = Orientation.TopDown;
            }
            else if (bank == 0 && i >= (size - (width * numDataElements) - numDataElements) && isOrientationUndefinedOrEqualTo(Orientation.BottomUp)) {
                orientation = Orientation.BottomUp;
            }
            else {
                // TODO: Expand with more options as apropriate
                orientation = Orientation.Unsupported;
            }

            return 0;
        }

        private boolean isOrientationUndefinedOrEqualTo(final Orientation orientation) {
            return this.orientation == Orientation.Undefined || this.orientation == orientation;
        }

        @Override public void setElem(final int bank, final int i, final int val) {
        }

        public final void resetOrientation() {
            orientation = Orientation.Undefined;
        }

        public final Orientation getOrientation() {
            return orientation;
        }
    }

    // TODO: This has to be generalized to be used for any BufferedImage type.
    // I justy happen to know that 3BYTE_BGR uses PixelInterleavedSampleModel and has BGR order.
    private static class BottomUpSampleModel extends PixelInterleavedSampleModel {
        public BottomUpSampleModel(final SampleModel sampleModel) {
            super(sampleModel.getDataType(), sampleModel.getWidth(), sampleModel.getHeight(),
                  sampleModel.getNumDataElements(), sampleModel.getNumDataElements() * sampleModel.getWidth(),
                  new int[] {2, 1, 0} // B, G, R
            );
        }

        @Override public Object getDataElements(final int x, final int y, final Object obj, final DataBuffer data) {
            return super.getDataElements(x, getHeight() - 1 - y, obj, data);
        }

        @Override public int getSample(final int x, final int y, final int b, final DataBuffer data) {
            return super.getSample(x, getHeight() - 1 - y, b, data);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top