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 jj2000.j2k.image.*; 048import jj2000.j2k.*; 049import 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 * */ 064public 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, IOException { 119 this.in = in; 120 121 confirmFileType(); 122 skipCommentAndWhiteSpace(); 123 this.w = readHeaderInt(); 124 skipCommentAndWhiteSpace(); 125 this.h = readHeaderInt(); 126 skipCommentAndWhiteSpace(); 127 /*Read the highest pixel value from header (not used)*/ 128 readHeaderInt(); 129 this.nc=1; 130 this.rb=8; 131 } 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 /** 171 * Returns the position of the fixed point in the specified component 172 * (i.e. the number of fractional bits), which is always 0 for this 173 * ImgReader. 174 * 175 * @param c The index of the component. 176 * 177 * @return The position of the fixed-point (i.e. the number of fractional 178 * bits). Always 0 for this ImgReader. 179 * */ 180 public int getFixedPoint(int c) { 181 // Check component index 182 if (c != 0) 183 throw new IllegalArgumentException(); 184 return 0; 185 } 186 187 188 /** 189 * Returns, in the blk argument, the block of image data containing the 190 * specifed rectangular area, in the specified component. The data is 191 * returned, as a reference to the internal data, if any, instead of as a 192 * copy, therefore the returned data should not be modified. 193 * 194 * <P> After being read the coefficients are level shifted by subtracting 195 * 2^(nominal bit range - 1) 196 * 197 * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w' 198 * and 'h' members of the 'blk' argument, relative to the current 199 * tile. These members are not modified by this method. The 'offset' and 200 * 'scanw' of the returned data can be arbitrary. See the 'DataBlk' class. 201 * 202 * <P>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one 203 * is created if necessary. The implementation of this interface may 204 * choose to return the same array or a new one, depending on what is more 205 * efficient. Therefore, the data array in <tt>blk</tt> prior to the 206 * method call should not be considered to contain the returned data, a 207 * new array may have been created. Instead, get the array from 208 * <tt>blk</tt> after the method has returned. 209 * 210 * <P>The returned data always has its 'progressive' attribute unset 211 * (i.e. false). 212 * 213 * <P>When an I/O exception is encountered the JJ2KExceptionHandler is 214 * used. The exception is passed to its handleException method. The action 215 * that is taken depends on the action that has been registered in 216 * JJ2KExceptionHandler. See JJ2KExceptionHandler for details. 217 * 218 * @param blk Its coordinates and dimensions specify the area to 219 * return. Some fields in this object are modified to return the data. 220 * 221 * @param c The index of the component from which to get the data. Only 0 222 * is valid. 223 * 224 * @return The requested DataBlk 225 * 226 * @see #getCompData 227 * 228 * @see JJ2KExceptionHandler 229 * */ 230 public final DataBlk getInternCompData(DataBlk blk, int c) { 231 int k,j,i,mi; 232 int barr[]; 233 234 // Check component index 235 if (c != 0) 236 throw new IllegalArgumentException(); 237 238 // Check type of block provided as an argument 239 if(blk.getDataType()!=DataBlk.TYPE_INT){ 240 if(intBlk==null) 241 intBlk = new DataBlkInt(blk.ulx,blk.uly,blk.w,blk.h); 242 else{ 243 intBlk.ulx = blk.ulx; 244 intBlk.uly = blk.uly; 245 intBlk.w = blk.w; 246 intBlk.h = blk.h; 247 } 248 blk = intBlk; 249 } 250 251 // Get data array 252 barr = (int[]) blk.getData(); 253 if (barr == null || barr.length < blk.w*blk.h) { 254 barr = new int[blk.w*blk.h]; 255 blk.setData(barr); 256 } 257 258 // Check line buffer 259 if (buf == null || buf.length < blk.w) { 260 buf = new byte[blk.w]; 261 } 262 263 try { 264 // Read line by line 265 mi = blk.uly + blk.h; 266 for (i = blk.uly; i < mi; i++) { 267 // Reposition in input 268 in.seek(offset+i*w+blk.ulx); 269 in.read(buf,0,blk.w); 270 for (k = (i-blk.uly)*blk.w+blk.w-1, j = blk.w-1; 271 j >= 0; j--, k--) { 272 barr[k] = (((int)buf[j])&0xFF)-DC_OFFSET; 273 } 274 } 275 } 276 catch (IOException e) { 277 JJ2KExceptionHandler.handleException(e); 278 } 279 280 // Turn off the progressive attribute 281 blk.progressive = false; 282 // Set buffer attributes 283 blk.offset = 0; 284 blk.scanw = blk.w; 285 return blk; 286 } 287 288 /** 289 * Returns, in the blk argument, a block of image data containing the 290 * specifed rectangular area, in the specified component. The data is 291 * returned, as a copy of the internal data, therefore the returned data 292 * can be modified "in place". 293 * 294 * <P> After being read the coefficients are level shifted by subtracting 295 * 2^(nominal bit range - 1) 296 * 297 * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w' 298 * and 'h' members of the 'blk' argument, relative to the current 299 * tile. These members are not modified by this method. The 'offset' of 300 * the returned data is 0, and the 'scanw' is the same as the block's 301 * width. See the 'DataBlk' class. 302 * 303 * <P>If the data array in 'blk' is 'null', then a new one is created. If 304 * the data array is not 'null' then it is reused, and it must be large 305 * enough to contain the block's data. Otherwise an 'ArrayStoreException' 306 * or an 'IndexOutOfBoundsException' is thrown by the Java system. 307 * 308 * <P>The returned data has its 'progressive' attribute unset 309 * (i.e. false). 310 * 311 * <P>This method just calls 'getInternCompData(blk, n)'. 312 * 313 * <P>When an I/O exception is encountered the JJ2KExceptionHandler is 314 * used. The exception is passed to its handleException method. The action 315 * that is taken depends on the action that has been registered in 316 * JJ2KExceptionHandler. See JJ2KExceptionHandler for details. 317 * 318 * @param blk Its coordinates and dimensions specify the area to 319 * return. If it contains a non-null data array, then it must have the 320 * correct dimensions. If it contains a null data array a new one is 321 * created. The fields in this object are modified to return the data. 322 * 323 * @param c The index of the component from which to get the data. Only 0 324 * is valid. 325 * 326 * @return The requested DataBlk 327 * 328 * @see #getInternCompData 329 * 330 * @see JJ2KExceptionHandler 331 * */ 332 public DataBlk getCompData(DataBlk blk, int c) { 333 return getInternCompData(blk,c); 334 } 335 336 /** 337 * Returns a byte read from the RandomAccessIO. The number of read byted 338 * are counted to keep track of the offset of the pixel data in the PGM 339 * file 340 * 341 * @return One byte read from the header of the PGM file. 342 * 343 * @exception IOException If an I/O error occurs. 344 * 345 * @exception EOFException If an EOF is read 346 * */ 347 private byte countedByteRead() throws IOException, EOFException{ 348 offset++; 349 return in.readByte(); 350 } 351 352 /** 353 * Checks that the RandomAccessIO begins with 'P5' 354 * 355 * @exception IOException If an I/O error occurs. 356 * @exception EOFException If an EOF is read 357 * */ 358 private void confirmFileType() throws IOException, EOFException{ 359 byte[] type={80,53}; // 'P5' 360 int i; 361 byte b; 362 363 for(i=0;i<2;i++){ 364 b = countedByteRead(); 365 if(b!=type[i]){ 366 if( i==1 && b==50 ) { //i.e 'P2' 367 throw new 368 IllegalArgumentException("JJ2000 does not support"+ 369 " ascii-PGM files. Use "+ 370 " raw-PGM file instead. "); 371 } else { 372 throw new IllegalArgumentException("Not a raw-PGM file"); 373 } 374 } 375 } 376 } 377 378 /** 379 * Skips any line in the header starting with '#' and any space, tab, line 380 * feed or carriage return. 381 * 382 * @exception IOException If an I/O error occurs. 383 * @exception EOFException if an EOF is read 384 * */ 385 private void skipCommentAndWhiteSpace() throws IOException, EOFException { 386 387 boolean done=false; 388 byte b; 389 390 while(!done){ 391 b=countedByteRead(); 392 if(b==35){ // Comment start 393 while(b!=10 && b!=13){ // Comment ends in end of line 394 b=countedByteRead(); 395 } 396 }else if(!(b==9||b==10||b==13||b==32)){ // If not whitespace 397 done=true; 398 } 399 } 400 // Put last valid byte in 401 offset--; 402 in.seek(offset); 403 } 404 405 406 /** 407 * Returns an int read from the header of the PGM file. 408 * 409 * @return One int read from the header of the PGM file. 410 * 411 * @exception IOException If an I/O error occurs. 412 * @exception EOFException If an EOF is read 413 * */ 414 private int readHeaderInt() throws IOException, EOFException{ 415 int res=0; 416 byte b=0; 417 418 b=countedByteRead(); 419 while(b!=32&&b!=10&&b!=9&&b!=13){ // While not whitespace 420 res=res*10+b-48; // Covert ASCII to numerical value 421 b=countedByteRead(); 422 } 423 return res; 424 } 425 426 /** 427 * Returns true if the data read was originally signed in the specified 428 * component, false if not. This method returns always false since PGM 429 * data is always unsigned. 430 * 431 * @param c The index of the component, from 0 to N-1. 432 * 433 * @return always false, since PGM data is always unsigned. 434 * */ 435 public boolean isOrigSigned(int c) { 436 // Check component index 437 if (c != 0) 438 throw new IllegalArgumentException(); 439 return false; 440 } 441 442 /** 443 * Returns a string of information about the object, more than 1 line 444 * long. The information string includes information from the underlying 445 * RandomAccessIO (its toString() method is called in turn). 446 * 447 * @return A string of information about the object. 448 * */ 449 public String toString() { 450 return "ImgReaderPGM: WxH = " + w + "x" + h + ", Component = 0" + 451 "\nUnderlying RandomAccessIO:\n" + in.toString(); 452 } 453}