Package org.libtiff.jai.codecimpl

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

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.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Vector;

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

import com.sun.media.jai.codec.ImageEncodeParam;
import com.sun.media.jai.codec.TIFFEncodeParam;
import com.sun.media.jai.codec.TIFFField;
import com.sun.media.jai.codecimpl.TIFFImageDecoder;
import com.sun.media.jai.codecimpl.TIFFImageEncoder;

/**
* A baseline TIFF writer. The writer outputs TIFF images in either Bilevel,
* Greyscale, Palette color or Full Color modes.
*
*/
public class XTIFFImageEncoder extends TIFFImageEncoder {

    long firstIFDOffset = 0;
    XTIFFDirectory directory;
    XTIFFEncodeParam tparam;
    int width;
    int length;
    SampleModel sampleModel;
    int numBands;
    int sampleSize[];
    int dataType;
    boolean dataTypeIsShort;
    ColorModel colorModel;
    int numTiles;
    int compression;
    boolean isTiled;
    long tileLength;
    long tileWidth;
    byte[] bpixels = null;
    long stripTileByteCounts[];
    long stripTileOffsets[];
    long currentOffset = 0;

    // Image Types
    public static final int XTIFF_BILEVEL_WHITE_IS_ZERO = 0;
    public static final int XTIFF_BILEVEL_BLACK_IS_ZERO = 1;
    public static final int XTIFF_PALETTE = 2;
    public static final int XTIFF_FULLCOLOR = 3;
    public static final int XTIFF_GREYSCALE = 4;

    /**
     * Standard constructor
     */
    public XTIFFImageEncoder(OutputStream output, ImageEncodeParam param) {
        super(output, param);
        if (this.param == null || !(param instanceof XTIFFEncodeParam)) {
            this.param = new XTIFFEncodeParam((TIFFEncodeParam) param);
        }
        tparam = (XTIFFEncodeParam) this.param;
        directory = tparam.getDirectory();
    }

    private File createTemp() throws IOException {
        String tmpdir = System.getProperty("tiff.io.tmpdir");
        File file = null;
        if (tmpdir != null)
            file = File.createTempFile("libtiff.jai.", ".dat", new File(tmpdir));
        else
            file = File.createTempFile("libtiff.jai.", ".dat");
        file.deleteOnExit();
        return file;
    }

    private void copyImageData(File tmp, OutputStream out, int total)
            throws IOException {
        int bufsize = 1024;
        int bytes = 0;
        byte[] buf = new byte[bufsize];
        FileInputStream in = new FileInputStream(tmp);
        do {
            bytes = in.read(buf);
            out.write(buf, 0, bytes);
            total -= bytes;
        } while (total > 0);
        in.close();
    }

    /**
     * Encodes a RenderedImage and writes the output to the OutputStream
     * associated with this ImageEncoder.
     */
    public void encode(RenderedImage im) throws IOException {

        // Set comp into directory
        compression = tparam.getCompression();

        // see if tiled
        isTiled = ((TIFFEncodeParam) param).getWriteTiled();

        // Setup Directory fields.
        getImageFields(im);

        if (compression == XTIFF.COMPRESSION_NONE) {
            computeIFDOffset();
            writeFileHeader(firstIFDOffset);
            currentOffset = 8;
            writeImageData(im, output);
            writeDirectory(directory.getFields(), 0);
        } else {
            // We have to write compressed data out to
            // a temp file to compute the IFD offset.
            // The only alternative is to compress the
            // data twice, which is just about as bad.
            currentOffset = 8;
            File tmp = null;
            try {
                tmp = createTemp();
                OutputStream tmpOut = new FileOutputStream(tmp);
                int total = writeImageData(im, tmpOut);
                tmpOut.close();
                writeFileHeader(currentOffset + currentOffset % 2);
                copyImageData(tmp, output, total);
                writeDirectory(directory.getFields(), 0);
            } finally {
                if (tmp != null)
                    tmp.delete();
            }
        }
    }

    /**
     * Precomputes the IFD Offset for uncompressed data.
     */
    private void computeIFDOffset() {

        long bytesPerRow = (long) Math.ceil((sampleSize[0] / 8.0) * tileWidth
                * numBands);
        long bytesPerTile = bytesPerRow * tileLength;
        long lastTile = bytesPerTile;

        if (!isTiled) {
            // Last strip may have lesser rows
            long lastStripRows = length - (tileLength * (numTiles - 1));
            lastTile = lastStripRows * bytesPerRow;
        }

        long totalBytesOfData = bytesPerTile * (numTiles - 1) + lastTile;

        // File header always occupies 8 bytes and we write the image data
        // after that.
        firstIFDOffset = 8 + totalBytesOfData;
        // Must begin on a word boundary
        if ((firstIFDOffset % 2) != 0) {
            firstIFDOffset++;
        }
    }

    private void writeFileHeader(long firstIFDOffset) throws IOException {
        // 8 byte image file header

        // Byte order used within the file - Big Endian
        output.write('M');
        output.write('M');

        // Magic value
        output.write(0);
        output.write(42);

        // Offset in bytes of the first IFD, must begin on a word boundary
        writeLong(firstIFDOffset);

    }

    // method for adding tags that haven't been set by user
    private void addIfAbsent(int tag, int type, int count, Object obj) {
        if (directory.getField(tag) == null)
            directory.addField(tag, type, count, obj);
    }

    private void getImageFields(RenderedImage im)
    /* throws IOException */{

        width = im.getWidth();
        length = im.getHeight(); // TIFF calls it length

        sampleModel = im.getSampleModel();
        numBands = sampleModel.getNumBands();
        sampleSize = sampleModel.getSampleSize();

        dataType = sampleModel.getDataType();
        if (dataType != DataBuffer.TYPE_BYTE
                && dataType != DataBuffer.TYPE_SHORT
                && dataType != DataBuffer.TYPE_USHORT) {
            // Support only byte and (unsigned) short.
            throw new Error(JaiI18N.getString("TIFFImageEncoder0"));
        }

        dataTypeIsShort = dataType == DataBuffer.TYPE_SHORT
                || dataType == DataBuffer.TYPE_USHORT;

        colorModel = im.getColorModel();
        if (colorModel != null && colorModel instanceof IndexColorModel
                && dataTypeIsShort) {
            // Don't support (unsigned) short palette-color images.
            throw new Error(JaiI18N.getString("TIFFImageEncoder2"));
        }
        IndexColorModel icm = null;
        int sizeOfColormap = 0;
        char colormap[] = null;

        // Basic fields - have to be in increasing numerical order BILEVEL
        // ImageWidth 256
        // ImageLength 257
        // BitsPerSample 258
        // Compression 259
        // PhotoMetricInterpretation 262
        // StripOffsets 273
        // RowsPerStrip 278
        // StripByteCounts 279
        // XResolution 282
        // YResolution 283
        // ResolutionUnit 296

        int photometricInterpretation = XTIFF.PHOTOMETRIC_RGB;
        int imageType = XTIFF_FULLCOLOR;

        // IMAGE TYPES POSSIBLE

        // Bilevel
        // BitsPerSample = 1
        // Compression = 1, 2, or 32773
        // PhotometricInterpretation either 0 or 1

        // Greyscale
        // BitsPerSample = 4 or 8
        // Compression = 1, 32773
        // PhotometricInterpretation either 0 or 1

        // Palette
        // ColorMap 320
        // BitsPerSample = 4 or 8
        // PhotometrciInterpretation = 3

        // Full color
        // BitsPerSample = 8, 8, 8
        // SamplesPerPixel = 3 or more 277
        // Compression = 1, 32773
        // PhotometricInterpretation = 2

        if (colorModel instanceof IndexColorModel) {

            icm = (IndexColorModel) colorModel;
            int mapSize = icm.getMapSize();

            if (sampleSize[0] == 1) {
                // Bilevel image

                if (mapSize != 2) {
                    throw new IllegalArgumentException(JaiI18N.getString("TIFFImageEncoder1"));
                }

                byte r[] = new byte[mapSize];
                icm.getReds(r);
                byte g[] = new byte[mapSize];
                icm.getGreens(g);
                byte b[] = new byte[mapSize];
                icm.getBlues(b);

                if ((r[0] & 0xff) == 0 && (r[1] & 0xff) == 255
                        && (g[0] & 0xff) == 0 && (g[1] & 0xff) == 255
                        && (b[0] & 0xff) == 0 && (b[1] & 0xff) == 255) {

                    imageType = XTIFF_BILEVEL_BLACK_IS_ZERO;

                } else if ((r[0] & 0xff) == 255 && (r[1] & 0xff) == 0
                        && (g[0] & 0xff) == 255 && (g[1] & 0xff) == 0
                        && (b[0] & 0xff) == 255 && (b[1] & 0xff) == 0) {

                    imageType = XTIFF_BILEVEL_WHITE_IS_ZERO;

                } else {
                    imageType = XTIFF_PALETTE;
                }

            } else {
                // Palette color image.
                imageType = XTIFF_PALETTE;
            }
        } else {

            // If it is not an IndexColorModel, it can either be a greyscale
            // image or a full color image

            if ((colorModel == null || colorModel.getColorSpace().getType() == ColorSpace.TYPE_GRAY)
                    && numBands == 1) {
                // Greyscale image
                imageType = XTIFF_GREYSCALE;
            } else {
                // Full color image
                imageType = XTIFF_FULLCOLOR;
            }
        }

        switch (imageType) {

        case XTIFF_BILEVEL_WHITE_IS_ZERO:
            photometricInterpretation = XTIFF.PHOTOMETRIC_WHITE_IS_ZERO;
            break;

        case XTIFF_BILEVEL_BLACK_IS_ZERO:
            photometricInterpretation = XTIFF.PHOTOMETRIC_BLACK_IS_ZERO;
            break;

        case XTIFF_GREYSCALE:
            // Since the CS_GRAY colorspace is always of type black_is_zero
            photometricInterpretation = XTIFF.PHOTOMETRIC_BLACK_IS_ZERO;
            break;

        case XTIFF_PALETTE:
            photometricInterpretation = XTIFF.PHOTOMETRIC_PALETTE;

            icm = (IndexColorModel) colorModel;
            sizeOfColormap = icm.getMapSize();

            byte r[] = new byte[sizeOfColormap];
            icm.getReds(r);
            byte g[] = new byte[sizeOfColormap];
            icm.getGreens(g);
            byte b[] = new byte[sizeOfColormap];
            icm.getBlues(b);

            int redIndex = 0,
            greenIndex = sizeOfColormap;
            int blueIndex = 2 * sizeOfColormap;
            colormap = new char[sizeOfColormap * 3];
            for (int i = 0; i < sizeOfColormap; i++) {
                colormap[redIndex++] = (char) (r[i] << 8);
                colormap[greenIndex++] = (char) (g[i] << 8);
                colormap[blueIndex++] = (char) (b[i] << 8);
            }

            sizeOfColormap *= 3;

            // Since we will be writing the colormap field.
            break;

        case XTIFF_FULLCOLOR:
            photometricInterpretation = XTIFF.PHOTOMETRIC_RGB;
            break;

        }

        if (isTiled) {
            tileWidth = 16L;
            tileLength = 16L;
            XTIFFField fld = directory.getField(XTIFF.TIFFTAG_TILE_WIDTH);
            if (fld != null)
                tileWidth = (int) fld.getAsLong(0);
            fld = directory.getField(XTIFF.TIFFTAG_TILE_LENGTH);
            if (fld != null)
                tileLength = (int) fld.getAsLong(0);
        } else {
            // Default strip is 8 rows.
            tileLength = 8L;
            // tileWidth of strip is width

            tileWidth = width;
            XTIFFField fld = directory.getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP);
            if (fld != null)
                tileLength = fld.getAsLong(0);
        }

        numTiles = (int) Math.ceil((double) length / (double) tileLength)
                * (int) Math.ceil((double) width / (double) tileWidth);

        stripTileByteCounts = new long[numTiles];
        stripTileOffsets = new long[numTiles];

        // Image Width
        directory.addField(XTIFF.TIFFTAG_IMAGE_WIDTH,
                TIFFField.TIFF_LONG,
                1,
                (Object) (new long[] { width }));

        // Image Length
        directory.addField(XTIFF.TIFFTAG_IMAGE_LENGTH,
                TIFFField.TIFF_LONG,
                1,
                new long[] { length });

        directory.addField(XTIFF.TIFFTAG_BITS_PER_SAMPLE,
                TIFFField.TIFF_SHORT,
                numBands,
                convertToChars(sampleSize));

        directory.addField(XTIFF.TIFFTAG_COMPRESSION,
                TIFFField.TIFF_SHORT,
                1,
                new char[] { (char) compression });

        directory.addField(XTIFF.TIFFTAG_PHOTOMETRIC_INTERPRETATION,
                TIFFField.TIFF_SHORT,
                1,
                new char[] { (char) photometricInterpretation });

        directory.addField(XTIFF.TIFFTAG_SAMPLES_PER_PIXEL,
                TIFFField.TIFF_SHORT,
                1,
                new char[] { (char) numBands });

        if (isTiled) {
            directory.addField(XTIFF.TIFFTAG_TILE_WIDTH,
                    TIFFField.TIFF_LONG,
                    1,
                    new long[] { tileWidth });

            directory.addField(XTIFF.TIFFTAG_TILE_LENGTH,
                    TIFFField.TIFF_LONG,
                    1,
                    new long[] { tileLength });

            directory.addField(XTIFF.TIFFTAG_TILE_OFFSETS,
                    TIFFField.TIFF_LONG,
                    numTiles,
                    stripTileOffsets);

            directory.addField(XTIFF.TIFFTAG_TILE_BYTE_COUNTS,
                    TIFFField.TIFF_LONG,
                    numTiles,
                    stripTileByteCounts);
        } else {
            directory.addField(XTIFF.TIFFTAG_STRIP_OFFSETS,
                    TIFFField.TIFF_LONG,
                    numTiles,
                    stripTileOffsets);

            directory.addField(XTIFF.TIFFTAG_ROWS_PER_STRIP,
                    TIFFField.TIFF_LONG,
                    1,
                    new long[] { tileLength });

            directory.addField(XTIFF.TIFFTAG_STRIP_BYTE_COUNTS,
                    TIFFField.TIFF_LONG,
                    numTiles,
                    stripTileByteCounts);
        }

        addIfAbsent(XTIFF.TIFFTAG_X_RESOLUTION,
                TIFFField.TIFF_RATIONAL,
                1,
                new long[][] { { 72, 1 } });

        addIfAbsent(XTIFF.TIFFTAG_Y_RESOLUTION,
                TIFFField.TIFF_RATIONAL,
                1,
                new long[][] { { 72, 1 } });

        addIfAbsent(XTIFF.TIFFTAG_RESOLUTION_UNIT,
                TIFFField.TIFF_SHORT,
                1,
                new char[] { (char) 2 });

        if (colormap != null) {
            directory.addField(XTIFF.TIFFTAG_COLORMAP,
                    TIFFField.TIFF_SHORT,
                    sizeOfColormap,
                    colormap);
        }

        // Data Sample Format Extension fields.
        if (dataTypeIsShort) {
            // SampleFormat
            int[] sampleFormat = new int[numBands];
            sampleFormat[0] = dataType == DataBuffer.TYPE_USHORT ? 1 : 2;
            for (int b = 1; b < numBands; b++) {
                sampleFormat[b] = sampleFormat[0];
            }
            directory.addField(XTIFF.TIFFTAG_SAMPLE_FORMAT,
                    TIFFField.TIFF_SHORT,
                    numBands,
                    convertToChars(sampleFormat));

            // SMinSampleValue: set to data type minimum.
            int[] minValue = new int[numBands];
            minValue[0] = dataType == DataBuffer.TYPE_USHORT ? 0
                    : Short.MIN_VALUE;
            for (int b = 1; b < numBands; b++) {
                minValue[b] = minValue[0];
            }
            directory.addField(XTIFF.TIFFTAG_S_MIN_SAMPLE_VALUE,
                    TIFFField.TIFF_SHORT,
                    numBands,
                    convertToChars(minValue));

            // SMaxSampleValue: set to data type maximum.
            int[] maxValue = new int[numBands];
            maxValue[0] = dataType == DataBuffer.TYPE_USHORT ? 65535
                    : Short.MAX_VALUE;
            for (int b = 1; b < numBands; b++) {
                maxValue[b] = maxValue[0];
            }
            directory.addField(XTIFF.TIFFTAG_S_MAX_SAMPLE_VALUE,
                    TIFFField.TIFF_SHORT,
                    numBands,
                    convertToChars(maxValue));
        }

    }

    private char[] convertToChars(int[] shorts) {
        char[] out = new char[shorts.length];
        for (int i = 0; i < shorts.length; i++)
            out[i] = (char) shorts[i];
        return out;
    }

    protected int getSampleSize() {
        if (dataType == DataBuffer.TYPE_BYTE)
            return 1;
        else if (dataTypeIsShort)
            return 2;
        return 1; // what should go here?
    }

    protected int getTileSize() {
        return (int) (tileLength * tileWidth * numBands);
    }

    private int writeImageData(RenderedImage im, OutputStream out)
            throws IOException {
        int total = 0;

        // Get the encoder
        XTIFFTileCodec codec = directory.createTileCodec(tparam);

        // Create a buffer to hold the data
        // to be written to the file, so we can use array writes.
        int tsize = codec.getCompressedTileSize(im);
        bpixels = new byte[tsize];

        // Encode one tile at a time
        Rectangle rect = new Rectangle();
        float minX = (float) im.getMinX();
        float minY = (float) im.getMinY();
        float rows = (float) tileLength;
        float cols = (float) tileWidth;
        int i = 0;
        for (int row = 0; row < length; row += tileLength) {
            for (int col = 0; col < width; col += tileWidth) {
                if (!isTiled)
                    rows = Math.min(tileLength, length - row);
                rect.setRect(minX + col, minY + row, cols, rows);
                int tileSize = codec.encode(im, rect, bpixels);
                out.write(bpixels, 0, tileSize);
                stripTileOffsets[i] = currentOffset;
                stripTileByteCounts[i++] = tileSize;
                currentOffset += tileSize;
                total += tileSize;
            }
        }
        return total;
    }

    private void writeDirectory(XTIFFField fields[], int nextIFDOffset)
            throws IOException {

        if (currentOffset % 2 == 1) {
            output.write(0);
            currentOffset++;
        }

        // 2 byte count of number of directory entries (fields)
        int numEntries = fields.length;

        long offsetBeyondIFD = currentOffset + 12 * numEntries + 4 + 2;
        Vector tooBig = new Vector();

        XTIFFField field;
        int tag;
        int type;
        int count;

        // Write number of fields in the IFD
        writeUnsignedShort(numEntries);

        for (int i = 0; i < numEntries; i++) {

            field = fields[i];

            // 12 byte field entry TIFFField

            // byte 0-1 Tag that identifies a field
            tag = field.getTag();
            writeUnsignedShort(tag);

            // byte 2-3 The field type
            type = field.getType();
            writeUnsignedShort(type);

            // bytes 4-7 the number of values of the indicated type
            count = field.getCount();
            writeLong(count);

            // bytes 8 - 11 the value offset
            if (count * sizeOfType[type] > 4) {

                // We need an offset as data won't fit into 4 bytes
                writeLong(offsetBeyondIFD);
                offsetBeyondIFD += (count * sizeOfType[type]);
                tooBig.add(new Integer(i));

            } else {

                writeValuesAsFourBytes(field);
            }

        }

        // Address of next IFD
        writeLong(nextIFDOffset);

        int index;
        // Write the tag values that did not fit into 4 bytes

        for (int i = 0; i < tooBig.size(); i++) {
            index = ((Integer) tooBig.elementAt(i)).intValue();
            writeValues(fields[index]);
        }
    }

    private static final int[] sizeOfType = { 0, // 0 = n/a
            1, // 1 = byte
            1, // 2 = ascii
            2, // 3 = short
            4, // 4 = long
            8, // 5 = rational
            1, // 6 = sbyte
            1, // 7 = undefined
            2, // 8 = sshort
            4, // 9 = slong
            8, // 10 = srational
            4, // 11 = float
            8 // 12 = double
    };

    private void writeValuesAsFourBytes(XTIFFField field) throws IOException {

        int dataType = field.getType();
        int count = field.getCount();

        switch (dataType) {

        // unsigned 8 bits
        case TIFFField.TIFF_BYTE:
            byte bytes[] = field.getAsBytes();

            for (int i = 0; i < count; i++) {
                output.write(bytes[i]);
            }

            for (int i = 0; i < (4 - count); i++) {
                output.write(0);
            }

            break;

        // unsigned 16 bits
        case TIFFField.TIFF_SHORT:
            char shorts[] = field.getAsChars();

            for (int i = 0; i < count; i++) {
                writeUnsignedShort((int) shorts[i]);
            }

            for (int i = 0; i < (2 - count); i++) {
                writeUnsignedShort(0);
            }

            break;

        // unsigned 32 bits
        case TIFFField.TIFF_LONG:
            long longs[] = field.getAsLongs();

            for (int i = 0; i < count; i++) {
                writeLong(longs[i]);
            }
            break;
        }

    }

    private void writeValues(XTIFFField field) throws IOException {

        int dataType = field.getType();
        int count = field.getCount();

        switch (dataType) {

        // character data with NULL termination
        case TIFFField.TIFF_ASCII:
            String strings[] = field.getAsStrings();
            for (int i = 0; i < strings.length; i++) {
                byte bytes[] = strings[i].getBytes();
                for (int j = 0; j < bytes.length; j++) {
                    output.write(bytes[j]);
                }
                if ((i + 1) < count)
                    output.write(0);
            }
            break;

        // unsigned 8 bits
        case TIFFField.TIFF_BYTE:
            byte bytes[] = field.getAsBytes();
            for (int i = 0; i < count; i++) {
                output.write(bytes[i]);
            }
            break;

        // unsigned 16 bits
        case TIFFField.TIFF_SHORT:
            char shorts[] = field.getAsChars();
            for (int i = 0; i < count; i++) {
                writeUnsignedShort((int) shorts[i]);
            }
            break;

        // unsigned 32 bits
        case TIFFField.TIFF_LONG:
            long longs[] = field.getAsLongs();
            for (int i = 0; i < count; i++) {
                writeLong(longs[i]);
            }
            break;

        // IEEE 8-byte double
        case TIFFField.TIFF_DOUBLE:
            double doubles[] = field.getAsDoubles();
            for (int i = 0; i < count; i++) {
                writeDouble(doubles[i]);
            }
            break;

        case TIFFField.TIFF_RATIONAL:
            long rationals[][] = field.getAsRationals();
            for (int i = 0; i < count; i++) {
                writeLong(rationals[i][0]);
                writeLong(rationals[i][1]);
            }
            break;

        }

    }

    // Here s is never expected to have value greater than what can be
    // stored in 2 bytes.
    private void writeUnsignedShort(int s) throws IOException {
        output.write((s & 0xff00) >>> 8);
        output.write(s & 0x00ff);
    }

    private void writeLong(long l) throws IOException {
        output.write((int) ((l & 0xff000000) >>> 24));
        output.write((int) ((l & 0x00ff0000) >>> 16));
        output.write((int) ((l & 0x0000ff00) >>> 8));
        output.write(((int) l & 0x000000ff));
    }

    // write 8-byte IEEE double
    private void writeDouble(double d) throws IOException {
        long lval = Double.doubleToLongBits(d);
        writeLong(lval >>> 32);
        writeLong((lval & 0xffffffff));
    }

}
TOP

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

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.