Package org.libtiff.jai.codec

Source Code of org.libtiff.jai.codec.XTIFFTileCodecImpl

package org.libtiff.jai.codec;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.io.IOException;

import javax.media.jai.JAI;
import javax.media.jai.RasterFactory;

import org.libtiff.jai.util.JaiI18N;

/**
* Provides a base class for writing TIFF tile codecs, to be registered with the
* XTIFFDirectory. This codec allows for both decoding and (optionally) encoding
* of tiles, and also handles the colorspace conversion in decoding.
* <p>
* At the minimum you will need to implement the two methods decodeTilePixels()
* for byte and short data, as well as the methods register() and create(). If
* your decoder requires additional parameters from the tags, set them up in
* initializeDecoding(), and initializeEncoding() for encoding.
* <p>
* To implement encoding, you must override the canEncode() method to return
* true, and implement encodeTilePixels().
*
* @author Niles Ritter
* @see XTIFFTileCodec
*/
public abstract class XTIFFTileCodecImpl implements XTIFFTileCodec {

    // ////////////////////////////////////////////////////
    // // Implementation Section
    // // Override or implement methods here
    // ////////////////////////////////////////////////////

    /**
     * Registration method. Must be implemented by the extended class to
     * register itself with the XTIFFDirectory for all compression codes it
     * supports (e.g Fax codec supports 3 codes).
     *
     * @see XTIFFDirectory
     */
    public abstract void register();

    /**
     * Implement this to return the corresponding empty codec object.
     */
    public abstract XTIFFTileCodec create();

    /**
     * Indicate whether this codec can encode data. Override to return true only
     * if your codec implments encoding.
     */
    public boolean canEncode() {
        return false;
    }

    /**
     * The initialization method particular to decoding. Extend for whatever
     * compression-specific information or parameters is needed. The decoding
     * parameter has already been assigned at this point, as well as the
     * XTIFFDirectory parsed from the input stream, and so all XTIFFFields are
     * available.
     */
    public void initializeDecoding() {}

    /**
     * The initialization method particular to encoding. Extend for whatever
     * compression-specific information or parameters is needed. The decoding
     * parameter has already been assigned at this point, as well as the
     * XTIFFDirectory parsed from the input stream, and so all XTIFFFields are
     * available.
     */
    public void initializeEncoding() {}

    /**
     * decode bpixel byte array of data into pixels, packed for 1,2,4 8 bit
     * pixels. Must implment this.
     *
     * @param bpixels the byte array of compressed input data
     * @param rect the rectangular shape of the target pixels
     * @param pixels the target decompressed pixels.
     */
    public abstract void decodeTilePixels(byte[] bpixels, Rectangle rect,
                                          byte[] pixels);

    /**
     * decode bpixel byte array of data into pixels, packed for 16 bit pixels.
     * Must implment this.
     *
     * @param bpixels the byte array of compressed input data
     * @param rect the rectangular shape of the target pixels
     * @param pixels the target decompressed pixels.
     */
    public abstract void decodeTilePixels(byte[] bpixels, Rectangle rect,
                                          short[] pixels);

    /**
     * encode the tile in pixels into bpixels and return the byte size of the
     * compressed data. Override this method if canEncode() = true;
     *
     * @param pixels input pixels
     * @param rect the array dimensions of samples
     * @param bpixels the target array of compressed byte data
     */

    public int encodeTilePixels(int[] pixels, Rectangle rect, byte[] bpixels) {
        return 0;
    }

    // ////////////////////////////////////////////////////
    // // Common Section
    // ////////////////////////////////////////////////////

    protected XTIFFDirectory directory = null;
    protected RenderedImage image = null;
    protected int minY;
    protected int minX;
    protected int width;
    protected int length;
    protected int numBands;
    protected int tileLength;
    protected int tileWidth;
    protected int compression;
    protected SampleModel sampleModel;
    protected int[] sampleSize;
    protected char[] bitsPerSample;
    protected char[] colormap = null;

    /**
     * The empty constructor.
     */
    public XTIFFTileCodecImpl() {}

    /**
     * The method for initializing information common to both encoder and
     * decoder.
     */
    public void initialize() {
        width = (int) getLongField(XTIFF.TIFFTAG_IMAGE_WIDTH);
        length = (int) getLongField(XTIFF.TIFFTAG_IMAGE_LENGTH);
        isTiled = directory.isTiled();
        if (isTiled) {
            tileWidth = (int) getLongField(XTIFF.TIFFTAG_TILE_WIDTH);
            tileLength = (int) getLongField(XTIFF.TIFFTAG_TILE_LENGTH);
        } else {
            tileWidth = width;
            tileLength = (int) getLongField(XTIFF.TIFFTAG_ROWS_PER_STRIP);
        }
        // Figure out what compression if any, is being used.
        XTIFFField compField = directory.getField(XTIFF.TIFFTAG_COMPRESSION);
        if (compField != null) {
            compression = compField.getAsInt(0);
        } else {
            compression = XTIFF.COMPRESSION_NONE;
        }
        XTIFFField cfield = directory.getField(XTIFF.TIFFTAG_COLORMAP);
        if (cfield != null)
            colormap = cfield.getAsChars();

        // Read the TIFFTAG_BITS_PER_SAMPLE field
        XTIFFField bitsField = directory.getField(XTIFF.TIFFTAG_BITS_PER_SAMPLE);

        if (bitsField == null) {
            // Default
            bitsPerSample = new char[1];
            bitsPerSample[0] = 1;
        } else {
            bitsPerSample = bitsField.getAsChars();
        }
        image_type = directory.getImageType();
    }

    /**
     * A common utility method for accessing the XTIFFFields in the current
     * image directory.
     */
    protected long getLongField(int fld) {
        XTIFFField field = directory.getField(fld);
        if (field == null)
            return 0;
        else
            return field.getAsLong(0);
    }

    /**
     * This method may be used by the implementations register() method to
     * register itself with the XTIFFDirectory.
     *
     * @see XTIFFDirectory
     */
    public void register(int comp) {
        XTIFFDirectory.registerTileCodec(comp, this);
    }

    /**
     * One-time common image parameter setup
     *
     * @param img the source image that will be encoded into a TIFF formatted
     *        stream, or the TIFF image from which Raster tiles will be decoded.
     */
    protected void setupSourceImage(RenderedImage img) {
        image = img;

        // Get raster parameters
        minY = image.getMinY();
        minX = image.getMinX();
        sampleModel = image.getSampleModel();
        numBands = sampleModel.getNumBands();
        sampleSize = sampleModel.getSampleSize();

    }

    /**
     * Returns the TIFF compression type
     */
    public int getCompression() {
        return compression;
    }

    // ////////////////////////////////////////////////////
    // // Encoding Section
    // ////////////////////////////////////////////////////

    protected XTIFFEncodeParam encodeParam = null;
    private int _pixels[];
    protected boolean isTiled;

    /**
     * The method for creating an encoder from the XTIFFEncodeParam information.
     */
    public XTIFFTileCodec create(XTIFFEncodeParam param) throws IOException {
        XTIFFTileCodecImpl codec = (XTIFFTileCodecImpl) create();
        codec.initialize(param);
        return codec;
    }

    protected void initialize(XTIFFEncodeParam param) throws IOException {
        if (!canEncode())
            throw new IOException("encoding not supported");
        encodeParam = param;
        directory = param.getDirectory();
        initialize();
        initializeEncoding();
    }

    /**
     * Encode the data into buffer and return byte count Normally you will not
     * need to override this method, but instead implement the
     * <code>encodeTilePixels()</code> method.
     */
    public int encode(RenderedImage img, Rectangle rect, byte[] bpixels) {
        if (image == null) {
            setupSourceImage(img);
            setupBufferForEncoding();
        }

        // Fill tile buffer, padding right with zeroes.
        getTilePixels(rect);

        // encode and return number of bytes compressed
        return encodeTilePixels(_pixels, rect, bpixels);
    }

    /**
     * One-time setup for encoding
     */
    protected void setupBufferForEncoding() {
        // Set up input tile/strip buffer
        _pixels = new int[tileWidth * tileLength * numBands];

        // if padding necessary do it now.
        int padRight = (tileWidth - (width % tileWidth)) % tileWidth;
        int padBottom = (tileLength - (length % tileLength)) % tileLength;
        if (!isTiled)
            padBottom = 0;
        if (padRight > 0 || padBottom > 0) {
            ParameterBlock pb = new ParameterBlock();
            pb.addSource(image);
            pb.add(null)
                    .add(padRight)
                    .add(null)
                    .add(padBottom)
                    .add(null)
                    .add(null);
            image = JAI.create("border", pb);
        }
    }

    /**
     * Get the portion of tile fitting into buffer. You probably won't need to
     * override this.
     *
     * @param rect the region to extract from image.
     */

    protected void getTilePixels(Rectangle rect) {
        // Grab the pixels
        Raster src = image.getData(rect);
        int col = (int) rect.getX();
        int row = (int) rect.getY();
        int rows = (int) rect.getHeight();
        int cols = (int) rect.getWidth();
        src.getPixels(col, row, cols, rows, _pixels);
    }

    /**
     * If derived classes can make a better estimate for the maximum size of a
     * compressed tile, they should override this, which assumes conservatively
     * that it won't be worse than twice the original size.
     *
     * @param im the rendered image containing the image data
     */
    public int getCompressedTileSize(RenderedImage im) {
        sampleModel = im.getSampleModel();
        numBands = sampleModel.getNumBands();
        sampleSize = sampleModel.getSampleSize();
        return (int) Math.ceil(2 * tileWidth * tileLength * numBands
                * (sampleSize[0] / 8.0));
    }

    // ////////////////////////////////////////////////////
    // // Decoding Section
    // ////////////////////////////////////////////////////

    protected XTIFFDecodeParam decodeParam = null;
    protected boolean decodePaletteAsShorts = false;
    protected int unitsInThisTile;
    protected byte _bdata[] = null;
    protected short _sdata[] = null;
    protected byte[] bpixvals = null;
    protected short[] spixvals = null;
    protected DataBuffer buffer = null;
    protected int dataType;
    protected int image_type;

    /**
     * The standard decoder creation method
     */
    public XTIFFTileCodec create(XTIFFDecodeParam param) throws IOException {
        XTIFFTileCodecImpl codec = (XTIFFTileCodecImpl) create();
        codec.initialize(param);
        return codec;
    }

    protected void initialize(XTIFFDecodeParam param) throws IOException {
        decodeParam = param;
        decodePaletteAsShorts = param.getDecodePaletteAsShorts();
        directory = param.getDirectory();
        initialize();
        initializeDecoding();
    }

    /**
     * One-time setup for encoding. Some configurations require a temp array for
     * unpacking 16-bit palette data.
     */
    protected void setupBufferForDecoding() {

        // int length;
        buffer = sampleModel.createDataBuffer();
        dataType = sampleModel.getDataType();

        if (dataType == DataBuffer.TYPE_BYTE) {
            _bdata = ((DataBufferByte) buffer).getData();
            bpixvals = _bdata;
        } else if (dataType == DataBuffer.TYPE_USHORT) {
            _sdata = ((DataBufferUShort) buffer).getData();
            if (!decodePaletteAsShorts)
                spixvals = _sdata;
        } else if (dataType == DataBuffer.TYPE_SHORT) {
            _sdata = ((DataBufferShort) buffer).getData();
            if (!decodePaletteAsShorts)
                spixvals = _sdata;
        }
        if (decodePaletteAsShorts) {
            int len = _sdata.length;
            if (bitsPerSample[0] == 16)
                spixvals = new short[len];
            else
                bpixvals = new byte[len];
        }
    }

    /**
     * Decode a rectangle of data stored in bpixels into a raster tile. Usually
     * you will not need to override this, but instead implement the
     * decodeTilePixels methods.
     */
    public WritableRaster decode(RenderedImage img, Rectangle newRect,
                                 byte[] bpixels) {
        if (image == null) {
            setupSourceImage(img);
        }

        setupBufferForDecoding(); // set up every time

        unitsInThisTile = newRect.width * newRect.height * numBands;

        // uncompress data
        decodeTilePixels(bpixels, newRect);

        // post-processing of color data
        decodeColor(newRect);

        // put buffer into a tile
        return setTilePixels(newRect);
    }

    /**
     * Postprocess the uncompressed color data into the appropriate display
     * color model. This implementation Does a number of things:
     * <ul>
     * <li> For RGB color, reverse to BGR which apparently is faster for Java 2D
     * display
     * <li> For one-bit WHITE_IS_ZERO data, flip the values so that they will
     * look correct
     * <li> If the decodePaletteAsShorts flag is true then unpack the bits and
     * apply the lookup table, as 16-bit lookup is not supported in JAI.
     * </ul>
     * Override this if you have other color types.
     *
     * @see XTIFFDecodeParam
     */
    protected void decodeColor(Rectangle newRect) {
        switch (dataType) {
        case DataBuffer.TYPE_BYTE:
            decodeColor(bpixvals, _bdata, newRect);
            break;
        case DataBuffer.TYPE_SHORT:
        case DataBuffer.TYPE_USHORT:
            if (bpixvals != null)
                decodeColor(bpixvals, _sdata, newRect);
            else
                decodeColor(spixvals, _sdata, newRect);
        }
    }

    /**
     * Decode a tile of data into either byte or short pixel buffers. Override
     * this if you have other buffer types (e.g. int)
     */
    protected void decodeTilePixels(byte[] bpixels, Rectangle newRect) {

        // decodeTilePixels into the appropriate buffer
        if (bpixvals != null)
            decodeTilePixels(bpixels, newRect, bpixvals);
        else
            decodeTilePixels(bpixels, newRect, spixvals);
    }

    /**
     * Take the values from the buffer and store them in a WritableRaster
     * object.
     */
    protected WritableRaster setTilePixels(Rectangle rect) {
        return (WritableRaster) RasterFactory.createWritableRaster(sampleModel,
                buffer,
                new Point((int) rect.getX(), (int) rect.getY()));
    }

    /**
     * A useful Method to interpret a byte array as shorts. Method depends on
     * whether the bytes are stored in a big endian or little endian format.
     */

    protected void unpackShorts(byte byteArray[], short output[], int shortCount) {

        int j;
        int firstByte, secondByte;

        if (directory.isBigEndian()) {

            for (int i = 0; i < shortCount; i++) {
                j = 2 * i;
                firstByte = byteArray[j] & 0xff;
                secondByte = byteArray[j + 1] & 0xff;
                output[i] = (short) ((firstByte << 8) + secondByte);
            }

        } else {

            for (int i = 0; i < shortCount; i++) {
                j = 2 * i;
                firstByte = byteArray[j] & 0xff;
                secondByte = byteArray[j + 1] & 0xff;
                output[i] = (short) ((secondByte << 8) + firstByte);
            }
        }
    }

    // ////////////////////////////////////////////////////////////////////////
    // /// Color decoding section
    // ////////////////////////////////////////////////////////////////////////

    /**
     * Decode short pixel data, or interpret palette data as short from byte.
     */
    protected void decodeColor(byte[] bpix, short[] sdata, Rectangle newRect) {
        // short sswap;

        switch (image_type) {
        case XTIFF.TYPE_PALETTE:
            if (bitsPerSample[0] == 8) {

                // At this point the data is 1 banded and will
                // become 3 banded only after we've done the palette
                // lookup, since unitsInThisTile was calculated with
                // 3 bands, we need to divide this by 3.
                int unitsBeforeLookup = unitsInThisTile / 3;

                // Expand the palette image into an rgb image with ushort
                // data type.
                int cmapValue;
                int count = 0, lookup, len = colormap.length / 3;
                int len2 = len * 2;
                for (int i = 0; i < unitsBeforeLookup; i++) {
                    // Get the index into the colormap
                    lookup = bpix[i] & 0xff;
                    // Get the blue value
                    cmapValue = colormap[lookup + len2];
                    sdata[count++] = (short) (cmapValue & 0xffff);
                    // Get the green value
                    cmapValue = colormap[lookup + len];
                    sdata[count++] = (short) (cmapValue & 0xffff);
                    // Get the red value
                    cmapValue = colormap[lookup];
                    sdata[count++] = (short) (cmapValue & 0xffff);
                }

            } else if (bitsPerSample[0] == 4) {

                int padding = newRect.width % 2;
//                int bytesPostDecoding = ((newRect.width + 1) / 2)
//                        * newRect.height;

                int bytes = unitsInThisTile / 3;

                // Unpack the 2 pixels packed into each byte.
                byte[] data = new byte[bytes];

                int srcCount = 0, dstCount = 0;
                for (int j = 0; j < newRect.height; j++) {
                    for (int i = 0; i < newRect.width / 2; i++) {
                        data[dstCount++] = (byte) ((bpix[srcCount] & 0xf0) >> 4);
                        data[dstCount++] = (byte) (bpix[srcCount++] & 0x0f);
                    }

                    if (padding == 1) {
                        data[dstCount++] = (byte) ((bpix[srcCount++] & 0xf0) >> 4);
                    }
                }

                int len = colormap.length / 3;
                int len2 = len * 2;
                int cmapValue, lookup;
                int count = 0;
                for (int i = 0; i < bytes; i++) {
                    lookup = data[i] & 0xff;
                    cmapValue = colormap[lookup + len2];
                    sdata[count++] = (short) (cmapValue & 0xffff);
                    cmapValue = colormap[lookup + len];
                    sdata[count++] = (short) (cmapValue & 0xffff);
                    cmapValue = colormap[lookup];
                    sdata[count++] = (short) (cmapValue & 0xffff);
                }
            } else {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder7"));
            }
            break;
        }
    }

    /**
     * Decode short color data, or interpret palette data as short.
     */
    protected void decodeColor(short[] spix, short[] sdata, Rectangle newRect) {
        short sswap;

        switch (image_type) {
        case XTIFF.TYPE_GREYSCALE_WHITE_IS_ZERO:
        case XTIFF.TYPE_GREYSCALE_BLACK_IS_ZERO:
            // Since we are using a ComponentColorModel with this image,
            // we need to change the WhiteIsZero data to BlackIsZero data
            // so it will display properly.
            if (image_type == XTIFF.TYPE_GREYSCALE_WHITE_IS_ZERO) {

                if (dataType == DataBuffer.TYPE_USHORT) {

                    for (int l = 0; l < sdata.length; l++) {
                        sdata[l] = (short) (65535 - spix[l]);
                    }

                } else if (dataType == DataBuffer.TYPE_SHORT) {

                    for (int l = 0; l < sdata.length; l++) {
                        sdata[l] = (short) (~spix[l]);
                    }
                }
            }

            break;

        case XTIFF.TYPE_RGB:
            // Change to BGR order, as Java2D displays that faster
            for (int i = 0; i < unitsInThisTile; i += 3) {
                sswap = spix[i];
                sdata[i] = spix[i + 2];
                sdata[i + 2] = sswap;
            }
            break;

        case XTIFF.TYPE_ORGB:
        case XTIFF.TYPE_ARGB_PRE:
        case XTIFF.TYPE_ARGB:
            // Change from RGBA to ABGR for Java2D's faster special cases
            for (int i = 0; i < unitsInThisTile; i += 4) {
                // Swap R and A
                sswap = spix[i];
                sdata[i] = spix[i + 3];
                sdata[i + 3] = sswap;

                // Swap G and B
                sswap = spix[i + 1];
                sdata[i + 1] = spix[i + 2];
                sdata[i + 2] = sswap;
            }
            break;

        case XTIFF.TYPE_RGB_EXTRA:
            break;

        case XTIFF.TYPE_PALETTE:
            if (decodePaletteAsShorts) {

                // At this point the data is 1 banded and will
                // become 3 banded only after we've done the palette
                // lookup, since unitsInThisTile was calculated with
                // 3 bands, we need to divide this by 3.
                int unitsBeforeLookup = unitsInThisTile / 3;

                // Since unitsBeforeLookup is the number of shorts,
                // but we do our decompression in terms of bytes, we
                // need to multiply it by 2 in order to figure out
                // how many bytes we'll get after decompression.
//                int entries = unitsBeforeLookup * 2;

                if (dataType == DataBuffer.TYPE_USHORT) {

                    // Expand the palette image into an rgb image with ushort
                    // data type.
                    int cmapValue;
                    int count = 0, lookup, len = colormap.length / 3;
                    int len2 = len * 2;
                    for (int i = 0; i < unitsBeforeLookup; i++) {
                        // Get the index into the colormap
                        lookup = spix[i] & 0xffff;
                        // Get the blue value
                        cmapValue = colormap[lookup + len2];
                        sdata[count++] = (short) (cmapValue & 0xffff);
                        // Get the green value
                        cmapValue = colormap[lookup + len];
                        sdata[count++] = (short) (cmapValue & 0xffff);
                        // Get the red value
                        cmapValue = colormap[lookup];
                        sdata[count++] = (short) (cmapValue & 0xffff);
                    }

                } else if (dataType == DataBuffer.TYPE_SHORT) {

                    // Expand the palette image into an rgb image with
                    // short data type.
                    int cmapValue;
                    int count = 0, lookup, len = colormap.length / 3;
                    int len2 = len * 2;
                    for (int i = 0; i < unitsBeforeLookup; i++) {
                        // Get the index into the colormap
                        lookup = spix[i] & 0xffff;
                        // Get the blue value
                        cmapValue = colormap[lookup + len2];
                        sdata[count++] = (short) cmapValue;
                        // Get the green value
                        cmapValue = colormap[lookup + len];
                        sdata[count++] = (short) cmapValue;
                        // Get the red value
                        cmapValue = colormap[lookup];
                        sdata[count++] = (short) cmapValue;
                    }
                }// dataType
            }// decodePaletteAsShorts
            break;

        case XTIFF.TYPE_TRANS:
            break;
        }
    }

    /**
     * Decode byte color data
     */
    protected void decodeColor(byte[] bpix, byte[] bdata, Rectangle newRect) {
        byte bswap;

        switch (image_type) {
        case XTIFF.TYPE_BILEVEL_WHITE_IS_ZERO:
        case XTIFF.TYPE_BILEVEL_BLACK_IS_ZERO:
        case XTIFF.TYPE_GREYSCALE_WHITE_IS_ZERO:
        case XTIFF.TYPE_GREYSCALE_BLACK_IS_ZERO:
        case XTIFF.TYPE_RGB_EXTRA:
        case XTIFF.TYPE_TRANS:
            // nothing
            break;

        case XTIFF.TYPE_RGB:
            if (bitsPerSample[0] == 8) {

                // Change to BGR order, as Java2D displays that faster
                for (int i = 0; i < unitsInThisTile; i += 3) {
                    bswap = bpix[i];
                    bdata[i] = bpix[i + 2];
                    bdata[i + 2] = bswap;
                }

            }
            break;

        case XTIFF.TYPE_ORGB:
        case XTIFF.TYPE_ARGB_PRE:
        case XTIFF.TYPE_ARGB:
            if (bitsPerSample[0] == 8) {
                // Convert from RGBA to ABGR for Java2D
                for (int i = 0; i < unitsInThisTile; i += 4) {
                    // Swap R and A
                    bswap = bpix[i];
                    bdata[i] = bpix[i + 3];
                    bdata[i + 3] = bswap;

                    // Swap G and B
                    bswap = bpix[i + 1];
                    bdata[i + 1] = bpix[i + 2];
                    bdata[i + 2] = bswap;
                }
            }
            break;

        case XTIFF.TYPE_PALETTE:
            //
            break;

        }// switch
    }// decodeColor

}
TOP

Related Classes of org.libtiff.jai.codec.XTIFFTileCodecImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.