//******************************************************************************
//
// File:    PJGGrayImage.java
// Package: edu.rit.image
// Unit:    Class edu.rit.image.PJGGrayImage
//
// This Java source file is copyright (C) 2008 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.image;

import edu.rit.swing.Displayable;

import edu.rit.util.Range;

import java.awt.image.BufferedImage;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.Arrays;

/**
 * Class PJGGrayImage provides an 8-bit grayscale image that is read from or
 * written to a file in Parallel Java Graphics (PJG) format. For further
 * information about the PJG format, see class {@linkplain PJGImage}. The image
 * is layered on top of a byte matrix (type byte[][]). The height and
 * width of the image are equal to the number of rows and columns in the
 * underlying matrix.
 * 

* To read and write the pixels of an image, use the getIntPixel(), * getPixel(), setIntPixel(), and setPixel() methods. * These methods represent a pixel value as an integer in the range 0 .. 255 or * as a floating point number in the range 0.0 .. 1.0. The image's * interpretation attribute specifies how to map between the numerical * pixel value and the shade of gray. If the interpretation is * ZERO_IS_WHITE, then a value of 0 or 0.0 represents white and 255 or * 1.0 represents black. If the interpretation is ZERO_IS_BLACK, then a * value of 0 or 0.0 represents black and 255 or 1.0 represents white. The * default interpretation is ZERO_IS_BLACK. To change the * interpretation, call the setInterpretation() method. *

* Changing the contents of the underlying matrix directly will also change the * image. In the underlying matrix, a value of 0 represents black and a value of * 255 represents white. *

* To write a PJGGrayImage object to a PJG image file, call the * prepareToWrite() method, specifying the output stream to write. The * prepareToWrite() method returns an instance of class {@linkplain * PJGImage.Writer}. Call the methods of the PJG image writer object to write * the pixel data, or sections of the pixel data, to the output stream. When * finished, close the PJG image writer. *

* When writing an image to a PJG file, class PJGGrayImage uses Huffman delta * encoded 8-bit grayscale pixel data segments (see class {@linkplain PJGImage}) * to compress the pixel data. This technique will work well if the image has * many runs of consecutive identical or nearly identical pixel values. *

* To read a PJGGrayImage object from a PJG image file, call the * prepareToRead() method, specifying the input stream to read. The * prepareToRead() method returns an instance of class {@linkplain * PJGImage.Reader}. Call the methods of the PJG image reader object to read * the pixel data, or sections of the pixel data, from the input stream. When * finished, close the PJG image reader. *

* To get a BufferedImage object that uses the same underlying pixel data matrix * as the PJGGrayImage object, call the getBufferedImage() method. You * can then do all the following with the BufferedImage: display it on the * screen, draw into it using a graphics context, copy another BufferedImage * into it, read it from or write it to a file using package javax.imageio * (which typically supports PNG, JPG, and GIF formats). The rows and columns of * the underlying matrix need not all be allocated when accessing the * BufferedImage. If you get a pixel from the BufferedImage in an unallocated * row or column, a pixel value of 0 (black) is returned. If you set a pixel in * the BufferedImage in an unallocated row or column, the pixel value is * discarded. *

* Note: Class PJGGrayImage is not multiple thread safe. * * @author Alan Kaminsky * @version 08-Apr-2008 */ public class PJGGrayImage extends PJGImage { // Exported enumerations. /** * Enum Interpretation specifies how to interpret a numerical pixel value as * a shade of gray in a grayscale image. * * @author Alan Kaminsky * @version 08-Nov-2007 */ public static enum Interpretation { /** * A pixel value of 0 or 0.0 is white, 255 or 1.0 is black. */ ZERO_IS_WHITE, /** * A pixel value of 0 or 0.0 is black, 255 or 1.0 is white. */ ZERO_IS_BLACK } /** * A pixel value of 0 or 0.0 is white, 255 or 1.0 is black. */ public static final Interpretation ZERO_IS_WHITE = Interpretation.ZERO_IS_WHITE; /** * A pixel value of 0 or 0.0 is black, 255 or 1.0 is white. */ public static final Interpretation ZERO_IS_BLACK = Interpretation.ZERO_IS_BLACK; // Hidden helper classes. /** * Interface Transformation specifies the interface for an object that * transforms a user-specified pixel value into the value stored in the * underlying matrix. * * @author Alan Kaminsky * @version 08-Nov-2007 */ static interface Transformation { /** * Transform the given integer pixel value to a byte value to be stored * in the underlying matrix. * * @param value Integer pixel value. * * @return Byte value. */ public byte transformInt (int value); /** * Transform the given float pixel value to a byte value to be stored in * the underlying matrix. * * @param value Float pixel value. * * @return Byte value. */ public byte transformFloat (float value); /** * Transform the given byte value from the underlying matrix to an * integer pixel value. * * @param value Byte value. * * @return Integer pixel value. */ public int inverseTransformInt (byte value); /** * Transform the given byte value from the underlying matrix to a float * pixel value. * * @param value Byte value. * * @return Float pixel value. */ public float inverseTransformFloat (byte value); } static final Transformation ZERO_IS_WHITE_TRANSFORMATION = new Transformation() { public byte transformInt (int value) { return (byte) (255 - Math.max (0, Math.min (value, 255))); } public byte transformFloat (float value) { return (byte) (255 - Math.max (0, Math.min ((int) (value*256.0f), 255))); } public int inverseTransformInt (byte value) { return 255 - (value & 0xFF); } public float inverseTransformFloat (byte value) { return (255 - (value & 0xFF)) / 256.0f; } }; static final Transformation ZERO_IS_BLACK_TRANSFORMATION = new Transformation() { public byte transformInt (int value) { return (byte) (Math.max (0, Math.min (value, 255))); } public byte transformFloat (float value) { return (byte) (Math.max (0, Math.min ((int) (value*256.0f), 255))); } public int inverseTransformInt (byte value) { return value & 0xFF; } public float inverseTransformFloat (byte value) { return (value & 0xFF) / 256.0f; } }; // Hidden data members. byte[][] myMatrix; Transformation myTransformation = ZERO_IS_BLACK_TRANSFORMATION; // Exported constructors. /** * Construct a new PJG grayscale image. The image's height and width are * uninitialized. Before accessing the image's pixels, specify the height * and width by calling the setMatrix() method or by reading the * image from an input stream. The image's interpretation is {@link * #ZERO_IS_BLACK}. */ public PJGGrayImage() { super (IMAGE_TYPE_8_BIT_GRAY); } /** * Construct a new PJG grayscale image with the given height, width, and * underlying matrix. The image's interpretation is {@link #ZERO_IS_BLACK}. * * @param theHeight Image height in pixels. * @param theWidth Image width in pixels. * @param theMatrix Underlying matrix. * * @exception NullPointerException * (unchecked exception) Thrown if theMatrix is null. * @exception IllegalArgumentException * (unchecked exception) Thrown if theHeight <= 0. Thrown if * theWidth <= 0. Thrown if theMatrix.length does * not equal theHeight. */ public PJGGrayImage (int theHeight, int theWidth, byte[][] theMatrix) { super (IMAGE_TYPE_8_BIT_GRAY); setMatrix (theHeight, theWidth, theMatrix); } // Exported operations. /** * Obtain this image's underlying matrix. * * @return Underlying matrix, or null if none. */ public byte[][] getMatrix() { return myMatrix; } /** * Set this image's height, width, and underlying matrix. * * @param theHeight Image height in pixels. * @param theWidth Image width in pixels. * @param theMatrix Underlying matrix. * * @exception NullPointerException * (unchecked exception) Thrown if theMatrix is null. * @exception IllegalArgumentException * (unchecked exception) Thrown if theHeight <= 0. Thrown if * theWidth <= 0. Thrown if theMatrix.length does * not equal theHeight. */ public void setMatrix (int theHeight, int theWidth, byte[][] theMatrix) { setHeightAndWidth (theHeight, theWidth); if (theMatrix.length != myHeight) { throw new IllegalArgumentException ("PJGGrayImage.setMatrix(): theMatrix.length (= " + theMatrix.length + ") does not equal image height (= " + myHeight + ")"); } myMatrix = theMatrix; } /** * Get this image's interpretation. * * @return {@link #ZERO_IS_WHITE} or {@link #ZERO_IS_BLACK}. */ public Interpretation getInterpretation() { if (myTransformation == ZERO_IS_WHITE_TRANSFORMATION) { return ZERO_IS_WHITE; } else { return ZERO_IS_BLACK; } } /** * Set this image's interpretation. If this method is not called, this * image's default interpretation is {@link #ZERO_IS_BLACK}. * * @param theInterpretation {@link #ZERO_IS_WHITE} or * {@link #ZERO_IS_BLACK}. */ public void setInterpretation (Interpretation theInterpretation) { switch (theInterpretation) { case ZERO_IS_WHITE: myTransformation = ZERO_IS_WHITE_TRANSFORMATION; break; case ZERO_IS_BLACK: myTransformation = ZERO_IS_BLACK_TRANSFORMATION; break; } } /** * Obtain the pixel at the given row and column in this image. * * @param r Row index. * @param c Column index. * * @return Pixel[r,c] as an integer in the range 0 .. 255. * * @exception ArrayIndexOutOfBoundsException * (unchecked exception) Thrown if r or c is out of * bounds. */ public int getIntPixel (int r, int c) { return myTransformation.inverseTransformInt (myMatrix[r][c]); } /** * Obtain the pixel at the given row and column in this image. * * @param r Row index. * @param c Column index. * * @return Pixel[r,c] as a floating point number in the range 0.0 .. * 1.0. * * @exception ArrayIndexOutOfBoundsException * (unchecked exception) Thrown if r or c is out of * bounds. */ public float getPixel (int r, int c) { return myTransformation.inverseTransformFloat (myMatrix[r][c]); } /** * Set the pixel at the given row and column in this image. If val * is outside the range 0 .. 255, it is pinned to the appropriate boundary. * * @param r Row index. * @param c Column index. * @param val New pixel value as an integer in the range 0 .. 255. * * @exception ArrayIndexOutOfBoundsException * (unchecked exception) Thrown if r or c is out of * bounds. */ public void setIntPixel (int r, int c, int val) { myMatrix[r][c] = myTransformation.transformInt (val); } /** * Set the pixel at the given row and column in this image. If val * is outside the range 0.0 .. 1.0, it is pinned to the appropriate * boundary. * * @param r Row index. * @param c Column index. * @param val New pixel value as a floating point number in the range 0.0 * .. 1.0. * * @exception ArrayIndexOutOfBoundsException * (unchecked exception) Thrown if r or c is out of * bounds. */ public void setPixel (int r, int c, float val) { myMatrix[r][c] = myTransformation.transformFloat (val); } /** * Set all pixels in this image to the given value. If val is * outside the range 0 .. 255, it is pinned to the appropriate boundary. * * @param val New pixel value as an integer in the range 0 .. 255. * * @exception NullPointerException * (unchecked exception) Thrown if the underlying matrix has no storage * allocated. */ public void fill (int val) { byte byteval = myTransformation.transformInt (val); for (int r = 0; r < myHeight; ++ r) { Arrays.fill (myMatrix[r], byteval); } } /** * Set all pixels in this image to the given value. If val is * outside the range 0.0 .. 1.0, it is pinned to the appropriate boundary. * * @param val New pixel value as a floating point number in the range 0.0 * .. 1.0. * * @exception NullPointerException * (unchecked exception) Thrown if the underlying matrix has no storage * allocated. */ public void fill (float val) { byte byteval = myTransformation.transformFloat (val); for (int r = 0; r < myHeight; ++ r) { Arrays.fill (myMatrix[r], byteval); } } /** * Prepare to write this image to the given output stream. Certain header * information is written to the output stream at this time. To write this * image's pixel data, call methods on the returned PJG image writer, then * close the PJG image writer. *

* For improved performance, specify an output stream with buffering, such * as an instance of class java.io.BufferedOutputStream. * * @param theStream Output stream. * * @return PJG image writer object with which to write this image. * * @exception NullPointerException * (unchecked exception) Thrown if theStream is null. * @exception IOException * Thrown if an I/O error occurred. */ public PJGImage.Writer prepareToWrite (OutputStream theStream) throws IOException { return new GrayWriter (theStream); } /** * Prepare to read this image from the given input stream. Certain header * information is read from the input stream at this time. To read this * image's pixel data, call methods on the returned PJG image reader, then * close the PJG image reader. *

* For improved performance, specify an input stream with buffering, such * as an instance of class java.io.BufferedInputStream. * * @param theStream Input stream. * * @return PJG image reader object with which to read this image. * * @exception NullPointerException * (unchecked exception) Thrown if theStream is null. * @exception IOException * Thrown if an I/O error occurred. */ public PJGImage.Reader prepareToRead (InputStream theStream) throws IOException { return new GrayReader (theStream); } /** * Obtain a BufferedImage whose pixel data comes from this image's * underlying matrix. * * @return BufferedImage. */ public BufferedImage getBufferedImage() { return new GrayBufferedImage (myHeight, myWidth, myMatrix); } /** * Obtain a Displayable object with which to display this image in a Swing * UI. * * @return Displayable object. */ public Displayable getDisplayable() { return new GrayDisplayable (myHeight, myWidth, myMatrix); } // Hidden operations. /** * Write a pixel data segment to the given data output stream. * * @param dos Data output stream. * @param row Row index. * @param col Column index. * @param rowlen Number of rows. * @param collen Number of columns. * * @exception IOException * Thrown if an I/O error occurred. */ private void writePixelData (DataOutputStream dos, int row, int col, int rowlen, int collen) throws IOException { // Write segment type, row index, column index, number of rows, number // of columns. dos.writeByte (SEGMENT_PIXEL_DATA_HDE_8_BIT_GRAY); dos.writeInt (row); dos.writeInt (col); dos.writeInt (rowlen); dos.writeInt (collen); // Prepare to write a bit stream. OutputBitStream obs = new OutputBitStream (dos); // Scan rows. int rowub = row + rowlen; int colub = col + collen; for (int r = row; r < rowub; ++ r) { byte[] matrix_r = myMatrix[r]; // Scan columns and write Huffman encoded bit string. int prevPixel = 0; int pixel; int delta; for (int c = col; c < colub; ++ c) { // Get pixel and delta. pixel = matrix_r[c] & 0xFF; delta = pixel - prevPixel; prevPixel = pixel; // Encode bits and write them to output stream. if (delta < -42 || delta > +42) { obs.writeBits (0x0F00 | pixel, 12); } else if (delta < -10) { obs.writeBits (0x0380 | ((delta + 10) & 0x3F), 10); } else if (delta > +10) { obs.writeBits (0x0380 | ((delta - 11) & 0x3F), 10); } else if (delta < -2) { obs.writeBits (0x0060 | ((delta + 2) & 0x0F), 7); } else if (delta > +2) { obs.writeBits (0x0060 | ((delta - 3) & 0x0F), 7); } else if (delta < 0) { obs.writeBits (0x0008 | (delta & 0x03), 4); } else if (delta > 0) { obs.writeBits (0x0008 | ((delta - 1) & 0x03), 4); } else { obs.writeBits (0, 1); } } } // Dump any remaining bits to output stream. obs.flush(); } /** * Read a pixel data segment from the given data input stream. Assumes the * segment type, row index, column index, row count, and column count have * already been read. * * @param dis Data input stream. * @param row Row index. * @param col Column index. * @param rowlen Number of rows. * @param collen Number of columns. * * @exception IOException * Thrown if an I/O error occurred. */ private void readPixelData (DataInputStream dis, int row, int col, int rowlen, int collen) throws IOException { // Allocate storage for matrix if necessary. if (myMatrix == null) myMatrix = new byte [myHeight] []; // Prepare to read a bit stream. InputBitStream ibs = new InputBitStream (dis); // Scan rows. int rowub = row + rowlen; int colub = col + collen; for (int r = row; r < rowub; ++ r) { // Allocate storage for row if necessary. byte[] matrix_r = myMatrix[r]; if (matrix_r == null) { matrix_r = new byte [colub]; myMatrix[r] = matrix_r; } else if (matrix_r.length < colub) { byte[] new_matrix_r = new byte [colub]; System.arraycopy (matrix_r, 0, new_matrix_r, 0, matrix_r.length); matrix_r = new_matrix_r; myMatrix[r] = new_matrix_r; } // Scan columns. int prevPixel = 0; int pixel; int delta; for (int c = col; c < colub; ++ c) { // Check high-order bits of Huffman code. if (ibs.peekBits (1) == 0) { // Delta = 0. ibs.skipBits (1); pixel = prevPixel; } else if (ibs.peekBits (2) == 0x02) { // Two-bit delta. ibs.skipBits (2); delta = ibs.readBits (2); if ((delta & 0x02) != 0) { delta = delta | 0xFFFFFFFC; } else { delta = delta + 1; } pixel = prevPixel + delta; } else if (ibs.peekBits (3) == 0x06) { // Four-bit delta. ibs.skipBits (3); delta = ibs.readBits (4); if ((delta & 0x08) != 0) { delta = (delta | 0xFFFFFFF0) - 2; } else { delta = delta + 3; } pixel = prevPixel + delta; } else if (ibs.peekBits (4) == 0x0E) { // Six-bit delta. ibs.skipBits (4); delta = ibs.readBits (6); if ((delta & 0x20) != 0) { delta = (delta | 0xFFFFFFC0) - 10; } else { delta = delta + 11; } pixel = prevPixel + delta; } else // (ibs.peekBits (4) == 0x0F) { // Eight-bit pixel. ibs.skipBits (4); pixel = ibs.readBits (8); } // Record pixel. matrix_r[c] = (byte) pixel; prevPixel = pixel; } } } // Hidden helper classes. /** * Class PJGGrayImage.GrayWriter provides an object with which to write a * {@linkplain PJGGrayImage} to a file. * * @author Alan Kaminsky * @version 09-Nov-2007 */ private class GrayWriter extends PJGImage.Writer { // Hidden constructors. /** * Construct a new PJG gray image writer. * * @param theStream Output stream. * * @exception NullPointerException * (unchecked exception) Thrown if theStream is null. * @exception IOException * Thrown if an I/O error occurred. */ private GrayWriter (OutputStream theStream) throws IOException { super (theStream); } // Exported operations. /** * Write all rows and columns of the image to the output stream. * * @exception IOException * Thrown if an I/O error occurred. */ public void write() throws IOException { writePixelData (myDos, 0, 0, myHeight, myWidth); } /** * Write the given row slice of the image to the output stream. Pixels * in the given range of rows and in all columns are written. *

* Note: theRowRange's stride must be 1. * * @param theRowRange Range of pixel rows. * * @exception NullPointerException * (unchecked exception) Thrown if theRowRange is null. * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if any index in theRowRange * is outside the range 0 .. image height - 1. * @exception IllegalArgumentException * (unchecked exception) Thrown if theRowRange's stride is * greater than 1. * @exception IOException * Thrown if an I/O error occurred. */ public void writeRowSlice (Range theRowRange) throws IOException { int rowlb = theRowRange.lb(); int rowub = theRowRange.ub(); int rowlen = theRowRange.length(); int rowstride = theRowRange.stride(); if (0 > rowlb || rowub >= myHeight) { throw new IndexOutOfBoundsException ("PJGImage.Writer.writeRowSlice(): Image row range = 0.." + (myHeight-1) + ", theRowRange = " + theRowRange); } if (rowstride > 1) { throw new IllegalArgumentException ("PJGImage.Writer.writeRowSlice(): theRowRange stride = " + rowstride + " illegal"); } writePixelData (myDos, rowlb, 0, rowlen, myWidth); } /** * Write the given column slice of the image to the output stream. * Pixels in all rows and in the given range of columns are written. *

* Note: theColRange's stride must be 1. * * @param theColRange Range of pixel columns. * * @exception NullPointerException * (unchecked exception) Thrown if theColRange is null. * @exception IllegalArgumentException * (unchecked exception) Thrown if theColRange's stride is * greater than 1. * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if any index in theColRange * is outside the range 0 .. image width - 1. * @exception IOException * Thrown if an I/O error occurred. */ public void writeColSlice (Range theColRange) throws IOException { int collb = theColRange.lb(); int colub = theColRange.ub(); int collen = theColRange.length(); int colstride = theColRange.stride(); if (0 > collb || colub >= myWidth) { throw new IndexOutOfBoundsException ("PJGImage.Writer.writeColSlice(): Image column range = 0.." + (myWidth-1) + ", theColRange = " + theColRange); } if (colstride > 1) { throw new IllegalArgumentException ("PJGImage.Writer.writeColSlice(): theColRange stride = " + colstride + " illegal"); } writePixelData (myDos, 0, collb, myHeight, collen); } /** * Write the given patch of the image to the output stream. Pixels in * the given range of rows and in the given range of columns are * written. *

* Note: theRowRange's stride must be 1. * theColRange's stride must be 1. * * @param theRowRange Range of pixel rows. * @param theColRange Range of pixel columns. * * @exception NullPointerException * (unchecked exception) Thrown if theRowRange is null. * Thrown if theColRange is null. * @exception IllegalArgumentException * (unchecked exception) Thrown if theRowRange's stride is * greater than 1. Thrown if theColRange's stride is * greater than 1. * @exception IndexOutOfBoundsException * (unchecked exception) Thrown if any index in theRowRange * is outside the range 0 .. image height - 1. Thrown if any index * in theColRange is outside the range 0 .. image width - * 1. * @exception IOException * Thrown if an I/O error occurred. */ public void writePatch (Range theRowRange, Range theColRange) throws IOException { int rowlb = theRowRange.lb(); int rowub = theRowRange.ub(); int rowlen = theRowRange.length(); int rowstride = theRowRange.stride(); if (0 > rowlb || rowub >= myHeight) { throw new IndexOutOfBoundsException ("PJGImage.Writer.writePatch(): Image row range = 0.." + (myHeight-1) + ", theRowRange = " + theRowRange); } if (rowstride > 1) { throw new IllegalArgumentException ("PJGImage.Writer.writePatch(): theRowRange stride = " + rowstride + " illegal"); } int collb = theColRange.lb(); int colub = theColRange.ub(); int collen = theColRange.length(); int colstride = theColRange.stride(); if (0 > collb || colub >= myWidth) { throw new IndexOutOfBoundsException ("PJGImage.Writer.writePatch(): Image column range = 0.." + (myWidth-1) + ", theColRange = " + theColRange); } if (colstride > 1) { throw new IllegalArgumentException ("PJGImage.Writer.writePatch(): theColRange stride = " + colstride + " illegal"); } writePixelData (myDos, rowlb, collb, rowlen, collen); } } /** * Class PJGGrayImage.GrayReader provides an object with which to read a * {@linkplain PJGGrayImage} from an input stream. *

* When a PJG image reader is created, the header, image type, height, * width, creation time, and comment segments are read from the input * stream. *

* To read the pixel data segments one at a time, first call the * getRowRange() and getColRange() methods to obtain the * range of rows and columns in the next pixel data segment. At this point, * allocate storage for the rows and columns in the underlying matrix if * necessary. Then call the readSegment() method to read the actual * pixel data. Repeat these steps if there are additional pixel data * segments. *

* To read all the pixel data segments (or all the remaining pixel data * segments), call the read() method. *

* When finished, call the close() method. *

* Note: Class PJGGrayImage.GrayReader is not multiple thread safe. * * @author Alan Kaminsky * @version 02-Nov-2007 */ private class GrayReader extends PJGImage.Reader { // Hidden data members. int myRow; int myCol; int myRowLen; int myColLen; // Hidden constructors. /** * Construct a new PJG gray image reader. * * @param theStream Input stream. * * @exception NullPointerException * (unchecked exception) Thrown if theStream is null. * @exception IOException * Thrown if an I/O error occurred. */ GrayReader (InputStream theStream) throws IOException { super (theStream); getPixelDataSegmentParameters(); } // Exported operations. /** * Read all pixel data segments from the input stream. If some pixel * data segments have already been read, the read() method * reads all remaining pixel data segments. If there are no more pixel * data segments, the read() method does nothing. If storage is * not already allocated in the underlying matrix for the pixel rows and * columns in a pixel data segment, the read() method allocates * the necessary storage. * * @exception IOException * Thrown if an I/O error occurred. */ public void read() throws IOException { while (myNextSegmentType != -1) readSegment(); } /** * Obtain the row range of the next pixel data segment in the input * stream. If there are no more pixel data segments, null is returned. * * @return Row range, or null. * * @exception IOException * Thrown if an I/O error occurred. */ public Range getRowRange() { return myNextSegmentType == -1 ? null : new Range (myRow, myRow + myRowLen - 1); } /** * Obtain the column range of the next pixel data segment in the input * stream. If there are no more pixel data segments, null is returned. * * @return Column range, or null. * * @exception IOException * Thrown if an I/O error occurred. */ public Range getColRange() { return myNextSegmentType == -1 ? null : new Range (myCol, myCol + myColLen - 1); } /** * Read the next pixel data segment from the input stream. If there are * no more pixel data segments, the readSegment() method does * nothing. If storage is not already allocated in the underlying matrix * for the pixel rows and columns in the pixel data segment, the * readSegment() method allocates the necessary storage. * * @exception IOException * Thrown if an I/O error occurred. */ public void readSegment() throws IOException { // Early return if end of stream. if (myNextSegmentType == -1) return; // Read pixel data. readPixelData (myDis, myRow, myCol, myRowLen, myColLen); // Prepare for next segment. myNextSegmentType = myDis.read(); getPixelDataSegmentParameters(); } // Hidden operations. /** * Get the row index, column index, row count, and column count for the * next pixel data segment. Store the results in myRowRange and * myColRange. Assumes the segment type has been read into * myNextSegmentType. * * @exception IOException * Thrown if an I/O error occurred. */ private void getPixelDataSegmentParameters() throws IOException { // Early return if end of stream. if (myNextSegmentType == -1) return; // Verify segment type. if (myNextSegmentType != SEGMENT_PIXEL_DATA_HDE_8_BIT_GRAY) { throw new PJGImageFileFormatException ("Invalid PJG pixel data segment type (= " + myNextSegmentType + ")"); } // Read and verify row index. myRow = myDis.readInt(); if (0 > myRow || myRow >= myHeight) { throw new PJGImageFileFormatException ("Invalid PJG pixel data segment row index (= " + myRow + ")"); } // Read and verify column index. myCol = myDis.readInt(); if (0 > myCol || myCol >= myWidth) { throw new PJGImageFileFormatException ("Invalid PJG pixel data segment column index (= " + myCol + ")"); } // Read and verify row count. myRowLen = myDis.readInt(); if (1 > myRowLen || myRow + myRowLen > myHeight) { throw new PJGImageFileFormatException ("Invalid PJG pixel data segment row count (= " + myRowLen + ")"); } // Read and verify column count. myColLen = myDis.readInt(); if (1 > myColLen || myCol + myColLen > myWidth) { throw new PJGImageFileFormatException ("Invalid PJG pixel data segment column count (= " + myColLen + ")"); } } } }

Ads help maintain this website.