/* * 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>>24))&0xff )<<24); //System.out.println("a"+(javapixel>>>24)); //} switch(numComponents){ case 1: pixelbytes[pixeloffset++]= (byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); break; case 2: pixelbytes[pixeloffset++]= (byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; case 3: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); break; case 4: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; } } } } else{ for (int i=0;i>>24))&0xff )<<24); //System.out.println("a"+(javapixel>>>24)); //} switch(numComponents){ case 1: pixelbytes[pixeloffset++]=(byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); break; case 2: pixelbytes[pixeloffset++]=(byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; case 3: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); break; case 4: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; } } } } } /** * 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, byte[] 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, pixeloffset; int repeat=1; int dim= w*h; if (dim != pixels.length){ repeat= dim/pixels.length; } if (repeat>1){ for (int i=0;i>>24))&0xff )<<24); //System.out.println("a"+(javapixel>>>24)); //} switch(numComponents){ case 1: pixelbytes[pixeloffset++]=(byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); break; case 2: pixelbytes[pixeloffset++]=(byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; case 3: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); break; case 4: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; } } } } else{ for (int i=0;i>>24))&0xff )<<24); //System.out.println("a"+(javapixel>>>24)); //} switch(numComponents){ case 1: pixelbytes[pixeloffset++]=(byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); break; case 2: pixelbytes[pixeloffset++]=(byte)( .2125 * (javapixel >>> 16 & 0xff) + .7154 * (javapixel >>> 8 & 0xff) + .0721 * (javapixel & 0xff) ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; case 3: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); break; case 4: pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 16 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 8 & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel & 0xff ); pixelbytes[pixeloffset++]=(byte) ( javapixel >>> 24 & 0xff ); break; } } } } } /** * This method updates registered Observers. * If the image data at the current frameindex is errored or incomplete, the registered Observers are * notified with ImageConsumer.IMAGEERROR. */ public void updateObservers(){ if ( framevector.elementAt(frameindex)==null ){ setChanged(); notifyObservers(new Integer(ImageConsumer.IMAGEERROR)); return; } setChanged(); notifyObservers( new Integer(status) ); } /** * This method sets the frame index to the index argument and notifies registered Observers. * If index is greater than or equal to frames() the frame index is set to index modulo frames(). * @param index index of the frame to set as current frameindex */ public void updateObservers(int index){ if (index >= 0 && index < frames() ) {frameindex=index;} else {frameindex=0;} if ( framevector.elementAt(index)==null ){ setChanged(); notifyObservers(new Integer(SFImageConverter.IMAGEERROR)); return; } setChanged(); notifyObservers( new Integer(status) ); } /** * @return completion status of the last call to imageComplete(int) or -1 if not yet set */ public int getStatus(){ return status; } /** * This method returns false while all image frames are not loaded or errored. */ public boolean isLoaded(){ return loaded; } /** * @return currently available frames or -1 if unknown */ public int frames(){ return framevector.size(); } /** * Sets the frameindex to index. If index is greater than or equal to frames(), * the frame index is set to index modulo frames(). * @param index index of frame to set as current frameindex */ public void setFrameIndex(int index){ if (index >= frames()) index%=frames(); if (index<0) return; frameindex=index; } /** * @return current frameindex */ public int getFrameIndex(){ return frameindex; } /** * @param boolean keepFrames */ public void setKeepFrames(boolean keep){ keepFrames=keep; if (keepFrames==false){ for (int i=0;i<(frames()-1);i++){ framevector.removeElementAt(0); } frameindex=0; } } /** * @return value of keepFrames variable */ public boolean getKeepFrames(){ return keepFrames; } }