Advertisement
Guest User

Patched gif image reader

a guest
Oct 5th, 2014
3,201
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 36.33 KB | None | 0 0
  1. package de.onyxbits.giftedmotion;
  2.  
  3. import java.awt.Point;
  4. import java.awt.Rectangle;
  5. import java.awt.image.BufferedImage;
  6. import java.awt.image.DataBuffer;
  7. import java.awt.image.WritableRaster;
  8. import java.io.BufferedInputStream;
  9. import java.io.DataInputStream;
  10. import java.io.EOFException;
  11. import java.io.InputStream;
  12. import java.io.IOException;
  13. import java.nio.ByteOrder;
  14. import java.util.ArrayList;
  15. import java.util.Arrays;
  16. import java.util.Iterator;
  17. import java.util.List;
  18.  
  19. import javax.imageio.IIOException;
  20. import javax.imageio.ImageReader;
  21. import javax.imageio.ImageReadParam;
  22. import javax.imageio.ImageTypeSpecifier;
  23. import javax.imageio.metadata.IIOMetadata;
  24. import javax.imageio.spi.ImageReaderSpi;
  25. import javax.imageio.stream.ImageInputStream;
  26.  
  27. import com.sun.imageio.plugins.common.ReaderUtil;
  28. import com.sun.imageio.plugins.gif.GIFImageMetadata;
  29. import com.sun.imageio.plugins.gif.GIFImageReader;
  30. import com.sun.imageio.plugins.gif.GIFStreamMetadata;
  31.  
  32. public class PatchedGIFImageReader extends ImageReader {
  33.  
  34.     public PatchedGIFImageReader(ImageReaderSpi originatingProvider) {super(originatingProvider);}
  35.  
  36.     // The current ImageInputStream source.
  37.     ImageInputStream stream = null;
  38.  
  39.     // Per-stream settings
  40.  
  41.     // True if the file header including stream metadata has been read.
  42.     boolean gotHeader = false;
  43.  
  44.     // Global metadata, read once per input setting.
  45.     GIFStreamMetadata streamMetadata = null;
  46.  
  47.     // The current image index
  48.     int currIndex = -1;
  49.  
  50.     // Metadata for image at 'currIndex', or null.
  51.     GIFImageMetadata imageMetadata = null;
  52.  
  53.     // A List of Longs indicating the stream positions of the
  54.     // start of the metadata for each image.  Entries are added
  55.     // as needed.
  56.     List imageStartPosition = new ArrayList();
  57.  
  58.     // Length of metadata for image at 'currIndex', valid only if
  59.     // imageMetadata != null.
  60.     int imageMetadataLength;
  61.  
  62.     // The number of images in the stream, if known, otherwise -1.
  63.     int numImages = -1;
  64.  
  65.     // Variables used by the LZW decoding process
  66.     byte[] block = new byte[255];
  67.     int blockLength = 0;
  68.     int bitPos = 0;
  69.     int nextByte = 0;
  70.     int initCodeSize;
  71.     int clearCode;
  72.     int eofCode;
  73.  
  74.     // 32-bit lookahead buffer
  75.     int next32Bits = 0;
  76.  
  77.     // Try if the end of the data blocks has been found,
  78.     // and we are simply draining the 32-bit buffer
  79.     boolean lastBlockFound = false;
  80.  
  81.     // The image to be written.
  82.     BufferedImage theImage = null;
  83.  
  84.     // The image's tile.
  85.     WritableRaster theTile = null;
  86.  
  87.     // The image dimensions (from the stream).
  88.     int width = -1, height = -1;
  89.  
  90.     // The pixel currently being decoded (in the stream's coordinates).
  91.     int streamX = -1, streamY = -1;
  92.  
  93.     // The number of rows decoded
  94.     int rowsDone = 0;
  95.  
  96.     // The current interlace pass, starting with 0.
  97.     int interlacePass = 0;
  98.  
  99.     // End per-stream settings
  100.  
  101.     // Constants used to control interlacing.
  102.     static final int[] interlaceIncrement = { 8, 8, 4, 2, -1 };
  103.     static final int[] interlaceOffset = { 0, 4, 2, 1, -1 };
  104.  
  105.     // Take input from an ImageInputStream
  106.     public void setInput(Object input,
  107.                          boolean seekForwardOnly,
  108.                          boolean ignoreMetadata) {
  109.         super.setInput(input, seekForwardOnly, ignoreMetadata);
  110.         if (input != null) {
  111.             if (!(input instanceof ImageInputStream)) {
  112.                 throw new IllegalArgumentException
  113.                     ("input not an ImageInputStream!");
  114.             }
  115.             this.stream = (ImageInputStream)input;
  116.         } else {
  117.             this.stream = null;
  118.         }
  119.  
  120.         // Clear all values based on the previous stream contents
  121.         resetStreamSettings();
  122.     }
  123.  
  124.     public int getNumImages(boolean allowSearch) throws IIOException {
  125.         if (stream == null) {
  126.             throw new IllegalStateException("Input not set!");
  127.         }
  128.         if (seekForwardOnly && allowSearch) {
  129.             throw new IllegalStateException
  130.                 ("seekForwardOnly and allowSearch can't both be true!");
  131.         }
  132.  
  133.         if (numImages > 0) {
  134.             return numImages;
  135.         }
  136.         if (allowSearch) {
  137.             this.numImages = locateImage(Integer.MAX_VALUE) + 1;
  138.         }
  139.         return numImages;
  140.     }
  141.  
  142.     // Throw an IndexOutOfBoundsException if index < minIndex,
  143.     // and bump minIndex if required.
  144.     private void checkIndex(int imageIndex) {
  145.         if (imageIndex < minIndex) {
  146.             throw new IndexOutOfBoundsException("imageIndex < minIndex!");
  147.         }
  148.         if (seekForwardOnly) {
  149.             minIndex = imageIndex;
  150.         }
  151.     }
  152.  
  153.     public int getWidth(int imageIndex) throws IIOException {
  154.         checkIndex(imageIndex);
  155.  
  156.         int index = locateImage(imageIndex);
  157.         if (index != imageIndex) {
  158.             throw new IndexOutOfBoundsException();
  159.         }
  160.         readMetadata();
  161.         return imageMetadata.imageWidth;
  162.     }
  163.  
  164.     public int getHeight(int imageIndex) throws IIOException {
  165.         checkIndex(imageIndex);
  166.  
  167.         int index = locateImage(imageIndex);
  168.         if (index != imageIndex) {
  169.             throw new IndexOutOfBoundsException();
  170.         }
  171.         readMetadata();
  172.         return imageMetadata.imageHeight;
  173.     }
  174.  
  175.     public Iterator getImageTypes(int imageIndex) throws IIOException {
  176.         checkIndex(imageIndex);
  177.  
  178.         int index = locateImage(imageIndex);
  179.         if (index != imageIndex) {
  180.             throw new IndexOutOfBoundsException();
  181.         }
  182.         readMetadata();
  183.  
  184.         List l = new ArrayList(1);
  185.  
  186.         byte[] colorTable;
  187.         if (imageMetadata.localColorTable != null) {
  188.             colorTable = imageMetadata.localColorTable;
  189.         } else {
  190.             colorTable = streamMetadata.globalColorTable;
  191.         }
  192.  
  193.         // Normalize color table length to 2^1, 2^2, 2^4, or 2^8
  194.         int length = colorTable.length/3;
  195.         int bits;
  196.         if (length == 2) {
  197.             bits = 1;
  198.         } else if (length == 4) {
  199.             bits = 2;
  200.         } else if (length == 8 || length == 16) {
  201.             // Bump from 3 to 4 bits
  202.             bits = 4;
  203.         } else {
  204.             // Bump to 8 bits
  205.             bits = 8;
  206.         }
  207.         int lutLength = 1 << bits;
  208.         byte[] r = new byte[lutLength];
  209.         byte[] g = new byte[lutLength];
  210.         byte[] b = new byte[lutLength];
  211.  
  212.         // Entries from length + 1 to lutLength - 1 will be 0
  213.         int rgbIndex = 0;
  214.         for (int i = 0; i < length; i++) {
  215.             r[i] = colorTable[rgbIndex++];
  216.             g[i] = colorTable[rgbIndex++];
  217.             b[i] = colorTable[rgbIndex++];
  218.         }
  219.  
  220.         byte[] a = null;
  221.         if (imageMetadata.transparentColorFlag) {
  222.             a = new byte[lutLength];
  223.             Arrays.fill(a, (byte)255);
  224.  
  225.             // Some files erroneously have a transparent color index
  226.             // of 255 even though there are fewer than 256 colors.
  227.             int idx = Math.min(imageMetadata.transparentColorIndex,
  228.                                lutLength - 1);
  229.             a[idx] = (byte)0;
  230.         }
  231.  
  232.         int[] bitsPerSample = new int[1];
  233.         bitsPerSample[0] = bits;
  234.         l.add(ImageTypeSpecifier.createIndexed(r, g, b, a, bits,
  235.                                                DataBuffer.TYPE_BYTE));
  236.         return l.iterator();
  237.     }
  238.  
  239.     public ImageReadParam getDefaultReadParam() {
  240.         return new ImageReadParam();
  241.     }
  242.  
  243.     public IIOMetadata getStreamMetadata() throws IIOException {
  244.         readHeader();
  245.         return streamMetadata;
  246.     }
  247.  
  248.     public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
  249.         checkIndex(imageIndex);
  250.  
  251.         int index = locateImage(imageIndex);
  252.         if (index != imageIndex) {
  253.             throw new IndexOutOfBoundsException("Bad image index!");
  254.         }
  255.         readMetadata();
  256.         return imageMetadata;
  257.     }
  258.  
  259.     // BEGIN LZW STUFF
  260.  
  261.     private void initNext32Bits() {
  262.         next32Bits = block[0] & 0xff;
  263.         next32Bits |= (block[1] & 0xff) << 8;
  264.         next32Bits |= (block[2] & 0xff) << 16;
  265.         next32Bits |= block[3] << 24;
  266.         nextByte = 4;
  267.     }
  268.  
  269.     // Load a block (1-255 bytes) at a time, and maintain
  270.     // a 32-bit lookahead buffer that is filled from the left
  271.     // and extracted from the right.
  272.     //
  273.     // When the last block is found, we continue to
  274.     //
  275.     private int getCode(int codeSize, int codeMask) throws IOException {
  276.         if (bitPos + codeSize > 32) {
  277.             return eofCode; // No more data available
  278.         }
  279.  
  280.         int code = (next32Bits >> bitPos) & codeMask;
  281.         bitPos += codeSize;
  282.  
  283.         // Shift in a byte of new data at a time
  284.         while (bitPos >= 8 && !lastBlockFound) {
  285.             next32Bits >>>= 8;
  286.             bitPos -= 8;
  287.  
  288.             // Check if current block is out of bytes
  289.             if (nextByte >= blockLength) {
  290.                 // Get next block size
  291.                 blockLength = stream.readUnsignedByte();
  292.                 if (blockLength == 0) {
  293.                     lastBlockFound = true;
  294.                     return code;
  295.                 } else {
  296.                     int left = blockLength;
  297.                     int off = 0;
  298.                     while (left > 0) {
  299.                         int nbytes = stream.read(block, off, left);
  300.                         off += nbytes;
  301.                         left -= nbytes;
  302.                     }
  303.                     nextByte = 0;
  304.                 }
  305.             }
  306.  
  307.             next32Bits |= block[nextByte++] << 24;
  308.         }
  309.  
  310.         return code;
  311.     }
  312.  
  313.     public void initializeStringTable(int[] prefix,
  314.                                       byte[] suffix,
  315.                                       byte[] initial,
  316.                                       int[] length) {
  317.         int numEntries = 1 << initCodeSize;
  318.         for (int i = 0; i < numEntries; i++) {
  319.             prefix[i] = -1;
  320.             suffix[i] = (byte)i;
  321.             initial[i] = (byte)i;
  322.             length[i] = 1;
  323.         }
  324.  
  325.         // Fill in the entire table for robustness against
  326.         // out-of-sequence codes.
  327.         for (int i = numEntries; i < 4096; i++) {
  328.             prefix[i] = -1;
  329.             length[i] = 1;
  330.         }
  331.  
  332.         // tableIndex = numEntries + 2;
  333.         // codeSize = initCodeSize + 1;
  334.         // codeMask = (1 << codeSize) - 1;
  335.     }
  336.  
  337.     Rectangle sourceRegion;
  338.     int sourceXSubsampling;
  339.     int sourceYSubsampling;
  340.     int sourceMinProgressivePass;
  341.     int sourceMaxProgressivePass;
  342.  
  343.     Point destinationOffset;
  344.     Rectangle destinationRegion;
  345.  
  346.     // Used only if IIOReadUpdateListeners are present
  347.     int updateMinY;
  348.     int updateYStep;
  349.  
  350.     boolean decodeThisRow = true;
  351.     int destY = 0;
  352.  
  353.     byte[] rowBuf;
  354.  
  355.     private void outputRow() {
  356.         // Clip against ImageReadParam
  357.         int width = Math.min(sourceRegion.width,
  358.                              destinationRegion.width*sourceXSubsampling);
  359.         int destX = destinationRegion.x;
  360.  
  361.         if (sourceXSubsampling == 1) {
  362.             theTile.setDataElements(destX, destY, width, 1, rowBuf);
  363.         } else {
  364.             for (int x = 0; x < width; x += sourceXSubsampling, destX++) {
  365.                 theTile.setSample(destX, destY, 0, rowBuf[x] & 0xff);
  366.             }
  367.         }
  368.  
  369.         // Update IIOReadUpdateListeners, if any
  370.         if (updateListeners != null) {
  371.             int[] bands = { 0 };
  372.             // updateYStep will have been initialized if
  373.             // updateListeners is non-null
  374.             processImageUpdate(theImage,
  375.                                destX, destY,
  376.                                width, 1, 1, updateYStep,
  377.                                bands);
  378.         }
  379.     }
  380.  
  381.     private void computeDecodeThisRow() {
  382.         this.decodeThisRow =
  383.             (destY < destinationRegion.y + destinationRegion.height) &&
  384.             (streamY >= sourceRegion.y) &&
  385.             (streamY < sourceRegion.y + sourceRegion.height) &&
  386.             (((streamY - sourceRegion.y) % sourceYSubsampling) == 0);
  387.     }
  388.  
  389.     private void outputPixels(byte[] string, int len) {
  390.         if (interlacePass < sourceMinProgressivePass ||
  391.             interlacePass > sourceMaxProgressivePass) {
  392.             return;
  393.         }
  394.  
  395.         for (int i = 0; i < len; i++) {
  396.             if (streamX >= sourceRegion.x) {
  397.                 rowBuf[streamX - sourceRegion.x] = string[i];
  398.             }
  399.  
  400.             // Process end-of-row
  401.             ++streamX;
  402.             if (streamX == width) {
  403.                 // Update IIOReadProgressListeners
  404.                 ++rowsDone;
  405.                 processImageProgress(100.0F*rowsDone/height);
  406.  
  407.                 if (decodeThisRow) {
  408.                     outputRow();
  409.                 }
  410.  
  411.                 streamX = 0;
  412.                 if (imageMetadata.interlaceFlag) {
  413.                     streamY += interlaceIncrement[interlacePass];
  414.                     if (streamY >= height) {
  415.                         // Inform IIOReadUpdateListeners of end of pass
  416.                         if (updateListeners != null) {
  417.                             processPassComplete(theImage);
  418.                         }
  419.  
  420.                         ++interlacePass;
  421.                         if (interlacePass > sourceMaxProgressivePass) {
  422.                             return;
  423.                         }
  424.                         streamY = interlaceOffset[interlacePass];
  425.                         startPass(interlacePass);
  426.                     }
  427.                 } else {
  428.                     ++streamY;
  429.                 }
  430.  
  431.                 // Determine whether pixels from this row will
  432.                 // be written to the destination
  433.                 this.destY = destinationRegion.y +
  434.                     (streamY - sourceRegion.y)/sourceYSubsampling;
  435.                 computeDecodeThisRow();
  436.             }
  437.         }
  438.     }
  439.  
  440.     // END LZW STUFF
  441.  
  442.     private void readHeader() throws IIOException {
  443.         if (gotHeader) {
  444.             return;
  445.         }
  446.         if (stream == null) {
  447.             throw new IllegalStateException("Input not set!");
  448.         }
  449.  
  450.         // Create an object to store the stream metadata
  451.         this.streamMetadata = new GIFStreamMetadata();
  452.  
  453.         try {
  454.             stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
  455.  
  456.             byte[] signature = new byte[6];
  457.             stream.readFully(signature);
  458.  
  459.             StringBuffer version = new StringBuffer(3);
  460.             version.append((char)signature[3]);
  461.             version.append((char)signature[4]);
  462.             version.append((char)signature[5]);
  463.             streamMetadata.version = version.toString();
  464.  
  465.             streamMetadata.logicalScreenWidth = stream.readUnsignedShort();
  466.             streamMetadata.logicalScreenHeight = stream.readUnsignedShort();
  467.  
  468.             int packedFields = stream.readUnsignedByte();
  469.             boolean globalColorTableFlag = (packedFields & 0x80) != 0;
  470.             streamMetadata.colorResolution = ((packedFields >> 4) & 0x7) + 1;
  471.             streamMetadata.sortFlag = (packedFields & 0x8) != 0;
  472.             int numGCTEntries = 1 << ((packedFields & 0x7) + 1);
  473.  
  474.             streamMetadata.backgroundColorIndex = stream.readUnsignedByte();
  475.             streamMetadata.pixelAspectRatio = stream.readUnsignedByte();
  476.  
  477.             if (globalColorTableFlag) {
  478.                 streamMetadata.globalColorTable = new byte[3*numGCTEntries];
  479.                 stream.readFully(streamMetadata.globalColorTable);
  480.             } else {
  481.                 streamMetadata.globalColorTable = null;
  482.             }
  483.  
  484.             // Found position of metadata for image 0
  485.             imageStartPosition.add(Long.valueOf(stream.getStreamPosition()));
  486.         } catch (IOException e) {
  487.             throw new IIOException("I/O error reading header!", e);
  488.         }
  489.  
  490.         gotHeader = true;
  491.     }
  492.  
  493.     private boolean skipImage() throws IIOException {
  494.         // Stream must be at the beginning of an image descriptor
  495.         // upon exit
  496.  
  497.         try {
  498.             while (true) {
  499.                 int blockType = stream.readUnsignedByte();
  500.  
  501.                 if (blockType == 0x2c) {
  502.                     stream.skipBytes(8);
  503.  
  504.                     int packedFields = stream.readUnsignedByte();
  505.                     if ((packedFields & 0x80) != 0) {
  506.                         // Skip color table if any
  507.                         int bits = (packedFields & 0x7) + 1;
  508.                         stream.skipBytes(3*(1 << bits));
  509.                     }
  510.  
  511.                     stream.skipBytes(1);
  512.  
  513.                     int length = 0;
  514.                     do {
  515.                         length = stream.readUnsignedByte();
  516.                         stream.skipBytes(length);
  517.                     } while (length > 0);
  518.  
  519.                     return true;
  520.                 } else if (blockType == 0x3b) {
  521.                     return false;
  522.                 } else if (blockType == 0x21) {
  523.                     int label = stream.readUnsignedByte();
  524.  
  525.                     int length = 0;
  526.                     do {
  527.                         length = stream.readUnsignedByte();
  528.                         stream.skipBytes(length);
  529.                     } while (length > 0);
  530.                 } else if (blockType == 0x0) {
  531.                     // EOF
  532.                     return false;
  533.                 } else {
  534.                     int length = 0;
  535.                     do {
  536.                         length = stream.readUnsignedByte();
  537.                         stream.skipBytes(length);
  538.                     } while (length > 0);
  539.                 }
  540.             }
  541.         } catch (EOFException e) {
  542.             return false;
  543.         } catch (IOException e) {
  544.             throw new IIOException("I/O error locating image!", e);
  545.         }
  546.     }
  547.  
  548.     private int locateImage(int imageIndex) throws IIOException {
  549.         readHeader();
  550.  
  551.         try {
  552.             // Find closest known index
  553.             int index = Math.min(imageIndex, imageStartPosition.size() - 1);
  554.  
  555.             // Seek to that position
  556.             Long l = (Long)imageStartPosition.get(index);
  557.             stream.seek(l.longValue());
  558.  
  559.             // Skip images until at desired index or last image found
  560.             while (index < imageIndex) {
  561.                 if (!skipImage()) {
  562.                     --index;
  563.                     return index;
  564.                 }
  565.  
  566.                 Long l1 = new Long(stream.getStreamPosition());
  567.                 imageStartPosition.add(l1);
  568.                 ++index;
  569.             }
  570.         } catch (IOException e) {
  571.             throw new IIOException("Couldn't seek!", e);
  572.         }
  573.  
  574.         if (currIndex != imageIndex) {
  575.             imageMetadata = null;
  576.         }
  577.         currIndex = imageIndex;
  578.         return imageIndex;
  579.     }
  580.  
  581.     // Read blocks of 1-255 bytes, stop at a 0-length block
  582.     private byte[] concatenateBlocks() throws IOException {
  583.         byte[] data = new byte[0];
  584.         while (true) {
  585.             int length = stream.readUnsignedByte();
  586.             if (length == 0) {
  587.                 break;
  588.             }
  589.             byte[] newData = new byte[data.length + length];
  590.             System.arraycopy(data, 0, newData, 0, data.length);
  591.             stream.readFully(newData, data.length, length);
  592.             data = newData;
  593.         }
  594.  
  595.         return data;
  596.     }
  597.  
  598.     // Stream must be positioned at start of metadata for 'currIndex'
  599.     private void readMetadata() throws IIOException {
  600.         if (stream == null) {
  601.             throw new IllegalStateException("Input not set!");
  602.         }
  603.  
  604.         try {
  605.             // Create an object to store the image metadata
  606.             this.imageMetadata = new GIFImageMetadata();
  607.  
  608.             long startPosition = stream.getStreamPosition();
  609.             while (true) {
  610.                 int blockType = stream.readUnsignedByte();
  611.                 if (blockType == 0x2c) { // Image Descriptor
  612.                     imageMetadata.imageLeftPosition =
  613.                         stream.readUnsignedShort();
  614.                     imageMetadata.imageTopPosition =
  615.                         stream.readUnsignedShort();
  616.                     imageMetadata.imageWidth = stream.readUnsignedShort();
  617.                     imageMetadata.imageHeight = stream.readUnsignedShort();
  618.  
  619.                     int idPackedFields = stream.readUnsignedByte();
  620.                     boolean localColorTableFlag =
  621.                         (idPackedFields & 0x80) != 0;
  622.                     imageMetadata.interlaceFlag = (idPackedFields & 0x40) != 0;
  623.                     imageMetadata.sortFlag = (idPackedFields & 0x20) != 0;
  624.                     int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1);
  625.  
  626.                     if (localColorTableFlag) {
  627.                         // Read color table if any
  628.                         imageMetadata.localColorTable =
  629.                             new byte[3*numLCTEntries];
  630.                         stream.readFully(imageMetadata.localColorTable);
  631.                     } else {
  632.                         imageMetadata.localColorTable = null;
  633.                     }
  634.  
  635.                     // Record length of this metadata block
  636.                     this.imageMetadataLength =
  637.                         (int)(stream.getStreamPosition() - startPosition);
  638.  
  639.                     // Now positioned at start of LZW-compressed pixels
  640.                     return;
  641.                 } else if (blockType == 0x21) { // Extension block
  642.                     int label = stream.readUnsignedByte();
  643.  
  644.                     if (label == 0xf9) { // Graphics Control Extension
  645.                         int gceLength = stream.readUnsignedByte(); // 4
  646.                         int gcePackedFields = stream.readUnsignedByte();
  647.                         imageMetadata.disposalMethod =
  648.                             (gcePackedFields >> 2) & 0x3;
  649.                         imageMetadata.userInputFlag =
  650.                             (gcePackedFields & 0x2) != 0;
  651.                         imageMetadata.transparentColorFlag =
  652.                             (gcePackedFields & 0x1) != 0;
  653.  
  654.                         imageMetadata.delayTime = stream.readUnsignedShort();
  655.                         imageMetadata.transparentColorIndex
  656.                             = stream.readUnsignedByte();
  657.  
  658.                         int terminator = stream.readUnsignedByte();
  659.                     } else if (label == 0x1) { // Plain text extension
  660.                         int length = stream.readUnsignedByte();
  661.                         imageMetadata.hasPlainTextExtension = true;
  662.                         imageMetadata.textGridLeft =
  663.                             stream.readUnsignedShort();
  664.                         imageMetadata.textGridTop =
  665.                             stream.readUnsignedShort();
  666.                         imageMetadata.textGridWidth =
  667.                             stream.readUnsignedShort();
  668.                         imageMetadata.textGridHeight =
  669.                             stream.readUnsignedShort();
  670.                         imageMetadata.characterCellWidth =
  671.                             stream.readUnsignedByte();
  672.                         imageMetadata.characterCellHeight =
  673.                             stream.readUnsignedByte();
  674.                         imageMetadata.textForegroundColor =
  675.                             stream.readUnsignedByte();
  676.                         imageMetadata.textBackgroundColor =
  677.                             stream.readUnsignedByte();
  678.                         imageMetadata.text = concatenateBlocks();
  679.                     } else if (label == 0xfe) { // Comment extension
  680.                         byte[] comment = concatenateBlocks();
  681.                         if (imageMetadata.comments == null) {
  682.                             imageMetadata.comments = new ArrayList();
  683.                         }
  684.                         imageMetadata.comments.add(comment);
  685.                     } else if (label == 0xff) { // Application extension
  686.                         int blockSize = stream.readUnsignedByte();
  687.                         byte[] applicationID = new byte[8];
  688.                         byte[] authCode = new byte[3];
  689.  
  690.                         // read available data
  691.                         byte[] blockData = new byte[blockSize];
  692.                         stream.readFully(blockData);
  693.  
  694.                         int offset = copyData(blockData, 0, applicationID);
  695.                         offset = copyData(blockData, offset, authCode);
  696.  
  697.                         byte[] applicationData = concatenateBlocks();
  698.  
  699.                         if (offset < blockSize) {
  700.                             int len = blockSize - offset;
  701.                             byte[] data =
  702.                                 new byte[len + applicationData.length];
  703.  
  704.                             System.arraycopy(blockData, offset, data, 0, len);
  705.                             System.arraycopy(applicationData, 0, data, len,
  706.                                              applicationData.length);
  707.  
  708.                             applicationData = data;
  709.                         }
  710.  
  711.                         // Init lists if necessary
  712.                         if (imageMetadata.applicationIDs == null) {
  713.                             imageMetadata.applicationIDs = new ArrayList();
  714.                             imageMetadata.authenticationCodes =
  715.                                 new ArrayList();
  716.                             imageMetadata.applicationData = new ArrayList();
  717.                         }
  718.                         imageMetadata.applicationIDs.add(applicationID);
  719.                         imageMetadata.authenticationCodes.add(authCode);
  720.                         imageMetadata.applicationData.add(applicationData);
  721.                     } else {
  722.                         // Skip over unknown extension blocks
  723.                         int length = 0;
  724.                         do {
  725.                             length = stream.readUnsignedByte();
  726.                             stream.skipBytes(length);
  727.                         } while (length > 0);
  728.                     }
  729.                 } else if (blockType == 0x3b) { // Trailer
  730.                     throw new IndexOutOfBoundsException
  731.                         ("Attempt to read past end of image sequence!");
  732.                 } else {
  733.                     throw new IIOException("Unexpected block type " +
  734.                                            blockType + "!");
  735.                 }
  736.             }
  737.         } catch (IIOException iioe) {
  738.             throw iioe;
  739.         } catch (IOException ioe) {
  740.             throw new IIOException("I/O error reading image metadata!", ioe);
  741.         }
  742.     }
  743.  
  744.     private int copyData(byte[] src, int offset, byte[] dst) {
  745.         int len = dst.length;
  746.         int rest = src.length - offset;
  747.         if (len > rest) {
  748.             len = rest;
  749.         }
  750.         System.arraycopy(src, offset, dst, 0, len);
  751.         return offset + len;
  752.     }
  753.  
  754.     private void startPass(int pass) {
  755.         if (updateListeners == null) {
  756.             return;
  757.         }
  758.  
  759.         int y = 0;
  760.         int yStep = 1;
  761.         if (imageMetadata.interlaceFlag) {
  762.             y = interlaceOffset[interlacePass];
  763.             yStep = interlaceIncrement[interlacePass];
  764.         }
  765.  
  766.         int[] vals = ReaderUtil.
  767.             computeUpdatedPixels(sourceRegion,
  768.                                  destinationOffset,
  769.                                  destinationRegion.x,
  770.                                  destinationRegion.y,
  771.                                  destinationRegion.x +
  772.                                  destinationRegion.width - 1,
  773.                                  destinationRegion.y +
  774.                                  destinationRegion.height - 1,
  775.                                  sourceXSubsampling,
  776.                                  sourceYSubsampling,
  777.                                  0,
  778.                                  y,
  779.                                  destinationRegion.width,
  780.                                  (destinationRegion.height + yStep - 1)/yStep,
  781.                                  1,
  782.                                  yStep);
  783.  
  784.         // Initialized updateMinY and updateYStep
  785.         this.updateMinY = vals[1];
  786.         this.updateYStep = vals[5];
  787.  
  788.         // Inform IIOReadUpdateListeners of new pass
  789.         int[] bands = { 0 };
  790.  
  791.         processPassStarted(theImage,
  792.                            interlacePass,
  793.                            sourceMinProgressivePass,
  794.                            sourceMaxProgressivePass,
  795.                            0,
  796.                            updateMinY,
  797.                            1,
  798.                            updateYStep,
  799.                            bands);
  800.     }
  801.  
  802.     public BufferedImage read(int imageIndex, ImageReadParam param)
  803.         throws IIOException {
  804.         if (stream == null) {
  805.             throw new IllegalStateException("Input not set!");
  806.         }
  807.         checkIndex(imageIndex);
  808.  
  809.         int index = locateImage(imageIndex);
  810.         if (index != imageIndex) {
  811.             throw new IndexOutOfBoundsException("imageIndex out of bounds!");
  812.         }
  813.  
  814.         clearAbortRequest();
  815.         readMetadata();
  816.  
  817.         // A null ImageReadParam means we use the default
  818.         if (param == null) {
  819.             param = getDefaultReadParam();
  820.         }
  821.  
  822.         // Initialize the destination image
  823.         Iterator imageTypes = getImageTypes(imageIndex);
  824.         this.theImage = getDestination(param,
  825.                                        imageTypes,
  826.                                        imageMetadata.imageWidth,
  827.                                        imageMetadata.imageHeight);
  828.         this.theTile = theImage.getWritableTile(0, 0);
  829.         this.width = imageMetadata.imageWidth;
  830.         this.height = imageMetadata.imageHeight;
  831.         this.streamX = 0;
  832.         this.streamY = 0;
  833.         this.rowsDone = 0;
  834.         this.interlacePass = 0;
  835.  
  836.         // Get source region, taking subsampling offsets into account,
  837.         // and clipping against the true source bounds
  838.  
  839.         this.sourceRegion = new Rectangle(0, 0, 0, 0);
  840.         this.destinationRegion = new Rectangle(0, 0, 0, 0);
  841.         computeRegions(param, width, height, theImage,
  842.                        sourceRegion, destinationRegion);
  843.         this.destinationOffset = new Point(destinationRegion.x,
  844.                                            destinationRegion.y);
  845.  
  846.         this.sourceXSubsampling = param.getSourceXSubsampling();
  847.         this.sourceYSubsampling = param.getSourceYSubsampling();
  848.         this.sourceMinProgressivePass =
  849.             Math.max(param.getSourceMinProgressivePass(), 0);
  850.         this.sourceMaxProgressivePass =
  851.             Math.min(param.getSourceMaxProgressivePass(), 3);
  852.  
  853.         this.destY = destinationRegion.y +
  854.             (streamY - sourceRegion.y)/sourceYSubsampling;
  855.         computeDecodeThisRow();
  856.  
  857.         // Inform IIOReadProgressListeners of start of image
  858.         processImageStarted(imageIndex);
  859.         startPass(0);
  860.  
  861.         this.rowBuf = new byte[width];
  862.  
  863.         try {
  864.             // Read and decode the image data, fill in theImage
  865.             this.initCodeSize = stream.readUnsignedByte();
  866.  
  867.             // Read first data block
  868.             this.blockLength = stream.readUnsignedByte();
  869.             int left = blockLength;
  870.             int off = 0;
  871.             while (left > 0) {
  872.                 int nbytes = stream.read(block, off, left);
  873.                 left -= nbytes;
  874.                 off += nbytes;
  875.             }
  876.  
  877.             this.bitPos = 0;
  878.             this.nextByte = 0;
  879.             this.lastBlockFound = false;
  880.             this.interlacePass = 0;
  881.  
  882.             // Init 32-bit buffer
  883.             initNext32Bits();
  884.  
  885.             this.clearCode = 1 << initCodeSize;
  886.             this.eofCode = clearCode + 1;
  887.  
  888.             int code, oldCode = 0;
  889.  
  890.             int[] prefix = new int[4096];
  891.             byte[] suffix = new byte[4096];
  892.             byte[] initial = new byte[4096];
  893.             int[] length = new int[4096];
  894.             byte[] string = new byte[4096];
  895.  
  896.             initializeStringTable(prefix, suffix, initial, length);
  897.             int tableIndex = (1 << initCodeSize) + 2;
  898.             int codeSize = initCodeSize + 1;
  899.             int codeMask = (1 << codeSize) - 1;
  900.  
  901.             while (!abortRequested()) {
  902.                 code = getCode(codeSize, codeMask);
  903.  
  904.                 if (code == clearCode) {
  905.                     initializeStringTable(prefix, suffix, initial, length);
  906.                     tableIndex = (1 << initCodeSize) + 2;
  907.                     codeSize = initCodeSize + 1;
  908.                     codeMask = (1 << codeSize) - 1;
  909.  
  910.                     code = getCode(codeSize, codeMask);
  911.                     if (code == eofCode) {
  912.                         // Inform IIOReadProgressListeners of end of image
  913.                         processImageComplete();
  914.                         return theImage;
  915.                     }
  916.                 } else if (code == eofCode) {
  917.                     // Inform IIOReadProgressListeners of end of image
  918.                     processImageComplete();
  919.                     return theImage;
  920.                 } else {
  921.                     int newSuffixIndex;
  922.                     if (code < tableIndex) {
  923.                         newSuffixIndex = code;
  924.                     } else { // code == tableIndex
  925.                         newSuffixIndex = oldCode;
  926.                         if (code != tableIndex) {
  927.                             // warning - code out of sequence
  928.                             // possibly data corruption
  929.                             processWarningOccurred("Out-of-sequence code!");
  930.                         }
  931.                     }
  932.  
  933.                     try
  934.                     {
  935.                         int ti = tableIndex;
  936.                    
  937.                         int oc = oldCode;
  938.    
  939.                         prefix[ti] = oc;
  940.                         suffix[ti] = initial[newSuffixIndex];
  941.                         initial[ti] = initial[oc];
  942.                         length[ti] = length[oc] + 1;
  943.    
  944.                         ++tableIndex;
  945.                         if ((tableIndex == (1 << codeSize)) &&
  946.                             (tableIndex < 4096)) {
  947.                             ++codeSize;
  948.                             codeMask = (1 << codeSize) - 1;
  949.                         }
  950.                     } catch (ArrayIndexOutOfBoundsException e)
  951.                     {
  952.                         //Die.
  953.                         //Pretend that the clearcode was found.
  954.                         initializeStringTable(prefix, suffix, initial, length);
  955.                         tableIndex = (1 << initCodeSize) + 2;
  956.                         codeSize = initCodeSize + 1;
  957.                         codeMask = (1 << codeSize) - 1;
  958.  
  959.                         code = getCode(codeSize, codeMask);
  960.                         if (code == eofCode) {
  961.                             // Inform IIOReadProgressListeners of end of image
  962.                             processImageComplete();
  963.                             return theImage;
  964.                         }
  965.                     }
  966.                 }
  967.  
  968.                 // Reverse code
  969.                 int c = code;
  970.                 int len = length[c];
  971.                 for (int i = len - 1; i >= 0; i--) {
  972.                     string[i] = suffix[c];
  973.                     c = prefix[c];
  974.                 }
  975.  
  976.                 outputPixels(string, len);
  977.                 oldCode = code;
  978.             }
  979.  
  980.             processReadAborted();
  981.             return theImage;
  982.         } catch (IOException e) {
  983.             e.printStackTrace();
  984.             throw new IIOException("I/O error reading image!", e);
  985.         }
  986.     }
  987.  
  988.     /**
  989.      * Remove all settings including global settings such as
  990.      * <code>Locale</code>s and listeners, as well as stream settings.
  991.      */
  992.     public void reset() {
  993.         super.reset();
  994.         resetStreamSettings();
  995.     }
  996.  
  997.     /**
  998.      * Remove local settings based on parsing of a stream.
  999.      */
  1000.     private void resetStreamSettings() {
  1001.         gotHeader = false;
  1002.         streamMetadata = null;
  1003.         currIndex = -1;
  1004.         imageMetadata = null;
  1005.         imageStartPosition = new ArrayList();
  1006.         numImages = -1;
  1007.  
  1008.         // No need to reinitialize 'block'
  1009.         blockLength = 0;
  1010.         bitPos = 0;
  1011.         nextByte = 0;
  1012.  
  1013.         next32Bits = 0;
  1014.         lastBlockFound = false;
  1015.  
  1016.         theImage = null;
  1017.         theTile = null;
  1018.         width = -1;
  1019.         height = -1;
  1020.         streamX = -1;
  1021.         streamY = -1;
  1022.         rowsDone = 0;
  1023.         interlacePass = 0;
  1024.     }
  1025. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement