FileDocCategorySizeDatePackage
GIFImageReader.javaAPI DocJava SE 6 API34579Tue Jun 10 00:21:52 BST 2008com.sun.imageio.plugins.gif

GIFImageReader

public class GIFImageReader extends ImageReader
version
0.5

Fields Summary
ImageInputStream
stream
boolean
gotHeader
GIFStreamMetadata
streamMetadata
int
currIndex
GIFImageMetadata
imageMetadata
List
imageStartPosition
int
imageMetadataLength
int
numImages
byte[]
block
int
blockLength
int
bitPos
int
nextByte
int
initCodeSize
int
clearCode
int
eofCode
int
next32Bits
boolean
lastBlockFound
BufferedImage
theImage
WritableRaster
theTile
int
width
int
height
int
streamX
int
streamY
int
rowsDone
int
interlacePass
static final int[]
interlaceIncrement
static final int[]
interlaceOffset
Rectangle
sourceRegion
int
sourceXSubsampling
int
sourceYSubsampling
int
sourceMinProgressivePass
int
sourceMaxProgressivePass
Point
destinationOffset
Rectangle
destinationRegion
int
updateMinY
int
updateYStep
boolean
decodeThisRow
int
destY
byte[]
rowBuf
Constructors Summary
public GIFImageReader(ImageReaderSpi originatingProvider)


       
        super(originatingProvider);
    
Methods Summary
private voidcheckIndex(int imageIndex)

        if (imageIndex < minIndex) {
            throw new IndexOutOfBoundsException("imageIndex < minIndex!");
        }
        if (seekForwardOnly) {
            minIndex = imageIndex;
        }
    
private voidcomputeDecodeThisRow()

        this.decodeThisRow =
            (destY < destinationRegion.y + destinationRegion.height) &&
            (streamY >= sourceRegion.y) &&
            (streamY < sourceRegion.y + sourceRegion.height) &&
            (((streamY - sourceRegion.y) % sourceYSubsampling) == 0);
    
private byte[]concatenateBlocks()

        byte[] data = new byte[0];
        while (true) {
            int length = stream.readUnsignedByte();
            if (length == 0) {
                break;
            }
            byte[] newData = new byte[data.length + length];
            System.arraycopy(data, 0, newData, 0, data.length);
            stream.readFully(newData, data.length, length);
            data = newData;
        }

        return data;
    
private intgetCode(int codeSize, int codeMask)

        if (bitPos + codeSize > 32) {
            return eofCode; // No more data available
        }

        int code = (next32Bits >> bitPos) & codeMask;
        bitPos += codeSize;

        // Shift in a byte of new data at a time
        while (bitPos >= 8 && !lastBlockFound) {
            next32Bits >>>= 8;
            bitPos -= 8;
            
            // Check if current block is out of bytes
            if (nextByte >= blockLength) {
                // Get next block size
                blockLength = stream.readUnsignedByte();
                if (blockLength == 0) {
                    lastBlockFound = true;
                    return code;
                } else {
                    int left = blockLength;
                    int off = 0;
                    while (left > 0) {
                        int nbytes = stream.read(block, off, left);
                        off += nbytes;
                        left -= nbytes;
                    }
                    nextByte = 0;
                }
            }

            next32Bits |= block[nextByte++] << 24;
        }

        return code;
    
public javax.imageio.ImageReadParamgetDefaultReadParam()

        return new ImageReadParam();
    
public intgetHeight(int imageIndex)

        checkIndex(imageIndex);

        int index = locateImage(imageIndex);
        if (index != imageIndex) {
            throw new IndexOutOfBoundsException();
        }
        readMetadata();
        return imageMetadata.imageHeight;
    
public javax.imageio.metadata.IIOMetadatagetImageMetadata(int imageIndex)

        checkIndex(imageIndex);

        int index = locateImage(imageIndex);
        if (index != imageIndex) {
            throw new IndexOutOfBoundsException("Bad image index!");
        }
        readMetadata();
        return imageMetadata;
    
public java.util.IteratorgetImageTypes(int imageIndex)

        checkIndex(imageIndex);

        int index = locateImage(imageIndex);
        if (index != imageIndex) {
            throw new IndexOutOfBoundsException();
        }
        readMetadata();

        List l = new ArrayList(1);

        byte[] colorTable;
        if (imageMetadata.localColorTable != null) {
            colorTable = imageMetadata.localColorTable;
        } else {
            colorTable = streamMetadata.globalColorTable;
        }

        // Normalize color table length to 2^1, 2^2, 2^4, or 2^8
        int length = colorTable.length/3;
        int bits;
        if (length == 2) {
            bits = 1;
        } else if (length == 4) {
            bits = 2;
        } else if (length == 8 || length == 16) {
            // Bump from 3 to 4 bits
            bits = 4;
        } else {
            // Bump to 8 bits
            bits = 8;
        }
        int lutLength = 1 << bits;
        byte[] r = new byte[lutLength];
        byte[] g = new byte[lutLength];
        byte[] b = new byte[lutLength];

        // Entries from length + 1 to lutLength - 1 will be 0
        int rgbIndex = 0;
        for (int i = 0; i < length; i++) {
            r[i] = colorTable[rgbIndex++];
            g[i] = colorTable[rgbIndex++];
            b[i] = colorTable[rgbIndex++];
        }

        byte[] a = null;
        if (imageMetadata.transparentColorFlag) {
            a = new byte[lutLength];
            Arrays.fill(a, (byte)255);
            
            // Some files erroneously have a transparent color index
            // of 255 even though there are fewer than 256 colors.
            int idx = Math.min(imageMetadata.transparentColorIndex,
                               lutLength - 1);
            a[idx] = (byte)0;
        }

        int[] bitsPerSample = new int[1];
        bitsPerSample[0] = bits;
        l.add(ImageTypeSpecifier.createIndexed(r, g, b, a, bits,
                                               DataBuffer.TYPE_BYTE));
        return l.iterator();
    
public intgetNumImages(boolean allowSearch)

        if (stream == null) {
            throw new IllegalStateException("Input not set!");
        }
        if (seekForwardOnly && allowSearch) {
            throw new IllegalStateException
                ("seekForwardOnly and allowSearch can't both be true!");
        }

        if (numImages > 0) {
            return numImages;
        }
        if (allowSearch) {
            this.numImages = locateImage(Integer.MAX_VALUE) + 1;
        }
        return numImages;
    
public javax.imageio.metadata.IIOMetadatagetStreamMetadata()

        readHeader();
        return streamMetadata;
    
public intgetWidth(int imageIndex)

        checkIndex(imageIndex);

        int index = locateImage(imageIndex);
        if (index != imageIndex) {
            throw new IndexOutOfBoundsException();
        }
        readMetadata();
        return imageMetadata.imageWidth;
    
private voidinitNext32Bits()

        next32Bits = block[0] & 0xff;
        next32Bits |= (block[1] & 0xff) << 8;
        next32Bits |= (block[2] & 0xff) << 16;
        next32Bits |= block[3] << 24;
        nextByte = 4;
    
public voidinitializeStringTable(int[] prefix, byte[] suffix, byte[] initial, int[] length)

        int numEntries = 1 << initCodeSize;
  	for (int i = 0; i < numEntries; i++) {
            prefix[i] = -1;
            suffix[i] = (byte)i;
            initial[i] = (byte)i;
            length[i] = 1;
        }
        
        // Fill in the entire table for robustness against
        // out-of-sequence codes.
  	for (int i = numEntries; i < 4096; i++) {
            prefix[i] = -1;
            length[i] = 1;
        }
        
	// tableIndex = numEntries + 2;
        // codeSize = initCodeSize + 1;
        // codeMask = (1 << codeSize) - 1;
    
private intlocateImage(int imageIndex)

        readHeader();

        try {
            // Find closest known index
            int index = Math.min(imageIndex, imageStartPosition.size() - 1);
            
            // Seek to that position
            Long l = (Long)imageStartPosition.get(index);
            stream.seek(l.longValue());

            // Skip images until at desired index or last image found
            while (index < imageIndex) {
                if (!skipImage()) {
                    --index;
                    return index;
                }
                
                Long l1 = new Long(stream.getStreamPosition());                
                imageStartPosition.add(l1);
                ++index;
            }
        } catch (IOException e) {
            throw new IIOException("Couldn't seek!", e);
        }

        if (currIndex != imageIndex) {
            imageMetadata = null;
        }
        currIndex = imageIndex;
        return imageIndex;
    
private voidoutputPixels(byte[] string, int len)

        if (interlacePass < sourceMinProgressivePass ||
            interlacePass > sourceMaxProgressivePass) {
            return;
        }

        for (int i = 0; i < len; i++) {
            if (streamX >= sourceRegion.x) {
                rowBuf[streamX - sourceRegion.x] = string[i];
            }

            // Process end-of-row
            ++streamX;
            if (streamX == width) {
                // Update IIOReadProgressListeners
                ++rowsDone;
                processImageProgress(100.0F*rowsDone/height);

                if (decodeThisRow) {
                    outputRow();
                }
                
                streamX = 0;
                if (imageMetadata.interlaceFlag) {
                    streamY += interlaceIncrement[interlacePass];
                    if (streamY >= height) {
                        // Inform IIOReadUpdateListeners of end of pass
                        if (updateListeners != null) {
                            processPassComplete(theImage);
                        }

                        ++interlacePass;
                        if (interlacePass > sourceMaxProgressivePass) {
                            return;
                        }
                        streamY = interlaceOffset[interlacePass];
                        startPass(interlacePass);
                    }
                } else {
                    ++streamY;
                }
                
                // Determine whether pixels from this row will 
                // be written to the destination
                this.destY = destinationRegion.y +
                    (streamY - sourceRegion.y)/sourceYSubsampling;
                computeDecodeThisRow();
            }
        }
    
private voidoutputRow()


       
        // Clip against ImageReadParam
        int width = Math.min(sourceRegion.width,
                             destinationRegion.width*sourceXSubsampling);
        int destX = destinationRegion.x;

        if (sourceXSubsampling == 1) {
            theTile.setDataElements(destX, destY, width, 1, rowBuf);
        } else {
            for (int x = 0; x < width; x += sourceXSubsampling, destX++) {
                theTile.setSample(destX, destY, 0, rowBuf[x] & 0xff);
            }
        }

        // Update IIOReadUpdateListeners, if any
        if (updateListeners != null) {
            int[] bands = { 0 };
            // updateYStep will have been initialized if
            // updateListeners is non-null
            processImageUpdate(theImage,
                               destX, destY,
                               width, 1, 1, updateYStep,
                               bands);
        }
    
public java.awt.image.BufferedImageread(int imageIndex, javax.imageio.ImageReadParam param)

        if (stream == null) {
            throw new IllegalStateException("Input not set!");
        }
        checkIndex(imageIndex);

        int index = locateImage(imageIndex);
        if (index != imageIndex) {
            throw new IndexOutOfBoundsException("imageIndex out of bounds!");
        }

        clearAbortRequest();
        readMetadata();

        // A null ImageReadParam means we use the default
        if (param == null) {
            param = getDefaultReadParam();
        }

        // Initialize the destination image
        Iterator imageTypes = getImageTypes(imageIndex);
        this.theImage = getDestination(param,
                                       imageTypes,
                                       imageMetadata.imageWidth,
                                       imageMetadata.imageHeight);
        this.theTile = theImage.getWritableTile(0, 0);
        this.width = imageMetadata.imageWidth;
        this.height = imageMetadata.imageHeight;
        this.streamX = 0;
        this.streamY = 0;
        this.rowsDone = 0;
        this.interlacePass = 0;

        // Get source region, taking subsampling offsets into account,
        // and clipping against the true source bounds
        
        this.sourceRegion = new Rectangle(0, 0, 0, 0);
        this.destinationRegion = new Rectangle(0, 0, 0, 0);
        computeRegions(param, width, height, theImage,
                       sourceRegion, destinationRegion);
        this.destinationOffset = new Point(destinationRegion.x,
                                           destinationRegion.y);

        this.sourceXSubsampling = param.getSourceXSubsampling();
        this.sourceYSubsampling = param.getSourceYSubsampling();
        this.sourceMinProgressivePass =
            Math.max(param.getSourceMinProgressivePass(), 0);
        this.sourceMaxProgressivePass =
            Math.min(param.getSourceMaxProgressivePass(), 3);

        this.destY = destinationRegion.y +
            (streamY - sourceRegion.y)/sourceYSubsampling;
        computeDecodeThisRow();

        // Inform IIOReadProgressListeners of start of image
        processImageStarted(imageIndex);
        startPass(0);

        this.rowBuf = new byte[width];

        try {
            // Read and decode the image data, fill in theImage
            this.initCodeSize = stream.readUnsignedByte();

            // Read first data block
            this.blockLength = stream.readUnsignedByte();
            int left = blockLength;
            int off = 0;
            while (left > 0) {
                int nbytes = stream.read(block, off, left);
                left -= nbytes;
                off += nbytes;
            }

            this.bitPos = 0;
            this.nextByte = 0;
            this.lastBlockFound = false;
            this.interlacePass = 0;

            // Init 32-bit buffer
            initNext32Bits();

            this.clearCode = 1 << initCodeSize;
            this.eofCode = clearCode + 1;

            int code, oldCode = 0;

            int[] prefix = new int[4096];
            byte[] suffix = new byte[4096];
            byte[] initial = new byte[4096];
            int[] length = new int[4096];
            byte[] string = new byte[4096];

            initializeStringTable(prefix, suffix, initial, length);
            int tableIndex = (1 << initCodeSize) + 2;
            int codeSize = initCodeSize + 1;
            int codeMask = (1 << codeSize) - 1;

            while (!abortRequested()) {
                code = getCode(codeSize, codeMask);

                if (code == clearCode) {
                    initializeStringTable(prefix, suffix, initial, length);
                    tableIndex = (1 << initCodeSize) + 2;
                    codeSize = initCodeSize + 1;
                    codeMask = (1 << codeSize) - 1;

                    code = getCode(codeSize, codeMask);
                    if (code == eofCode) {
                        // Inform IIOReadProgressListeners of end of image
                        processImageComplete();
                        return theImage;
                    }
                } else if (code == eofCode) {
                    // Inform IIOReadProgressListeners of end of image
                    processImageComplete();
                    return theImage;
                } else {
                    int newSuffixIndex;
                    if (code < tableIndex) {
                        newSuffixIndex = code;
                    } else { // code == tableIndex
                        newSuffixIndex = oldCode;
                        if (code != tableIndex) {
                            // warning - code out of sequence
                            // possibly data corruption
                            processWarningOccurred("Out-of-sequence code!");
                        }
                    }

                    int ti = tableIndex;
                    int oc = oldCode;

                    prefix[ti] = oc;
                    suffix[ti] = initial[newSuffixIndex];
                    initial[ti] = initial[oc];
                    length[ti] = length[oc] + 1;
                    
                    ++tableIndex;
                    if ((tableIndex == (1 << codeSize)) &&
                        (tableIndex < 4096)) {
                        ++codeSize;
                        codeMask = (1 << codeSize) - 1;
                    }
                }

                // Reverse code
                int c = code;
                int len = length[c];
                for (int i = len - 1; i >= 0; i--) {
                    string[i] = suffix[c];
                    c = prefix[c];
                }
                
                outputPixels(string, len);
                oldCode = code;
            }

            processReadAborted();
            return theImage;
        } catch (IOException e) {
            e.printStackTrace();
            throw new IIOException("I/O error reading image!", e);
        }
    
private voidreadHeader()

        if (gotHeader) {
            return;
        }
        if (stream == null) {
            throw new IllegalStateException("Input not set!");
        }

        // Create an object to store the stream metadata
        this.streamMetadata = new GIFStreamMetadata();

        try {
            stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
            
            byte[] signature = new byte[6];
            stream.readFully(signature);

            StringBuffer version = new StringBuffer(3);
            version.append((char)signature[3]);
            version.append((char)signature[4]);
            version.append((char)signature[5]);
            streamMetadata.version = version.toString();

            streamMetadata.logicalScreenWidth = stream.readUnsignedShort();
            streamMetadata.logicalScreenHeight = stream.readUnsignedShort();

            int packedFields = stream.readUnsignedByte();
            boolean globalColorTableFlag = (packedFields & 0x80) != 0;
            streamMetadata.colorResolution = ((packedFields >> 4) & 0x7) + 1;
            streamMetadata.sortFlag = (packedFields & 0x8) != 0;
            int numGCTEntries = 1 << ((packedFields & 0x7) + 1);

            streamMetadata.backgroundColorIndex = stream.readUnsignedByte();
            streamMetadata.pixelAspectRatio = stream.readUnsignedByte();

            if (globalColorTableFlag) {
                streamMetadata.globalColorTable = new byte[3*numGCTEntries];
                stream.readFully(streamMetadata.globalColorTable);
            } else {
                streamMetadata.globalColorTable = null;
            }

            // Found position of metadata for image 0
            imageStartPosition.add(new Long(stream.getStreamPosition()));
        } catch (IOException e) {
            throw new IIOException("I/O error reading header!", e);
        }

        gotHeader = true;
    
private voidreadMetadata()

        if (stream == null) {
            throw new IllegalStateException("Input not set!");
        }

        try {
            // Create an object to store the image metadata
            this.imageMetadata = new GIFImageMetadata();
            
            long startPosition = stream.getStreamPosition();
            while (true) {
                int blockType = stream.readUnsignedByte();
                if (blockType == 0x2c) { // Image Descriptor
                    imageMetadata.imageLeftPosition =
                        stream.readUnsignedShort();
                    imageMetadata.imageTopPosition =
                        stream.readUnsignedShort();
                    imageMetadata.imageWidth = stream.readUnsignedShort();
                    imageMetadata.imageHeight = stream.readUnsignedShort();

                    int idPackedFields = stream.readUnsignedByte();
                    boolean localColorTableFlag =
                        (idPackedFields & 0x80) != 0;
                    imageMetadata.interlaceFlag = (idPackedFields & 0x40) != 0;
                    imageMetadata.sortFlag = (idPackedFields & 0x20) != 0;
                    int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1);

                    if (localColorTableFlag) {
                        // Read color table if any
                        imageMetadata.localColorTable =
                            new byte[3*numLCTEntries];
                        stream.readFully(imageMetadata.localColorTable);
                    } else {
                        imageMetadata.localColorTable = null;
                    }

                    // Record length of this metadata block
                    this.imageMetadataLength =
                        (int)(stream.getStreamPosition() - startPosition);

                    // Now positioned at start of LZW-compressed pixels
                    return;
                } else if (blockType == 0x21) { // Extension block
                    int label = stream.readUnsignedByte();

                    if (label == 0xf9) { // Graphics Control Extension
                        int gceLength = stream.readUnsignedByte(); // 4
                        int gcePackedFields = stream.readUnsignedByte();
                        imageMetadata.disposalMethod =
                            (gcePackedFields >> 2) & 0x3;
                        imageMetadata.userInputFlag = 
                            (gcePackedFields & 0x2) != 0;
                        imageMetadata.transparentColorFlag = 
                            (gcePackedFields & 0x1) != 0;

                        imageMetadata.delayTime = stream.readUnsignedShort();
                        imageMetadata.transparentColorIndex 
                            = stream.readUnsignedByte();
                        
                        int terminator = stream.readUnsignedByte();
                    } else if (label == 0x1) { // Plain text extension 
                        int length = stream.readUnsignedByte();
                        imageMetadata.hasPlainTextExtension = true;
                        imageMetadata.textGridLeft =
                            stream.readUnsignedShort();
                        imageMetadata.textGridTop =
                            stream.readUnsignedShort();
                        imageMetadata.textGridWidth =
                            stream.readUnsignedShort();
                        imageMetadata.textGridHeight =
                            stream.readUnsignedShort();
                        imageMetadata.characterCellWidth =
                            stream.readUnsignedByte();
                        imageMetadata.characterCellHeight =
                            stream.readUnsignedByte();
                        imageMetadata.textForegroundColor =
                            stream.readUnsignedByte();
                        imageMetadata.textBackgroundColor =
                            stream.readUnsignedByte();
                        imageMetadata.text = concatenateBlocks();
                    } else if (label == 0xfe) { // Comment extension
                        byte[] comment = concatenateBlocks();
                        if (imageMetadata.comments == null) {
                            imageMetadata.comments = new ArrayList();
                        }
                        imageMetadata.comments.add(comment);
                    } else if (label == 0xff) { // Application extension
                        int blockSize = stream.readUnsignedByte();
                        byte[] applicationID = new byte[8];
                        stream.readFully(applicationID);
                        byte[] authCode = new byte[3];
                        stream.readFully(authCode);
                        byte[] applicationData = concatenateBlocks();

                        // Init lists if necessary
                        if (imageMetadata.applicationIDs == null) {
                            imageMetadata.applicationIDs = new ArrayList();
                            imageMetadata.authenticationCodes =
                                new ArrayList();
                            imageMetadata.applicationData = new ArrayList();
                        }
                        imageMetadata.applicationIDs.add(applicationID);
                        imageMetadata.authenticationCodes.add(authCode);
                        imageMetadata.applicationData.add(applicationData);
                    } else {
                        // Skip over unknown extension blocks
                        int length = 0;
                        do {
                            length = stream.readUnsignedByte();
                            stream.skipBytes(length);
                        } while (length > 0);
                    }
                } else if (blockType == 0x3b) { // Trailer
                    throw new IndexOutOfBoundsException
                        ("Attempt to read past end of image sequence!");
                } else {
                    throw new IIOException("Unexpected block type " +
                                           blockType + "!");
                }
            }
        } catch (IIOException iioe) {
            throw iioe;
        } catch (IOException ioe) {
            throw new IIOException("I/O error reading image metadata!", ioe);
        }
    
public voidreset()
Remove all settings including global settings such as Locales and listeners, as well as stream settings.

        super.reset();
        resetStreamSettings();
    
private voidresetStreamSettings()
Remove local settings based on parsing of a stream.

        gotHeader = false;
        streamMetadata = null;
        currIndex = -1;
        imageMetadata = null;
        imageStartPosition = new ArrayList();
        numImages = -1;

        // No need to reinitialize 'block'
        blockLength = 0;
        bitPos = 0;
        nextByte = 0;

        next32Bits = 0;
        lastBlockFound = false;

        theImage = null;
        theTile = null;
        width = -1;
        height = -1;
        streamX = -1;
        streamY = -1;
        rowsDone = 0;
        interlacePass = 0;
    
public voidsetInput(java.lang.Object input, boolean seekForwardOnly, boolean ignoreMetadata)

        super.setInput(input, seekForwardOnly, ignoreMetadata);
        if (input != null) {
            if (!(input instanceof ImageInputStream)) {
                throw new IllegalArgumentException
                    ("input not an ImageInputStream!"); 
            }
            this.stream = (ImageInputStream)input;
        } else {
            this.stream = null;
        }
        
        // Clear all values based on the previous stream contents
        resetStreamSettings();
    
private booleanskipImage()

        // Stream must be at the beginning of an image descriptor
        // upon exit 

        try {
            while (true) {
                int blockType = stream.readUnsignedByte();

                if (blockType == 0x2c) {
                    stream.skipBytes(8);

                    int packedFields = stream.readUnsignedByte();
                    if ((packedFields & 0x80) != 0) {
                        // Skip color table if any
                        int bits = (packedFields & 0x7) + 1;
                        stream.skipBytes(3*(1 << bits));
                    }

                    stream.skipBytes(1);
                    
                    int length = 0;
                    do {
                        length = stream.readUnsignedByte();
                        stream.skipBytes(length);
                    } while (length > 0);

                    return true;
                } else if (blockType == 0x3b) {
                    return false;
                } else if (blockType == 0x21) {
                    int label = stream.readUnsignedByte();

                    int length = 0;
                    do {
                        length = stream.readUnsignedByte();
                        stream.skipBytes(length);
                    } while (length > 0);
                } else if (blockType == 0x0) {
                    // EOF
                    return false;
                } else {
                    int length = 0;
                    do {
                        length = stream.readUnsignedByte();
                        stream.skipBytes(length);
                    } while (length > 0);
                }
            }
        } catch (EOFException e) {
            return false;
        } catch (IOException e) {
            throw new IIOException("I/O error locating image!", e);
        }
    
private voidstartPass(int pass)

        if (updateListeners == null) {
            return;
        }

        int y = 0;
        int yStep = 1;
        if (imageMetadata.interlaceFlag) {
            y = interlaceOffset[interlacePass];
            yStep = interlaceIncrement[interlacePass];
        }

        int[] vals = ReaderUtil.
            computeUpdatedPixels(sourceRegion,
                                 destinationOffset,
                                 destinationRegion.x, 
                                 destinationRegion.y,
                                 destinationRegion.x +
                                 destinationRegion.width - 1,
                                 destinationRegion.y +
                                 destinationRegion.height - 1,
                                 sourceXSubsampling,
                                 sourceYSubsampling,
                                 0,
                                 y,
                                 destinationRegion.width,
                                 (destinationRegion.height + yStep - 1)/yStep,
                                 1,
                                 yStep);

        // Initialized updateMinY and updateYStep
        this.updateMinY = vals[1];
        this.updateYStep = vals[5];

        // Inform IIOReadUpdateListeners of new pass
        int[] bands = { 0 };

        processPassStarted(theImage,
                           interlacePass,
                           sourceMinProgressivePass,
                           sourceMaxProgressivePass,
                           0,
                           updateMinY,
                           1,
                           updateYStep,
                           bands);