1 /* 2 * $RCSfile: ImageComponentState.java,v $ 3 * 4 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * - Redistribution of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * - Redistribution in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * Neither the name of Sun Microsystems, Inc. or the names of 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * This software is provided "AS IS," without a warranty of any 23 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 24 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 26 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 27 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 28 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 29 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 30 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 31 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 32 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 33 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGES. 35 * 36 * You acknowledge that this software is not designed, licensed or 37 * intended for use in the design, construction, operation or 38 * maintenance of any nuclear facility. 39 * 40 * $Revision: 1.5 $ 41 * $Date: 2007/02/09 17:20:35 $ 42 * $State: Exp $ 43 */ 44 45 package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d; 46 47 import java.io.DataOutput; 48 import java.io.DataInput; 49 import java.io.ByteArrayInputStream; 50 import java.io.ByteArrayOutputStream; 51 import java.io.DataInputStream; 52 import java.io.DataOutputStream; 53 import java.util.zip.GZIPInputStream; 54 import java.util.zip.GZIPOutputStream; 55 import java.io.IOException; 56 import java.awt.Point; 57 import java.awt.image.*; 58 import javax.media.j3d.ImageComponent; 59 import com.sun.j3d.utils.scenegraph.io.retained.Controller; 60 import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData; 61 import com.sun.j3d.utils.scenegraph.io.retained.SGIORuntimeException; 62 import java.awt.color.ColorSpace; 63 import java.awt.image.DataBuffer; 64 import javax.imageio.ImageIO; 65 66 public abstract class ImageComponentState extends NodeComponentState { 67 68 protected int format; 69 protected int height; 70 protected int width; 71 protected boolean byReference; 72 protected boolean yUp; 73 74 private static final int DIRECT_COLOR_MODEL = 1; 75 76 private static final int SINGLE_PIXEL_PACKED_SAMPLE_MODEL = 1; 77 78 private static final int DATA_BUFFER_INT = 1; 79 80 /** 81 * Do not compress the images 82 */ 83 public static final byte NO_COMPRESSION = 0; 84 85 /** 86 * Use GZIP to compress images. 87 * 88 * GZIP decompression is very slow 89 */ 90 public static final byte GZIP_COMPRESSION = 1; // GZIP is slow to decompress 91 92 /** 93 * Use JPEG compression for images 94 * 95 * JPEG compression is currently the default. The file format 96 * supports other compression algorithms but there is currently 97 * no API to select the algorithm. This feature is on hold pending 98 * imageio in Java 1.4 99 */ 100 public static final byte JPEG_COMPRESSION = 2; 101 ImageComponentState( SymbolTableData symbol, Controller control )102 public ImageComponentState( SymbolTableData symbol, Controller control ) { 103 super( symbol, control ); 104 } 105 106 writeConstructorParams( DataOutput out )107 protected void writeConstructorParams( DataOutput out ) throws 108 IOException { 109 super.writeConstructorParams( out ); 110 out.writeInt( ((ImageComponent)node).getFormat()); 111 out.writeInt( ((ImageComponent)node).getHeight()); 112 out.writeInt( ((ImageComponent)node).getWidth()); 113 out.writeBoolean( ((ImageComponent)node).isByReference() ); 114 out.writeBoolean( ((ImageComponent)node).isYUp() ); 115 } 116 readConstructorParams( DataInput in )117 protected void readConstructorParams( DataInput in ) throws 118 IOException { 119 super.readConstructorParams( in ); 120 format = in.readInt(); 121 height = in.readInt(); 122 width = in.readInt(); 123 byReference = in.readBoolean(); 124 yUp = in.readBoolean(); 125 } 126 writeBufferedImage( DataOutput out, BufferedImage image )127 protected void writeBufferedImage( DataOutput out, 128 BufferedImage image ) throws IOException { 129 130 int compressionType = control.getImageCompression(); 131 132 out.writeByte( compressionType ); 133 134 if (compressionType==NO_COMPRESSION) 135 writeBufferedImageNoCompression( out, image ); 136 else if (compressionType==GZIP_COMPRESSION) 137 writeBufferedImageGzipCompression( out, image ); 138 else if (compressionType==JPEG_COMPRESSION) 139 writeBufferedImageJpegCompression( out, image ); 140 } 141 writeBufferedImageNoCompression( DataOutput out, BufferedImage image )142 private void writeBufferedImageNoCompression( DataOutput out, BufferedImage image ) throws IOException { 143 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 144 DataOutputStream dataOut = new DataOutputStream( byteStream ); 145 ColorModel colorModel = (ColorModel) image.getColorModel(); 146 147 if (colorModel instanceof ComponentColorModel) { 148 ComponentColorModel cm = (ComponentColorModel) colorModel; 149 int numComponents = cm.getNumComponents(); 150 int type; 151 switch (numComponents) { 152 case 3: 153 type = BufferedImage.TYPE_INT_RGB; 154 break; 155 case 4: 156 type = BufferedImage.TYPE_INT_ARGB; 157 break; 158 default: 159 throw new SGIORuntimeException("Unsupported ColorModel "+colorModel.getClass().getName() ); 160 161 } 162 163 BufferedImage tmpBuf = new BufferedImage(image.getWidth(), image.getHeight(), type); 164 WritableRaster dstRaster = tmpBuf.getRaster(); 165 Raster srcRaster = image.getRaster(); 166 dstRaster.setRect(srcRaster); 167 image = tmpBuf; 168 } 169 170 writeColorModel( dataOut, image.getColorModel() ); 171 writeWritableRaster( dataOut, image.getRaster() ); 172 dataOut.writeBoolean( image.isAlphaPremultiplied() ); 173 174 dataOut.close(); 175 176 byte[] buffer = byteStream.toByteArray(); 177 out.writeInt( buffer.length ); 178 out.write( buffer ); 179 } 180 writeBufferedImageGzipCompression( DataOutput out, BufferedImage image )181 private void writeBufferedImageGzipCompression( DataOutput out, BufferedImage image ) throws IOException { 182 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 183 GZIPOutputStream gzipStream = new GZIPOutputStream( byteStream ); 184 DataOutputStream dataOut = new DataOutputStream( gzipStream ); 185 186 writeColorModel( dataOut, image.getColorModel() ); 187 writeWritableRaster( dataOut, image.getRaster() ); 188 dataOut.writeBoolean( image.isAlphaPremultiplied() ); 189 190 dataOut.flush(); 191 gzipStream.finish(); 192 193 194 byte[] buffer = byteStream.toByteArray(); 195 196 out.writeInt( buffer.length ); 197 out.write( buffer); 198 dataOut.close(); 199 } 200 writeBufferedImageJpegCompression( DataOutput out, BufferedImage image )201 private void writeBufferedImageJpegCompression( DataOutput out, BufferedImage image ) throws IOException { 202 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 203 if (!ImageIO.write(image, "jpeg", byteStream)) { 204 throw new AssertionError("No JPEG encoder available"); 205 } 206 207 byte[] buffer = byteStream.toByteArray(); 208 out.writeInt( buffer.length ); 209 out.write( buffer ); 210 } 211 readBufferedImage( DataInput in )212 protected BufferedImage readBufferedImage( DataInput in ) throws IOException { 213 byte compression = in.readByte(); 214 215 if (compression==NO_COMPRESSION) 216 return readBufferedImageNoCompression( in ); 217 else if (compression==GZIP_COMPRESSION) 218 return readBufferedImageGzipCompression( in ); 219 else if (compression==JPEG_COMPRESSION) 220 return readBufferedImageJpegCompression( in ); 221 throw new SGIORuntimeException("Unknown Image Compression"); 222 } 223 readBufferedImageNoCompression( DataInput in )224 private BufferedImage readBufferedImageNoCompression( DataInput in ) throws IOException { 225 int size = in.readInt(); 226 byte[] buffer = new byte[ size ]; 227 in.readFully( buffer ); 228 ByteArrayInputStream byteIn = new ByteArrayInputStream( buffer ); 229 DataInputStream dataIn = new DataInputStream( byteIn ); 230 231 ColorModel colorModel = readColorModel( dataIn ); 232 WritableRaster raster = readWritableRaster( dataIn ); 233 boolean alphaPreMult = dataIn.readBoolean(); 234 dataIn.close(); 235 236 return new BufferedImage( colorModel, raster, alphaPreMult, null ); 237 } 238 readBufferedImageGzipCompression( DataInput in )239 private BufferedImage readBufferedImageGzipCompression( DataInput in ) throws IOException { 240 int size = in.readInt(); 241 byte[] buffer = new byte[ size ]; 242 in.readFully( buffer ); 243 ByteArrayInputStream byteIn = new ByteArrayInputStream( buffer ); 244 GZIPInputStream gzipIn = new GZIPInputStream( byteIn ); 245 DataInputStream dataIn = new DataInputStream( gzipIn ); 246 247 ColorModel colorModel = readColorModel( dataIn ); 248 WritableRaster raster = readWritableRaster( dataIn ); 249 boolean alphaPremult = dataIn.readBoolean(); 250 dataIn.close(); 251 252 return new BufferedImage( colorModel, raster, alphaPremult, null ); 253 } 254 readBufferedImageJpegCompression( DataInput in )255 private BufferedImage readBufferedImageJpegCompression( DataInput in ) throws IOException { 256 int size = in.readInt(); 257 byte[] buffer = new byte[ size ]; 258 in.readFully( buffer ); 259 ByteArrayInputStream byteStream = new ByteArrayInputStream( buffer ); 260 try { 261 BufferedImage img = ImageIO.read(byteStream); 262 if (img == null) { 263 throw new AssertionError("No ImageReader available."); 264 } 265 return img; 266 } finally { 267 byteStream.close(); 268 } 269 } 270 writeColorModel( DataOutput out, ColorModel colorModel )271 private void writeColorModel( DataOutput out, ColorModel colorModel ) throws IOException { 272 if (colorModel instanceof DirectColorModel) { 273 out.writeInt( DIRECT_COLOR_MODEL ); 274 writeDirectColorModel( out, (DirectColorModel)colorModel ); 275 } 276 else 277 throw new SGIORuntimeException("Unsupported ColorModel "+colorModel.getClass().getName() ); 278 } 279 readColorModel( DataInput in )280 private ColorModel readColorModel( DataInput in ) throws IOException { 281 switch( in.readInt() ) { 282 case DIRECT_COLOR_MODEL: 283 return readDirectColorModel( in ); 284 } 285 286 throw new SGIORuntimeException( "Invalid ColorModel - File corrupt" ); 287 } 288 writeDirectColorModel( DataOutput out, DirectColorModel colorModel )289 private void writeDirectColorModel( DataOutput out, 290 DirectColorModel colorModel ) throws IOException { 291 out.writeInt( colorModel.getPixelSize() ); 292 out.writeInt( colorModel.getRedMask() ); 293 out.writeInt( colorModel.getGreenMask() ); 294 out.writeInt( colorModel.getBlueMask() ); 295 out.writeInt( colorModel.getAlphaMask() ); 296 } 297 readDirectColorModel( DataInput in )298 private DirectColorModel readDirectColorModel( DataInput in ) throws IOException { 299 return new DirectColorModel( in.readInt(), 300 in.readInt(), 301 in.readInt(), 302 in.readInt(), 303 in.readInt() ); 304 } 305 writeWritableRaster( DataOutput out, WritableRaster raster )306 private void writeWritableRaster( DataOutput out, WritableRaster raster ) throws IOException{ 307 writeSampleModel( out, raster.getSampleModel() ); 308 writeDataBuffer( out, raster.getDataBuffer() ); 309 Point origin = new Point(); 310 // TODO Get the origin of the raster - seems to be missing from the raster API 311 out.writeInt( origin.x ); 312 out.writeInt( origin.y ); 313 } 314 readWritableRaster( DataInput in )315 private WritableRaster readWritableRaster( DataInput in ) throws IOException { 316 return Raster.createWritableRaster( readSampleModel( in ), 317 readDataBuffer( in ), 318 new Point( in.readInt(), in.readInt() )); 319 } 320 writeSampleModel( DataOutput out, SampleModel model )321 private void writeSampleModel( DataOutput out, SampleModel model ) throws IOException { 322 if (model instanceof SinglePixelPackedSampleModel) { 323 out.writeInt( SINGLE_PIXEL_PACKED_SAMPLE_MODEL ); 324 writeSinglePixelPackedSampleModel( out, (SinglePixelPackedSampleModel)model ); 325 } else 326 throw new SGIORuntimeException("Unsupported SampleModel "+model.getClass().getName() ); 327 } 328 readSampleModel( DataInput in )329 private SampleModel readSampleModel( DataInput in ) throws IOException { 330 switch( in.readInt() ) { 331 case SINGLE_PIXEL_PACKED_SAMPLE_MODEL: 332 return readSinglePixelPackedSampleModel( in ); 333 } 334 335 throw new SGIORuntimeException("Invalid SampleModel - file corrupt"); 336 } 337 writeSinglePixelPackedSampleModel( DataOutput out, SinglePixelPackedSampleModel model )338 private void writeSinglePixelPackedSampleModel( DataOutput out, 339 SinglePixelPackedSampleModel model ) throws IOException { 340 341 int[] masks = model.getBitMasks(); 342 out.writeInt( masks.length ); 343 for(int i=0; i<masks.length; i++) 344 out.writeInt( masks[i] ); 345 346 out.writeInt( model.getDataType() ); 347 out.writeInt( model.getWidth() ); 348 out.writeInt( model.getHeight() ); 349 out.writeInt( model.getScanlineStride() ); 350 351 } 352 readSinglePixelPackedSampleModel( DataInput in )353 private SinglePixelPackedSampleModel readSinglePixelPackedSampleModel( DataInput in ) 354 throws IOException { 355 356 int masks[] = new int[ in.readInt() ]; 357 for(int i=0; i<masks.length; i++) 358 masks[i] = in.readInt(); 359 360 return new SinglePixelPackedSampleModel( in.readInt(), 361 in.readInt(), 362 in.readInt(), 363 in.readInt(), 364 masks ); 365 } 366 writeDataBuffer( DataOutput out, DataBuffer buffer )367 private void writeDataBuffer( DataOutput out, DataBuffer buffer ) throws IOException { 368 if (buffer instanceof DataBufferInt) { 369 out.writeInt( DATA_BUFFER_INT ); 370 writeDataBufferInt( out, (DataBufferInt)buffer ); 371 } else 372 throw new SGIORuntimeException("Unsupported DataBuffer "+buffer.getClass().getName() ); 373 } 374 readDataBuffer( DataInput in )375 private DataBuffer readDataBuffer( DataInput in ) throws IOException { 376 switch( in.readInt() ) { 377 case DATA_BUFFER_INT: 378 return readDataBufferInt( in ); 379 } 380 381 throw new SGIORuntimeException("Incorrect DataBuffer - file corrupt"); 382 } 383 writeDataBufferInt( DataOutput out, DataBufferInt buffer )384 private void writeDataBufferInt( DataOutput out, DataBufferInt buffer ) throws IOException { 385 int[][] data = buffer.getBankData(); 386 out.writeInt( data.length ); 387 for(int i=0; i<data.length; i++) { 388 out.writeInt( data[i].length ); 389 for( int j=0; j<data[i].length; j++) 390 out.writeInt( data[i][j] ); 391 } 392 393 out.writeInt( buffer.getSize() ); 394 395 // TODO Handle DataBufferInt offsets 396 397 } 398 readDataBufferInt( DataInput in )399 private DataBufferInt readDataBufferInt( DataInput in ) throws IOException { 400 int[][] data = new int[in.readInt()][]; 401 for(int i=0; i<data.length; i++) { 402 data[i] = new int[ in.readInt() ]; 403 for( int j=0; j<data[i].length; j++) 404 data[i][j] = in.readInt(); 405 } 406 407 408 return new DataBufferInt( data, in.readInt() ); 409 } 410 } 411