Source for gnu.javax.imageio.png.PNGChunk

   1: /* PNGChunk.java -- Generic PNG chunk
   2:    Copyright (C) 2006 Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.javax.imageio.png;
  39: 
  40: import java.io.InputStream;
  41: import java.io.OutputStream;
  42: import java.io.IOException;
  43: 
  44: /**
  45:  * Class to load and validate a generic PNG chunk.
  46:  */
  47: public class PNGChunk
  48: {
  49: 
  50:   /**
  51:    * CRC table and initialization code.
  52:    */
  53:   private static long[] crcTable;
  54: 
  55:   static
  56:    {
  57:      long c;
  58:      crcTable = new long[256];
  59: 
  60:      for(int i = 0; i < 256; i++)
  61:        {
  62:          c = i;
  63:          for(int j = 0; j < 8; j++)
  64:            if( (c & 1) == 1 )
  65:              c = 0xEDB88320L ^ (c >> 1);
  66:            else
  67:              c = c >> 1;
  68:          crcTable[i] = c;
  69:        }
  70:    }
  71: 
  72:   /**
  73:    * (recognized) PNG chunk types.
  74:    */
  75:   public static final int TYPE_HEADER = 0x49484452; // 'IHDR'
  76:   public static final int TYPE_PALETTE = 0x504c5445;// 'PLTE'
  77:   public static final int TYPE_DATA = 0x49444154;   // 'IDAT'
  78:   public static final int TYPE_TIME = 0x74494d45;   // 'tIME'
  79:   public static final int TYPE_END = 0x49454e44;    // 'IEND'
  80:   public static final int TYPE_PHYS = 0x70485973;   // 'pHYS'
  81:   public static final int TYPE_GAMMA = 0x67414d41;  // 'gAMA'
  82:   public static final int TYPE_PROFILE = 0x69434350;  // 'iCCP'
  83: 
  84:   /**
  85:    * The chunk type - Represented in the file as 4 ASCII bytes,
  86:    */
  87:   private int type;
  88: 
  89:   /**
  90:    * The chunk data
  91:    */
  92:   protected byte[] data;
  93: 
  94:   /**
  95:    * The chunk's crc
  96:    */
  97:   private int crc;
  98: 
  99:   /**
 100:    * Constructor for reading a generic chunk.
 101:    */
 102:   protected PNGChunk( int type, byte[] data, int crc )
 103:   {
 104:     this.type = type;
 105:     this.data = data;
 106:     this.crc = crc;
 107:   }
 108: 
 109:   /**
 110:    * Constructor for creating new chunks.
 111:    * (only used by subclasses - creating a generic chunk is rather useless)
 112:    */
 113:   protected PNGChunk( int type )
 114:   {
 115:     this.type = type;
 116:   }
 117: 
 118:   /**
 119:    * Loads a chunk from an InputStream. Does not perform validation,
 120:    * but will throw an IOException if the read fails.
 121:    * @param in - th einputstream to read from
 122:    * @param strict - if true, a PNGException is thrown on all invalid chunks,
 123:    * if false, only critical chunks will throw PNGExceptions.
 124:    */
 125:   public static PNGChunk readChunk(InputStream in, boolean strict)
 126:     throws IOException, PNGException
 127:   {
 128:     byte data[] = new byte[4];
 129:     if( in.read( data ) != 4 )
 130:       throw new IOException("Could not read chunk length.");
 131:     int length = ((data[0] & 0xFF) << 24) | ((data[1] & 0xFF) << 16 ) |
 132:       ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
 133: 
 134:     if( in.read( data ) != 4 )
 135:       throw new IOException("Could not read chunk type.");
 136:     int type = ((data[0] & 0xFF) << 24) | ((data[1] & 0xFF) << 16 ) |
 137:       ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
 138: 
 139:     byte[] chkdata = new byte[ length ];
 140:     if( in.read( chkdata ) != length )
 141:       throw new IOException("Could not read chunk data.");
 142: 
 143:     if( in.read( data ) != 4 )
 144:       throw new IOException("Could not read chunk CRC.");
 145: 
 146:     int crc = ((data[0] & 0xFF) << 24) | ( (data[1] & 0xFF) << 16 ) |
 147:       ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
 148: 
 149:     if( strict )
 150:       return getChunk( type, chkdata, crc );
 151:     else
 152:       {
 153:         try
 154:           {
 155:             return getChunk( type, chkdata, crc );
 156:           }
 157:         catch(PNGException pnge)
 158:           {
 159:             if( isEssentialChunk( type ) )
 160:               throw pnge;
 161:             return null;
 162:           }
 163:       }
 164:   }
 165: 
 166:   /**
 167:    * Returns a specialied object for a chunk, if we have one.
 168:    */
 169:   private static PNGChunk getChunk( int type, byte[] data, int crc )
 170:     throws PNGException
 171:   {
 172:     switch( type )
 173:       {
 174:       case TYPE_HEADER:
 175:         return new PNGHeader( type, data, crc );
 176:       case TYPE_DATA:
 177:         return new PNGData( type, data, crc );
 178:       case TYPE_PALETTE:
 179:         return new PNGPalette( type, data, crc );
 180:       case TYPE_TIME:
 181:         return new PNGTime( type, data, crc );
 182:       case TYPE_PHYS:
 183:         return new PNGPhys( type, data, crc );
 184:       case TYPE_GAMMA:
 185:         return new PNGGamma( type, data, crc );
 186:       case TYPE_PROFILE:
 187:         return new PNGICCProfile( type, data, crc );
 188:       default:
 189:         return new PNGChunk( type, data, crc );
 190:       }
 191:   }
 192: 
 193:   /**
 194:    * Returns whether the chunk is essential or not
 195:    */
 196:   private static boolean isEssentialChunk( int type )
 197:   {
 198:     switch( type )
 199:       {
 200:       case TYPE_HEADER:
 201:       case TYPE_DATA:
 202:       case TYPE_PALETTE:
 203:       case TYPE_END:
 204:         return true;
 205:       default:
 206:         return false;
 207:       }
 208:   }
 209: 
 210:   /**
 211:    * Validates the chunk
 212:    */
 213:   public boolean isValidChunk()
 214:   {
 215:     return (crc == calcCRC());
 216:   }
 217: 
 218:   /**
 219:    * Returns the chunk type.
 220:    */
 221:   public int getType()
 222:   {
 223:     return type;
 224:   }
 225: 
 226:   /**
 227:    * Writes a PNG chunk to an output stream,
 228:    * performing the CRC calculation as well.
 229:    */
 230:   public void writeChunk(OutputStream out) throws IOException
 231:   {
 232:     out.write( getInt(data.length) );
 233:     out.write( getInt(type) );
 234:     out.write( data );
 235:     out.write( getInt(calcCRC()) );
 236:   }
 237: 
 238:   /**
 239:    * Return whether the chunk contains any data.
 240:    */
 241:   public boolean isEmpty()
 242:   {
 243:     return ( data.length == 0 );
 244:   }
 245: 
 246:   /**
 247:    * Convenience method. Cast an int to four bytes (big endian).
 248:    * (Now why doesn't java have a simple way of doing this?)
 249:    */
 250:   public static byte[] getInt(int intValue)
 251:   {
 252:     long i = (intValue & 0xFFFFFFFFL);
 253:     byte[] b = new byte[4];
 254:     b[0] = (byte)((i & 0xFF000000L) >> 24);
 255:     b[1] = (byte)((i & 0x00FF0000L) >> 16);
 256:     b[2] = (byte)((i & 0x0000FF00L) >> 8);
 257:     b[3] = (byte)(i & 0x000000FFL);
 258:     return b;
 259:   }
 260: 
 261:   /**
 262:    * Calculates this chunk's CRC value.
 263:    */
 264:   private int calcCRC()
 265:   {
 266:     long c = 0xFFFFFFFFL;
 267:     byte[] t = getInt( type );
 268:     for(int i = 0; i < 4; i++)
 269:       c = crcTable[ (int)((c ^ t[i]) & 0xFF) ] ^ (c >> 8);
 270: 
 271:     for(int i = 0; i < data.length; i++)
 272:       c = crcTable[ (int)((c ^ data[i]) & 0xFF) ] ^ (c >> 8);
 273: 
 274:     return (int)(c ^ 0xFFFFFFFFL);
 275:   }
 276: 
 277:   public String toString()
 278:   {
 279:     return "PNG Chunk. Type: " + new String( getInt(type) ) + " , CRC: " +
 280:       crc + " , calculated CRC: "+calcCRC();
 281:   }
 282: 
 283: }