/* * com.fc.vrml.image.SFImageConverter * * Copyright (c) 1996-1998 Finlayson Consulting (FC). All Rights Reserved. * * FC grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) this software is not reverse engineereed without written * consent. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. FC AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL FC OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF FC HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or * redistribute the Software for such purposes. * */ package com.fc.vrml.image; import java.awt.Image; import java.awt.image.*; import vrml.field.ConstSFImage; import java.util.Observable; import java.util.Observer; import java.util.Vector; import java.util.Hashtable; import java.util.*; /** * * The SFImageConverter is used to asynchronously load images to a byte * array suitable for use as the source for VRML97 SFImage data. It * converts a Java (tm) image to a 1, 2, 3 or 4 color component ExternalSFImage. *
* To load VRML image data into the converter, create an SFImageConverter and start * production on it from an ImageProducer. Do not start production of more than one ImageProducer on an SFImageConverter. *
* The SFImageConverter implements the Observable interface, so objects that are interested * in SFImage data can register to receive this information by implementing * Observer interface and registering themselves as Observers with the addObserver(observer) * method. When this notifies registered Observers the Observers' update method will * be called with this and the status of the last call to imageComplete(). *
* That default notification behavior is to notify all Observers as each frame is loaded. * Set the load parameters to change the default * behavior of Observer notification as image frames are loaded. *
* If the keepFrames variable is set to false, the SFImageConverter only keeps the last frame delivered from * the ImageProducer and the frame index does not increment. If there are already frames stored then all except the last are discarded. * This is suitable for use when the ImageProducer produces an indefinite number of frames. * @see com.fc.vrml.image.ExternalSFImage * @see com.fc.vrml.image.ExternalSFImageObserver * @see com.fc.vrml.image.SFImageObserver * @version 0.5 * @author Ross A. Finlayson * @author FC VRML Group */ public class SFImageConverter extends Observable implements ImageConsumer{ private ColorModel _cm; private byte[] pixelbytes=null; private int _height=-1; private int _width=-1; private int numComponents=4; private boolean loading=false; private boolean loaded=false; private int flags; private int status=-1; private int frameindex=0; private Vector framevector; private int loadParameter=3; private boolean first=true; private int frameIncrementParameter=2; private int registeredObserverCount=0; private ExternalSFImage currentsfimage; /** * @see #loadImage */ public static final int LOADONLY=1; /** * @see #loadImage */ public static final int NOTIFYONFIRST=2; /** * @see #loadImage */ public static final int NOTIFYONFRAME=3; /** * @see #loadImage */ public static final int NOTIFYONLOAD=4; /** * @see #getExternalSFImage() */ public static final int NOINCREMENT=0; /** * @see #getExternalSFImage() */ public static final int INCREMENTONEACH=1; /** * @see #getExternalSFImage() */ public static final int INCREMENTONALL=2; /** * Default value is true. * @see #setKeepFrames */ protected boolean keepFrames=true; /** * Default constructor for 4 color components. */ public SFImageConverter(){ this(4); } /** * Constructor for given number of color components. */ public SFImageConverter(int components){ if ((components!=1)&&(components!=2)&&(components!=3)&&(components!=4)) components=4; numComponents=components; framevector=new Vector(); } /** * This method loads data from an image source. If the load parameter is NOTIFYONFIRST, * the first frame of the image is notified to the registered Observers on load * and remaining frames are loaded. If the load parameter equals NOTIFYONFRAME, * registered observers are notified as each frame is loaded. If the load parameter is NOTIFYONLOAD, * all frames are loaded and then the registered Observers are notified of the first frame. * If the load parameter * is LOADONLY there is no notification. *
* ImageProducer.startProduction() is parallel to this method and will follow load parameters. *
* If loadImage is called while image is loading, this is ignored. If loadImage is called after image * loading is complete, then image data will be reloaded. */ public void loadImage(Image image){ loadImage(image.getSource()); } /** * This method loads data from an image source. If the load parameter is NOTIFYONFIRST, * the first frame of the image is notified to the registered Observers on load * and remaining frames are loaded. If the load parameter equals NOTIFYONFRAME, * registered observers are notified as each frame is loaded. If the load parameter is NOTIFYONLOAD, * all frames are loaded and then the registered Observers are notified of the first frame. * If the load parameter * is LOADONLY there is no notification. *
* ImageProducer.startProduction() is parallel to this method and will follow load parameters. *
* If loadImage is called while image is loading, this is ignored. If loadImage is called after image
* loading is complete, then image data will be reloaded.
*/
public void loadImage(ImageProducer imageproducer){
if (loading==true) return;
loading=true;
imageproducer.startProduction(this);
}
/**
* Default load parameter is NOTIFYONFRAME.
* @param parameter parameter for load notification.
* @see #loadImage
*/
public void setLoadParameter(int parameter){
loadParameter=parameter;
}
/**
* @return parameter for load notification
*/
public int getLoadParameter(){
return loadParameter;
}
private synchronized ExternalSFImage createExternalSFImage(){
return new ExternalSFImage(getWidth(), getHeight(), getComponents(), getBytes() );
}
/**
* Returns ExternalSFImage at current frame index. If frame increment parameter is
* NOINCREMENT, the frame index is not incremented. If the frame index parameter
* is INCREMENTONEACH, the frame index is incremented by one on each call to this method. If the frame
* index parameter is INCREMENTONALL, the frame index is incremented when all registered Observers have called this method.
* If frame index
* is set equal to frames() then the frame index is reset to zero.
* @return ExternalSFImage of current frame index or null if incomplete or nonexistent
*/
public synchronized ExternalSFImage getExternalSFImage(){
currentsfimage=(ExternalSFImage)framevector.elementAt(frameindex);
if (frameIncrementParameter==INCREMENTONEACH){
frameindex++;
if (frameindex>=frames()) frameindex=0;
}
if (frameIncrementParameter==INCREMENTONALL){
if (registeredObserverCount==0 ) registeredObserverCount=countObservers();
registeredObserverCount--;
if (registeredObserverCount==0){
frameindex++;
if (frameindex>=frames()) frameindex=0;
}
}
return currentsfimage;
}
/**
* Returns ExternalSFImage at frame index index. If index
* is greater than or equals to frames() then this returns the frame at frame index index modulo frames.
* The current frame index is not changed.
* @param index frameindex of frame to return
* @return ExternalSFImage of frame or null if incomplete or nonexistent
*/
public synchronized ExternalSFImage getExternalSFImage(int index){
if (index >= frames()) index%=frames();
if (index < 0 ) return null;
return (ExternalSFImage) framevector.elementAt(index);
}
/**
* Default frame increment parameter is INCREMENTONALL.
* @param parameter parameter to set as frame index increment parameter
* @see #getExternalSFImage
*/
public void setFrameIncrementParameter(int parameter){
frameIncrementParameter=parameter;
}
/**
* @return current frame increment parameter
*/
public int getFrameIncrementParameter(){
return frameIncrementParameter;
}
/**
* @return width of current frame of image or -1 if unknown
*/
public int getWidth(){
return _width;
}
/**
* @return height of current frame of image or -1 if unknown
*/
public int getHeight(){
return _height;
}
/**
* @return current frame's SFImage pixel data byte array or null
*/
public byte[] getBytes(){
return pixelbytes;
}
/**
* @return number of color components of SFImage
*/
public int getComponents(){
return numComponents;
}
/**
* Calls the imageComplete method of this ImageConsumer. Do not call this method directly.
*/
public synchronized void imageComplete(int status){
//System.out.println("status: "+status);
if (loading==false) loading=true;
status=status;
if (status==SINGLEFRAMEDONE){
if (keepFrames==true){
framevector.addElement(createExternalSFImage());
}
else{
framevector.setElementAt(createExternalSFImage(), 0);
}
if (loadParameter == LOADONLY){
return;
}
if (loadParameter == NOTIFYONFIRST){
if (first==true){
setChanged();
notifyObservers(new Integer(status));
first=false;
}
return;
}
if (loadParameter == NOTIFYONFRAME){
setChanged();
notifyObservers(new Integer(status));
return;
}
}
if (status==STATICIMAGEDONE){
loading=false;
loaded=true;
if (keepFrames==true){
framevector.addElement(createExternalSFImage());
}
else{
framevector.setElementAt(createExternalSFImage(), 0);
}
if (loadParameter == LOADONLY){
return;
}
if (loadParameter == NOTIFYONFIRST){
if (first==true){
setChanged();
notifyObservers(new Integer(status));
first=false;
}
return;
}
if (loadParameter == NOTIFYONLOAD){
setChanged();
notifyObservers(new Integer(status));
return;
}
if (loadParameter == NOTIFYONFRAME){
setChanged();
notifyObservers(new Integer(status));
return;
}
}
if ((status==IMAGEERROR) | (status==IMAGEABORTED)) {
if (loaded==false){
loading=false;
setChanged();
notifyObservers(new Integer(status));
return;
}
else {
return;
}
}
}
/**
* Sets the ColorModel of this ImageConsumer. Do not call this method directly.
*/
public void setColorModel(ColorModel cm){
_cm=cm;
}
/**
* Sets the Dimensions of this ImageConsumer. Do not call this method directly.
*/
public void setDimensions(int width, int height){
_width=width;
_height=height;
if ((_width!=-1) && (_height!=-1)){
pixelbytes=new byte[_width*_height*numComponents];
}
}
/**
* Sets the image hints of this ImageConsumer. Do not call this method directly.
*/
public void setHints(int flags){
if ( (flags & SINGLEPASS) != 0) {
//System.out.println("SINGLEPASS");
// prepare buffer for single pass frame delivery
}
if ( (flags & TOPDOWNLEFTRIGHT) !=0) {
//System.out.println("TOPDOWNLEFTRIGHT");
// height will increment ordinally
}
if ( (flags & COMPLETESCANLINES) !=0) {
//System.out.println("COMPLETESCANLINES");
// completescanlines
// possible error
// if not complete scan lines? it's all on width
}
if ( (flags & RANDOMPIXELORDER) !=0) {
//System.out.println("RANDOMPIXELORDER");
// possible error
}
if ( (flags & SINGLEFRAME) !=0){
//System.out.println("SINGLEFRAME");
// image is single frame optimize for such
}
return;
}
/**
* Sets the properties of this ImageConsumer. Do not call this method directly.
*/
public void setProperties(Hashtable props){
return;
}
/**
* Calls the setPixels() method of this ImageConsumer. Do not call this method directly.
*/
public final void setPixels(int x, int y, int w, int h, ColorModel cm, int[] pixels, int offset, int scansize){
if (loading==false) loading=true;
if (loaded==true) loaded=false;
int heightminusone=_height-1;
int widthplusoffset=w+offset;
int itimesw;
if (_cm!=cm)_cm=cm;
int javapixel=0;
int pixeloffset=0;
int repeat=1;
int dim= w*h;
if (dim != pixels.length){
repeat= dim/pixels.length;
}
if (repeat>1){
for (int i=0;i