Package org.libtiff.jai.codecimpl

Source Code of org.libtiff.jai.codecimpl.XTIFFImage

package org.libtiff.jai.codecimpl;

/*
* XTIFF: eXtensible TIFF libraries for JAI.
*
* The contents of this file are subject to the  JAVA ADVANCED IMAGING
* SAMPLE INPUT-OUTPUT CODECS AND WIDGET HANDLING SOURCE CODE  License
* Version 1.0 (the "License"); You may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.sun.com/software/imaging/JAI/index.html
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is JAVA ADVANCED IMAGING SAMPLE INPUT-OUTPUT CODECS
* AND WIDGET HANDLING SOURCE CODE.
* The Initial Developer of the Original Code is: Sun Microsystems, Inc..
* Portions created by: Niles Ritter
* are Copyright (C): Niles Ritter, GeoTIFF.org, 1999,2000.
* All Rights Reserved.
* Contributor(s): Niles Ritter
*/

import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;

import javax.media.jai.RasterFactory;

import org.libtiff.jai.codec.XTIFF;
import org.libtiff.jai.codec.XTIFFDecodeParam;
import org.libtiff.jai.codec.XTIFFDirectory;
import org.libtiff.jai.codec.XTIFFField;
import org.libtiff.jai.codec.XTIFFTileCodec;
import org.libtiff.jai.util.JaiI18N;

import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.SeekableStream;
import com.sun.media.jai.codec.TIFFDecodeParam;
import com.sun.media.jai.codecimpl.SimpleRenderedImage;

public class XTIFFImage extends SimpleRenderedImage {

    XTIFFTileCodec codec;
    XTIFFDirectory dir;
    TIFFDecodeParam param;
    int photometric_interp;
    SeekableStream stream;
    int tileSize;
    int tilesX, tilesY;
    long[] tileOffsets;
    long tileByteCounts[];
    char colormap[];
    char bitsPerSample[];
    int samplesPerPixel;
    int extraSamples;
    byte palette[];
    int bands;
    char sampleFormat[];

    boolean decodePaletteAsShorts;

    boolean isBigEndian;

    // Image types
    int image_type;
    int dataType;

    /**
     * Constructs a XTIFFImage that acquires its data from a given
     * SeekableStream and reads from a particular IFD of the stream. The index
     * of the first IFD is 0.
     *
     * @param stream the SeekableStream to read from.
     * @param param an instance of TIFFDecodeParam, or null.
     * @param directory the index of the IFD to read from.
     */
    public XTIFFImage(SeekableStream stream, TIFFDecodeParam param,
            int directory) throws IOException {

        this.stream = stream;
        if (param == null || !(param instanceof XTIFFDecodeParam)) {
            param = new XTIFFDecodeParam(param);
        }
        this.param = param;

        decodePaletteAsShorts = param.getDecodePaletteAsShorts();

        // Read the specified directory.
        dir = XTIFFDirectory.create(stream, directory);
        properties.put("tiff.directory", dir);
        ((XTIFFDecodeParam) param).setDirectory(dir);

        // Check whether big endian or little endian format is used.
        isBigEndian = dir.isBigEndian();

        setupImageParameters();

        setupSamplesAndColor();

        dir.setImageType(image_type);

        // Calculate number of tiles and the tileSize in bytes
        tilesX = (width + tileWidth - 1) / tileWidth;
        tilesY = (height + tileHeight - 1) / tileHeight;
        tileSize = tileWidth * tileHeight * bands;

        try {
            codec = dir.createTileCodec((XTIFFDecodeParam) param);
        } catch (Exception e) {
        }
    }

    /**
     * This method gets the image parameters from fields
     */
    protected void setupImageParameters() {

        // Set basic image layout
        minX = minY = 0;
        width = (int) dir.getFieldAsLong(XTIFF.TIFFTAG_IMAGE_WIDTH);
        height = (int) dir.getFieldAsLong(XTIFF.TIFFTAG_IMAGE_LENGTH);

        photometric_interp = (int) dir.getFieldAsLong(XTIFF.TIFFTAG_PHOTOMETRIC_INTERPRETATION);

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

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

        for (int i = 1; i < bitsPerSample.length; i++) {
            if (bitsPerSample[i] != bitsPerSample[1]) {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder19"));
            }
        }

        // Get the number of samples per pixel
        XTIFFField sfield = dir.getField(XTIFF.TIFFTAG_SAMPLES_PER_PIXEL);
        if (sfield == null) {
            samplesPerPixel = 1;
        } else {
            samplesPerPixel = (int) sfield.getAsLong(0);
        }

        // Figure out if any extra samples are present.
        XTIFFField efield = dir.getField(XTIFF.TIFFTAG_EXTRA_SAMPLES);
        if (efield == null) {
            extraSamples = 0;
        } else {
            extraSamples = (int) efield.getAsLong(0);
        }

        // Read the TIFFTAG_SAMPLE_FORMAT tag to see whether the data might be
        // signed or floating point
        XTIFFField sampleFormatField = dir.getField(XTIFF.TIFFTAG_SAMPLE_FORMAT);

        if (sampleFormatField != null) {
            sampleFormat = sampleFormatField.getAsChars();

            // Check that all the samples have the same format
            for (int l = 1; l < sampleFormat.length; l++) {
                if (sampleFormat[l] != sampleFormat[0]) {
                    throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder20"));
                }
            }

        } else {
            sampleFormat = new char[] { 1 };
        }

        if (sampleFormat[0] == 1 || sampleFormat[0] == 4) {

            // Unsigned or unknown
            if (bitsPerSample[0] == 8) {
                dataType = DataBuffer.TYPE_BYTE;
            } else if (bitsPerSample[0] == 16) {
                dataType = DataBuffer.TYPE_USHORT;
            } else if (bitsPerSample[0] == 32) {
                dataType = DataBuffer.TYPE_INT;
            }

        } else if (sampleFormat[0] == 2) {
            // Signed

            if (bitsPerSample[0] == 1 || bitsPerSample[0] == 4
                    || bitsPerSample[0] == 8) {

                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder21"));

            } else if (bitsPerSample[0] == 16) {
                dataType = DataBuffer.TYPE_SHORT;
            } else if (bitsPerSample[0] == 32) {
                dataType = DataBuffer.TYPE_INT;
            }

        } else if (sampleFormat[0] == 3) {
            // Floating point
            // dataType = DataBuffer.TYPE_FLOAT;
            throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder22"));
        }

        if (dir.getField(XTIFF.TIFFTAG_TILE_WIDTH) != null) {
            // Image is in tiled format
            tileWidth = (int) dir.getFieldAsLong(XTIFF.TIFFTAG_TILE_WIDTH);
            tileHeight = (int) dir.getFieldAsLong(XTIFF.TIFFTAG_TILE_LENGTH);
            tileOffsets = (dir.getField(XTIFF.TIFFTAG_TILE_OFFSETS)).getAsLongs();
            tileByteCounts = dir.getField(XTIFF.TIFFTAG_TILE_BYTE_COUNTS)
                    .getAsLongs();

        } else {

            // Image is in stripped format, looks like tiles to us
            tileWidth = width;
            XTIFFField field = dir.getField(XTIFF.TIFFTAG_ROWS_PER_STRIP);
            if (field == null) {
                // Default is infinity (2^32 -1), basically the entire image
                // TODO: Can do a better job of tiling here
                tileHeight = height;
            } else {
                long l = field.getAsLong(0);
                long infinity = 1;
                infinity = (infinity << 32) - 1;
                if (l == infinity) {
                    // 2^32 - 1 (effectively infinity, entire image is 1 strip)
                    tileHeight = height;
                } else {
                    tileHeight = (int) l;
                }
            }

            XTIFFField tileOffsetsField = dir.getField(XTIFF.TIFFTAG_STRIP_OFFSETS);
            if (tileOffsetsField == null) {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder11"));
            } else {
                tileOffsets = tileOffsetsField.getAsLongs();
            }

            XTIFFField tileByteCountsField = dir.getField(XTIFF.TIFFTAG_STRIP_BYTE_COUNTS);
            if (tileByteCountsField == null) {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder12"));
            } else {
                tileByteCounts = tileByteCountsField.getAsLongs();
            }
        }
    }

    /**
     * This method constructs the sampleModel, colorModel, determines the
     * image_type and the bands parameter.
     */
    protected void setupSamplesAndColor() {

        // Figure out which kind of image we are dealing with.
        switch (photometric_interp) {

        case XTIFF.PHOTOMETRIC_WHITE_IS_ZERO:

            bands = 1;

            // Bilevel or Grayscale - WhiteIsZero
            if (bitsPerSample[0] == 1) {

                image_type = XTIFF.TYPE_BILEVEL_WHITE_IS_ZERO;

                // Keep pixels packed, use IndexColorModel
                sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, 1);

                // Set up the palette
                byte r[] = new byte[] { (byte) 255, (byte) 0 };
                byte g[] = new byte[] { (byte) 255, (byte) 0 };
                byte b[] = new byte[] { (byte) 255, (byte) 0 };

                colorModel = new IndexColorModel(1, 2, r, g, b);

            } else {

                image_type = XTIFF.TYPE_GREYSCALE_WHITE_IS_ZERO;

                if (bitsPerSample[0] == 4) {
                    sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, 4);

                    colorModel = ImageCodec.createGrayIndexColorModel(sampleModel,
                            false);

                } else if (bitsPerSample[0] == 8) {
                    sampleModel = RasterFactory.createPixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                            tileWidth,
                            tileHeight,
                            bands);

                    colorModel = ImageCodec.createGrayIndexColorModel(sampleModel,
                            false);

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

                    sampleModel = RasterFactory.createPixelInterleavedSampleModel(dataType,
                            tileWidth,
                            tileHeight,
                            bands);

                    colorModel = ImageCodec.createComponentColorModel(sampleModel);

                } else {
                    throw new IllegalArgumentException(JaiI18N.getString("XTIFFImageDecoder14"));
                }
            }

            break;

        case XTIFF.PHOTOMETRIC_BLACK_IS_ZERO:

            bands = 1;

            // Bilevel or Grayscale - BlackIsZero
            if (bitsPerSample[0] == 1) {

                image_type = XTIFF.TYPE_BILEVEL_BLACK_IS_ZERO;

                // Keep pixels packed, use IndexColorModel
                sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, 1);

                // Set up the palette
                byte r[] = new byte[] { (byte) 0, (byte) 255 };
                byte g[] = new byte[] { (byte) 0, (byte) 255 };
                byte b[] = new byte[] { (byte) 0, (byte) 255 };

                // 1 Bit pixels packed into a byte, use IndexColorModel
                colorModel = new IndexColorModel(1, 2, r, g, b);

            } else {

                image_type = XTIFF.TYPE_GREYSCALE_BLACK_IS_ZERO;

                if (bitsPerSample[0] == 4) {
                    sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, 4);
                    colorModel = ImageCodec.createGrayIndexColorModel(sampleModel,
                            true);
                } else if (bitsPerSample[0] == 8) {
                    sampleModel = RasterFactory.createPixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                            tileWidth,
                            tileHeight,
                            bands);
                    colorModel = ImageCodec.createComponentColorModel(sampleModel);

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

                    sampleModel = RasterFactory.createPixelInterleavedSampleModel(dataType,
                            tileWidth,
                            tileHeight,
                            bands);
                    colorModel = ImageCodec.createComponentColorModel(sampleModel);

                } else {
                    throw new IllegalArgumentException(JaiI18N.getString("XTIFFImageDecoder14"));
                }
            }

            break;

        case XTIFF.PHOTOMETRIC_RGB:

            bands = samplesPerPixel;

            // RGB full color image
            if (bitsPerSample[0] == 8) {

                sampleModel = RasterFactory.createPixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                        tileWidth,
                        tileHeight,
                        bands);
            } else if (bitsPerSample[0] == 16) {

                sampleModel = RasterFactory.createPixelInterleavedSampleModel(dataType,
                        tileWidth,
                        tileHeight,
                        bands);
            } else {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder15"));
            }

            if (samplesPerPixel < 3) {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder1"));

            } else if (samplesPerPixel == 3) {

                image_type = XTIFF.TYPE_RGB;
                // No alpha
                colorModel = ImageCodec.createComponentColorModel(sampleModel);

            } else if (samplesPerPixel == 4) {

                if (extraSamples == 0) {

                    image_type = XTIFF.TYPE_ORGB;
                    // Transparency.OPAQUE signifies image data that is
                    // completely opaque, meaning that all pixels have an alpha
                    // value of 1.0. So the extra band gets ignored, which is
                    // what we want.
                    colorModel = createAlphaComponentColorModel(dataType,
                            true,
                            false,
                            Transparency.OPAQUE);

                } else if (extraSamples == 1) {

                    image_type = XTIFF.TYPE_ARGB_PRE;
                    // Pre multiplied alpha.
                    colorModel = createAlphaComponentColorModel(dataType,
                            true,
                            true,
                            Transparency.TRANSLUCENT);

                } else if (extraSamples == 2) {

                    image_type = XTIFF.TYPE_ARGB;
                    // The extra sample here is unassociated alpha, usually a
                    // transparency mask, also called soft matte.
                    colorModel = createAlphaComponentColorModel(dataType,
                            true,
                            false,
                            Transparency.BITMASK);
                }

            } else {
                image_type = XTIFF.TYPE_RGB_EXTRA;

                // For this case we can't display the image, so there is no
                // point in trying to reformat the data to be BGR followed by
                // the ExtraSamples, the way Java2D would like it, because
                // Java2D can't display it anyway. Therefore create a sample
                // model with increasing bandOffsets, and keep the colorModel
                // as null, as there is no appropriate ColorModel.

                int bandOffsets[] = new int[bands];
                for (int i = 0; i < bands; i++) {
                    bandOffsets[i] = i;
                }

                if (bitsPerSample[0] == 8) {

                    sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, bands, bands
                            * tileWidth, bandOffsets);
                    colorModel = null;

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

                    sampleModel = new PixelInterleavedSampleModel(dataType, tileWidth, tileHeight, bands, bands
                            * tileWidth, bandOffsets);
                    colorModel = null;
                }
            }

            break;

        case XTIFF.PHOTOMETRIC_PALETTE:

            image_type = XTIFF.TYPE_PALETTE;

            // Get the colormap
            XTIFFField cfield = dir.getField(XTIFF.TIFFTAG_COLORMAP);
            if (cfield == null) {
                throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder2"));
            } else {
                colormap = cfield.getAsChars();
            }

            // Could be either 1 or 3 bands depending on whether we use
            // IndexColorModel or not.
            if (decodePaletteAsShorts) {
                bands = 3;

                if (bitsPerSample[0] != 4 && bitsPerSample[0] != 8
                        && bitsPerSample[0] != 16) {
                    throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder13"));
                }

                // If no SampleFormat tag was specified and if the
                // bitsPerSample are less than or equal to 8, then the
                // dataType was initially set to byte, but now we want to
                // expand the palette as shorts, so the dataType should
                // be ushort.
                if (dataType == DataBuffer.TYPE_BYTE) {
                    dataType = DataBuffer.TYPE_USHORT;
                }

                // Data will have to be unpacked into a 3 band short image
                // as we do not have a IndexColorModel that can deal with
                // a colormodel whose entries are of short data type.
                sampleModel = RasterFactory.createPixelInterleavedSampleModel(dataType,
                        tileWidth,
                        tileHeight,
                        bands);
                colorModel = ImageCodec.createComponentColorModel(sampleModel);

            } else {

                bands = 1;

                if (bitsPerSample[0] == 4) {
                    // Pixel data will not be unpacked, will use MPPSM to store
                    // packed data and IndexColorModel to do the unpacking.
                    sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, bitsPerSample[0]);
                } else if (bitsPerSample[0] == 8) {
                    sampleModel = RasterFactory.createPixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                            tileWidth,
                            tileHeight,
                            bands);
                } else if (bitsPerSample[0] == 16) {

                    // Here datatype has to be unsigned since we are storing
                    // indices into the IndexColorModel palette. Ofcourse
                    // the actual palette entries are allowed to be negative.
                    sampleModel = RasterFactory.createPixelInterleavedSampleModel(DataBuffer.TYPE_USHORT,
                            tileWidth,
                            tileHeight,
                            bands);
                } else {
                    throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder13"));
                }

                int bandLength = colormap.length / 3;
                byte r[] = new byte[bandLength];
                byte g[] = new byte[bandLength];
                byte b[] = new byte[bandLength];

                int gIndex = bandLength;
                int bIndex = bandLength * 2;

                if (dataType == DataBuffer.TYPE_SHORT) {

                    for (int i = 0; i < bandLength; i++) {
                        r[i] = param.decodeSigned16BitsTo8Bits((short) colormap[i]);
                        g[i] = param.decodeSigned16BitsTo8Bits((short) colormap[gIndex
                                + i]);
                        b[i] = param.decodeSigned16BitsTo8Bits((short) colormap[bIndex
                                + i]);
                    }

                } else {

                    for (int i = 0; i < bandLength; i++) {
                        r[i] = param.decode16BitsTo8Bits(colormap[i] & 0xffff);
                        g[i] = param.decode16BitsTo8Bits(colormap[gIndex + i] & 0xffff);
                        b[i] = param.decode16BitsTo8Bits(colormap[bIndex + i] & 0xffff);
                    }

                }

                colorModel = new IndexColorModel(bitsPerSample[0], bandLength, r, g, b);
            }

            break;

        case XTIFF.PHOTOMETRIC_TRANSPARENCY:

            image_type = XTIFF.TYPE_TRANS;

            // Transparency Mask
            throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder3"));
            // break;

        default:
            throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder4"));
        }

    }

    /**
     * Reads a private IFD from a given offset in the stream. This method may be
     * used to obtain IFDs that are referenced only by private tag values.
     */
    public XTIFFDirectory getPrivateIFD(long offset) throws IOException {
        return XTIFFDirectory.create(stream, offset);
    }

    private WritableRaster tile00 = null;

    /**
     * Returns tile (tileX, tileY) as a Raster.
     */
    public synchronized Raster getTile(int tileX, int tileY) {
        if (tileX == 0 && tileY == 0 && tile00 != null) {
            return tile00;
        }

        if ((tileX < 0) || (tileX >= tilesX) || (tileY < 0)
                || (tileY >= tilesY)) {
            throw new IllegalArgumentException(JaiI18N.getString("XTIFFImageDecoder5"));
        }

        // file setup1

        // Save original file pointer position and seek to tile data location.
        long save_offset = 0;
        try {
            save_offset = stream.getFilePointer();
            stream.seek(tileOffsets[tileY * tilesX + tileX]);
        } catch (IOException ioe) {
            throw new RuntimeException(JaiI18N.getString("XTIFFImageDecoder8"));
        }

        // Number of bytes in this tile (strip) after compression.
        int byteCount = (int) tileByteCounts[tileY * tilesX + tileX];

        // Find out the number of bytes in the current tile
        Rectangle tileRect = new Rectangle(tileXToX(tileX), tileYToY(tileY), tileWidth, tileHeight);
        Rectangle newRect = tileRect.intersection(getBounds());

        // file setup2

        byte data[] = new byte[byteCount];
        WritableRaster tile = null;
        try {
            stream.readFully(data, 0, byteCount);
            tile = codec.decode(this, newRect, data);
            stream.seek(save_offset);
        } catch (IOException e) {
            throw new RuntimeException("Failed to read raw tile data:" + e);
        }

        if (tileX == 0 && tileY == 0) {
            tile00 = tile;
        }
        return tile;
    }

    // Create ComponentColorModel for TYPE_RGB images
    private ComponentColorModel createAlphaComponentColorModel(
                                                               int dataType,
                                                               boolean hasAlpha,
                                                               boolean isAlphaPremultiplied,
                                                               int transparency) {

        ComponentColorModel ccm = null;
        int RGBBits[][] = new int[3][];

        RGBBits[0] = new int[] { 8, 8, 8, 8 }; // Byte
        RGBBits[1] = new int[] { 16, 16, 16, 16 }; // Short
        RGBBits[2] = new int[] { 16, 16, 16, 16 }; // UShort
        RGBBits[2] = new int[] { 32, 32, 32, 32 }; // Int

        ccm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits[dataType], hasAlpha, isAlphaPremultiplied, transparency, dataType);
        return ccm;
    }
}
TOP

Related Classes of org.libtiff.jai.codecimpl.XTIFFImage

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.