Package net.sourceforge.jiu.codecs.tiff

Source Code of net.sourceforge.jiu.codecs.tiff.TIFFDecoder

/*
* TIFFDecoder
*
* Copyright (c) 2002, 2003 Marco Schmidt.
* All rights reserved.
*/

package net.sourceforge.jiu.codecs.tiff;

import java.io.DataInput;
import java.io.IOException;
import java.io.RandomAccessFile;
import net.sourceforge.jiu.codecs.InvalidFileStructureException;
import net.sourceforge.jiu.color.conversion.CMYKConversion;
import net.sourceforge.jiu.color.conversion.LogLuvConversion;
import net.sourceforge.jiu.data.BilevelImage;
import net.sourceforge.jiu.data.ByteChannelImage;
import net.sourceforge.jiu.data.RGBIndex;
import net.sourceforge.jiu.data.ShortChannelImage;
import net.sourceforge.jiu.ops.MissingParameterException;
import net.sourceforge.jiu.util.ArrayConverter;

/**
* The abstract base class for a TIFF decoder, a class that decompresses one tile or
* strip of image data and understands one or more compression types.
* Each child class implements the decoding of a particular TIFF compression type
* in its {@link #decode} method.
* <p>
* This class does all the work of storing decompressed data (given as a byte array)
* in the image object.
* Given the many variants (sample order, color depth, color space etc.) this is
* a larger portion of code.
* @author Marco Schmidt
* @since 0.7.0
*/
public abstract class TIFFDecoder
{
  private TIFFCodec codec;
  private TIFFImageFileDirectory ifd;
  private int currentRow;
  private int leftColumn;
  private int rightColumn;
  private int topRow;
  private int bottomRow;
  private byte[] rowBuffer;
  private int bufferIndex;
  private int tileIndex;
  private int processedTileRows;
  private int totalTileRows;

  public TIFFDecoder()
  {
    tileIndex = -1;
  }

  /**
   * Decode data from input and write the decompressed pixel data to
   * the image associated with this decoder.
   * Child classes must override this method to implement the decoding
   * for a particular compression type.
   */
  public abstract void decode() throws
    InvalidFileStructureException,
    IOException;

  /**
   * Returns the number of bytes per row for the strip or tile
   * that this decoder deals with.
   * So with a tiled TIFF and an image width of 500 and a tile width of 100,
   * for an eight bit grayscale image this would return 100 (not 500).
   * @return number of bytes per row
   */
  public int getBytesPerRow()
  {
    return ifd.getBytesPerRow();
  }

  /**
   * Returns the codec from which this decoder is used.
   * @return TIFFCodec object using this decoder
   */
  public TIFFCodec getCodec()
  {
    return codec;
  }

  /**
   * Returns an array with Integer values of all compression types supported by
   * this decoder (see the COMPRESSION_xyz constants in {@link TIFFConstants}.
   * Normally, this is only one value, but some compression types got assigned more than one constant
   * (e.g. deflated).
   * Also, a decoder could be capable of dealing with more than one type of compression
   * if the compression types are similar enough to justify that.
   * However, typically a decoder can only deal with one type of compression.
   * @return array with Integer objects of all TIFF compression constants supported by this decoder
   */
  public abstract Integer[] getCompressionTypes();

  /**
   * Returns the IFD for the image this decoder is supposed to uncompress
   * (partially).
   * @return IFD object
   */
  public TIFFImageFileDirectory getImageFileDirectory()
  {
    return ifd;
  }

  /**
   * Returns the input stream from which this decoder is supposed
   * to read data.
   */
  public DataInput getInput()
  {
    return codec.getRandomAccessFile();
  }

  /**
   * Returns the zero-based index of the tile or strip this decoder
   * is supposed to be decompressing.
   * @return tile index
   */
  public int getTileIndex()
  {
    return tileIndex;
  }

  /**
   * Returns the leftmost column of the image strip / tile to be read
   * by this decoder.
   */
  public int getX1()
  {
    return leftColumn;
  }

  /**
   * Returns the rightmost column of the image strip / tile to be read
   * by this decoder.
   */
  public int getX2()
  {
    return rightColumn;
  }

  /**
   * Returns the top row of the image strip / tile to be read
   * by this decoder.
   */
  public int getY1()
  {
    return topRow;
  }

  /**
   * Returns the bottom row of the image strip / tile to be read
   * by this decoder.
   */
  public int getY2()
  {
    return bottomRow;
  }

  /**
   * Check if all necessary parameters have been given to this decoder
   * and initialize several internal fields from them.
   * Required parameters are a TIFFCodec object, a TIFFImageFileDirectory object and
   * a tile index.
   */
  public void initialize() throws
    IOException,
    MissingParameterException
  {
    if (tileIndex < 0)
    {
      throw new MissingParameterException("Tile index was not initialized.");
    }
    if (codec == null)
    {
      throw new MissingParameterException("No TIFFCodec object was given to this decoder.");
    }
    if (ifd == null)
    {
      throw new MissingParameterException("No TIFFImageFileDirectory object was given to this decoder.");
    }

    RandomAccessFile raf = codec.getRandomAccessFile();
    long offset = ifd.getTileOffset(tileIndex) & 0x00000000ffffffffL;
    raf.seek(offset);

    leftColumn = ifd.getTileX1(tileIndex);
    rightColumn = ifd.getTileX2(tileIndex);
    topRow = ifd.getTileY1(tileIndex);
    bottomRow = ifd.getTileY2(tileIndex);
    currentRow = topRow;
    processedTileRows = tileIndex * ifd.getTileHeight();
    totalTileRows = ifd.getTileHeight() * ifd.getNumTiles();
    rowBuffer = new byte[ifd.getBytesPerRow()];
  }

  /**
   * Adds a number of bytes to the internal row buffer.
   * If the row buffer gets full (a complete line is available)
   * that data will be copied to the image.
   * Note that more than one line, exactly one line or only part
   * of a line can be stored in the <code>number</code> bytes
   * in <code>data</code>.
   * @param data byte array with image data that has been decoded
   * @param offset int index into data where the first byte to be stored is situated
   * @param number int number of bytes to be stored
   */
  public void putBytes(byte[] data, int offset, int number)
  {
    // assert(bufferIndex < rowBuffer.length);
    while (number > 0)
    {
      int remaining = rowBuffer.length - bufferIndex;
      int numCopy;
      if (number > remaining)
      {
        numCopy = remaining;
      }
      else
      {
        numCopy = number;
      }
      System.arraycopy(data, offset, rowBuffer, bufferIndex, numCopy);
      number -= numCopy;
      offset += numCopy;
      bufferIndex += numCopy;
      if (bufferIndex == getBytesPerRow())
      {
        storeRow(rowBuffer, 0);
        bufferIndex = 0;
      }
    }
  }

  /**
   * Specify the codec to be used with this decoder.
   * This is a mandatory parameter - without it, {@link #initialize}
   * will throw an exception.
   * @param tiffCodec TIFFCodec object to be used by this decoder
   * @see #getCodec
   */
  public void setCodec(TIFFCodec tiffCodec)
  {
    codec = tiffCodec;
  }

  /**
   * Specify the IFD to be used with this decoder.
   * This is a mandatory parameter - without it, {@link #initialize}
   * will throw an exception.
   * @param tiffIfd object to be used by this decoder
   * @see #getImageFileDirectory
   */
  public void setImageFileDirectory(TIFFImageFileDirectory tiffIfd)
  {
    ifd = tiffIfd;
  }

  /**
   * Specify the zero-based tile index for the tile or strip to be decompressed
   * by this decoder.
   * This is a mandatory parameter - without it, {@link #initialize}
   * will throw an exception.
   * @param index zero-based tile / strip index
   * @see #getTileIndex
   */
  public void setTileIndex(int index)
  {
    if (index < 0)
    {
      throw new IllegalArgumentException("Tile index must be 0 or larger.");
    }
    tileIndex = index;
  }

  private void storeRow(byte[] data, int offset)
  {
    codec.setProgress(processedTileRows++, totalTileRows);
    // get current row number and increase field currentRow by one
    int y = currentRow++;
    // buffer index field is reset to zero so that putBytes will start at the beginning of the buffer next time
    bufferIndex = 0;
    // leave if we don't need that row because of bounds
    if (!codec.isRowRequired(y))
    {
      return;
    }
    // adjust y so that it will be in bounds coordinate space
    y -= codec.getBoundsY1();
    // get leftmost and rightmost pixel index of the current tile
    int x1 = getX1();
    int x2 = getX2();
    // compute number of pixels, adjust for bounds
    int numPixels = x2 - x1 + 1;
    int leftPixels = 0;
    if (getX1() < codec.getBoundsX1())
    {
      leftPixels = codec.getBoundsX1() - getX1();
    }
    int rightPixels = 0;
    if (getX2() > codec.getBoundsX2())
    {
      rightPixels = getX2() - codec.getBoundsX2();
    }
    numPixels -= (rightPixels + leftPixels);
    switch(ifd.getImageType())
    {
      case(TIFFImageFileDirectory.TYPE_BILEVEL_BYTE):
      {
        BilevelImage image = (BilevelImage)codec.getImage();
        int index = offset + leftPixels;
        int x = getX1() - codec.getBoundsX1() + leftPixels;
        while (numPixels-- > 0)
        {
          if (data[index++] == (byte)BilevelImage.BLACK)
          {
            image.putBlack(x++, y);
          }
          else
          {
            image.putWhite(x++, y);
          }
        }
        break;
      }
      case(TIFFImageFileDirectory.TYPE_BILEVEL_PACKED):
      {
        BilevelImage image = (BilevelImage)codec.getImage();
        int x = getX1() - codec.getBoundsX1() + leftPixels;
        image.putPackedBytes(x, y, numPixels, data, offset + (leftPixels / 8), leftPixels % 8);
        break;
      }
      case(TIFFImageFileDirectory.TYPE_GRAY4):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        byte[] dest = new byte[data.length * 2];
        ArrayConverter.decodePacked4Bit(data, 0, dest, 0, data.length);
        for (int i = 0; i < dest.length; i++)
        {
          int value = dest[i] & 15;
          value = (value << 4) | value;
          dest[i] = (byte)value;
        }
        image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, offset + leftPixels);
        break;
      }
      case(TIFFImageFileDirectory.TYPE_PALETTED4):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        byte[] dest = new byte[data.length * 2];
        ArrayConverter.decodePacked4Bit(data, 0, dest, 0, data.length);
        image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, offset + leftPixels);
        break;
      }
      case(TIFFImageFileDirectory.TYPE_GRAY8):
      case(TIFFImageFileDirectory.TYPE_PALETTED8):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, data, offset + leftPixels);
        break;
      }
      case(TIFFImageFileDirectory.TYPE_CMYK32_INTERLEAVED):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        byte[] dest = new byte[data.length];
        int numSamples = ifd.getTileWidth();
        CMYKConversion.convertCMYK32InterleavedToRGB24Planar(
          data, 0,
          dest, 0,
          dest, numSamples,
          dest, numSamples * 2,
          numSamples);
        image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, leftPixels);
        image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, numSamples + leftPixels);
        image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, 2 * numSamples + leftPixels);
        break;
      }
/*      case(TIFFImageFileDirectory.TYPE_CMYK32_PLANAR):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        byte[] dest = new byte[data.length];
        int numSamples = ifd.getTileWidth();
        CMYKConversion.convertCMYK32PlanarToRGB24Planar(
          data, 0,
          data, numPixels,
          data, numPixels * 2,
          data, numPixels * 3,
          dest, 0,
          dest, numSamples,
          dest, numSamples * 2,
          numSamples);
        image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, leftPixels);
        image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, numSamples + leftPixels);
        image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, dest, 2 * numSamples + leftPixels);
        break;
      }*/
      case(TIFFImageFileDirectory.TYPE_RGB24_INTERLEAVED):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        offset += leftPixels * 3;
        for (int i = 0, x = getX1() - codec.getBoundsX1() + leftPixels; i < numPixels; i++, x++)
        {
          image.putByteSample(RGBIndex.INDEX_RED, x, y, data[offset++]);
          image.putByteSample(RGBIndex.INDEX_GREEN, x, y, data[offset++]);
          image.putByteSample(RGBIndex.INDEX_BLUE, x, y, data[offset++]);
        }
        break;
      }
      case(TIFFImageFileDirectory.TYPE_RGB48_INTERLEAVED):
      {
        ShortChannelImage image = (ShortChannelImage)codec.getImage();
        offset += leftPixels * 3;
        short[] triplet = new short[3];
        boolean littleEndian = codec.getByteOrder() == TIFFCodec.BYTE_ORDER_INTEL;
        for (int i = 0, x = getX1() - codec.getBoundsX1() + leftPixels; i < numPixels; i++, x++)
        {
          for (int j = 0; j < 3; j++, offset += 2)
          {
            if (littleEndian)
            {
              triplet[j] = ArrayConverter.getShortLE(data, offset);
            }
            else
            {
              triplet[j] = ArrayConverter.getShortBE(data, offset);
            }
          }
          image.putShortSample(RGBIndex.INDEX_RED, x, y, triplet[0]);
          image.putShortSample(RGBIndex.INDEX_GREEN, x, y, triplet[1]);
          image.putShortSample(RGBIndex.INDEX_BLUE, x, y, triplet[2]);
        }
        break;
      }
      case(TIFFImageFileDirectory.TYPE_LOGLUV32_INTERLEAVED):
      {
        if (getImageFileDirectory().getCompression() == TIFFConstants.COMPRESSION_SGI_LOG_RLE)
        {
          ByteChannelImage image = (ByteChannelImage)codec.getImage();
          int numSamples = ifd.getTileWidth();
          byte[] red = new byte[numSamples];
          byte[] green = new byte[numSamples];
          byte[] blue = new byte[numSamples];
          LogLuvConversion.convertLogLuv32InterleavedtoRGB24Planar(data, red, green, blue, numSamples);
          image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, red, leftPixels);
          image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, green, leftPixels);
          image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, blue, leftPixels);
        }
        else
        if (getImageFileDirectory().getCompression() == TIFFConstants.COMPRESSION_SGI_LOG_24_PACKED)
        {
          ByteChannelImage image = (ByteChannelImage)codec.getImage();
          int numSamples = ifd.getTileWidth();
          byte[] red = new byte[numSamples];
          byte[] green = new byte[numSamples];
          byte[] blue = new byte[numSamples];
          LogLuvConversion.convertLogLuv24InterleavedtoRGB24Planar(data, red, green, blue, numSamples);
          image.putByteSamples(RGBIndex.INDEX_RED, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, red, leftPixels);
          image.putByteSamples(RGBIndex.INDEX_GREEN, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, green, leftPixels);
          image.putByteSamples(RGBIndex.INDEX_BLUE, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, blue, leftPixels);
        }
        break;
      }
      case(TIFFImageFileDirectory.TYPE_LOGL):
      {
        ByteChannelImage image = (ByteChannelImage)codec.getImage();
        int numSamples = ifd.getTileWidth();
        byte[] gray = new byte[numSamples];
        LogLuvConversion.convertLogL16toGray8(data, gray, numSamples);
        image.putByteSamples(0, getX1() - codec.getBoundsX1() + leftPixels, y, numPixels, 1, gray, leftPixels);
        break;
      }
    }
  }
}
TOP

Related Classes of net.sourceforge.jiu.codecs.tiff.TIFFDecoder

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.