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