FileDocCategorySizeDatePackage
NativeImageFormat.javaAPI DocAndroid 1.5 API19905Wed May 06 22:41:54 BST 2009org.apache.harmony.awt.gl.color

NativeImageFormat

public class NativeImageFormat extends Object
This class converts java color/sample models to the LCMS pixel formats. It also encapsulates all the information about the image format, which native CMM needs to have in order to read/write data. At present planar formats (multiple bands) are not supported and they are handled as a common (custom) case. Samples other than 1 - 7 bytes and multiple of 8 bits are also handled as custom (and won't be supported in the nearest future).

Fields Summary
private static final int
PT_ANY
private static final int
PT_GRAY
private static final int
PT_RGB
private static final int
INT_RGB_LCMS_FMT
private static final int
INT_ARGB_LCMS_FMT
private static final int
INT_BGR_LCMS_FMT
private static final int
THREE_BYTE_BGR_LCMS_FMT
private static final int
FOUR_BYTE_ABGR_LCMS_FMT
private static final int
BYTE_GRAY_LCMS_FMT
private static final int
USHORT_GRAY_LCMS_FMT
private int
cmmFormat
private int
rows
private int
cols
private int
scanlineStride
private Object
imageData
private int
dataOffset
private int
alphaOffset
Constructors Summary
public NativeImageFormat()

    
public NativeImageFormat(Object imgData, int nChannels, int nRows, int nCols)
Simple image layout for common case with not optimized workflow. For hifi colorspaces with 5+ color channels imgData should be byte array. For common colorspaces with up to 4 color channels it should be short array. Alpha channel is handled by caller, not by CMS. Color channels are in their natural order (not BGR but RGB).

param
imgData - array of byte or short
param
nChannels - number of channels
param
nRows - number of scanlines in the image
param
nCols - number of pixels in one row of the image

        if (imgData instanceof short[]) {
            cmmFormat |= bytesSh(2);
        }
        else if (imgData instanceof byte[]) {
            cmmFormat |= bytesSh(1);
        }
        else
            // awt.47=First argument should be byte or short array
            throw new IllegalArgumentException(Messages.getString("awt.47")); //$NON-NLS-1$

        cmmFormat |= channelsSh(nChannels);

        rows = nRows;
        cols = nCols;

        imageData = imgData;

        dataOffset = 0;
    
Methods Summary
private static intbytesSh(int s)

        return s;
    
private static intcalculateAlphaOffset(java.awt.image.SampleModel sm, java.awt.image.Raster r)
Calculates byte offset of the alpha channel from the beginning of the pixel data

param
sm - sample model
param
r - raster
return
byte offset of the alpha channel

        if (sm instanceof ComponentSampleModel) {
            ComponentSampleModel csm = (ComponentSampleModel) sm;
            int dataTypeSize =
                DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
            return
                csm.getBandOffsets()[csm.getBandOffsets().length - 1] * dataTypeSize;
        } else if (sm instanceof SinglePixelPackedSampleModel) {
            SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
            return sppsm.getBitOffsets()[sppsm.getBitOffsets().length - 1] / 8;
        } else {
            return -1; // No offset, don't copy alpha
        }
    
private static intcalculateScanlineStrideCSM(java.awt.image.ComponentSampleModel csm, java.awt.image.Raster r)
Calculates scanline stride in bytes

param
csm - component sample model
param
r - raster
return
scanline stride in bytes

        if (csm.getScanlineStride() != csm.getPixelStride()*csm.getWidth()) {
            int dataTypeSize = DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
            return csm.getScanlineStride()*dataTypeSize;
        }
        return -1;
    
private static intcalculateScanlineStrideSPPSM(java.awt.image.SinglePixelPackedSampleModel sppsm, java.awt.image.Raster r)
Calculates scanline stride in bytes

param
sppsm - sample model
param
r - raster
return
scanline stride in bytes

        if (sppsm.getScanlineStride() != sppsm.getWidth()) {
            int dataTypeSize = DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
            return sppsm.getScanlineStride()*dataTypeSize;
        }
        return -1;
    
private static intchannelsSh(int s)

        return (s << 3);
    
private static intcolorspaceSh(int s)


    // initializes proper field IDs
        

     
        NativeCMM.loadCMM();
        initIDs();
    
        return (s << 16);
    
public static org.apache.harmony.awt.gl.color.NativeImageFormatcreateNativeImageFormat(java.awt.image.BufferedImage bi)
Deduces image format from the buffered image type or color and sample models.

param
bi - image
return
image format object

        NativeImageFormat fmt = new NativeImageFormat();

        switch (bi.getType()) {
            case BufferedImage.TYPE_INT_RGB: {
                fmt.cmmFormat = INT_RGB_LCMS_FMT;
                break;
            }

            case BufferedImage.TYPE_INT_ARGB:
            case BufferedImage.TYPE_INT_ARGB_PRE: {
                fmt.cmmFormat = INT_ARGB_LCMS_FMT;
                fmt.alphaOffset = 3;
                break;
            }

            case BufferedImage.TYPE_INT_BGR: {
                fmt.cmmFormat = INT_BGR_LCMS_FMT;
                break;
            }

            case BufferedImage.TYPE_3BYTE_BGR: {
                fmt.cmmFormat = THREE_BYTE_BGR_LCMS_FMT;
                break;
            }

            case BufferedImage.TYPE_4BYTE_ABGR_PRE:
            case BufferedImage.TYPE_4BYTE_ABGR: {
                fmt.cmmFormat = FOUR_BYTE_ABGR_LCMS_FMT;
                fmt.alphaOffset = 0;
                break;
            }

            case BufferedImage.TYPE_BYTE_GRAY: {
                fmt.cmmFormat = BYTE_GRAY_LCMS_FMT;
                break;
            }

            case BufferedImage.TYPE_USHORT_GRAY: {
                fmt.cmmFormat = USHORT_GRAY_LCMS_FMT;
                break;
            }

            case BufferedImage.TYPE_BYTE_BINARY:
            case BufferedImage.TYPE_USHORT_565_RGB:
            case BufferedImage.TYPE_USHORT_555_RGB:
            case BufferedImage.TYPE_BYTE_INDEXED: {
                // A bunch of unsupported formats
                return null;
            }

            default:
                break; // Try to look at sample model and color model
        }


        if (fmt.cmmFormat == 0) {
            ColorModel cm = bi.getColorModel();
            SampleModel sm = bi.getSampleModel();

            if (sm instanceof ComponentSampleModel) {
                ComponentSampleModel csm = (ComponentSampleModel) sm;
                fmt.cmmFormat = getFormatFromComponentModel(csm, cm.hasAlpha());
                fmt.scanlineStride = calculateScanlineStrideCSM(csm, bi.getRaster());
            } else if (sm instanceof SinglePixelPackedSampleModel) {
                SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
                fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, cm.hasAlpha());
                fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, bi.getRaster());
            }

            if (cm.hasAlpha())
                fmt.alphaOffset = calculateAlphaOffset(sm, bi.getRaster());
        }

        if (fmt.cmmFormat == 0)
            return null;

        if (!fmt.setImageData(bi.getRaster().getDataBuffer())) {
            return null;
        }

        fmt.rows = bi.getHeight();
        fmt.cols = bi.getWidth();

        fmt.dataOffset = bi.getRaster().getDataBuffer().getOffset();

        return fmt;
    
public static org.apache.harmony.awt.gl.color.NativeImageFormatcreateNativeImageFormat(java.awt.image.Raster r)
Deduces image format from the raster sample model.

param
r - raster
return
image format object

        NativeImageFormat fmt = new NativeImageFormat();
        SampleModel sm = r.getSampleModel();

        // Assume that there's no alpha
        if (sm instanceof ComponentSampleModel) {
            ComponentSampleModel csm = (ComponentSampleModel) sm;
            fmt.cmmFormat = getFormatFromComponentModel(csm, false);
            fmt.scanlineStride = calculateScanlineStrideCSM(csm, r);
        } else if (sm instanceof SinglePixelPackedSampleModel) {
            SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
            fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, false);
            fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, r);
        }

        if (fmt.cmmFormat == 0)
            return null;

        fmt.cols = r.getWidth();
        fmt.rows = r.getHeight();
        fmt.dataOffset = r.getDataBuffer().getOffset();

        if (!fmt.setImageData(r.getDataBuffer()))
            return null;

        return fmt;
    
private static intdoswapSh(int s)

        return (s << 10);
    
private static intendianSh(int s)

        return (s << 11);
    
private static intextraSh(int s)

        return (s << 7);
    
private static intflavorSh(int s)

        return (s << 13);
    
java.lang.ObjectgetChannelData()

        return imageData;
    
private static intgetFormatFromComponentModel(java.awt.image.ComponentSampleModel sm, boolean hasAlpha)
Obtains LCMS format from the component sample model

param
sm - sample model
param
hasAlpha - true if there's an alpha channel
return
LCMS format

        // Multiple data arrays (banks) not supported
        int bankIndex = sm.getBankIndices()[0];
        for (int i=1; i < sm.getNumBands(); i++) {
            if (sm.getBankIndices()[i] != bankIndex) {
                return 0;
            }
        }

        int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands();
        int extra = hasAlpha ? 1 : 0;
        int bytes = 1;
        switch (sm.getDataType()) {
            case DataBuffer.TYPE_BYTE:
                bytes = 1; break;
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_USHORT:
                bytes = 2; break;
            case DataBuffer.TYPE_INT:
                bytes = 4; break;
            case DataBuffer.TYPE_DOUBLE:
                bytes = 0; break;
            default:
                return 0; // Unsupported data type
        }

        int doSwap = 0;
        int swapFirst = 0;
        boolean knownFormat = false;

        int i;

        // "RGBA"
        for (i=0; i < sm.getNumBands(); i++) {
            if (sm.getBandOffsets()[i] != i) break;
        }
        if (i == sm.getNumBands()) { // Ok, it is it
            doSwap = 0;
            swapFirst = 0;
            knownFormat = true;
        }

        // "ARGB"
        if (!knownFormat) {
            for (i=0; i < sm.getNumBands()-1; i++) {
                if (sm.getBandOffsets()[i] != i+1) break;
            }
            if (sm.getBandOffsets()[i] == 0) i++;
            if (i == sm.getNumBands()) { // Ok, it is it
                doSwap = 0;
                swapFirst = 1;
                knownFormat = true;
            }
        }

        // "BGRA"
        if (!knownFormat) {
            for (i=0; i < sm.getNumBands()-1; i++) {
                if (sm.getBandOffsets()[i] != sm.getNumBands() - 2 - i) break;
            }
            if (sm.getBandOffsets()[i] == sm.getNumBands()-1) i++;
            if (i == sm.getNumBands()) { // Ok, it is it
                doSwap = 1;
                swapFirst = 1;
                knownFormat = true;
            }
        }

        // "ABGR"
        if (!knownFormat) {
            for (i=0; i < sm.getNumBands(); i++) {
                if (sm.getBandOffsets()[i] != sm.getNumBands() - 1 - i) break;
            }
            if (i == sm.getNumBands()) { // Ok, it is it
                doSwap = 1;
                swapFirst = 0;
                knownFormat = true;
            }
        }

        // XXX - Planar formats are not supported yet
        if (!knownFormat)
            return 0;

        return
            channelsSh(channels) |
            bytesSh(bytes) |
            extraSh(extra) |
            doswapSh(doSwap) |
            swapfirstSh(swapFirst);
    
private static intgetFormatFromSPPSampleModel(java.awt.image.SinglePixelPackedSampleModel sm, boolean hasAlpha)
Obtains LCMS format from the single pixel packed sample model

param
sm - sample model
param
hasAlpha - true if there's an alpha channel
return
LCMS format

        // Can we extract bytes?
        int mask = sm.getBitMasks()[0] >>> sm.getBitOffsets()[0];
        if (!(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF))
            return 0;

        // All masks are same?
        for (int i = 1; i < sm.getNumBands(); i++) {
            if ((sm.getBitMasks()[i] >>> sm.getBitOffsets()[i]) != mask)
                return 0;
        }

        int pixelSize = 0;
        // Check if data type is supported
        if (sm.getDataType() == DataBuffer.TYPE_USHORT)
            pixelSize = 2;
        else if (sm.getDataType() == DataBuffer.TYPE_INT)
            pixelSize = 4;
        else
            return 0;


        int bytes = 0;
        switch (mask) {
            case 0xFF:
                bytes = 1;
                break;
            case 0xFFFF:
                bytes = 2;
                break;
            case 0xFFFFFFFF:
                bytes = 4;
                break;
            default: return 0;
        }


        int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands();
        int extra = hasAlpha ? 1 : 0;
        extra +=  pixelSize/bytes - sm.getNumBands(); // Unused bytes?

        // Form an ArrayList containing offset for each band
        ArrayList<Integer> offsetsLst = new ArrayList<Integer>();
        for (int k=0; k < sm.getNumBands(); k++) {
            offsetsLst.add(new Integer(sm.getBitOffsets()[k]/(bytes*8)));
        }

        // Add offsets for unused space
        for (int i=0; i<pixelSize/bytes; i++) {
            if (offsetsLst.indexOf(new Integer(i)) < 0)
                offsetsLst.add(new Integer(i));
        }

        int offsets[] = new int[pixelSize/bytes];
        for (int i=0; i<offsetsLst.size(); i++) {
            offsets[i] = offsetsLst.get(i).intValue();
        }

        int doSwap = 0;
        int swapFirst = 0;
        boolean knownFormat = false;

        int i;

        // "RGBA"
        for (i=0; i < pixelSize; i++) {
            if (offsets[i] != i) break;
        }
        if (i == pixelSize) { // Ok, it is it
            doSwap = 0;
            swapFirst = 0;
            knownFormat = true;
        }

        // "ARGB"
        if (!knownFormat) {
            for (i=0; i < pixelSize-1; i++) {
                if (offsets[i] != i+1) break;
            }
            if (offsets[i] == 0) i++;
            if (i == pixelSize) { // Ok, it is it
                doSwap = 0;
                swapFirst = 1;
                knownFormat = true;
            }
        }

        // "BGRA"
        if (!knownFormat) {
            for (i=0; i < pixelSize-1; i++) {
                if (offsets[i] != pixelSize - 2 - i) break;
            }
            if (offsets[i] == pixelSize-1) i++;
            if (i == pixelSize) { // Ok, it is it
                doSwap = 1;
                swapFirst = 1;
                knownFormat = true;
            }
        }

        // "ABGR"
        if (!knownFormat) {
            for (i=0; i < pixelSize; i++) {
                if (offsets[i] != pixelSize - 1 - i) break;
            }
            if (i == pixelSize) { // Ok, it is it
                doSwap = 1;
                swapFirst = 0;
                knownFormat = true;
            }
        }

        // XXX - Planar formats are not supported yet
        if (!knownFormat)
            return 0;

        return
            channelsSh(channels) |
            bytesSh(bytes) |
            extraSh(extra) |
            doswapSh(doSwap) |
            swapfirstSh(swapFirst);
    
intgetNumCols()

        return cols;
    
intgetNumRows()

        return rows;
    
private static native voidinitIDs()

private static intplanarSh(int s)

        return (s << 12);
    
private booleansetImageData(java.awt.image.DataBuffer db)
Obtains data array from the DataBuffer object

param
db - data buffer
return
- true if successful

        AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance();
        try {
            imageData = dbAccess.getData(db);
        } catch (IllegalArgumentException e) {
            return false; // Unknown data buffer type
        }

        return true;
    
private static intswapfirstSh(int s)

        return (s << 14);