001: /*
002: * $RCSfile: ImgReaderPGM.java,v $
003: * $Revision: 1.1 $
004: * $Date: 2005/02/11 05:02:14 $
005: * $State: Exp $
006: *
007: * Class: ImageWriterRawPGM
008: *
009: * Description: Image writer for unsigned 8 bit data in
010: * PGM files.
011: *
012: *
013: *
014: * COPYRIGHT:
015: *
016: * This software module was originally developed by Raphaël Grosbois and
017: * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
018: * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
019: * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
020: * Centre France S.A) in the course of development of the JPEG2000
021: * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
022: * software module is an implementation of a part of the JPEG 2000
023: * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
024: * Systems AB and Canon Research Centre France S.A (collectively JJ2000
025: * Partners) agree not to assert against ISO/IEC and users of the JPEG
026: * 2000 Standard (Users) any of their rights under the copyright, not
027: * including other intellectual property rights, for this software module
028: * with respect to the usage by ISO/IEC and Users of this software module
029: * or modifications thereof for use in hardware or software products
030: * claiming conformance to the JPEG 2000 Standard. Those intending to use
031: * this software module in hardware or software products are advised that
032: * their use may infringe existing patents. The original developers of
033: * this software module, JJ2000 Partners and ISO/IEC assume no liability
034: * for use of this software module or modifications thereof. No license
035: * or right to this software module is granted for non JPEG 2000 Standard
036: * conforming products. JJ2000 Partners have full right to use this
037: * software module for his/her own purpose, assign or donate this
038: * software module to any third party and to inhibit third parties from
039: * using this software module for non JPEG 2000 Standard conforming
040: * products. This copyright notice must be included in all copies or
041: * derivative works of this software module.
042: *
043: * Copyright (c) 1999/2000 JJ2000 Partners.
044: * */
045: package jj2000.j2k.image.input;
046:
047: import jj2000.j2k.image.*;
048: import jj2000.j2k.*;
049: import java.io.*;
050:
051: /**
052: * This class implements the ImgData interface for reading 8 bit unsigned data
053: * from a binary PGM file.
054: *
055: * <p>After being read the coefficients are level shifted by subtracting
056: * 2^(nominal bit range-1)</p>
057: *
058: * <p>The TransferType (see ImgData) of this class is TYPE_INT.</p>
059: *
060: * <P>NOTE: This class is not thread safe, for reasons of internal buffering.
061: *
062: * @see jj2000.j2k.image.ImgData
063: * */
064: public class ImgReaderPGM extends ImgReader {
065:
066: /** DC offset value used when reading image */
067: public static int DC_OFFSET = 128;
068:
069: /** Where to read the data from */
070: private RandomAccessFile in;
071:
072: /** The offset of the raw pixel data in the PGM file */
073: private int offset;
074:
075: /** The number of bits that determine the nominal dynamic range */
076: private int rb;
077:
078: /** The line buffer. */
079: // This makes the class not thrad safe
080: // (but it is not the only one making it so)
081: private byte buf[];
082:
083: /** Temporary DataBlkInt object (needed when encoder uses floating-point
084: filters). This avoid allocating new DataBlk at each time */
085: private DataBlkInt intBlk;
086:
087: /**
088: * Creates a new PGM file reader from the specified file.
089: *
090: * @param file The input file.
091: *
092: * @exception IOException If an error occurs while opening the file.
093: * */
094: public ImgReaderPGM(File file) throws IOException {
095: this (new RandomAccessFile(file, "r"));
096: }
097:
098: /**
099: * Creates a new PGM file reader from the specified file name.
100: *
101: * @param fname The input file name.
102: *
103: * @exception IOException If an error occurs while opening the file.
104: * */
105: public ImgReaderPGM(String fname) throws IOException {
106: this (new RandomAccessFile(fname, "r"));
107: }
108:
109: /**
110: * Creates a new PGM file reader from the specified RandomAccessFile
111: * object. The file header is read to acquire the image size.
112: *
113: * @param in From where to read the data
114: *
115: * @exception EOFException if an EOF is read
116: * @exception IOException if an error occurs when opening the file
117: * */
118: public ImgReaderPGM(RandomAccessFile in) throws EOFException,
119: IOException {
120: this .in = in;
121:
122: confirmFileType();
123: skipCommentAndWhiteSpace();
124: this .w = readHeaderInt();
125: skipCommentAndWhiteSpace();
126: this .h = readHeaderInt();
127: skipCommentAndWhiteSpace();
128: /*Read the highest pixel value from header (not used)*/
129: readHeaderInt();
130: this .nc = 1;
131: this .rb = 8;
132: }
133:
134: /**
135: * Closes the underlying RandomAccessFile from where the image data is
136: * being read. No operations are possible after a call to this method.
137: *
138: * @exception IOException If an I/O error occurs.
139: * */
140: public void close() throws IOException {
141: in.close();
142: in = null;
143: }
144:
145: /**
146: * Returns the number of bits corresponding to the nominal range of the
147: * data in the specified component. This is the value rb (range bits) that
148: * was specified in the constructor, which normally is 8 for non bilevel
149: * data, and 1 for bilevel data.
150: *
151: * <P>If this number is <i>b</b> then the nominal range is between
152: * -2^(b-1) and 2^(b-1)-1, since unsigned data is level shifted to have a
153: * nominal average of 0.
154: *
155: * @param c The index of the component.
156: *
157: * @return The number of bits corresponding to the nominal range of the
158: * data. Fro floating-point data this value is not applicable and the
159: * return value is undefined.
160: * */
161: public int getNomRangeBits(int c) {
162: // Check component index
163: if (c != 0)
164: throw new IllegalArgumentException();
165:
166: return rb;
167: }
168:
169: /**
170: * Returns the position of the fixed point in the specified component
171: * (i.e. the number of fractional bits), which is always 0 for this
172: * ImgReader.
173: *
174: * @param c The index of the component.
175: *
176: * @return The position of the fixed-point (i.e. the number of fractional
177: * bits). Always 0 for this ImgReader.
178: * */
179: public int getFixedPoint(int c) {
180: // Check component index
181: if (c != 0)
182: throw new IllegalArgumentException();
183: return 0;
184: }
185:
186: /**
187: * Returns, in the blk argument, the block of image data containing the
188: * specifed rectangular area, in the specified component. The data is
189: * returned, as a reference to the internal data, if any, instead of as a
190: * copy, therefore the returned data should not be modified.
191: *
192: * <P> After being read the coefficients are level shifted by subtracting
193: * 2^(nominal bit range - 1)
194: *
195: * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
196: * and 'h' members of the 'blk' argument, relative to the current
197: * tile. These members are not modified by this method. The 'offset' and
198: * 'scanw' of the returned data can be arbitrary. See the 'DataBlk' class.
199: *
200: * <P>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one
201: * is created if necessary. The implementation of this interface may
202: * choose to return the same array or a new one, depending on what is more
203: * efficient. Therefore, the data array in <tt>blk</tt> prior to the
204: * method call should not be considered to contain the returned data, a
205: * new array may have been created. Instead, get the array from
206: * <tt>blk</tt> after the method has returned.
207: *
208: * <P>The returned data always has its 'progressive' attribute unset
209: * (i.e. false).
210: *
211: * <P>When an I/O exception is encountered the JJ2KExceptionHandler is
212: * used. The exception is passed to its handleException method. The action
213: * that is taken depends on the action that has been registered in
214: * JJ2KExceptionHandler. See JJ2KExceptionHandler for details.
215: *
216: * @param blk Its coordinates and dimensions specify the area to
217: * return. Some fields in this object are modified to return the data.
218: *
219: * @param c The index of the component from which to get the data. Only 0
220: * is valid.
221: *
222: * @return The requested DataBlk
223: *
224: * @see #getCompData
225: *
226: * @see JJ2KExceptionHandler
227: * */
228: public final DataBlk getInternCompData(DataBlk blk, int c) {
229: int k, j, i, mi;
230: int barr[];
231:
232: // Check component index
233: if (c != 0)
234: throw new IllegalArgumentException();
235:
236: // Check type of block provided as an argument
237: if (blk.getDataType() != DataBlk.TYPE_INT) {
238: if (intBlk == null)
239: intBlk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h);
240: else {
241: intBlk.ulx = blk.ulx;
242: intBlk.uly = blk.uly;
243: intBlk.w = blk.w;
244: intBlk.h = blk.h;
245: }
246: blk = intBlk;
247: }
248:
249: // Get data array
250: barr = (int[]) blk.getData();
251: if (barr == null || barr.length < blk.w * blk.h) {
252: barr = new int[blk.w * blk.h];
253: blk.setData(barr);
254: }
255:
256: // Check line buffer
257: if (buf == null || buf.length < blk.w) {
258: buf = new byte[blk.w];
259: }
260:
261: try {
262: // Read line by line
263: mi = blk.uly + blk.h;
264: for (i = blk.uly; i < mi; i++) {
265: // Reposition in input
266: in.seek(offset + i * w + blk.ulx);
267: in.read(buf, 0, blk.w);
268: for (k = (i - blk.uly) * blk.w + blk.w - 1, j = blk.w - 1; j >= 0; j--, k--) {
269: barr[k] = (((int) buf[j]) & 0xFF) - DC_OFFSET;
270: }
271: }
272: } catch (IOException e) {
273: JJ2KExceptionHandler.handleException(e);
274: }
275:
276: // Turn off the progressive attribute
277: blk.progressive = false;
278: // Set buffer attributes
279: blk.offset = 0;
280: blk.scanw = blk.w;
281: return blk;
282: }
283:
284: /**
285: * Returns, in the blk argument, a block of image data containing the
286: * specifed rectangular area, in the specified component. The data is
287: * returned, as a copy of the internal data, therefore the returned data
288: * can be modified "in place".
289: *
290: * <P> After being read the coefficients are level shifted by subtracting
291: * 2^(nominal bit range - 1)
292: *
293: * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
294: * and 'h' members of the 'blk' argument, relative to the current
295: * tile. These members are not modified by this method. The 'offset' of
296: * the returned data is 0, and the 'scanw' is the same as the block's
297: * width. See the 'DataBlk' class.
298: *
299: * <P>If the data array in 'blk' is 'null', then a new one is created. If
300: * the data array is not 'null' then it is reused, and it must be large
301: * enough to contain the block's data. Otherwise an 'ArrayStoreException'
302: * or an 'IndexOutOfBoundsException' is thrown by the Java system.
303: *
304: * <P>The returned data has its 'progressive' attribute unset
305: * (i.e. false).
306: *
307: * <P>This method just calls 'getInternCompData(blk, n)'.
308: *
309: * <P>When an I/O exception is encountered the JJ2KExceptionHandler is
310: * used. The exception is passed to its handleException method. The action
311: * that is taken depends on the action that has been registered in
312: * JJ2KExceptionHandler. See JJ2KExceptionHandler for details.
313: *
314: * @param blk Its coordinates and dimensions specify the area to
315: * return. If it contains a non-null data array, then it must have the
316: * correct dimensions. If it contains a null data array a new one is
317: * created. The fields in this object are modified to return the data.
318: *
319: * @param c The index of the component from which to get the data. Only 0
320: * is valid.
321: *
322: * @return The requested DataBlk
323: *
324: * @see #getInternCompData
325: *
326: * @see JJ2KExceptionHandler
327: * */
328: public DataBlk getCompData(DataBlk blk, int c) {
329: return getInternCompData(blk, c);
330: }
331:
332: /**
333: * Returns a byte read from the RandomAccessIO. The number of read byted
334: * are counted to keep track of the offset of the pixel data in the PGM
335: * file
336: *
337: * @return One byte read from the header of the PGM file.
338: *
339: * @exception IOException If an I/O error occurs.
340: *
341: * @exception EOFException If an EOF is read
342: * */
343: private byte countedByteRead() throws IOException, EOFException {
344: offset++;
345: return in.readByte();
346: }
347:
348: /**
349: * Checks that the RandomAccessIO begins with 'P5'
350: *
351: * @exception IOException If an I/O error occurs.
352: * @exception EOFException If an EOF is read
353: * */
354: private void confirmFileType() throws IOException, EOFException {
355: byte[] type = { 80, 53 }; // 'P5'
356: int i;
357: byte b;
358:
359: for (i = 0; i < 2; i++) {
360: b = countedByteRead();
361: if (b != type[i]) {
362: if (i == 1 && b == 50) { //i.e 'P2'
363: throw new IllegalArgumentException(
364: "JJ2000 does not support"
365: + " ascii-PGM files. Use "
366: + " raw-PGM file instead. ");
367: } else {
368: throw new IllegalArgumentException(
369: "Not a raw-PGM file");
370: }
371: }
372: }
373: }
374:
375: /**
376: * Skips any line in the header starting with '#' and any space, tab, line
377: * feed or carriage return.
378: *
379: * @exception IOException If an I/O error occurs.
380: * @exception EOFException if an EOF is read
381: * */
382: private void skipCommentAndWhiteSpace() throws IOException,
383: EOFException {
384:
385: boolean done = false;
386: byte b;
387:
388: while (!done) {
389: b = countedByteRead();
390: if (b == 35) { // Comment start
391: while (b != 10 && b != 13) { // Comment ends in end of line
392: b = countedByteRead();
393: }
394: } else if (!(b == 9 || b == 10 || b == 13 || b == 32)) { // If not whitespace
395: done = true;
396: }
397: }
398: // Put last valid byte in
399: offset--;
400: in.seek(offset);
401: }
402:
403: /**
404: * Returns an int read from the header of the PGM file.
405: *
406: * @return One int read from the header of the PGM file.
407: *
408: * @exception IOException If an I/O error occurs.
409: * @exception EOFException If an EOF is read
410: * */
411: private int readHeaderInt() throws IOException, EOFException {
412: int res = 0;
413: byte b = 0;
414:
415: b = countedByteRead();
416: while (b != 32 && b != 10 && b != 9 && b != 13) { // While not whitespace
417: res = res * 10 + b - 48; // Covert ASCII to numerical value
418: b = countedByteRead();
419: }
420: return res;
421: }
422:
423: /**
424: * Returns true if the data read was originally signed in the specified
425: * component, false if not. This method returns always false since PGM
426: * data is always unsigned.
427: *
428: * @param c The index of the component, from 0 to N-1.
429: *
430: * @return always false, since PGM data is always unsigned.
431: * */
432: public boolean isOrigSigned(int c) {
433: // Check component index
434: if (c != 0)
435: throw new IllegalArgumentException();
436: return false;
437: }
438:
439: /**
440: * Returns a string of information about the object, more than 1 line
441: * long. The information string includes information from the underlying
442: * RandomAccessIO (its toString() method is called in turn).
443: *
444: * @return A string of information about the object.
445: * */
446: public String toString() {
447: return "ImgReaderPGM: WxH = " + w + "x" + h + ", Component = 0"
448: + "\nUnderlying RandomAccessIO:\n" + in.toString();
449: }
450: }
|