public TIFFImage()

in src/main/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImage.java [181:726]


    public TIFFImage(SeekableStream stream,
                     TIFFDecodeParam param,
                     int directory)
        throws IOException {

        this.stream = stream;
        if (param == null) {
            param = new TIFFDecodeParam();
        }

        decodePaletteAsShorts = param.getDecodePaletteAsShorts();

        // Read the specified directory.
        TIFFDirectory dir = param.getIFDOffset() == null
            ? new TIFFDirectory(stream, directory)
            : new TIFFDirectory(stream, param.getIFDOffset(),
                              directory);

        // Get the number of samples per pixel
        TIFFField sfield = dir.getField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL);
        int samplesPerPixel = sfield == null ? 1 : (int)sfield.getAsLong(0);

        // Read the TIFF_PLANAR_CONFIGURATION field
        TIFFField planarConfigurationField =
            dir.getField(TIFFImageDecoder.TIFF_PLANAR_CONFIGURATION);
        char[] planarConfiguration = planarConfigurationField == null
            ? new char[] {1}
            : planarConfigurationField.getAsChars();

            // Support planar format (band sequential) only for 1 sample/pixel.
            if (planarConfiguration[0] != 1 && samplesPerPixel != 1) {
                throw new RuntimeException(PropertyUtil.getString("TIFFImage0"));
            }

            // Read the TIFF_BITS_PER_SAMPLE field
            TIFFField bitsField =
                dir.getField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE);
            char[] bitsPerSample = null;
            if (bitsField != null) {
                bitsPerSample = bitsField.getAsChars();
            } else {
                bitsPerSample = new char[] {1};

                // Ensure that all samples have the same bit depth.
                for (int i = 1; i < bitsPerSample.length; i++) {
                    if (bitsPerSample[i] != bitsPerSample[0]) {
                        throw new RuntimeException(PropertyUtil.getString("TIFFImage1"));
                    }
                }
            }
            sampleSize = bitsPerSample[0];

            // Read the TIFF_SAMPLE_FORMAT tag to see whether the data might be
            // signed or floating point
            TIFFField sampleFormatField =
                dir.getField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT);

            char[] sampleFormat = null;
            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(PropertyUtil.getString("TIFFImage2"));
                    }
                }

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

            // Set the data type based on the sample size and format.
            boolean isValidDataFormat = false;
            switch (sampleSize) {
            case 1:
            case 4:
            case 8:
                if (sampleFormat[0] != 3) {
                    // Ignore whether signed or unsigned: treat all as unsigned.
                    dataType = DataBuffer.TYPE_BYTE;
                    isValidDataFormat = true;
                }
                break;
            case 16:
                if (sampleFormat[0] != 3) {
                    dataType = sampleFormat[0] == 2
                        ? DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT;
                    isValidDataFormat = true;
                }
                break;
            case 32:
              if (sampleFormat[0] == 3) {
                isValidDataFormat = false;
            } else {
                dataType = DataBuffer.TYPE_INT;
                isValidDataFormat = true;
              }
              break;
            }

            if (!isValidDataFormat) {
                throw new RuntimeException(PropertyUtil.getString("TIFFImage3"));
            }

            // Figure out what compression if any, is being used.
            TIFFField compField = dir.getField(TIFFImageDecoder.TIFF_COMPRESSION);
            compression = compField == null ? COMP_NONE : compField.getAsInt(0);

            // Get the photometric interpretation.
            int photometricType;
            TIFFField photometricTypeField = dir.getField(
                    TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION);
            if (photometricTypeField == null) {
                photometricType = 0; // White is zero
            } else {
                photometricType = photometricTypeField.getAsInt(0);
            }

            // Determine which kind of image we are dealing with.
            imageType = TYPE_UNSUPPORTED;
            switch(photometricType) {
            case 0: // WhiteIsZero
                isWhiteZero = true;
            case 1: // BlackIsZero
                if (sampleSize == 1 && samplesPerPixel == 1) {
                    imageType = TYPE_BILEVEL;
                } else if (sampleSize == 4 && samplesPerPixel == 1) {
                    imageType = TYPE_GRAY_4BIT;
                } else if (sampleSize % 8 == 0) {
                    if (samplesPerPixel == 1) {
                        imageType = TYPE_GRAY;
                    } else if (samplesPerPixel == 2) {
                        imageType = TYPE_GRAY_ALPHA;
                    } else {
                        imageType = TYPE_GENERIC;
                    }
                }
                break;
            case 2: // RGB
                if (sampleSize % 8 == 0) {
                    if (samplesPerPixel == 3) {
                        imageType = TYPE_RGB;
                    } else if (samplesPerPixel == 4) {
                        imageType = TYPE_RGB_ALPHA;
                    } else {
                        imageType = TYPE_GENERIC;
                    }
                }
                break;
            case 3: // RGB Palette
                if (samplesPerPixel == 1
                   && (sampleSize == 4 || sampleSize == 8 || sampleSize == 16)) {
                    imageType = TYPE_PALETTE;
                }
                break;
            case 4: // Transparency mask
                if (sampleSize == 1 && samplesPerPixel == 1) {
                    imageType = TYPE_BILEVEL;
                }
                break;
            default: // Other including CMYK, CIE L*a*b*, unknown.
                if (sampleSize % 8 == 0) {
                    imageType = TYPE_GENERIC;
                }
            }

            // Bail out if not one of the supported types.
            if (imageType == TYPE_UNSUPPORTED) {
                throw new RuntimeException(PropertyUtil.getString("TIFFImage4") + ": "
                                           + imageType);
            }

            // Set basic image layout
            Rectangle bounds = new Rectangle(
                0, 0,
                 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_WIDTH),
                 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH));

            // Set a preliminary band count. This may be changed later as needed.
            numBands = samplesPerPixel;

            // Figure out if any extra samples are present.
            TIFFField efield = dir.getField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES);
            int extraSamples = efield == null ? 0 : (int)efield.getAsLong(0);

            int tileWidth;
            int tileHeight;
            if (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS) != null) {
                tiled = true;
                // Image is in tiled format
                tileWidth =
                    (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_WIDTH);
                tileHeight =
                    (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_LENGTH);
                tileOffsets =
                    (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS)).getAsLongs();
                tileByteCounts =
                    getFieldAsLongs(dir.getField(TIFFImageDecoder.TIFF_TILE_BYTE_COUNTS));

            } else {
                tiled = false;

                // Image is in stripped format, looks like tiles to us
                // Note: Some legacy files may have tile width and height
                // written but use the strip offsets and byte counts fields
                // instead of the tile offsets and byte counts. Therefore
                // we default here to the tile dimensions if they are written.
                tileWidth =
                    dir.getField(TIFFImageDecoder.TIFF_TILE_WIDTH) != null
                    ? (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_WIDTH)
                    : bounds.width;
                TIFFField field =
                    dir.getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP);
                if (field == null) {
                    // Default is infinity (2^32 -1), basically the entire image

                    tileHeight =
                        dir.getField(TIFFImageDecoder.TIFF_TILE_LENGTH) != null
                        ? (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_LENGTH)
                        : bounds.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 = bounds.height;
                    } else {
                        tileHeight = (int)l;
                    }
                }

                TIFFField tileOffsetsField =
                    dir.getField(TIFFImageDecoder.TIFF_STRIP_OFFSETS);
                if (tileOffsetsField == null) {
                    throw new RuntimeException(PropertyUtil.getString("TIFFImage5"));
                } else {
                    tileOffsets = getFieldAsLongs(tileOffsetsField);
                }

                TIFFField tileByteCountsField =
                    dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS);
                if (tileByteCountsField == null) {
                    throw new RuntimeException(PropertyUtil.getString("TIFFImage6"));
                } else {
                    tileByteCounts = getFieldAsLongs(tileByteCountsField);
                }
            }

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

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

            TIFFField fillOrderField =
                dir.getField(TIFFImageDecoder.TIFF_FILL_ORDER);
            if (fillOrderField != null) {
                fillOrder = fillOrderField.getAsInt(0);
            } else {
                // Default Fill Order
                fillOrder = 1;
            }

            switch(compression) {
            case COMP_NONE:
            case COMP_PACKBITS:
                // Do nothing.
                break;
            case COMP_DEFLATE:
                inflater = new Inflater();
                break;
            case COMP_FAX_G3_1D:
            case COMP_FAX_G3_2D:
            case COMP_FAX_G4_2D:
                if (sampleSize != 1) {
                    throw new RuntimeException(PropertyUtil.getString("TIFFImage7"));
                }

                // Fax T.4 compression options
                if (compression == 3) {
                    TIFFField t4OptionsField =
                        dir.getField(TIFFImageDecoder.TIFF_T4_OPTIONS);
                    if (t4OptionsField != null) {
                        tiffT4Options = t4OptionsField.getAsLong(0);
                    } else {
                        // Use default value
                        tiffT4Options = 0;
                    }
                }

                // Fax T.6 compression options
                if (compression == 4) {
                    TIFFField t6OptionsField =
                        dir.getField(TIFFImageDecoder.TIFF_T6_OPTIONS);
                    if (t6OptionsField != null) {
                        tiffT6Options = t6OptionsField.getAsLong(0);
                    } else {
                        // Use default value
                        tiffT6Options = 0;
                    }
                }

                // Fax encoding, need to create the Fax decoder.
                decoder = new TIFFFaxDecoder(fillOrder,
                                             tileWidth, tileHeight);
                break;

            case COMP_LZW:
                // LZW compression used, need to create the LZW decoder.
                TIFFField predictorField =
                    dir.getField(TIFFImageDecoder.TIFF_PREDICTOR);

                if (predictorField == null) {
                    predictor = 1;
                } else {
                    predictor = predictorField.getAsInt(0);

                    if (predictor != 1 && predictor != 2) {
                        throw new RuntimeException(PropertyUtil.getString("TIFFImage8"));
                    }

                    if (predictor == 2 && sampleSize != 8) {
                        throw new RuntimeException(PropertyUtil.getString("TIFFImage9"));
                    }
                }

                lzwDecoder = new TIFFLZWDecoder(tileWidth, predictor,
                                                samplesPerPixel);
                break;

            case COMP_JPEG_OLD:
                throw new RuntimeException(PropertyUtil.getString("TIFFImage15"));

            default:
                throw new RuntimeException(PropertyUtil.getString("TIFFImage10") + ": "
                                           + compression);
            }

            ColorModel  colorModel  = null;
            SampleModel sampleModel = null;
            switch(imageType) {
            case TYPE_BILEVEL:
            case TYPE_GRAY_4BIT:
                sampleModel =
                    new MultiPixelPackedSampleModel(dataType,
                                                    tileWidth,
                                                    tileHeight,
                                                    sampleSize);
                if (imageType == TYPE_BILEVEL) {
                    byte[] map = new byte[] {(byte)(isWhiteZero ? 255 : 0),
                                             (byte)(isWhiteZero ? 0 : 255)};
                    colorModel = new IndexColorModel(1, 2, map, map, map);
                } else {
                    byte [] map = new byte[16];
                    if (isWhiteZero) {
                        for (int i = 0; i < map.length; i++) {
                            map[i] = (byte)(255 - (16 * i));
                        }
                    } else {
                        for (int i = 0; i < map.length; i++) {
                            map[i] = (byte)(16 * i);
                        }
                    }
                    colorModel = new IndexColorModel(4, 16, map, map, map);
                }
                break;

            case TYPE_GRAY:
            case TYPE_GRAY_ALPHA:
            case TYPE_RGB:
            case TYPE_RGB_ALPHA:
                // Create a pixel interleaved SampleModel with decreasing
                // band offsets.
                int[] reverseOffsets = new int[numBands];
                for (int i = 0; i < numBands; i++) {
                    reverseOffsets[i] = numBands - 1 - i;
                }
                sampleModel = new PixelInterleavedSampleModel(
                    dataType, tileWidth, tileHeight,
                     numBands, numBands * tileWidth, reverseOffsets);

                if (imageType == TYPE_GRAY) {
                  colorModel = new ComponentColorModel(
                    ColorSpace.getInstance(ColorSpace.CS_GRAY),
                     new int[] {sampleSize}, false, false,
                     Transparency.OPAQUE, dataType);
                } else if (imageType == TYPE_RGB) {
                  colorModel = new ComponentColorModel(
                    ColorSpace.getInstance(ColorSpace.CS_sRGB),
                     new int[] {sampleSize, sampleSize, sampleSize},
                     false, false, Transparency.OPAQUE, dataType);
                } else { // hasAlpha
                    // 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.
                    int transparency = Transparency.OPAQUE;
                    if (extraSamples == 1) { // associated (premultiplied) alpha
                        transparency = Transparency.TRANSLUCENT;
                    } else if (extraSamples == 2) { // unassociated alpha
                        transparency = Transparency.BITMASK;
                    }

                    colorModel =
                        createAlphaComponentColorModel(dataType,
                                                       numBands,
                                                       extraSamples == 1,
                                                       transparency);
                }
                break;

            case TYPE_GENERIC:
            case TYPE_YCBCR_SUB:
                // For this case we can't display the image, so we create a
                // SampleModel with increasing bandOffsets, and keep the
                // ColorModel as null, as there is no appropriate ColorModel.

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

                sampleModel = new PixelInterleavedSampleModel(
                    dataType, tileWidth, tileHeight,
                     numBands, numBands * tileWidth, bandOffsets);
                colorModel = null;
                break;

            case TYPE_PALETTE:
                // Get the colormap
                TIFFField cfield = dir.getField(TIFFImageDecoder.TIFF_COLORMAP);
                if (cfield == null) {
                    throw new RuntimeException(PropertyUtil.getString("TIFFImage11"));
                } else {
                    colormap = cfield.getAsChars();
                }

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

                    // If no SampleFormat tag was specified and if the
                    // sampleSize is 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 = createPixelInterleavedSampleModel(
                        dataType, tileWidth, tileHeight, numBands);

                  colorModel = new ComponentColorModel(
                    ColorSpace.getInstance(ColorSpace.CS_sRGB),
                     new int[] {16, 16, 16}, false, false,
                     Transparency.OPAQUE, dataType);

                } else {

                    numBands = 1;

                    if (sampleSize == 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,
                             sampleSize);
                    } else if (sampleSize == 8) {

                        sampleModel = createPixelInterleavedSampleModel(
                            DataBuffer.TYPE_BYTE, tileWidth, tileHeight,
                             numBands);
                    } else if (sampleSize == 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.
                        dataType = DataBuffer.TYPE_USHORT;
                        sampleModel = createPixelInterleavedSampleModel(
                            DataBuffer.TYPE_USHORT, tileWidth, tileHeight,
                             numBands);
                    }

                    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(sampleSize,
                                                     bandLength, r, g, b);
                }
                break;

            default:
                throw new RuntimeException(PropertyUtil.getString("TIFFImage4") + ": "
                                           + imageType);
            }

        Map properties = new HashMap();
        // Set a property "tiff_directory".
        properties.put("tiff_directory", dir);

        // System.out.println("Constructed TIFF");

        init((CachableRed)null, bounds, colorModel, sampleModel,
             0, 0, properties);
    }