/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.io;
import org.jagatoo.image.DirectBufferedImage;
import org.jagatoo.util.image.ImageUtility;
import org.openmali.vecmath2.*;
import org.xith3d.utility.logging.X3DLog;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
import java.util.Map;
/**
* An output stream derived from DataOutputStream for writing out cosm objects
* to streams. ExtScribable objects which are written to the stream have their
* class type stored so they can be rebuilt when they are read. Each class name
* will be only stored once per file to reduce storage.
*
* :Id: ScribeOutputStream.java,v 1.10 2003/02/24 00:13:44 wurp Exp $
*
* :Log: ScribeOutputStream.java,v $ Revision 1.10 2003/02/24 00:13:44 wurp
* Formatted all java code for cvs (strictSunConvention.xml)
*
* Revision 1.9 2002/09/11 00:53:53 dilvish Big commit of new changes
*
* Revision 1.8 2002/01/23 04:38:33 wurp Integrating i18n code
*
* Revision 1.7 2002/01/20 05:26:02 dilvish JDK 1.4 port
*
* Revision 1.6 2002/01/15 03:36:55 dilvish Improvements to the animation system
*
* Revision 1.5 2002/01/12 03:16:27 dilvish Added read/write of buffered images
*
* Revision 1.4 2001/11/17 03:13:02 dilvish Added the ability to read/write
* arrays of vectors
*
* Revision 1.3 2001/07/14 14:48:10 wurp New animation, sky and avatar movement
*
* Revision 1.2 2001/06/20 04:05:41 wurp added log4j.
*
* Revision 1.1 2001/06/06 22:42:37 wizofid Massive commit / reintegration
*
* @author David Yazel
*/
public class ScribeOutputStream extends DataOutputStream
{
protected static final int CLASS_REF = 10000;
protected static final int CLASS_DEF = 10001;
private Map< String, Integer > map = new HashMap< String, Integer >();
// used for super fast reads of float arrays
private byte[] byteBuffer = null;
private FloatBuffer floatBuffer = null;
private ByteBuffer bBuffer = null;
private IntBuffer intBuffer = null;
private ShortBuffer shortBuffer = null;
public ScribeOutputStream( OutputStream stream )
{
super( stream );
}
private void clutchByteBuffer( int size )
{
if ( bBuffer != null )
{
if ( bBuffer.capacity() >= size )
{
return;
}
}
byteBuffer = new byte[ size ];
bBuffer = ByteBuffer.wrap( byteBuffer );
floatBuffer = null;
intBuffer = null;
shortBuffer = null;
}
/**
* Internal routine that creates a float buffer big enough to hold the
* number of floats specified. If the buffer is already big enough then it
* rewinds the buffer and returns
*
* @param size Number of floats to make room for
*/
private void clutchFloatBuffer( int size )
{
clutchByteBuffer( size * 4 );
if ( floatBuffer != null )
{
if ( floatBuffer.capacity() >= size )
{
floatBuffer.rewind();
return;
}
}
floatBuffer = bBuffer.asFloatBuffer();
}
/**
* Internal routine that creates an int buffer big enough to hold the number
* of ints specified. If the buffer is already big enough then it rewinds
* the buffer and returns
*
* @param size Number of floats to make room for
*/
private void clutchIntBuffer( int size )
{
clutchByteBuffer( size * 4 );
if ( intBuffer != null )
{
if ( intBuffer.capacity() >= size )
{
intBuffer.rewind();
return;
}
}
intBuffer = bBuffer.asIntBuffer();
}
private void clutchShortBuffer( int size )
{
clutchByteBuffer( size * 2 );
if ( shortBuffer != null )
{
if ( shortBuffer.capacity() >= size )
{
shortBuffer.rewind();
return;
}
}
shortBuffer = bBuffer.asShortBuffer();
}
/**
* Writes a scribable node to the output stream. Either the class or a class
* reference is written to the output file, followed by the scribed object.
*/
public void writeScribable( Scribable o ) throws java.io.IOException, UnscribableNodeEncountered
{
String key = o.getClass().getName();
X3DLog.debug( "Saving class ", key );
Integer ref = map.get( key );
if ( ref == null )
{
ref = new Integer( map.size() );
X3DLog.debug( " Not found, assigning to ref ", ref );
map.put( key, ref );
writeInt( CLASS_DEF );
writeUTF( key );
}
else
{
X3DLog.debug( " Found, assigning to ref ", ref );
writeInt( CLASS_REF );
writeInt( ref.intValue() );
}
o.save( this );
}
/**
* Writes out an array of float, using the number of elements specified
*/
public void writeFloatArray( float[] data, int len ) throws java.io.IOException
{
writeInt( len );
if ( Scribe.useNIO )
{
clutchFloatBuffer( len );
floatBuffer.put( data, 0, len );
this.write( byteBuffer, 0, len * 4 );
}
else
{
for (int i = 0; i < len; i++)
writeFloat( data[i] );
}
}
/**
* Writes out a complete array of float
*/
public void writeFloatArray( float[] data ) throws java.io.IOException
{
writeFloatArray( data, data.length );
}
/**
* Writes out an array of byte, using the number of elements specified
*/
public void writeByteArray( byte[] data, int len ) throws java.io.IOException
{
writeInt( len );
if ( Scribe.useNIO )
{
this.write( data, 0, len );
}
else
{
for (int i = 0; i < len; i++)
writeByte( data[i] );
}
}
/**
* Writes out a complete array of byte
*/
public void writeByteArray( byte[] data ) throws java.io.IOException
{
writeByteArray( data, data.length );
}
/**
* Writes out an array of float, using the number of elements specified
*/
public void writeVectorArray( Vector3f[] data, int len ) throws java.io.IOException
{
writeInt( len );
for ( int i = 0; i < len; i++ )
writeVector( data[i] );
}
/**
* Writes out a complete array of float
*/
public void writeVectorArray( Vector3f[] data ) throws java.io.IOException
{
writeVectorArray( data, data.length );
}
/**
* Writes out an array of int, using the number of elements specified
*/
public void writeIntArray( int[] data, int len ) throws java.io.IOException
{
writeInt( len );
if ( Scribe.useNIO )
{
clutchIntBuffer( len );
intBuffer.put( data, 0, len );
this.write( byteBuffer, 0, len * 4 );
}
else
{
for ( int i = 0; i < len; i++ )
writeInt( data[i] );
}
}
public void writeShortArray( short[] data, int len ) throws java.io.IOException
{
writeInt( len );
if ( Scribe.useNIO )
{
clutchShortBuffer( len );
shortBuffer.put( data, 0, len );
this.write( byteBuffer, 0, len * 2 );
}
else
{
for (int i = 0; i < len; i++)
writeFloat( data[i] );
}
}
/**
* Writes out a complete array of int
*/
public void writeIntArray( int[] data ) throws java.io.IOException
{
writeIntArray( data, data.length );
}
public void writeShortArray( short[] data ) throws java.io.IOException
{
writeShortArray( data, data.length );
}
/**
* Writes out a string
*/
public void writeString( String s ) throws java.io.IOException
{
writeInt( s.length() );
writeBytes( s );
}
public void writePoint( Point3f p ) throws java.io.IOException
{
writeFloat( p.getX() );
writeFloat( p.getY() );
writeFloat( p.getZ() );
}
public void writeVector( Vector3f v ) throws java.io.IOException
{
writeFloat( v.getX() );
writeFloat( v.getY() );
writeFloat( v.getZ() );
}
public void writeColor3f( Colorf v ) throws java.io.IOException
{
writeFloat( v.getRed() );
writeFloat( v.getGreen() );
writeFloat( v.getBlue() );
}
public void writeQuat( Quaternion4f v ) throws java.io.IOException
{
writeFloat( v.getA() );
writeFloat( v.getB() );
writeFloat( v.getC() );
writeFloat( v.getD() );
}
public void writeTexCoord( TexCoord2f v ) throws java.io.IOException
{
writeFloat( v.getS() );
writeFloat( v.getT() );
}
public void writeMatrix( Matrix3f v ) throws java.io.IOException
{
writeFloat( v.m00() );
writeFloat( v.m01() );
writeFloat( v.m02() );
writeFloat( v.m10() );
writeFloat( v.m11() );
writeFloat( v.m12() );
writeFloat( v.m20() );
writeFloat( v.m21() );
writeFloat( v.m22() );
}
public void writeMatrix( Matrix4f v ) throws java.io.IOException
{
writeFloat( v.m00() );
writeFloat( v.m01() );
writeFloat( v.m02() );
writeFloat( v.m03() );
writeFloat( v.m10() );
writeFloat( v.m11() );
writeFloat( v.m12() );
writeFloat( v.m13() );
writeFloat( v.m20() );
writeFloat( v.m21() );
writeFloat( v.m22() );
writeFloat( v.m23() );
writeFloat( v.m30() );
writeFloat( v.m31() );
writeFloat( v.m32() );
writeFloat( v.m33() );
}
public void writeDirectImage( DirectBufferedImage dbi ) throws IOException
{
if ( dbi != null )
{
int w = dbi.getWidth();
int h = dbi.getHeight();
writeInt( w );
writeInt( h );
writeInt( dbi.getDirectType().ordinal() );
byte[] bytes = ImageUtility.toByteArray( dbi );
write( bytes, 0, bytes.length );
}
else
{
writeInt( 0 );
}
}
/**
* writes out a buffered image. It is acceptable to pass in null since a
* byte is written to determine if there is an image or not. This writes out
* the raw image data, which can be a lot of disk space since there is no
* special compression being done for images.
*/
public void writeImage( BufferedImage b ) throws java.io.IOException
{
if ( b != null )
{
int w = b.getWidth();
int h = b.getHeight();
int[] pixels = new int[ w * h ];
b.getRGB( 0, 0, w, h, pixels, 0, w );
writeInt( w );
writeInt( h );
writeInt( b.getType() );
writeIntArray( pixels );
}
else
{
writeInt( 0 );
}
}
/**
* Writes the buffered image to the file using lossy JPG compression.
*
* @param b The image to save
* @param quality The quality of the output
* @throws IOException
*/
public void writeLossyImage( BufferedImage b, float quality ) throws java.io.IOException
{
if ( b != null )
{
writeInt( 1 ); // flag as non-null
int w = b.getWidth();
int h = b.getHeight();
ByteArrayOutputStream out = new ByteArrayOutputStream( w * h * 4 );
ImageIO.write(b, "JPG", out);
byte[] buf = out.toByteArray();
int size = out.size();
writeInt( size );
write( buf, 0, size );
out.close();
}
else
{
writeInt( 0 );
}
}
}