javax.media.j3d.Canvas3D.java Source code

Java tutorial

Introduction

Here is the source code for javax.media.j3d.Canvas3D.java

Source

/*
 * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 */

package javax.media.j3d;

import java.awt.AWTEvent;
import java.awt.Canvas;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Window;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import javax.vecmath.Color3f;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector4d;

/**
 * The Canvas3D class provides a drawing canvas for 3D rendering.  It
 * is used either for on-screen rendering or off-screen rendering.
 * Canvas3D is an extension of the AWT Canvas class that users may
 * further subclass to implement additional functionality.
 * <p>
 * The Canvas3D object extends the Canvas object to include
 * 3D-related information such as the size of the canvas in pixels,
 * the Canvas3D's location, also in pixels, within a Screen3D object,
 * and whether or not the canvas has stereo enabled.
 * <p>
 * Because all Canvas3D objects contain a
 * reference to a Screen3D object and because Screen3D objects define
 * the size of a pixel in physical units, Java 3D can convert a Canvas3D
 * size in pixels to a physical world size in meters. It can also
 * determine the Canvas3D's position and orientation in the
 * physical world.
 * <p>
 * <b>On-screen Rendering vs. Off-screen Rendering</b>
 * <p>
 * The Canvas3D class is used either for on-screen rendering or
 * off-screen rendering.
 * On-screen Canvas3Ds are added to AWT or Swing Container objects
 * like any other canvas.  Java 3D automatically and continuously
 * renders to all on-screen canvases that are attached to an active
 * View object.  On-screen Canvas3Ds can be either single or double
 * buffered and they can be either stereo or monoscopic.
 * <p>
 * Off-screen Canvas3Ds must not be added to any Container.  Java 3D
 * renders to off-screen canvases in response to the
 * <code>renderOffScreenBuffer</code> method.  Off-screen Canvas3Ds
 * are single buffered.  However, on many systems, the actual
 * rendering is done to an off-screen hardware buffer or to a 3D
 * library-specific buffer and only copied to the off-screen buffer of
 * the Canvas when the rendering is complete, at "buffer swap" time.
 * Off-screen Canvas3Ds are monoscopic.
 * <p>
 * The setOffScreenBuffer method sets the off-screen buffer for this
 * Canvas3D. The specified image is written into by the Java 3D renderer.
 * The size of the specified ImageComponent determines the size, in
 * pixels, of this Canvas3D - the size inherited from Component is
 * ignored. Note that the size, physical width, and physical height of the
 * associated Screen3D must be set
 * explicitly prior to rendering. Failure to do so will result in an
 * exception.
 * <p>
 * The getOffScreenBuffer method retrieves the off-screen
 * buffer for this Canvas3D.
 * <p>
 * The renderOffScreenBuffer method schedules the rendering of a frame
 * into this Canvas3D's off-screen buffer. The rendering is done from
 * the point of view of the View object to which this Canvas3D has been
 * added. No rendering is performed if this Canvas3D object has not been
 * added to an active View. This method does not wait for the rendering
 * to actually happen. An application that wishes to know when the
 * rendering is complete must either subclass Canvas3D and
 * override the postSwap method, or call waitForOffScreenRendering.
 * <p>
 * The setOfScreenLocation methods set the location of this off-screen
 * Canvas3D.  The location is the upper-left corner of the Canvas3D
 * relative to the upper-left corner of the corresponding off-screen
 * Screen3D. The function of these methods is similar to that of
 * Component.setLocation for on-screen Canvas3D objects. The default
 * location is (0,0).
 * <p>
 * <b>Accessing and Modifying an Eye's Image Plate Position</b>
 * <p>
 * A Canvas3D object provides sophisticated applications with access
 * to the eye's position information in head-tracked, room-mounted
 * runtime environments. It also allows applications to manipulate
 * the position of an eye relative to an image plate in non-head-tracked
 * runtime environments.
 * <p>
 * The setLeftManualEyeInImagePlate and setRightManualEyeInImagePlate
 * methods set the position of the manual left and right eyes in image
 * plate coordinates. These values determine eye placement when a head
 * tracker is not in use and the application is directly controlling the
 * eye position in image plate coordinates. In head-tracked mode or
 * when the windowEyepointPolicy is RELATIVE_TO_FIELD_OF_VIEW or
 * RELATIVE_TO_COEXISTENCE, this
 * value is ignored. When the windowEyepointPolicy is RELATIVE_TO_WINDOW,
 * only the Z value is used.
 * <p>
 * The getLeftEyeInImagePlate, getRightEyeInImagePlate, and
 * getCenterEyeInImagePlate methods retrieve the actual position of the
 * left eye, right eye, and center eye in image plate coordinates and
 * copy that value into the object provided. The center eye is the
 * fictional eye half-way between the left and right eye. These three
 * values are a function of the windowEyepointPolicy, the tracking
 * enable flag, and the manual left, right, and center eye positions.
 * <p>
 * <b>Monoscopic View Policy</b>
 * <p>
 * The setMonoscopicViewPolicy and getMonoscopicViewPolicy methods
 * set and retrieve the policy regarding how Java 3D generates monoscopic
 * view. If the policy is set to View.LEFT_EYE_VIEW, the view generated
 * corresponds to the view as seen from the left eye. If set to
 * View.RIGHT_EYE_VIEW, the view generated corresponds to the view as
 * seen from the right eye. If set to View.CYCLOPEAN_EYE_VIEW, the view
 * generated corresponds to the view as seen from the "center eye," the
 * fictional eye half-way between the left and right eye. The default
 * monoscopic view policy is View.CYCLOPEAN_EYE_VIEW.
 * <p>
 * <b>Immediate Mode Rendering</b>
 * <p>
 * Pure immediate-mode rendering provides for those applications and
 * applets that do not want Java 3D to do any automatic rendering of
 * the scene graph. Such applications may not even wish to build a
 * scene graph to represent their graphical data. However, they use
 * Java 3D's attribute objects to set graphics state and Java 3D's
 * geometric objects to render geometry.
 * <p>
 * A pure immediate mode application must create a minimal set of
 * Java 3D objects before rendering. In addition to a Canvas3D object,
 * the application must create a View object, with its associated
 * PhysicalBody and PhysicalEnvironment objects, and the following
 * scene graph elements: a VirtualUniverse object, a high-resolution
 * Locale object, a BranchGroup node object, a TransformGroup node
 * object with associated transform, and a ViewPlatform
 * leaf node object that defines the position and orientation within
 * the virtual universe that generates the view.
 * <p>
 * In immediate mode, all rendering is done completely under user
 * control. It is necessary for the user to clear the 3D canvas,
 * render all geometry, and swap the buffers.  Additionally,
 * rendering the right and left eye for stereo viewing becomes the
 * sole responsibility of the application.  In pure immediate mode,
 * the user must stop the Java 3D renderer, via the
 * Canvas3D object <code>stopRenderer</code> method, prior to adding the
 * Canvas3D object to an active View object (that is, one that is
 * attached to a live ViewPlatform object).
 * <p>
 * Other Canvas3D methods related to immediate mode rendering are:
 * <p>
 * <ul>
 * <code>getGraphicsContext3D</code> retrieves the immediate-mode
 * 3D graphics context associated with this Canvas3D. It creates a
 * new graphics context if one does not already exist.
 * <p>
 * <code>getGraphics2D</code> retrieves the
 * 2D graphics object associated with this Canvas3D. It creates a
 * new 2D graphics object if one does not already exist.
 * <p>
 * <code>swap</code> synchronizes and swaps buffers on a
 * double-buffered canvas for this Canvas3D object. This method
 * should only be called if the Java 3D renderer has been stopped.
 * In the normal case, the renderer automatically swaps
 * the buffer.
 * </ul>
 *
 * <p>
 * <b>Mixed Mode Rendering</b>
 * <p>
 * Mixing immediate mode and retained or compiled-retained mode
 * requires more structure than pure immediate mode. In mixed mode,
 * the Java 3D renderer is running continuously, rendering the scene
 * graph into the canvas.
 *
 * <p>
 * Canvas3D methods related to mixed mode rendering are:
 *
 * <p>
 * <ul>
 * <code>preRender</code> called by the Java 3D rendering loop after
 * clearing the canvas and before any rendering has been done for
 * this frame.
 * <p>
 * <code>postRender</code> called by the Java 3D rendering loop after
 * completing all rendering to the canvas for this frame and before
 * the buffer swap.
 * <p>
 * <code>postSwap</code> called by the Java 3D rendering loop after
 * completing all rendering to the canvas, and all other canvases
 * associated with this view, for this frame following the
 * buffer swap.
 * <p>
 * <code>renderField</code> called by the Java 3D rendering loop
 * during the execution of the rendering loop. It is called once
 * for each field (i.e., once per frame on a mono system or once
 * each for the right eye and left eye on a two-pass stereo system.
 * </ul>
 * <p>
 * The above callback methods are called by the Java 3D rendering system
 * and should <i>not</i> be called by an application directly.
 *
 * <p>
 * The basic Java 3D <i>stereo</i> rendering loop,
 * executed for each Canvas3D, is as follows:
 * <ul><pre>
 * clear canvas (both eyes)
 * call preRender()                           // user-supplied method
 * set left eye view
 * render opaque scene graph objects
 * call renderField(FIELD_LEFT)               // user-supplied method
 * render transparent scene graph objects
 * set right eye view
 * render opaque scene graph objects again
 * call renderField(FIELD_RIGHT)              // user-supplied method
 * render transparent scene graph objects again
 * call postRender()                          // user-supplied method
 * synchronize and swap buffers
 * call postSwap()                            // user-supplied method
 * </pre></ul>
 * <p>
 * The basic Java 3D <i>monoscopic</i> rendering loop is as follows:
 * <ul><pre>
 * clear canvas
 * call preRender()                            // user-supplied method
 * set view
 * render opaque scene graph objects
 * call renderField(FIELD_ALL)                 // user-supplied method
 * render transparent scene graph objects
 * call postRender()                           // user-supplied method
 * synchronize and swap buffers
 * call postSwap()                             // user-supplied method
 * </pre></ul>
 * <p>
 * In both cases, the entire loop, beginning with clearing the canvas
 * and ending with swapping the buffers, defines a frame. The application
 * is given the opportunity to render immediate-mode geometry at any of
 * the clearly identified spots in the rendering loop. A user specifies
 * his or her own rendering methods by extending the Canvas3D class and
 * overriding the preRender, postRender, postSwap, and/or renderField
 * methods.
 * Updates to live Geometry, Texture, and ImageComponent objects
 * in the scene graph are not allowed from any of these callback
 * methods.
 *
 * <p>
 * <b>Serialization</b>
 * <p>
 * Canvas3D does <i>not</i> support serialization.  An attempt to
 * serialize a Canvas3D object will result in an
 * UnsupportedOperationException being thrown.
 *
 * <p>
 * <b>Additional Information</b>
 * <p>
 * For more information, see the
 * <a href="doc-files/intro.html">Introduction to the Java 3D API</a> and
 * <a href="doc-files/ViewModel.html">View Model</a>
 * documents.
 *
 * @see Screen3D
 * @see View
 * @see GraphicsContext3D
 */
public class Canvas3D extends Canvas {
    /**
     * Specifies the left field of a field-sequential stereo rendering loop.
     * A left field always precedes a right field.
     */
    public static final int FIELD_LEFT = 0;

    /**
     * Specifies the right field of a field-sequential stereo rendering loop.
     * A right field always follows a left field.
     */
    public static final int FIELD_RIGHT = 1;

    /**
     * Specifies a single-field rendering loop.
     */
    public static final int FIELD_ALL = 2;

    //
    // The following constants are bit masks to specify which of the node
    // components are dirty and need updates.
    //
    // Values for the geometryType field.
    static final int POLYGONATTRS_DIRTY = 0x01;
    static final int LINEATTRS_DIRTY = 0x02;
    static final int POINTATTRS_DIRTY = 0x04;
    static final int MATERIAL_DIRTY = 0x08;
    static final int TRANSPARENCYATTRS_DIRTY = 0x10;
    static final int COLORINGATTRS_DIRTY = 0x20;

    // Values for lightbin, env set, texture, texture setting etc.
    static final int LIGHTBIN_DIRTY = 0x40;
    static final int LIGHTENABLES_DIRTY = 0x80;
    static final int AMBIENTLIGHT_DIRTY = 0x100;
    static final int ATTRIBUTEBIN_DIRTY = 0x200;
    static final int TEXTUREBIN_DIRTY = 0x400;
    static final int TEXTUREATTRIBUTES_DIRTY = 0x800;
    static final int RENDERMOLECULE_DIRTY = 0x1000;
    static final int FOG_DIRTY = 0x2000;
    static final int MODELCLIP_DIRTY = 0x4000;
    static final int VIEW_MATRIX_DIRTY = 0x8000;
    // static final int SHADER_DIRTY              = 0x10000; Not ready for this yet -- JADA

    //
    // Flag that indicates whether this Canvas3D is an off-screen Canvas3D
    //
    boolean offScreen = false;

    //
    // Issue 131: Flag that indicates whether this Canvas3D is a manually
    // rendered Canvas3D (versus an automatically rendered Canvas3D).
    //
    // NOTE: manualRendering only applies to off-screen Canvas3Ds at this time.
    // We have no plans to ever change this, but if we do, it might be necessary
    // to determine which, if any, of the uses of "manualRendering" should be
    // changed to "manualRendering&&offScreen"
    //
    boolean manualRendering = false;

    // user specified offScreen Canvas location
    Point offScreenCanvasLoc;

    // user specified offScreen Canvas dimension
    Dimension offScreenCanvasSize;

    //
    // Flag that indicates whether off-screen rendering is in progress or not
    //
    volatile boolean offScreenRendering = false;

    //
    // Flag that indicates we are waiting for an off-screen buffer to be
    // created or destroyed by the Renderer.
    //
    volatile boolean offScreenBufferPending = false;

    //
    // ImageComponent used for off-screen rendering
    //
    ImageComponent2D offScreenBuffer = null;

    // flag that indicates whether this canvas will use shared context
    boolean useSharedCtx = true;

    //
    // Read-only flag that indicates whether stereo is supported for this
    // canvas.  This is always false for off-screen canvases.
    //
    boolean stereoAvailable;

    //
    // Flag to enable stereo rendering, if allowed by the
    // stereoAvailable flag.
    //
    boolean stereoEnable = true;

    //
    // This flag is set when stereo mode is both enabled and
    // available.  Code that looks at stereo mode should use this
    // flag.
    //
    boolean useStereo;

    // Indicate whether it is left or right stereo pass currently
    boolean rightStereoPass = false;

    //
    // Specifies how Java 3D generates monoscopic view
    // (LEFT_EYE_VIEW, RIGHT_EYE_VIEW, or CYCLOPEAN_EYE_VIEW).
    //
    int monoscopicViewPolicy = View.CYCLOPEAN_EYE_VIEW;

    // User requested stencil size
    int requestedStencilSize;

    // Actual stencil size return for this canvas
    int actualStencilSize;

    // True if stencil buffer is available for user
    boolean userStencilAvailable;

    // True if stencil buffer is available for system ( decal )
    boolean systemStencilAvailable;

    //
    // Read-only flag that indicates whether double buffering is supported
    // for this canvas.  This is always false for off-screen canvases.
    //
    boolean doubleBufferAvailable;

    //
    // Flag to enable double buffered rendering, if allowed by the
    // doubleBufferAvailable flag.
    //
    boolean doubleBufferEnable = true;

    //
    // This flag is set when doubleBuffering is both enabled and
    // available Code that enables or disables double buffering should
    // use this flag.
    //
    boolean useDoubleBuffer;

    //
    // Read-only flag that indicates whether scene antialiasing
    // is supported for this canvas.
    //
    boolean sceneAntialiasingAvailable;
    boolean sceneAntialiasingMultiSamplesAvailable;

    // Use to see whether antialiasing is already set
    private boolean antialiasingSet = false;

    //
    // Read-only flag that indicates the size of the texture color
    // table for this canvas.  A value of 0 indicates that the texture
    // color table is not supported.
    //
    int textureColorTableSize;

    // number of active/enabled texture unit
    int numActiveTexUnit = 0;

    // index iof last enabled texture unit
    int lastActiveTexUnit = -1;

    // True if shadingLanguage is supported, otherwise false.
    boolean shadingLanguageGLSL = false;

    // Query properties
    J3dQueryProps queryProps;

    // Flag indicating a fatal rendering error of some sort
    private boolean fatalError = false;

    //
    // The positions of the manual left and right eyes in image-plate
    // coordinates.
    // By default, we will use the center of the screen for X and Y values
    // (X values are adjusted for default eye separation), and
    // 0.4572 meters (18 inches) for the Z value.
    // These match defaults elsewhere in the system.
    //
    Point3d leftManualEyeInImagePlate = new Point3d(0.142, 0.135, 0.4572);
    Point3d rightManualEyeInImagePlate = new Point3d(0.208, 0.135, 0.4572);

    //
    // View that is attached to this Canvas3D.
    //
    View view = null;

    // View waiting to be set
    View pendingView;

    //
    // View cache for this canvas and its associated view.
    //
    CanvasViewCache canvasViewCache = null;

    // Issue 109: View cache for this canvas, for computing view frustum planes
    CanvasViewCache canvasViewCacheFrustum = null;

    // Since multiple renderAtomListInfo, share the same vecBounds
    // we want to do the intersection test only once per renderAtom
    // this flag is set to true after the first intersect and set to
    // false during checkForCompaction in renderBin
    boolean raIsVisible = false;

    RenderAtom ra = null;

    // Stereo related field has changed.
    static final int STEREO_DIRTY = 0x01;
    // MonoscopicViewPolicy field has changed.
    static final int MONOSCOPIC_VIEW_POLICY_DIRTY = 0x02;
    // Left/right eye in image plate field has changed.
    static final int EYE_IN_IMAGE_PLATE_DIRTY = 0x04;
    // Canvas has moved/resized.
    static final int MOVED_OR_RESIZED_DIRTY = 0x08;

    // Canvas Background changed (this may affect doInfinite flag)
    static final int BACKGROUND_DIRTY = 0x10;

    // Canvas Background Image changed
    static final int BACKGROUND_IMAGE_DIRTY = 0x20;

    // Mask that indicates this Canvas view dependence info. has changed,
    // and CanvasViewCache may need to recompute the final view matries.
    static final int VIEW_INFO_DIRTY = (STEREO_DIRTY | MONOSCOPIC_VIEW_POLICY_DIRTY | EYE_IN_IMAGE_PLATE_DIRTY
            | MOVED_OR_RESIZED_DIRTY | BACKGROUND_DIRTY | BACKGROUND_IMAGE_DIRTY);

    // Issue 163: Array of dirty bits is used because the Renderer and
    // RenderBin run asynchronously. Now that they each have a separate
    // instance of CanvasViewCache (due to the fix for Issue 109), they
    // need separate dirty bits. Array element 0 is used for the Renderer and
    // element 1 is used for the RenderBin.
    static final int RENDERER_DIRTY_IDX = 0;
    static final int RENDER_BIN_DIRTY_IDX = 1;
    int[] cvDirtyMask = new int[2];

    // This boolean informs the J3DGraphics2DImpl that the window is resized
    boolean resizeGraphics2D = true;
    //
    // This boolean allows an application to start and stop the render
    // loop on this canvas.
    //
    volatile boolean isRunning = true;

    // This is used by MasterControl only. MC relay on this in a
    // single loop to set renderer thread. During this time,
    // the isRunningStatus can't change by user thread.
    volatile boolean isRunningStatus = true;

    // This is true when the canvas is ready to be rendered into
    boolean active = false;

    // This is true when the canvas is visible
    boolean visible = false;

    // This is true if context need to recreate
    boolean ctxReset = true;

    // The Screen3D that corresponds to this Canvas3D
    Screen3D screen = null;

    // Flag to indicate that image is render completely
    // so swap is valid.
    boolean imageReady = false;

    // The 3D Graphics context used for immediate mode rendering
    // into this canvas.
    GraphicsContext3D graphicsContext3D = null;
    boolean waiting = false;
    boolean swapDone = false;

    GraphicsConfiguration graphicsConfiguration;

    // The Java 3D Graphics2D object used for Java2D/AWT rendering
    // into this Canvas3D
    J3DGraphics2DImpl graphics2D = null;

    // Lock used to synchronize the creation of the 2D and 3D
    // graphics context objects
    Object gfxCreationLock = new Object();

    // The source of the currently loaded localToVWorld for this Canvas3D
    // (used to only update the model matrix when it changes)
    //    Transform3D   localToVWorldSrc = null;

    // The current vworldToEc Transform
    Transform3D vworldToEc = new Transform3D();

    // The view transform (VPC to EC) for the current eye.
    // NOTE that this is *read-only*
    Transform3D vpcToEc;

    // Opaque object representing the underlying drawable (window). This
    // is defined by the Pipeline.
    Drawable drawable = null;

    // graphicsConfigTable is a static hashtable which allows getBestConfiguration()
    // in NativeConfigTemplate3D to map a GraphicsConfiguration to the pointer
    // to the actual GLXFBConfig that glXChooseFBConfig() returns.  The Canvas3D
    // doesn't exist at the time getBestConfiguration() is called, and
    // X11GraphicsConfig neither maintains this pointer nor provides a public
    // constructor to allow Java 3D to extend it.
    static Hashtable<GraphicsConfiguration, GraphicsConfigInfo> graphicsConfigTable = new Hashtable<GraphicsConfiguration, GraphicsConfigInfo>();

    // The native graphics version, vendor, and renderer information
    String nativeGraphicsVersion = "<UNKNOWN>";
    String nativeGraphicsVendor = "<UNKNOWN>";
    String nativeGraphicsRenderer = "<UNKNOWN>";

    boolean firstPaintCalled = false;

    // This reflects whether or not this canvas has seen an addNotify. It is
    // forced to true for off-screen canvases
    boolean added = false;

    // Flag indicating whether addNotify has been called (so we don't process it twice).
    private boolean addNotifyCalled = false;

    // This is the id for the underlying graphics context structure.
    Context ctx = null;

    // since the ctx id can be the same as the previous one,
    // we need to keep a time stamp to differentiate the contexts with the
    // same id
    volatile long ctxTimeStamp = 0;

    // The current context setting for local eye lighting
    boolean ctxEyeLightingEnable = false;

    // This AppearanceRetained Object refelects the current state of this
    // canvas.  It is used to optimize setting of attributes at render time.
    AppearanceRetained currentAppear = new AppearanceRetained();

    // This MaterialRetained Object refelects the current state of this canvas.
    // It is used to optimize setting of attributes at render time.
    MaterialRetained currentMaterial = new MaterialRetained();

    /**
     * The object used for View Frustum Culling
     */
    CachedFrustum viewFrustum = new CachedFrustum();

    /**
     * The RenderBin bundle references used to decide what the underlying
     * context contains.
     */
    LightBin lightBin = null;
    EnvironmentSet environmentSet = null;
    AttributeBin attributeBin = null;
    ShaderBin shaderBin = null;
    RenderMolecule renderMolecule = null;
    PolygonAttributesRetained polygonAttributes = null;
    LineAttributesRetained lineAttributes = null;
    PointAttributesRetained pointAttributes = null;
    MaterialRetained material = null;
    boolean enableLighting = false;
    TransparencyAttributesRetained transparency = null;
    ColoringAttributesRetained coloringAttributes = null;
    Transform3D modelMatrix = null;
    Transform3D projTrans = null;
    TextureBin textureBin = null;

    /**
     * cached RenderBin states for lazy native states update
     */
    LightRetained lights[] = null;
    int frameCount[] = null;
    long enableMask = -1;
    FogRetained fog = null;
    ModelClipRetained modelClip = null;
    Color3f sceneAmbient = new Color3f();
    TextureUnitStateRetained[] texUnitState = null;

    /**
     * These cached values are only used in Pure Immediate and Mixed Mode rendering
     */
    TextureRetained texture = null;
    TextureAttributesRetained texAttrs = null;
    TexCoordGenerationRetained texCoordGeneration = null;
    RenderingAttributesRetained renderingAttrs = null;
    AppearanceRetained appearance = null;

    ShaderProgramRetained shaderProgram = null;

    // only used in Mixed Mode rendering
    Object appHandle = null;

    /**
     * Dirty bit to determine if the NodeComponent needs to be re-sent
     * down to the underlying API
     */
    int canvasDirty = 0xffff;

    // True when either one of dirtyRenderMoleculeList,
    // dirtyDlistPerRinfoList, dirtyRenderAtomList size > 0
    boolean dirtyDisplayList = false;

    ArrayList<RenderMolecule> dirtyRenderMoleculeList = new ArrayList<RenderMolecule>();
    ArrayList<RenderAtomListInfo> dirtyRenderAtomList = new ArrayList<RenderAtomListInfo>();
    // List of (Rm, rInfo) pair of individual dlists that need to be rebuilt
    ArrayList<Object[]> dirtyDlistPerRinfoList = new ArrayList<Object[]>();

    ArrayList<Integer> displayListResourceFreeList = new ArrayList<Integer>();
    ArrayList<Integer> textureIdResourceFreeList = new ArrayList<Integer>();

    // an unique bit to identify this canvas
    int canvasBit = 0;
    // an unique number to identify this canvas : ( canvasBit = 1 << canvasId)
    int canvasId = 0;
    // Indicates whether the canvasId has been allocated
    private boolean canvasIdAlloc = false;

    // Avoid using this as lock, it cause deadlock
    Object cvLock = new Object();
    Object evaluateLock = new Object();
    Object dirtyMaskLock = new Object();

    // For D3D, instead of using the same variable in Renderer,
    // each canvas has to build its own displayList.
    boolean needToRebuildDisplayList = false;

    // Read-only flag that indicates whether the following texture features
    // are supported for this canvas.

    static final int TEXTURE_3D = 0x0001;
    static final int TEXTURE_COLOR_TABLE = 0x0002;
    static final int TEXTURE_MULTI_TEXTURE = 0x0004;
    static final int TEXTURE_COMBINE = 0x0008;
    static final int TEXTURE_COMBINE_DOT3 = 0x0010;
    static final int TEXTURE_COMBINE_SUBTRACT = 0x0020;
    static final int TEXTURE_REGISTER_COMBINERS = 0x0040;
    static final int TEXTURE_CUBE_MAP = 0x0080;
    static final int TEXTURE_SHARPEN = 0x0100;
    static final int TEXTURE_DETAIL = 0x0200;
    static final int TEXTURE_FILTER4 = 0x0400;
    static final int TEXTURE_ANISOTROPIC_FILTER = 0x0800;
    static final int TEXTURE_LOD_RANGE = 0x1000;
    static final int TEXTURE_LOD_OFFSET = 0x2000;
    // Use by D3D to indicate using one pass Blend mode
    // if Texture interpolation mode is support.
    static final int TEXTURE_LERP = 0x4000;
    static final int TEXTURE_NON_POWER_OF_TWO = 0x8000;
    static final int TEXTURE_AUTO_MIPMAP_GENERATION = 0x10000;

    int textureExtendedFeatures = 0;

    // Extensions supported by the underlying canvas
    //
    // NOTE: we should remove EXT_BGR and EXT_ABGR when the imaging code is
    // rewritten
    // killed global alpha
    //static final int SUN_GLOBAL_ALPHA            = 0x1;
    static final int EXT_ABGR = 0x2;
    static final int EXT_BGR = 0x4;
    static final int MULTISAMPLE = 0x8;

    // The following 10 variables are set by the native
    // createNewContext()/createQueryContext() methods

    // Supported Extensions
    int extensionsSupported = 0;

    // Anisotropic Filter degree
    float anisotropicDegreeMax = 1.0f;

    // Texture Boundary Width Max
    int textureBoundaryWidthMax = 0;

    boolean multiTexAccelerated = false;

    // Max number of texture coordinate sets
    int maxTexCoordSets = 1;

    // Max number of fixed-function texture units
    int maxTextureUnits = 1;

    // Max number of fragment shader texture units
    int maxTextureImageUnits = 0;

    // Max number of vertex shader texture units
    int maxVertexTextureImageUnits = 0;

    // Max number of combined shader texture units
    int maxCombinedTextureImageUnits = 0;

    // Max number of vertex attrs (not counting coord, etc.)
    int maxVertexAttrs = 0;

    // End of variables set by createNewContext()/createQueryContext()

    // The total available number of texture units used by either the
    // fixed-function or programmable shader pipeline.
    // This is computed as: max(maxTextureUnits, maxTextureImageUnits)
    int maxAvailableTextureUnits;

    // Texture Width, Height Max
    int textureWidthMax = 0;
    int textureHeightMax = 0;

    // Texture3D Width, Heigh, Depth Max
    int texture3DWidthMax = -1;
    int texture3DHeightMax = -1;
    int texture3DDepthMax = -1;

    // Cached position & size for CanvasViewCache.
    // We don't want to call canvas.getxx method in Renderer
    // since it will cause deadlock as removeNotify() need to get
    // component lock of Canvas also and need to wait Renderer to
    // finish before continue. So we invoke the method now in
    // CanvasViewEventCatcher.
    Point newPosition = new Point();
    Dimension newSize = new Dimension();
    double xscale = 1.0;
    double yscale = 1.0;

    // Remember OGL context resources to free
    // before context is destroy.
    // It is used when sharedCtx = false;
    ArrayList<TextureRetained> textureIDResourceTable = new ArrayList<TextureRetained>(5);

    // The following variables are used by the lazy download of
    // states code to keep track of the set of current to be update bins

    static final int LIGHTBIN_BIT = 0x0;
    static final int ENVIRONMENTSET_BIT = 0x1;
    static final int ATTRIBUTEBIN_BIT = 0x2;
    static final int TEXTUREBIN_BIT = 0x3;
    static final int RENDERMOLECULE_BIT = 0x4;
    static final int TRANSPARENCY_BIT = 0x5;
    static final int SHADERBIN_BIT = 0x6;

    // bitmask to specify if the corresponding "bin" needs to be updated
    int stateUpdateMask = 0;

    // the set of current "bins" that is to be updated, the stateUpdateMask
    // specifies if each bin in this set is updated or not.
    Object curStateToUpdate[] = new Object[7];

    /**
     * The list of lights that are currently being represented in the native
     * graphics context.
     */
    LightRetained[] currentLights = null;

    /**
     * Flag to override RenderAttributes.depthBufferWriteEnable
     */
    boolean depthBufferWriteEnableOverride = false;

    /**
     * Flag to override RenderAttributes.depthBufferEnable
     */
    boolean depthBufferEnableOverride = false;

    // current state of depthBufferWriteEnable
    boolean depthBufferWriteEnable = true;

    boolean vfPlanesValid = false;

    // The event catcher for this canvas.
    EventCatcher eventCatcher;

    // The view event catcher for this canvas.
    private CanvasViewEventCatcher canvasViewEventCatcher;

    // The top-level parent window for this canvas.
    private Window windowParent;

    // Issue 458 - list of all parent containers for this canvas
    // (includes top-level parent window)
    private LinkedList<Container> containerParentList = new LinkedList<Container>();

    // flag that indicates if light has changed
    boolean lightChanged = false;

    // resource control object
    DrawingSurfaceObject drawingSurfaceObject;

    // true if context is valid for rendering
    boolean validCtx = false;

    // true if canvas is valid for rendering
    boolean validCanvas = false;

    // true if ctx changed between render and swap. In this case
    // cv.canvasDirty flag will not reset in Renderer.
    // This case happen when GraphicsContext3D invoked doClear()
    // and canvas removeNotify() called while Renderer is running
    boolean ctxChanged = false;

    // Default graphics configuration
    private static GraphicsConfiguration defaultGcfg = null;

    // Returns default graphics configuration if user passes null
    // into the Canvas3D constructor
    private static synchronized GraphicsConfiguration defaultGraphicsConfiguration() {
        if (defaultGcfg == null) {
            GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
            defaultGcfg = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
                    .getBestConfiguration(template);
        }
        return defaultGcfg;
    }

    // Returns true if this is a valid graphics configuration, obtained
    // via a GraphicsConfigTemplate3D.
    private static boolean isValidConfig(GraphicsConfiguration gconfig) {
        // If this is a valid GraphicsConfiguration object, then it will
        // be in the graphicsConfigTable
        return graphicsConfigTable.containsKey(gconfig);
    }

    // Checks the given graphics configuration, and throws an exception if
    // the config is null or invalid.
    private static synchronized GraphicsConfiguration checkForValidGraphicsConfig(GraphicsConfiguration gconfig,
            boolean offScreen) {

        // Issue 266 - for backwards compatibility with legacy applications,
        // we will accept a null GraphicsConfiguration for an on-screen Canvas3D
        // only if the "allowNullGraphicsConfig" system property is set to true.
        if (!offScreen && VirtualUniverse.mc.allowNullGraphicsConfig) {
            if (gconfig == null) {
                // Print out warning if Canvas3D is called with a
                // null GraphicsConfiguration
                System.err.println(J3dI18N.getString("Canvas3D7"));
                System.err.println("    " + J3dI18N.getString("Canvas3D18"));

                // Use a default graphics config
                gconfig = defaultGraphicsConfiguration();
            }
        }

        // Validate input graphics config
        if (gconfig == null) {
            throw new NullPointerException(J3dI18N.getString("Canvas3D19"));
        } else if (!isValidConfig(gconfig)) {
            throw new IllegalArgumentException(J3dI18N.getString("Canvas3D17"));
        }

        return gconfig;
    }

    // Return the actual graphics config that will be used to construct
    // the AWT Canvas. This is permitted to be non-unique or null.
    private static GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig) {
        return Pipeline.getPipeline().getGraphicsConfig(gconfig);
    }

    /**
     * Constructs and initializes a new Canvas3D object that Java 3D
     * can render into. The following Canvas3D attributes are initialized
     * to default values as shown:
     * <ul>
     * left manual eye in image plate : (0.142, 0.135, 0.4572)<br>
     * right manual eye in image plate : (0.208, 0.135, 0.4572)<br>
     * stereo enable : true<br>
     * double buffer enable : true<br>
     * monoscopic view policy : View.CYCLOPEAN_EYE_VIEW<br>
     * off-screen mode : false<br>
     * off-screen buffer : null<br>
     * off-screen location : (0,0)<br>
     * </ul>
     *
     * @param graphicsConfiguration a valid GraphicsConfiguration object that
     * will be used to create the canvas.  This object should not be null and
     * should be created using a GraphicsConfigTemplate3D or the
     * getPreferredConfiguration() method of the SimpleUniverse utility.  For
     * backward compatibility with earlier versions of Java 3D, a null or
     * default GraphicsConfiguration will still work when used to create a
     * Canvas3D on the default screen, but an error message will be printed.
     * A NullPointerException or IllegalArgumentException will be thrown in a
     * subsequent release.
     *
     * @exception IllegalArgumentException if the specified
     * GraphicsConfiguration does not support 3D rendering
     */
    public Canvas3D(GraphicsConfiguration graphicsConfiguration) {
        this(null, checkForValidGraphicsConfig(graphicsConfiguration, false), false);
    }

    /**
     * Constructs and initializes a new Canvas3D object that Java 3D
     * can render into.
     *
     * @param graphicsConfiguration a valid GraphicsConfiguration object
     * that will be used to create the canvas.  This must be created either
     * with a GraphicsConfigTemplate3D or by using the
     * getPreferredConfiguration() method of the SimpleUniverse utility.
     *
     * @param offScreen a flag that indicates whether this canvas is
     * an off-screen 3D rendering canvas.  Note that if offScreen
     * is set to true, this Canvas3D object cannot be used for normal
     * rendering; it should not be added to any Container object.
     *
     * @exception NullPointerException if the GraphicsConfiguration
     * is null.
     *
     * @exception IllegalArgumentException if the specified
     * GraphicsConfiguration does not support 3D rendering
     *
     * @since Java 3D 1.2
     */
    public Canvas3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
        this(null, checkForValidGraphicsConfig(graphicsConfiguration, offScreen), offScreen);
    }

    // Private constructor only called by the two public constructors after
    // they have validated the graphics config (and possibly constructed a new
    // default config).
    // The graphics config must be valid, unique, and non-null.
    private Canvas3D(Object dummyObj1, GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
        this(dummyObj1, graphicsConfiguration, getGraphicsConfig(graphicsConfiguration), offScreen);
    }

    // Private constructor only called by the previous private constructor.
    // The graphicsConfiguration parameter is used by Canvas3D to lookup the
    // graphics device and graphics template. The graphicsConfiguration2
    // parameter is generated by the Pipeline from graphicsConfiguration and
    // is only used to initialize the java.awt.Canvas.
    private Canvas3D(Object dummyObj1, GraphicsConfiguration graphicsConfiguration,
            GraphicsConfiguration graphicsConfiguration2, boolean offScreen) {

        super(graphicsConfiguration2);

        this.offScreen = offScreen;
        this.graphicsConfiguration = graphicsConfiguration;

        // Issue 131: Set the autoOffScreen variable based on whether this
        // canvas3d implements the AutoOffScreenCanvas3D tagging interface.
        // Eventually, we may replace this with an actual API.
        boolean autoOffScreenCanvas3D = false;
        if (this instanceof AutoOffScreenCanvas3D) {
            autoOffScreenCanvas3D = true;
        }

        // Throw an illegal argument exception if an on-screen canvas is tagged
        // as an  auto-off-screen canvas
        if (autoOffScreenCanvas3D && !offScreen) {
            throw new IllegalArgumentException(J3dI18N.getString("Canvas3D25"));
        }

        // Issue 163 : Set dirty bits for both Renderer and RenderBin
        cvDirtyMask[0] = VIEW_INFO_DIRTY;
        cvDirtyMask[1] = VIEW_INFO_DIRTY;

        GraphicsConfigInfo gcInfo = graphicsConfigTable.get(graphicsConfiguration);
        requestedStencilSize = gcInfo.getGraphicsConfigTemplate3D().getStencilSize();

        if (offScreen) {

            // Issue 131: set manual rendering flag based on whether this is
            // an auto-off-screen Canvas3D.
            manualRendering = !autoOffScreenCanvas3D;

            screen = new Screen3D(graphicsConfiguration, offScreen);

            // QUESTION: keep a list of off-screen Screen3D objects?
            // Does this list need to be grouped by GraphicsDevice?

            synchronized (dirtyMaskLock) {
                cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY;
                cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY;
            }

            // this canvas will not receive the paint callback,
            // so we need to set the necessary flags here
            firstPaintCalled = true;

            if (manualRendering) {
                // since this canvas will not receive the addNotify
                // callback from AWT, set the added flag here for
                // evaluateActive to work correctly
                added = true;
            }

            evaluateActive();

            // create the rendererStructure object
            //rendererStructure = new RendererStructure();
            offScreenCanvasLoc = new Point(0, 0);
            offScreenCanvasSize = new Dimension(0, 0);

            this.setLocation(offScreenCanvasLoc);
            this.setSize(offScreenCanvasSize);
            newSize = offScreenCanvasSize;
            newPosition = offScreenCanvasLoc;

            // Issue 131: create event catchers for auto-offScreen
            if (!manualRendering) {
                eventCatcher = new EventCatcher(this);
                canvasViewEventCatcher = new CanvasViewEventCatcher(this);
            }
        } else {

            GraphicsDevice graphicsDevice;
            graphicsDevice = graphicsConfiguration.getDevice();

            eventCatcher = new EventCatcher(this);
            canvasViewEventCatcher = new CanvasViewEventCatcher(this);

            synchronized (VirtualUniverse.mc.deviceScreenMap) {
                screen = VirtualUniverse.mc.deviceScreenMap.get(graphicsDevice);

                if (screen == null) {
                    screen = new Screen3D(graphicsConfiguration, offScreen);
                    VirtualUniverse.mc.deviceScreenMap.put(graphicsDevice, screen);
                }
            }

        }

        lights = new LightRetained[VirtualUniverse.mc.maxLights];
        frameCount = new int[VirtualUniverse.mc.maxLights];
        for (int i = 0; i < frameCount.length; i++) {
            frameCount[i] = -1;
        }

        // Construct the drawing surface object for this Canvas3D
        drawingSurfaceObject = Pipeline.getPipeline().createDrawingSurfaceObject(this);

        // Get double buffer, stereo available, scene antialiasing
        // flags from graphics config
        GraphicsConfigTemplate3D.getGraphicsConfigFeatures(this);

        useDoubleBuffer = doubleBufferEnable && doubleBufferAvailable;
        useStereo = stereoEnable && stereoAvailable;
        useSharedCtx = VirtualUniverse.mc.isSharedCtx;

        // Issue 131: assert that only an off-screen canvas can be demand-driven
        assert (!offScreen && manualRendering) == false;

        // Assert that offScreen is *not* stereo
        assert (offScreen && useStereo) == false;
    }

    /**
     * This method overrides AWT's handleEvent class...
     */
    void sendEventToBehaviorScheduler(AWTEvent evt) {

        ViewPlatform vp;

        if ((view != null) && ((vp = view.getViewPlatform()) != null)) {
            VirtualUniverse univ = ((ViewPlatformRetained) (vp.retained)).universe;
            if (univ != null) {
                univ.behaviorStructure.handleAWTEvent(evt);
            }
        }
    }

    /**
     * Method to return whether or not the Canvas3D is recursively visible;
     * that is, whether the Canas3D is currently visible on the screen. Note
     * that we don't directly use isShowing() because that won't work for an
     * auto-offScreen Canvas3D.
     */
    private boolean isRecursivelyVisible() {
        Container parent = getParent();
        return isVisible() && parent != null && parent.isShowing();
    }

    /**
     * Method to return whether the top-level Window parent is iconified
     */
    private boolean isIconified() {
        if (windowParent instanceof Frame) {
            return (((Frame) windowParent).getExtendedState() & Frame.ICONIFIED) != 0;
        }

        return false;
    }

    // Issue 458 - evaluate this Canvas3D's visibility whenever we get a
    // Window or Component Event that could change it.
    void evaluateVisiblilty() {
        boolean nowVisible = isRecursivelyVisible() && !isIconified();

        // Only need to reevaluate and repaint if visibility has changed
        if (this.visible != nowVisible) {
            this.visible = nowVisible;
            evaluateActive();
            if (nowVisible) {
                if (view != null) {
                    view.repaint();
                }
            }
        }
    }

    /**
     * This version looks for the view and notifies it.
     */
    void redraw() {
        if ((view != null) && active && isRunning) {
            view.repaint();
        }
    }

    /**
     * Canvas3D uses the paint callback to track when it is possible to
     * render into the canvas.  Subclasses of Canvas3D that override this
     * method need to call super.paint() in their paint method for Java 3D
     * to function properly.
     * @param g the graphics context
     */
    @Override
    public void paint(Graphics g) {

        if (!firstPaintCalled && added && validCanvas && validGraphicsMode()) {

            final Graphics2D g2d = (Graphics2D) g;
            final AffineTransform t = g2d.getTransform();

            try {
                Dimension scaledSize = getSize();
                xscale = t.getScaleX();
                yscale = t.getScaleY();
                newSize = new Dimension((int) (scaledSize.getWidth() * xscale),
                        (int) (scaledSize.getHeight() * yscale));
                newPosition = getLocationOnScreen();
            } catch (IllegalComponentStateException e) {
                return;
            }

            synchronized (drawingSurfaceObject) {
                drawingSurfaceObject.getDrawingSurfaceObjectInfo();
            }

            firstPaintCalled = true;
            visible = true;
            evaluateActive();
        }
        redraw();
    }

    // When this canvas is added to a frame, this notification gets called.  We
    // can get drawing surface information at this time.  Note: we cannot get
    // the X11 window id yet, unless it is a reset condition.
    /**
     * Canvas3D uses the addNotify callback to track when it is added
     * to a container.  Subclasses of Canvas3D that override this
     * method need to call super.addNotify() in their addNotify() method for Java 3D
     * to function properly.
     */
    @Override
    public void addNotify() {
        // Return immediately if addNotify called twice with no removeNotify
        if (addNotifyCalled) {
            return;
        }
        addNotifyCalled = true;

        // Issue 131: This method is now being called by JCanvas3D for its
        // off-screen Canvas3D, so we need to handle off-screen properly here.
        // Do nothing for manually-rendered off-screen canvases
        if (manualRendering) {
            return;
        }

        Renderer rdr = null;

        if (isRunning && (screen != null)) {
            // If there is other Canvas3D in the same screen
            // rendering, stop it before JDK create new Canvas

            rdr = screen.renderer;
            if (rdr != null) {
                VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, rdr);
                while (!rdr.userStop) {
                    MasterControl.threadYield();
                }
            }
        }

        // Issue 131: Don't call super for off-screen Canvas3D
        if (!offScreen) {
            super.addNotify();
        }
        screen.addUser(this);

        // Issue 458 - Add the eventCatcher as a component listener for each
        // parent container in the window hierarchy
        assert containerParentList.isEmpty();

        windowParent = null;
        Container container = this.getParent();
        while (container != null) {
            if (container instanceof Window) {
                windowParent = (Window) container;
            }
            container.addComponentListener(eventCatcher);
            container.addComponentListener(canvasViewEventCatcher);
            containerParentList.add(container);
            container = container.getParent();
        }

        this.addComponentListener(eventCatcher);
        this.addComponentListener(canvasViewEventCatcher);

        if (windowParent != null) {
            windowParent.addWindowListener(eventCatcher);
        }

        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY;
            cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY;
        }

        allocateCanvasId();

        validCanvas = true;
        added = true;

        // Since we won't get a paint call for off-screen canvases, we need
        // to set the first paint and visible flags here. We also need to
        // call evaluateActive for the same reason.
        if (offScreen) {
            firstPaintCalled = true;
            visible = true;
            evaluateActive();
        }

        // In case the same canvas is removed and add back,
        // we have to change isRunningStatus back to true;
        if (isRunning && !fatalError) {
            isRunningStatus = true;
        }

        ctxTimeStamp = 0;
        if ((view != null) && (view.universe != null)) {
            view.universe.checkForEnableEvents();
        }

        if (rdr != null) {
            // Issue 84: Send a message to MC to restart renderer
            // Note that this also obviates the need for the earlier fix to
            // issue 131 which called redraw() for auto-off-screen Canvas3Ds
            // (and this is a more robust fix)
            VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, rdr);
            while (rdr.userStop) {
                MasterControl.threadYield();
            }
        }
    }

    // When this canvas is removed a frame, this notification gets called.  We
    // need to release the native context at this time.  The underlying window
    // is about to go away.
    /**
     * Canvas3D uses the removeNotify callback to track when it is removed
     * from a container.  Subclasses of Canvas3D that override this
     * method need to call super.removeNotify() in their removeNotify()
     * method for Java 3D to function properly.
     */
    @Override
    public void removeNotify() {
        // Return immediately if addNotify not called first
        if (!addNotifyCalled) {
            return;
        }
        addNotifyCalled = false;

        // Do nothing for manually-rendered off-screen canvases
        if (manualRendering) {
            return;
        }

        Renderer rdr = null;

        if (isRunning && (screen != null)) {
            // If there is other Canvas3D in the same screen
            // rendering, stop it before JDK create new Canvas

            rdr = screen.renderer;
            if (rdr != null) {
                VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, rdr);
                while (!rdr.userStop) {
                    MasterControl.threadYield();
                }
            }
        }

        // Note that although renderer userStop is true,
        // MasterControl can still schedule renderer to run through
        // runMonotor(RUN_RENDERER_CLEANUP) which skip userStop
        // thread checking.
        // For non-offscreen rendering the following call will
        // block waiting until all resources is free before
        // continue

        synchronized (drawingSurfaceObject) {
            validCtx = false;
            validCanvas = false;
        }

        removeCtx();

        Pipeline.getPipeline().freeDrawingSurface(this, drawingSurfaceObject);

        // Clear first paint and visible flags
        firstPaintCalled = false;
        visible = false;

        screen.removeUser(this);
        evaluateActive();

        freeCanvasId();

        ra = null;
        graphicsContext3D = null;

        ctx = null;
        // must be after removeCtx() because
        // it will free graphics2D textureID
        graphics2D = null;

        super.removeNotify();

        // Release and clear.
        for (Container container : containerParentList) {
            container.removeComponentListener(eventCatcher);
            container.removeComponentListener(canvasViewEventCatcher);
        }
        containerParentList.clear();
        this.removeComponentListener(eventCatcher);
        this.removeComponentListener(canvasViewEventCatcher);

        if (eventCatcher != null) {
            this.removeFocusListener(eventCatcher);
            this.removeKeyListener(eventCatcher);
            this.removeMouseListener(eventCatcher);
            this.removeMouseMotionListener(eventCatcher);
            this.removeMouseWheelListener(eventCatcher);
            eventCatcher.reset();
        }

        if (windowParent != null) {
            windowParent.removeWindowListener(eventCatcher);
            windowParent.requestFocus();
        }

        added = false;

        if (rdr != null) {
            // Issue 84: Send a message to MC to restart renderer
            VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, rdr);
            while (rdr.userStop) {
                MasterControl.threadYield();
            }
        }

        // Fix for issue 102 removing strong reference and avoiding memory leak
        // due retention of parent container
        this.windowParent = null;
    }

    void allocateCanvasId() {
        if (!canvasIdAlloc) {
            canvasId = VirtualUniverse.mc.getCanvasId();
            canvasBit = 1 << canvasId;
            canvasIdAlloc = true;
        }
    }

    void freeCanvasId() {
        if (canvasIdAlloc) {
            VirtualUniverse.mc.freeCanvasId(canvasId);
            canvasBit = 0;
            canvasId = 0;
            canvasIdAlloc = false;
        }
    }

    // This decides if the canvas is active
    void evaluateActive() {
        // Note that no need to check for isRunning, we want
        // view register in order to create scheduler in pure immedite mode
        // Also we can't use this as lock, otherwise there is a
        // deadlock where updateViewCache get a lock of this and
        // get a lock of this component. But Container
        // remove will get a lock of this component follows by evaluateActive.

        synchronized (evaluateLock) {
            if ((visible || manualRendering) && firstPaintCalled) {

                if (!active) {
                    active = true;
                    if (pendingView != null) {
                        pendingView.evaluateActive();
                    }
                } else {
                    if ((pendingView != null) && !pendingView.activeStatus) {
                        pendingView.evaluateActive();
                    }
                }
            } else {
                if (active) {
                    active = false;
                    if (view != null) {
                        view.evaluateActive();
                    }
                }
            }
        }

        if ((view != null) && (!active)) {
            VirtualUniverse u = view.universe;
            if ((u != null) && !u.isSceneGraphLock) {
                u.waitForMC();
            }
        }
    }

    void setFrustumPlanes(Vector4d[] planes) {

        if (VirtualUniverse.mc.viewFrustumCulling) {
            /* System.err.println("Canvas3D.setFrustumPlanes()"); */
            viewFrustum.set(planes);
        }
    }

    /**
     * Retrieve the Screen3D object that this Canvas3D is attached to.
     * If this Canvas3D is an off-screen buffer, a new Screen3D object
     * is created corresponding to the off-screen buffer.
     * @return the 3D screen object that this Canvas3D is attached to
     */
    public Screen3D getScreen3D() {
        return screen;
    }

    /**
     * Get the immediate mode 3D graphics context associated with
     * this Canvas3D.  A new graphics context object is created if one does
     * not already exist.
     * @return a GraphicsContext3D object that can be used for immediate
     * mode rendering to this Canvas3D.
     */
    public GraphicsContext3D getGraphicsContext3D() {

        synchronized (gfxCreationLock) {
            if (graphicsContext3D == null)
                graphicsContext3D = new GraphicsContext3D(this);
        }

        return graphicsContext3D;
    }

    /**
     * Get the 2D graphics object associated with
     * this Canvas3D.  A new 2D graphics object is created if one does
     * not already exist.
     *
     * @return a Graphics2D object that can be used for Java 2D
     * rendering into this Canvas3D.
     *
     * @since Java 3D 1.2
     */
    public J3DGraphics2D getGraphics2D() {
        synchronized (gfxCreationLock) {
            if (graphics2D == null)
                graphics2D = new J3DGraphics2DImpl(this);
        }

        return graphics2D;
    }

    /**
     * This routine is called by the Java 3D rendering loop after clearing
     * the canvas and before any rendering has been done for this frame.
     * Applications that wish to perform operations in the rendering loop,
     * prior to any actual rendering may override this function.
     *
     * <p>
     * Updates to live Geometry, Texture, and ImageComponent objects
     * in the scene graph are not allowed from this method.
     *
     * <p>
     * NOTE: Applications should <i>not</i> call this method.
     */
    public void preRender() {
        // Do nothing; the user overrides this to cause some action
    }

    /**
     * This routine is called by the Java 3D rendering loop after completing
     * all rendering to the canvas for this frame and before the buffer swap.
     * Applications that wish to perform operations in the rendering loop,
     * following any actual rendering may override this function.
     *
     * <p>
     * Updates to live Geometry, Texture, and ImageComponent objects
     * in the scene graph are not allowed from this method.
     *
     * <p>
     * NOTE: Applications should <i>not</i> call this method.
     */
    public void postRender() {
        // Do nothing; the user overrides this to cause some action
    }

    /**
     * This routine is called by the Java 3D rendering loop after completing
     * all rendering to the canvas, and all other canvases associated with
     * this view, for this frame following the buffer swap.
     * Applications that wish to perform operations at the very
     * end of the rendering loop may override this function.
     * In off-screen mode, all rendering is copied to the off-screen
     * buffer before this method is called.
     *
     * <p>
     * Updates to live Geometry, Texture, and ImageComponent objects
     * in the scene graph are not allowed from this method.
     *
     * <p>
     * NOTE: Applications should <i>not</i> call this method.
     */
    public void postSwap() {
        // Do nothing; the user overrides this to cause some action
    }

    /**
     * This routine is called by the Java 3D rendering loop during the
     * execution of the rendering loop.  It is called once for each
     * field (i.e., once per frame on
     * a mono system or once each for the right eye and left eye on a
     * two-pass stereo system.  This is intended for use by applications that
     * want to mix retained/compiled-retained mode rendering with some
     * immediate mode rendering.  Applications that wish to perform
     * operations during the rendering loop, may override this
     * function.
     *
     * <p>
     * Updates to live Geometry, Texture, and ImageComponent objects
     * in the scene graph are not allowed from this method.
     *
     * <p>
     * NOTE: Applications should <i>not</i> call this method.
     * <p>
     *
     * @param fieldDesc field description, one of: FIELD_LEFT, FIELD_RIGHT or
     * FIELD_ALL.  Applications that wish to work correctly in stereo mode
     * should render the same image for both FIELD_LEFT and FIELD_RIGHT calls.
     * If Java 3D calls the renderer with FIELD_ALL then the immediate mode
     * rendering only needs to be done once.
     */
    public void renderField(int fieldDesc) {
        // Do nothing; the user overrides this to cause some action
    }

    /**
     * Stop the Java 3D renderer on this Canvas3D object.  If the
     * Java 3D renderer is currently running, the rendering will be
     * synchronized before being stopped.  No further rendering will be done
     * to this canvas by Java 3D until the renderer is started again.
     * In pure immediate mode this method should be called prior to adding
     * this canvas to an active View object.
     *
     * @exception IllegalStateException if this Canvas3D is in
     * off-screen mode.
     */
    public final void stopRenderer() {
        // Issue 131: renderer can't be stopped only if it is an offscreen,
        // manual canvas. Otherwise, it has to be seen as an onscreen canvas.
        if (manualRendering)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D14"));

        if (isRunning) {
            VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this);
            isRunning = false;
        }
    }

    /**
     * Start the Java 3D renderer on this Canvas3D object.  If the
     * Java 3D renderer is not currently running, any rendering to other
     * Canvas3D objects sharing the same View will be synchronized before this
     * Canvas3D's renderer is (re)started.  When a Canvas3D is created, it is
     * initially marked as being started.  This means that as soon as the
     * Canvas3D is added to an active View object, the rendering loop will
     * render the scene graph to the canvas.
     */
    public final void startRenderer() {
        // Issue 260 : ignore attempt to start renderer if fatal error
        if (fatalError) {
            return;
        }

        if (!isRunning) {
            VirtualUniverse.mc.postRequest(MasterControl.START_RENDERER, this);
            isRunning = true;
            redraw();
        }
    }

    /**
     * Retrieves the state of the renderer for this Canvas3D object.
     * @return the state of the renderer
     *
     * @since Java 3D 1.2
     */
    public final boolean isRendererRunning() {
        return isRunning;
    }

    // Returns the state of the fatal error flag
    boolean isFatalError() {
        return fatalError;
    }

    // Sets the fatal error flag to true; stop the renderer for this canvas
    void setFatalError() {
        fatalError = true;

        if (isRunning) {
            isRunning = false;

            if (!manualRendering) {
                VirtualUniverse.mc.postRequest(MasterControl.STOP_RENDERER, this);
            }
        }
    }

    /**
     * Retrieves a flag indicating whether this Canvas3D is an
     * off-screen canvas.
     *
     * @return <code>true</code> if this Canvas3D is an off-screen canvas;
     * <code>false</code> if this is an on-screen canvas.
     *
     * @since Java 3D 1.2
     */
    public boolean isOffScreen() {
        return offScreen;
    }

    /**
     * Sets the off-screen buffer for this Canvas3D.  The specified
     * image is written into by the Java 3D renderer.  The size of the
     * specified ImageComponent determines the size, in pixels, of
     * this Canvas3D--the size inherited from Component is ignored.
     * <p>
     * NOTE: the size, physical width, and physical height of the associated
     * Screen3D must be set explicitly prior to rendering.
     * Failure to do so will result in an exception.
     * <p>
     *
     * @param buffer the image component that will be rendered into by
     * subsequent calls to renderOffScreenBuffer. The image component must not
     * be part of a live scene graph, nor may it subsequently be made part of a
     * live scene graph while being used as an off-screen buffer; an
     * IllegalSharingException is thrown in such cases. The buffer may be null,
     * indicating that the previous off-screen buffer is released without a new
     * buffer being set.
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode.
     *
     * @exception RestrictedAccessException if an off-screen rendering
     * is in process for this Canvas3D.
     *
     * @exception IllegalSharingException if the specified ImageComponent2D
     * is part of a live scene graph
     *
     * @exception IllegalSharingException if the specified ImageComponent2D is
     * being used by an immediate mode context, or by another Canvas3D as
     * an off-screen buffer.
     *
     * @exception IllegalArgumentException if the image class of the specified
     * ImageComponent2D is <i>not</i> ImageClass.BUFFERED_IMAGE.
     *
     * @exception IllegalArgumentException if the specified
     * ImageComponent2D is in by-reference mode and its
     * RenderedImage is null.
     *
     * @exception IllegalArgumentException if the ImageComponent2D format
     * is <i>not</i> a 3-component format (e.g., FORMAT_RGB)
     * or a 4-component format (e.g., FORMAT_RGBA).
     *
     * @see #renderOffScreenBuffer
     * @see Screen3D#setSize(int, int)
     * @see Screen3D#setSize(Dimension)
     * @see Screen3D#setPhysicalScreenWidth
     * @see Screen3D#setPhysicalScreenHeight
     *
     * @since Java 3D 1.2
     */
    public void setOffScreenBuffer(ImageComponent2D buffer) {
        int width, height;
        boolean freeCanvasId = false;

        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        if (offScreenRendering)
            throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2"));

        // Check that offScreenBufferPending is not already set
        J3dDebug.doAssert(!offScreenBufferPending, "!offScreenBufferPending");

        if (offScreenBuffer != null && offScreenBuffer != buffer) {
            ImageComponent2DRetained i2dRetained = (ImageComponent2DRetained) offScreenBuffer.retained;
            i2dRetained.setUsedByOffScreen(false);
        }

        if (buffer != null) {
            ImageComponent2DRetained bufferRetained = (ImageComponent2DRetained) buffer.retained;

            if (bufferRetained.byReference && !(bufferRetained.getRefImage(0) instanceof BufferedImage)) {

                throw new IllegalArgumentException(J3dI18N.getString("Canvas3D15"));
            }

            if (bufferRetained.getNumberOfComponents() < 3) {
                throw new IllegalArgumentException(J3dI18N.getString("Canvas3D16"));
            }

            if (buffer.isLive()) {
                throw new IllegalSharingException(J3dI18N.getString("Canvas3D26"));
            }

            if (bufferRetained.getInImmCtx()) {
                throw new IllegalSharingException(J3dI18N.getString("Canvas3D27"));
            }

            if (buffer != offScreenBuffer && bufferRetained.getUsedByOffScreen()) {
                throw new IllegalSharingException(J3dI18N.getString("Canvas3D28"));
            }

            bufferRetained.setUsedByOffScreen(true);

            width = bufferRetained.width;
            height = bufferRetained.height;

            // Issues 347, 348 - assign a canvasId for off-screen Canvas3D
            if (manualRendering) {
                sendAllocateCanvasId();
            }
        } else {
            width = height = 0;

            // Issues 347, 348 - release canvasId for off-screen Canvas3D
            if (manualRendering) {
                freeCanvasId = true;
            }
        }

        if ((offScreenCanvasSize.width != width) || (offScreenCanvasSize.height != height)) {

            if (drawable != null) {
                // Fix for Issue 18 and Issue 175
                // Will do destroyOffScreenBuffer in the Renderer thread.
                sendDestroyCtxAndOffScreenBuffer();
                drawable = null;
            }
            // Issue 396. Since context is invalid here, we should set it to null.
            ctx = null;

            // set the canvas dimension according to the buffer dimension
            offScreenCanvasSize.setSize(width, height);
            this.setSize(offScreenCanvasSize);

            if (width > 0 && height > 0) {
                sendCreateOffScreenBuffer();
            }

        } else if (ctx != null) {
            removeCtx();
        }

        if (freeCanvasId) {
            sendFreeCanvasId();
        }

        offScreenBuffer = buffer;

        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= MOVED_OR_RESIZED_DIRTY;
            cvDirtyMask[1] |= MOVED_OR_RESIZED_DIRTY;
        }
    }

    /**
     * Retrieves the off-screen buffer for this Canvas3D.
     *
     * @return the current off-screen buffer for this Canvas3D.
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode.
     *
     * @since Java 3D 1.2
     */
    public ImageComponent2D getOffScreenBuffer() {

        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        return (offScreenBuffer);
    }

    /**
     * Schedules the rendering of a frame into this Canvas3D's
     * off-screen buffer.  The rendering is done from the point of
     * view of the View object to which this Canvas3D has been added.
     * No rendering is performed if this Canvas3D object has not been
     * added to an active View.  This method does not wait for the rendering
     * to actually happen.  An application that wishes to know when
     * the rendering is complete must either subclass Canvas3D and
     * override the <code>postSwap</code> method, or call
     * <code>waitForOffScreenRendering</code>.
     *
     * @exception NullPointerException if the off-screen buffer is null.
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode, or if either the width or the height of
     * the associated Screen3D's size is <= 0, or if the associated
     * Screen3D's physical width or height is <= 0.
     * @exception RestrictedAccessException if an off-screen rendering
     * is already in process for this Canvas3D or if the Java 3D renderer
     * is stopped.
     *
     * @see #setOffScreenBuffer
     * @see Screen3D#setSize(int, int)
     * @see Screen3D#setSize(Dimension)
     * @see Screen3D#setPhysicalScreenWidth
     * @see Screen3D#setPhysicalScreenHeight
     * @see #waitForOffScreenRendering
     * @see #postSwap
     *
     * @since Java 3D 1.2
     */
    public void renderOffScreenBuffer() {

        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        // Issue 131: Cannot manually render to an automatic canvas.
        if (!manualRendering)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D24"));

        // Issue 260 : Cannot render if we already have a fatal error
        if (fatalError) {
            throw new IllegalRenderingStateException(J3dI18N.getString("Canvas3D30"));
        }

        if (offScreenBuffer == null)
            throw new NullPointerException(J3dI18N.getString("Canvas3D10"));

        Dimension screenSize = screen.getSize();

        if (screenSize.width <= 0)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D8"));

        if (screenSize.height <= 0)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D9"));

        if (screen.getPhysicalScreenWidth() <= 0.0)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D12"));

        if (screen.getPhysicalScreenHeight() <= 0.0)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D13"));

        if (offScreenRendering)
            throw new RestrictedAccessException(J3dI18N.getString("Canvas3D2"));

        if (!isRunning)
            throw new RestrictedAccessException(J3dI18N.getString("Canvas3D11"));

        // Fix to issue 66
        if ((!active) || (pendingView == null)) {
            /* No rendering is performed if this Canvas3D object has not been
               added to an active View. */
            return;
        }

        // Issue 131: moved code that determines off-screen boundary to separate
        // method that is called from the renderer

        offScreenRendering = true;

        // Fix to issue 66.
        /* This is an attempt to do the following check in one atomic operation :
           ((view != null) && (view.inCanvasCallback)) */

        boolean inCanvasCallback = false;
        try {
            inCanvasCallback = view.inCanvasCallback;

        } catch (NullPointerException npe) {
            /* Do nothing here */
        }

        if (inCanvasCallback) {
            // Here we assume that view is stable if inCanvasCallback
            // is true. This assumption is valid among all j3d threads as
            // all access to view is synchronized by MasterControl.
            // Issue : user threads access to view isn't synchronize hence
            // is model will break.
            if (screen.renderer == null) {

                // It is possible that screen.renderer = null when this View
                // is shared by another onScreen Canvas and this callback
                // is from that Canvas. In this case it need one more
                // round before the renderer.
                screen.renderer = Screen3D.deviceRendererMap.get(screen.graphicsDevice);
                // screen.renderer may equal to null when multiple
                // screen is used and this Canvas3D is in different
                // screen sharing the same View not yet initialize.
            }

            // if called from render call back, send a message directly to
            // the renderer message queue, and call renderer doWork
            // to do the offscreen rendering now
            if (Thread.currentThread() == screen.renderer) {

                J3dMessage createMessage = new J3dMessage();
                createMessage.threads = J3dThread.RENDER_THREAD;
                createMessage.type = J3dMessage.RENDER_OFFSCREEN;
                createMessage.universe = this.view.universe;
                createMessage.view = this.view;
                createMessage.args[0] = this;

                screen.renderer.rendererStructure.addMessage(createMessage);

                // modify the args to reflect offScreen rendering
                screen.renderer.args = new Object[4];
                screen.renderer.args[0] = new Integer(Renderer.REQUESTRENDER);
                screen.renderer.args[1] = this;
                screen.renderer.args[2] = view;
                // This extra argument 3 is needed in MasterControl to
                // test whether offscreen Rendering is used or not
                screen.renderer.args[3] = null;

                // call renderer doWork directly since we are already in
                // the renderer thread
                screen.renderer.doWork(0);
            } else {

                // XXXX:
                // Now we are in trouble, this will cause deadlock if
                // waitForOffScreenRendering() is invoked
                J3dMessage createMessage = new J3dMessage();
                createMessage.threads = J3dThread.RENDER_THREAD;
                createMessage.type = J3dMessage.RENDER_OFFSCREEN;
                createMessage.universe = this.view.universe;
                createMessage.view = this.view;
                createMessage.args[0] = this;
                screen.renderer.rendererStructure.addMessage(createMessage);
                VirtualUniverse.mc.setWorkForRequestRenderer();
            }

        } else if (Thread.currentThread() instanceof BehaviorScheduler) {

            // If called from behavior scheduler, send a message directly to
            // the renderer message queue.
            // Note that we didn't use
            // currentThread() == view.universe.behaviorScheduler
            // since the caller may be another universe Behavior
            // scheduler.
            J3dMessage createMessage = new J3dMessage();
            createMessage.threads = J3dThread.RENDER_THREAD;
            createMessage.type = J3dMessage.RENDER_OFFSCREEN;
            createMessage.universe = this.view.universe;
            createMessage.view = this.view;
            createMessage.args[0] = this;
            screen.renderer.rendererStructure.addMessage(createMessage);
            VirtualUniverse.mc.setWorkForRequestRenderer();

        } else {
            // send a message to renderBin
            // Fix for issue 66 : Since view might not been set yet,
            // we have to use pendingView instead.
            J3dMessage createMessage = new J3dMessage();
            createMessage.threads = J3dThread.UPDATE_RENDER;
            createMessage.type = J3dMessage.RENDER_OFFSCREEN;
            createMessage.universe = this.pendingView.universe;
            createMessage.view = this.pendingView;
            createMessage.args[0] = this;
            createMessage.args[1] = offScreenBuffer;
            VirtualUniverse.mc.processMessage(createMessage);
        }
    }

    /**
     * Waits for this Canvas3D's off-screen rendering to be done.
     * This method will wait until the <code>postSwap</code> method of this
     * off-screen Canvas3D has completed.  If this Canvas3D has not
     * been added to an active view or if the renderer is stopped for this
     * Canvas3D, then this method will return
     * immediately.  This method must not be called from a render
     * callback method of an off-screen Canvas3D.
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode, or if this method is called from a render
     * callback method of an off-screen Canvas3D.
     *
     * @see #renderOffScreenBuffer
     * @see #postSwap
     *
     * @since Java 3D 1.2
     */
    public void waitForOffScreenRendering() {

        if (!offScreen) {
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));
        }

        if (Thread.currentThread() instanceof Renderer) {
            throw new IllegalStateException(J3dI18N.getString("Canvas3D31"));
        }

        while (offScreenRendering) {
            MasterControl.threadYield();
        }
    }

    /**
     * Sets the location of this off-screen Canvas3D.  The location is
     * the upper-left corner of the Canvas3D relative to the
     * upper-left corner of the corresponding off-screen Screen3D.
     * The function of this method is similar to that of
     * <code>Component.setLocation</code> for on-screen Canvas3D
     * objects.  The default location is (0,0).
     *
     * @param x the <i>x</i> coordinate of the upper-left corner of
     * the new location.
     * @param y the <i>y</i> coordinate of the upper-left corner of
     * the new location.
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode.
     *
     * @since Java 3D 1.2
     */
    public void setOffScreenLocation(int x, int y) {

        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        synchronized (cvLock) {
            offScreenCanvasLoc.setLocation(x, y);
        }
    }

    /**
     * Sets the location of this off-screen Canvas3D.  The location is
     * the upper-left corner of the Canvas3D relative to the
     * upper-left corner of the corresponding off-screen Screen3D.
     * The function of this method is similar to that of
     * <code>Component.setLocation</code> for on-screen Canvas3D
     * objects.  The default location is (0,0).
     *
     * @param p the point defining the upper-left corner of the new
     * location.
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode.
     *
     * @since Java 3D 1.2
     */
    public void setOffScreenLocation(Point p) {

        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        synchronized (cvLock) {
            offScreenCanvasLoc.setLocation(p);
        }
    }

    /**
     * Retrieves the location of this off-screen Canvas3D.  The
     * location is the upper-left corner of the Canvas3D relative to
     * the upper-left corner of the corresponding off-screen Screen3D.
     * The function of this method is similar to that of
     * <code>Component.getLocation</code> for on-screen Canvas3D
     * objects.
     *
     * @return a new point representing the upper-left corner of the
     * location of this off-screen Canvas3D.
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode.
     *
     * @since Java 3D 1.2
     */
    public Point getOffScreenLocation() {
        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        return (new Point(offScreenCanvasLoc));
    }

    /**
     * Retrieves the location of this off-screen Canvas3D and stores
     * it in the specified Point object.  The location is the
     * upper-left corner of the Canvas3D relative to the upper-left
     * corner of the corresponding off-screen Screen3D.  The function
     * of this method is similar to that of
     * <code>Component.getLocation</code> for on-screen Canvas3D
     * objects. This version of <code>getOffScreenLocation</code> is
     * useful if the caller wants to avoid allocating a new Point
     * object on the heap.
     *
     * @param rv Point object into which the upper-left corner of the
     * location of this off-screen Canvas3D is copied.
     * If <code>rv</code> is null, a new Point is allocated.
     *
     * @return <code>rv</code>
     *
     * @exception IllegalStateException if this Canvas3D is not in
     * off-screen mode.
     *
     * @since Java 3D 1.2
     */
    public Point getOffScreenLocation(Point rv) {

        if (!offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D1"));

        if (rv == null)
            return (new Point(offScreenCanvasLoc));

        else {
            rv.setLocation(offScreenCanvasLoc);
            return rv;
        }
    }

    void endOffScreenRendering() {

        ImageComponent2DRetained icRetained = (ImageComponent2DRetained) offScreenBuffer.retained;
        boolean isByRef = icRetained.isByReference();
        ImageComponentRetained.ImageData imageData = icRetained.getImageData(false);

        if (!isByRef) {
            // If icRetained has a null image ( BufferedImage)
            if (imageData == null) {
                assert (!isByRef);
                icRetained.createBlankImageData();
                imageData = icRetained.getImageData(false);
            }
            // Check for possible format conversion in imageData
            else {
                // Format convert imageData if format is unsupported.
                icRetained.evaluateExtensions(this);
            }
            // read the image from the offscreen buffer
            readOffScreenBuffer(ctx, icRetained.getImageFormatTypeIntValue(false),
                    icRetained.getImageDataTypeIntValue(), imageData.get(), offScreenCanvasSize.width,
                    offScreenCanvasSize.height);

        } else {
            icRetained.geomLock.getLock();
            // Create a copy of format converted image in imageData if format is unsupported.
            icRetained.evaluateExtensions(this);

            // read the image from the offscreen buffer
            readOffScreenBuffer(ctx, icRetained.getImageFormatTypeIntValue(false),
                    icRetained.getImageDataTypeIntValue(), imageData.get(), offScreenCanvasSize.width,
                    offScreenCanvasSize.height);

            // For byRef, we might have to copy buffer back into
            // the user's referenced ImageComponent2D
            if (!imageData.isDataByRef()) {
                if (icRetained.isImageTypeSupported()) {
                    icRetained.copyToRefImage(0);
                } else {
                    // This method only handle RGBA conversion.
                    icRetained.copyToRefImageWithFormatConversion(0);
                }
            }

            icRetained.geomLock.unLock();
        }
    }

    /**
     * Synchronize and swap buffers on a double buffered canvas for
     * this Canvas3D object.  This method should only be called if the
     * Java 3D renderer has been stopped.  In the normal case, the renderer
     * automatically swaps the buffer.
     * This method calls the <code>flush(true)</code> methods of the
     * associated 2D and 3D graphics contexts, if they have been allocated.
     *
     * @exception RestrictedAccessException if the Java 3D renderer is
     * running.
     * @exception IllegalStateException if this Canvas3D is in
     * off-screen mode.
     *
     * @see #stopRenderer
     * @see GraphicsContext3D#flush
     * @see J3DGraphics2D#flush
     */
    public void swap() {
        if (offScreen)
            throw new IllegalStateException(J3dI18N.getString("Canvas3D14"));

        if (isRunning)
            throw new RestrictedAccessException(J3dI18N.getString("Canvas3D0"));

        if (!firstPaintCalled) {
            return;
        }

        if (view != null && graphicsContext3D != null) {
            if ((view.universe != null) && (Thread.currentThread() == view.universe.behaviorScheduler)) {
                graphicsContext3D.sendRenderMessage(false, GraphicsContext3D.SWAP, null, null);
            } else {
                graphicsContext3D.sendRenderMessage(true, GraphicsContext3D.SWAP, null, null);
            }
            graphicsContext3D.runMonitor(J3dThread.WAIT);
        }
    }

    void doSwap() {

        if (firstPaintCalled && useDoubleBuffer) {
            try {
                if (validCtx && (ctx != null) && (view != null)) {
                    synchronized (drawingSurfaceObject) {
                        if (validCtx) {
                            if (!drawingSurfaceObject.renderLock()) {
                                graphicsContext3D.runMonitor(J3dThread.NOTIFY);
                                return;
                            }
                            this.syncRender(ctx, true);
                            swapBuffers(ctx, drawable);
                            drawingSurfaceObject.unLock();
                        }
                    }
                }
            } catch (NullPointerException ne) {
                drawingSurfaceObject.unLock();
            }
        }
        // Increment the elapsedFrame for the behavior structure
        // to trigger any interpolators
        view.universe.behaviorStructure.incElapsedFrames();
        graphicsContext3D.runMonitor(J3dThread.NOTIFY);
    }

    /**
     * Wrapper for native createNewContext method.
     */
    Context createNewContext(Context shareCtx, boolean isSharedCtx) {
        Context retVal = createNewContext(this.drawable, shareCtx, isSharedCtx, this.offScreen);
        // compute the max available texture units
        maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits);
        // reset 'antialiasingSet' if new context is created for an already existing Canvas3D,
        // e.g. resizing offscreen Canvas3D
        antialiasingSet = false;

        return retVal;
    }

    /**
     * Make the context associated with the specified canvas current.
     */
    final void makeCtxCurrent() {
        makeCtxCurrent(ctx, drawable);
    }

    /**
     * Make the specified context current.
     */
    final void makeCtxCurrent(Context ctx) {
        makeCtxCurrent(ctx, drawable);
    }

    final void makeCtxCurrent(Context ctx, Drawable drawable) {
        if (ctx != screen.renderer.currentCtx || drawable != screen.renderer.currentDrawable) {
            if (!drawingSurfaceObject.isLocked()) {
                drawingSurfaceObject.renderLock();
                useCtx(ctx, drawable);
                drawingSurfaceObject.unLock();
            } else {
                useCtx(ctx, drawable);
            }
            screen.renderer.currentCtx = ctx;
            screen.renderer.currentDrawable = drawable;
        }
    }

    // Give the pipeline a chance to release the context; the Pipeline may
    // or may not ignore this call.
    void releaseCtx() {
        if (screen.renderer.currentCtx != null) {
            boolean needLock = !drawingSurfaceObject.isLocked();
            if (needLock) {
                drawingSurfaceObject.renderLock();
            }
            if (releaseCtx(screen.renderer.currentCtx)) {
                screen.renderer.currentCtx = null;
                screen.renderer.currentDrawable = null;
            }
            if (needLock) {
                drawingSurfaceObject.unLock();
            }
        }
    }

    /**
     * Sets the position of the manual left eye in image-plate
     * coordinates.  This value determines eye placement when a head
     * tracker is not in use and the application is directly controlling
     * the eye position in image-plate coordinates.
     * In head-tracked mode or when the windowEyePointPolicy is
     * RELATIVE_TO_FIELD_OF_VIEW or RELATIVE_TO_COEXISTENCE, this value
     * is ignored.  When the
     * windowEyepointPolicy is RELATIVE_TO_WINDOW only the Z value is
     * used.
     * @param position the new manual left eye position
     */
    public void setLeftManualEyeInImagePlate(Point3d position) {

        this.leftManualEyeInImagePlate.set(position);
        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= EYE_IN_IMAGE_PLATE_DIRTY;
            cvDirtyMask[1] |= EYE_IN_IMAGE_PLATE_DIRTY;
        }
        redraw();
    }

    /**
     * Sets the position of the manual right eye in image-plate
     * coordinates.  This value determines eye placement when a head
     * tracker is not in use and the application is directly controlling
     * the eye position in image-plate coordinates.
     * In head-tracked mode or when the windowEyePointPolicy is
     * RELATIVE_TO_FIELD_OF_VIEW or RELATIVE_TO_COEXISTENCE, this value
     * is ignored.  When the
     * windowEyepointPolicy is RELATIVE_TO_WINDOW only the Z value is
     * used.
     * @param position the new manual right eye position
     */
    public void setRightManualEyeInImagePlate(Point3d position) {

        this.rightManualEyeInImagePlate.set(position);
        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= EYE_IN_IMAGE_PLATE_DIRTY;
            cvDirtyMask[1] |= EYE_IN_IMAGE_PLATE_DIRTY;
        }
        redraw();
    }

    /**
     * Retrieves the position of the user-specified, manual left eye
     * in image-plate
     * coordinates and copies that value into the object provided.
     * @param position the object that will receive the position
     */
    public void getLeftManualEyeInImagePlate(Point3d position) {
        position.set(this.leftManualEyeInImagePlate);
    }

    /**
     * Retrieves the position of the user-specified, manual right eye
     * in image-plate
     * coordinates and copies that value into the object provided.
     * @param position the object that will receive the position
     */
    public void getRightManualEyeInImagePlate(Point3d position) {
        position.set(this.rightManualEyeInImagePlate);
    }

    /**
     * Retrieves the actual position of the left eye
     * in image-plate
     * coordinates and copies that value into the object provided.
     * This value is a function of the windowEyepointPolicy, the tracking
     * enable flag, and the manual left eye position.
     * @param position the object that will receive the position
     */
    public void getLeftEyeInImagePlate(Point3d position) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                position.set(canvasViewCache.getLeftEyeInImagePlate());
            }
        } else {
            position.set(leftManualEyeInImagePlate);
        }
    }

    /**
     * Retrieves the actual position of the right eye
     * in image-plate
     * coordinates and copies that value into the object provided.
     * This value is a function of the windowEyepointPolicy, the tracking
     * enable flag, and the manual right eye position.
     * @param position the object that will receive the position
     */
    public void getRightEyeInImagePlate(Point3d position) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                position.set(canvasViewCache.getRightEyeInImagePlate());
            }
        } else {
            position.set(rightManualEyeInImagePlate);
        }
    }

    /**
     * Retrieves the actual position of the center eye
     * in image-plate
     * coordinates and copies that value into the object provided.
     * The center eye is the fictional eye half-way between the left and
     * right eye.
     * This value is a function of the windowEyepointPolicy, the tracking
     * enable flag, and the manual right and left eye positions.
     * @param position the object that will receive the position
     * @see #setMonoscopicViewPolicy
     */
    // XXXX: This might not make sense for field-sequential HMD.
    public void getCenterEyeInImagePlate(Point3d position) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                position.set(canvasViewCache.getCenterEyeInImagePlate());
            }
        } else {
            Point3d cenEye = new Point3d();
            cenEye.add(leftManualEyeInImagePlate, rightManualEyeInImagePlate);
            cenEye.scale(0.5);
            position.set(cenEye);
        }
    }

    /**
     * Retrieves the current ImagePlate coordinates to Virtual World
     * coordinates transform and places it into the specified object.
     * @param t the Transform3D object that will receive the
     * transform
     */
    // TODO: Document -- This will return the transform of left plate.
    public void getImagePlateToVworld(Transform3D t) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                t.set(canvasViewCache.getImagePlateToVworld());
            }
        } else {
            t.setIdentity();
        }
    }

    /**
     * Computes the position of the specified AWT pixel value
     * in image-plate
     * coordinates and copies that value into the object provided.
     * @param x the X coordinate of the pixel relative to the upper-left
     * hand corner of the window.
     * @param y the Y coordinate of the pixel relative to the upper-left
     * hand corner of the window.
     * @param imagePlatePoint the object that will receive the position in
     * physical image plate coordinates (relative to the lower-left
     * corner of the screen).
     */
    // TODO: Document -- This transform the pixel location to the left image plate.
    public void getPixelLocationInImagePlate(int x, int y, Point3d imagePlatePoint) {

        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                imagePlatePoint.x = canvasViewCache.getWindowXInImagePlate((double) x);
                imagePlatePoint.y = canvasViewCache.getWindowYInImagePlate((double) y);
                imagePlatePoint.z = 0.0;
            }
        } else {
            imagePlatePoint.set(0.0, 0.0, 0.0);
        }
    }

    void getPixelLocationInImagePlate(double x, double y, double z, Point3d imagePlatePoint) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                canvasViewCache.getPixelLocationInImagePlate(x, y, z, imagePlatePoint);
            }
        } else {
            imagePlatePoint.set(0.0, 0.0, 0.0);
        }
    }

    /**
     * Computes the position of the specified AWT pixel value
     * in image-plate
     * coordinates and copies that value into the object provided.
     * @param pixelLocation the coordinates of the pixel relative to
     * the upper-left hand corner of the window.
     * @param imagePlatePoint the object that will receive the position in
     * physical image plate coordinates (relative to the lower-left
     * corner of the screen).
     *
     * @since Java 3D 1.2
     */
    // TODO: Document -- This transform the pixel location to the left image plate.
    public void getPixelLocationInImagePlate(Point2d pixelLocation, Point3d imagePlatePoint) {

        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                imagePlatePoint.x = canvasViewCache.getWindowXInImagePlate(pixelLocation.x);
                imagePlatePoint.y = canvasViewCache.getWindowYInImagePlate(pixelLocation.y);
                imagePlatePoint.z = 0.0;
            }
        } else {
            imagePlatePoint.set(0.0, 0.0, 0.0);
        }
    }

    /**
     * Projects the specified point from image plate coordinates
     * into AWT pixel coordinates.  The AWT pixel coordinates are
     * copied into the object provided.
     * @param imagePlatePoint the position in
     * physical image plate coordinates (relative to the lower-left
     * corner of the screen).
     * @param pixelLocation the object that will receive the coordinates
     * of the pixel relative to the upper-left hand corner of the window.
     *
     * @since Java 3D 1.2
     */
    // TODO: Document -- This transform the pixel location from the left image plate.
    public void getPixelLocationFromImagePlate(Point3d imagePlatePoint, Point2d pixelLocation) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                canvasViewCache.getPixelLocationFromImagePlate(imagePlatePoint, pixelLocation);
            }
        } else {
            pixelLocation.set(0.0, 0.0);
        }
    }

    /**
     * Copies the current Vworld projection transform for each eye
     * into the specified Transform3D objects.  This transform takes
     * points in virtual world coordinates and projects them into
     * clipping coordinates, which are in the range [-1,1] in
     * <i>X</i>, <i>Y</i>, and <i>Z</i> after clipping and perspective
     * division.
     * In monoscopic mode, the same projection transform will be
     * copied into both the right and left eye Transform3D objects.
     *
     * @param leftProjection the Transform3D object that will receive
     * a copy of the current projection transform for the left eye.
     *
     * @param rightProjection the Transform3D object that will receive
     * a copy of the current projection transform for the right eye.
     *
     * @since Java 3D 1.3
     */
    public void getVworldProjection(Transform3D leftProjection, Transform3D rightProjection) {
        if (canvasViewCache != null) {
            ViewPlatformRetained viewPlatformRetained = (ViewPlatformRetained) view.getViewPlatform().retained;

            synchronized (canvasViewCache) {
                leftProjection.mul(canvasViewCache.getLeftProjection(), canvasViewCache.getLeftVpcToEc());
                leftProjection.mul(viewPlatformRetained.getVworldToVpc());

                // caluclate right eye if in stereo, otherwise
                // this is the same as the left eye.
                if (useStereo) {
                    rightProjection.mul(canvasViewCache.getRightProjection(), canvasViewCache.getRightVpcToEc());
                    rightProjection.mul(viewPlatformRetained.getVworldToVpc());
                } else {
                    rightProjection.set(leftProjection);
                }
            }
        } else {
            leftProjection.setIdentity();
            rightProjection.setIdentity();
        }
    }

    /**
     * Copies the inverse of the current Vworld projection transform
     * for each eye into the specified Transform3D objects.  This
     * transform takes points in clipping coordinates, which are in
     * the range [-1,1] in <i>X</i>, <i>Y</i>, and <i>Z</i> after
     * clipping and perspective division, and transforms them into
     * virtual world coordinates.
     * In monoscopic mode, the same inverse projection transform will
     * be copied into both the right and left eye Transform3D objects.
     *
     * @param leftInverseProjection the Transform3D object that will
     * receive a copy of the current inverse projection transform for
     * the left eye.
     * @param rightInverseProjection the Transform3D object that will
     * receive a copy of the current inverse projection transform for
     * the right eye.
     *
     * @since Java 3D 1.3
     */
    public void getInverseVworldProjection(Transform3D leftInverseProjection, Transform3D rightInverseProjection) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                leftInverseProjection.set(canvasViewCache.getLeftCcToVworld());

                // caluclate right eye if in stereo, otherwise
                // this is the same as the left eye.
                if (useStereo) {
                    rightInverseProjection.set(canvasViewCache.getRightCcToVworld());
                } else {
                    rightInverseProjection.set(leftInverseProjection);
                }
            }

        } else {
            leftInverseProjection.setIdentity();
            rightInverseProjection.setIdentity();
        }
    }

    /**
     * Retrieves the physical width of this canvas window in meters.
     * @return the physical window width in meters.
     */
    public double getPhysicalWidth() {
        double width = 0.0;

        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                width = canvasViewCache.getPhysicalWindowWidth();
            }
        }

        return width;
    }

    /**
     * Retrieves the physical height of this canvas window in meters.
     * @return the physical window height in meters.
     */
    public double getPhysicalHeight() {
        double height = 0.0;

        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                height = canvasViewCache.getPhysicalWindowHeight();
            }
        }

        return height;
    }

    /**
     * Retrieves the current Virtual World coordinates to ImagePlate
     * coordinates transform and places it into the specified object.
     * @param t the Transform3D object that will receive the
     * transform
     */
    // TODO: Document -- This will return the transform of left plate.
    public void getVworldToImagePlate(Transform3D t) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                t.set(canvasViewCache.getVworldToImagePlate());
            }
        } else {
            t.setIdentity();
        }
    }

    void getLastVworldToImagePlate(Transform3D t) {
        if (canvasViewCache != null) {
            synchronized (canvasViewCache) {
                t.set(canvasViewCache.getLastVworldToImagePlate());
            }
        } else {
            t.setIdentity();
        }
    }

    /**
     * Sets view that points to this Canvas3D.
     * @param view view object that points to this Canvas3D
     */
    void setView(View view) {
        pendingView = view;

        // We can't set View directly here in user thread since
        // other threads may using canvas.view
        // e.g. In Renderer, we use canvas3d.view.inCallBack
        // before and after postSwap(), if view change in between
        // than view.inCallBack may never reset to false.
        VirtualUniverse.mc.postRequest(MasterControl.SET_VIEW, this);
        evaluateActive();
    }

    void computeViewCache() {
        synchronized (cvLock) {
            if (view == null) {
                canvasViewCache = null;
                canvasViewCacheFrustum = null;
            } else {

                canvasViewCache = new CanvasViewCache(this, screen.screenViewCache, view.viewCache);
                // Issue 109 : construct a separate canvasViewCache for
                // computing view frustum
                canvasViewCacheFrustum = new CanvasViewCache(this, screen.screenViewCache, view.viewCache);
                synchronized (dirtyMaskLock) {
                    cvDirtyMask[0] = VIEW_INFO_DIRTY;
                    cvDirtyMask[1] = VIEW_INFO_DIRTY;
                }
            }
        }
    }

    /**
     * Gets view that points to this Canvas3D.
     * @return view object that points to this Canvas3D
     */
    public View getView() {
        return pendingView;
    }

    /**
     * Returns a status flag indicating whether or not stereo
     * is available.
     * This is equivalent to:
     * <ul>
     * <code>
     * ((Boolean)queryProperties().
     * get("stereoAvailable")).
     * booleanValue()
     * </code>
     * </ul>
     *
     * @return a flag indicating whether stereo is available
     */
    public boolean getStereoAvailable() {
        return ((Boolean) queryProperties().get("stereoAvailable")).booleanValue();
    }

    /**
     * Turns stereo on or off.  Note that this attribute is used
     * only when stereo is available.  Enabling stereo on a Canvas3D
     * that does not support stereo has no effect.
     * @param flag enables or disables the display of stereo
     *
     * @see #queryProperties
     */
    public void setStereoEnable(boolean flag) {
        stereoEnable = flag;
        useStereo = stereoEnable && stereoAvailable;
        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= STEREO_DIRTY;
            cvDirtyMask[1] |= STEREO_DIRTY;
        }
        redraw();
    }

    /**
     * Returns a status flag indicating whether or not stereo
     * is enabled.
     * @return a flag indicating whether stereo is enabled
     */
    public boolean getStereoEnable() {
        return this.stereoEnable;
    }

    /**
     * Specifies how Java 3D generates monoscopic view. If set to
     * View.LEFT_EYE_VIEW, the view generated corresponds to the view as
     * seen from the left eye. If set to View.RIGHT_EYE_VIEW, the view
     * generated corresponds to the view as seen from the right
     * eye. If set to View.CYCLOPEAN_EYE_VIEW, the view generated
     * corresponds to the view as seen from the 'center eye', the
     * fictional eye half-way between the left and right eye.  The
     * default monoscopic view policy is View.CYCLOPEAN_EYE_VIEW.
     * <p>
     * NOTE: for backward compatibility with Java 3D 1.1, if this
     * attribute is set to its default value of
     * View.CYCLOPEAN_EYE_VIEW, the monoscopic view policy in the
     * View object will be used.  An application should not use both
     * the deprecated View method and this Canvas3D method at the same
     * time.
     * @param policy one of View.LEFT_EYE_VIEW, View.RIGHT_EYE_VIEW, or
     * View.CYCLOPEAN_EYE_VIEW.
     *
     * @exception IllegalStateException if the specified
     * policy is CYCLOPEAN_EYE_VIEW, the canvas is a stereo canvas,
     * and the viewPolicy for the associated view is HMD_VIEW
     *
     * @since Java 3D 1.2
     */
    public void setMonoscopicViewPolicy(int policy) {

        if ((view != null) && (view.viewPolicy == View.HMD_VIEW)
                && (monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) && (!useStereo)) {
            throw new IllegalStateException(J3dI18N.getString("View31"));
        }

        monoscopicViewPolicy = policy;
        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= MONOSCOPIC_VIEW_POLICY_DIRTY;
            cvDirtyMask[1] |= MONOSCOPIC_VIEW_POLICY_DIRTY;
        }
        redraw();
    }

    /**
     * Returns policy on how Java 3D generates monoscopic view.
     * @return policy one of View.LEFT_EYE_VIEW, View.RIGHT_EYE_VIEW or
     * View.CYCLOPEAN_EYE_VIEW.
     *
     * @since Java 3D 1.2
     */
    public int getMonoscopicViewPolicy() {
        return this.monoscopicViewPolicy;
    }

    /**
     * Returns a status flag indicating whether or not double
     * buffering is available.
     * This is equivalent to:
     * <ul>
     * <code>
     * ((Boolean)queryProperties().
     * get("doubleBufferAvailable")).
     * booleanValue()
     * </code>
     * </ul>
     *
     * @return a flag indicating whether double buffering is available.
     */
    public boolean getDoubleBufferAvailable() {
        return ((Boolean) queryProperties().get("doubleBufferAvailable")).booleanValue();
    }

    /**
     * Turns double buffering on or off.  If double buffering
     * is off, all drawing is to the front buffer and no buffer swap
     * is done between frames. It should be stressed that running
     * Java 3D with double buffering disabled is not recommended.
     * Enabling double buffering on a Canvas3D
     * that does not support double buffering has no effect.
     *
     * @param flag enables or disables double buffering.
     *
     * @see #queryProperties
     */
    public void setDoubleBufferEnable(boolean flag) {
        doubleBufferEnable = flag;
        useDoubleBuffer = doubleBufferEnable && doubleBufferAvailable;
        if (Thread.currentThread() == screen.renderer) {
            setRenderMode(ctx, FIELD_ALL, useDoubleBuffer);
        }
        redraw();
    }

    /**
     * Returns a status flag indicating whether or not double
     * buffering is enabled.
     * @return a flag indicating if double buffering is enabled.
     */
    public boolean getDoubleBufferEnable() {
        return doubleBufferEnable;
    }

    /**
     * Returns a status flag indicating whether or not scene
     * antialiasing is available.
     * This is equivalent to:
     * <ul>
     * <code>
     * ((Boolean)queryProperties().
     * get("sceneAntialiasingAvailable")).
     * booleanValue()
     * </code>
     * </ul>
     *
     * @return a flag indicating whether scene antialiasing is available.
     */
    public boolean getSceneAntialiasingAvailable() {
        return ((Boolean) queryProperties().get("sceneAntialiasingAvailable")).booleanValue();
    }

    /**
     * Returns a flag indicating whether or not the specified shading
     * language is supported. A ShaderError will be generated if an
     * unsupported shading language is used.
     *
     * @param shadingLanguage the shading language being queried, one of:
     * <code>Shader.SHADING_LANGUAGE_GLSL</code> or
     * <code>Shader.SHADING_LANGUAGE_CG</code>.
     *
     * @return true if the specified shading language is supported,
     * false otherwise.
     *
     * @since Java 3D 1.4
     */
    public boolean isShadingLanguageSupported(int shadingLanguage) {
        // Call queryProperties to ensure that the shading language flags are valid
        queryProperties();

        if (shadingLanguage == Shader.SHADING_LANGUAGE_GLSL)
            return shadingLanguageGLSL;

        return false;
    }

    /**
     * Returns a read-only Map object containing key-value pairs that define
     * various properties for this Canvas3D.  All of the keys are
     * String objects.  The values are key-specific, but most will be
     * Boolean, Integer, Float, Double, or String objects.
     *
     * <p>
     * The currently defined keys are:
     *
     * <p>
     * <ul>
     * <table BORDER=1 CELLSPACING=1 CELLPADDING=1>
     * <tr>
     * <td><b>Key (String)</b></td>
     * <td><b>Value Type</b></td>
     * </tr>
     * <tr>
     * <td><code>shadingLanguageCg</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>shadingLanguageGLSL</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>doubleBufferAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>stereoAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>sceneAntialiasingAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>sceneAntialiasingNumPasses</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>stencilSize</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>texture3DAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureColorTableSize</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureLodRangeAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureLodOffsetAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureWidthMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureHeightMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureBoundaryWidthMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureEnvCombineAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureCombineDot3Available</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureCombineSubtractAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureCoordSetsMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureUnitStateMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureImageUnitsMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureImageUnitsVertexMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureImageUnitsCombinedMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>textureCubeMapAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureDetailAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureSharpenAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureFilter4Available</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>textureAnisotropicFilterDegreeMax</code></td>
     * <td>Float</td>
     * </tr>
     * <tr>
     * <td><code>textureNonPowerOfTwoAvailable</code></td>
     * <td>Boolean</td>
     * </tr>
     * <tr>
     * <td><code>vertexAttrsMax</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>compressedGeometry.majorVersionNumber</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>compressedGeometry.minorVersionNumber</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>compressedGeometry.minorMinorVersionNumber</code></td>
     * <td>Integer</td>
     * </tr>
     * <tr>
     * <td><code>native.version</code></td>
     * <td>String</td>
     * </tr>
     * </table>
     * </ul>
     *
     * <p>
     * The descriptions of the values returned for each key are as follows:
     *
     * <p>
     * <ul>
     * <li>
     * <code>shadingLanguageCg</code>
     * <ul>
     * A Boolean indicating whether or not Cg shading Language
     * is available for this Canvas3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>shadingLanguageGLSL</code>
     * <ul>
     * A Boolean indicating whether or not GLSL shading Language
     * is available for this Canvas3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>doubleBufferAvailable</code>
     * <ul>
     * A Boolean indicating whether or not double buffering
     * is available for this Canvas3D.  This is equivalent to
     * the getDoubleBufferAvailable method.  If this flag is false,
     * the Canvas3D will be rendered in single buffer mode; requests
     * to enable double buffering will be ignored.
     * </ul>
     * </li>
     *
     * <li>
     * <code>stereoAvailable</code>
     * <ul>
     * A Boolean indicating whether or not stereo
     * is available for this Canvas3D.  This is equivalent to
     * the getStereoAvailable method.  If this flag is false,
     * the Canvas3D will be rendered in monoscopic mode; requests
     * to enable stereo will be ignored.
     * </ul>
     * </li>
     *
     * <li>
     * <code>sceneAntialiasingAvailable</code>
     * <ul>
     * A Boolean indicating whether or not scene antialiasing
     * is available for this Canvas3D.  This is equivalent to
     * the getSceneAntialiasingAvailable method.  If this flag is false,
     * requests to enable scene antialiasing will be ignored.
     * </ul>
     * </li>
     *
     * <li>
     * <code>sceneAntialiasingNumPasses</code>
     * <ul>
     * An Integer indicating the number of passes scene antialiasing
     * requires to render a single frame for this Canvas3D.
     * If this value is zero, scene antialiasing is not supported.
     * If this value is one, multisampling antialiasing is used.
     * Otherwise, the number indicates the number of rendering passes
     * needed.
     * </ul>
     * </li>
     *
     * <li>
     * <code>stencilSize</code>
     * <ul>
     * An Integer indicating the number of stencil bits that are available
     * for this Canvas3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>texture3DAvailable</code>
     * <ul>
     * A Boolean indicating whether or not 3D Texture mapping
     * is available for this Canvas3D.  If this flag is false,
     * 3D texture mapping is either not supported by the underlying
     * rendering layer or is otherwise unavailable for this
     * particular Canvas3D.  All use of 3D texture mapping will be
     * ignored in this case.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureColorTableSize</code>
     * <ul>
     * An Integer indicating the maximum size of the texture color
     * table for this Canvas3D.  If the size is 0, the texture
     * color table is either not supported by the underlying rendering
     * layer or is otherwise unavailable for this particular
     * Canvas3D.  An attempt to use a texture color table larger than
     * textureColorTableSize will be ignored; no color lookup will be
     * performed.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureLodRangeAvailable</code>
     * <ul>
     * A Boolean indicating whether or not setting only a subset of mipmap
     * levels and setting a range of texture LOD are available for this
     * Canvas3D.
     * If it indicates false, setting a subset of mipmap levels and
     * setting a texture LOD range are not supported by the underlying
     * rendering layer, and an attempt to set base level, or maximum level,
     * or minimum LOD, or maximum LOD will be ignored. In this case,
     * images for all mipmap levels must be defined for the texture to be
     * valid.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureLodOffsetAvailable</code>
     * <ul>
     * A Boolean indicating whether or not setting texture LOD offset is
     * available for this Canvas3D. If it indicates false, setting
     * texture LOD offset is not supported by the underlying rendering
     * layer, and an attempt to set the texture LOD offset will be ignored.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureWidthMax</code>
     * <ul>
     * An Integer indicating the maximum texture width supported by
     * this Canvas3D. If the width of a texture exceeds the maximum texture
     * width for a Canvas3D, then the texture will be effectively disabled
     * for that Canvas3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureHeightMax</code>
     * <ul>
     * An Integer indicating the maximum texture height supported by
     * this Canvas3D. If the height of a texture exceeds the maximum texture
     * height for a Canvas3D, then the texture will be effectively disabled
     * for that Canvas3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureBoundaryWidthMax</code>
     * <ul>
     * An Integer indicating the maximum texture boundary width
     * supported by the underlying rendering layer for this Canvas3D. If
     * the maximum supported texture boundary width is 0, then texture
     * boundary is not supported by the underlying rendering layer.
     * An attempt to specify a texture boundary width > the
     * textureBoundaryWidthMax will effectively disable the texture.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureEnvCombineAvailable</code>
     * <ul>
     * A Boolean indicating whether or not texture environment combine
     * operation is supported for this Canvas3D. If it indicates false,
     * then texture environment combine is not supported by the
     * underlying rendering layer, and an attempt to specify COMBINE
     * as the texture mode will be ignored. The texture mode in effect
     * will be REPLACE.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureCombineDot3Available</code>
     * <ul>
     * A Boolean indicating whether or not texture combine mode
     * COMBINE_DOT3 is
     * supported for this Canvas3D. If it indicates false, then
     * texture combine mode COMBINE_DOT3 is not supported by
     * the underlying rendering layer, and an attempt to specify
     * COMBINE_DOT3 as the texture combine mode will be ignored.
     * The texture combine mode in effect will be COMBINE_REPLACE.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureCombineSubtractAvailable</code>
     * <ul>
     * A Boolean indicating whether or not texture combine mode
     * COMBINE_SUBTRACT is
     * supported for this Canvas3D. If it indicates false, then
     * texture combine mode COMBINE_SUBTRACT is not supported by
     * the underlying rendering layer, and an attempt to specify
     * COMBINE_SUBTRACT as the texture combine mode will be ignored.
     * The texture combine mode in effect will be COMBINE_REPLACE.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureCoordSetsMax</code>
     * <ul>
     * An Integer indicating the maximum number of texture coordinate sets
     * supported by the underlying rendering layer.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureUnitStateMax</code>
     * <ul>
     * An Integer indicating the maximum number of fixed-function texture units
     * supported by the underlying rendering layer. If the number of
     * application-sepcified texture unit states exceeds the maximum number
     * for a Canvas3D, and the fixed-function rendering pipeline is used, then
     * the texture will be effectively disabled for that Canvas3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureImageUnitsMax</code>
     * <ul>
     * An Integer indicating the maximum number of texture image units
     * that can be accessed by the fragment shader when programmable shaders
     * are used.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureImageUnitsVertexMax</code>
     * <ul>
     * An Integer indicating the maximum number of texture image units
     * that can be accessed by the vertex shader when programmable shaders
     * are used.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureImageUnitsCombinedMax</code>
     * <ul>
     * An Integer indicating the combined maximum number of texture image units
     * that can be accessed by the vertex shader and the fragment shader when
     * programmable shaders are used.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureCubeMapAvailable</code>
     * <ul>
     * A Boolean indicating whether or not texture cube map is supported
     * for this Canvas3D. If it indicates false, then texture cube map
     * is not supported by the underlying rendering layer, and an attempt
     * to specify NORMAL_MAP or REFLECTION_MAP as the texture generation
     * mode will be ignored. The texture generation mode in effect will
     * be SPHERE_MAP.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureDetailAvailable</code>
     * <ul>
     * A Boolean indicating whether or not detail texture is supported
     * for this Canvas3D. If it indicates false, then detail texture is
     * not supported by the underlying rendering layer, and an attempt
     * to specify LINEAR_DETAIL, LINEAR_DETAIL_ALPHA or
     * LINEAR_DETAIL_RGB as the texture magnification filter mode will
     * be ignored. The texture magnification filter mode in effect will
     * be BASE_LEVEL_LINEAR.
     * As of Java 3D 1.5, this property is always false.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureSharpenAvailable</code>
     * <ul>
     * A Boolean indicating whether or not sharpen texture is supported
     * for this Canvas3D. If it indicates false, then sharpen texture
     * is not supported by the underlying rendering layer, and an attempt
     * to specify LINEAR_SHARPEN, LINEAR_SHARPEN_ALPHA or
     * LINEAR_SHARPEN_RGB as the texture magnification filter mode
     * will be ignored. The texture magnification filter mode in effect
     * will be BASE_LEVEL_LINEAR.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureFilter4Available</code>
     * <ul>
     * A Boolean indicating whether or not filter4 is supported for this
     * Canvas3D. If it indicates flase, then filter4 is not supported
     * by the underlying rendering layer, and an attempt to specify
     * FILTER_4 as the texture minification filter mode or texture
     * magnification filter mode will be ignored. The texture filter mode
     * in effect will be BASE_LEVEL_LINEAR.
     * </ul>
     * </li>
     *
     * <li>
     * <code>textureAnisotropicFilterDegreeMax</code>
     * <ul>
     * A Float indicating the maximum degree of anisotropic filter
     * available for this Canvas3D. If it indicates 1.0, setting
     * anisotropic filter is not supported by the underlying rendering
     * layer, and an attempt to set anisotropic filter degree will be ignored.
     * </ul>
     * </li>
        
     * <li>
     * <code>textureNonPowerOfTwoAvailable</code>
     * <ul>
     * A Boolean indicating whether or not texture dimensions that are
     * not powers of two are supported for
     * for this Canvas3D. If it indicates false, then textures with
     * non power of two sizes will be ignored. Set the property
     * j3d.textureEnforcePowerOfTwo to revert to the pre-1.5 behavior
     * of throwing exceptions for non power of two textures.
     * </ul>
     * </li>
     *
     * <li>
     * <code>vertexAttrsMax</code>
     * <ul>
     * An Integer indicating the maximum number of vertex attributes
     * supported by the underlying rendering layer. This is in addition to
     * the vertex coordinate (position), color, normal, and so forth.
     * </ul>
     * </li>
     *
     * <li>
     * <code>compressedGeometry.majorVersionNumber</code><br>
     * <code>compressedGeometry.minorVersionNumber</code><br>
     * <code>compressedGeometry.minorMinorVersionNumber</code>
     * <ul>
     * Integers indicating the major, minor, and minor-minor
     * version numbers, respectively, of the version of compressed
     * geometry supported by this version of Java 3D.
     * </ul>
     * </li>
     *
     * <li>
     * <code>native.version</code>
     * <ul>
     * A String indicating the version number of the native graphics
     * library.  The format of this string is defined by the native
     * library.
     * </ul>
     * </li>
     * </ul>
     *
     * @return the properties of this Canavs3D
     *
     * @since Java 3D 1.2
     */
    public final Map queryProperties() {
        if (queryProps == null) {
            boolean createDummyCtx = false;

            synchronized (VirtualUniverse.mc.contextCreationLock) {
                if (ctx == null) {
                    createDummyCtx = true;
                }
            }

            if (createDummyCtx) {
                GraphicsConfigTemplate3D.setQueryProps(this);
            }

            //create query Properties
            createQueryProps();
        }

        if (fatalError) {
            throw new IllegalStateException(J3dI18N.getString("Canvas3D29"));
        }

        return queryProps;
    }

    void createQueryContext() {
        // create a dummy context to query for support of certain
        // extensions, the context will destroy immediately
        // inside the native code after setting the various
        // fields in this object
        createQueryContext(drawable, offScreen, 1, 1);
        // compute the max available texture units
        maxAvailableTextureUnits = Math.max(maxTextureUnits, maxTextureImageUnits);
    }

    /**
     * Creates the query properties for this Canvas.
     */
    private void createQueryProps() {
        // Create lists of keys and values
        ArrayList<String> keys = new ArrayList<String>();
        ArrayList<Object> values = new ArrayList<Object>();
        int pass = 0;

        // properties not associated with graphics context
        keys.add("doubleBufferAvailable");
        values.add(new Boolean(doubleBufferAvailable));

        keys.add("stereoAvailable");
        values.add(new Boolean(stereoAvailable));

        keys.add("sceneAntialiasingAvailable");
        values.add(new Boolean(sceneAntialiasingAvailable));

        keys.add("sceneAntialiasingNumPasses");

        if (sceneAntialiasingAvailable) {
            pass = (sceneAntialiasingMultiSamplesAvailable ? 1 : Renderer.NUM_ACCUMULATION_SAMPLES);
        }
        values.add(new Integer(pass));

        keys.add("stencilSize");
        // Return the actual stencil size if the user owns it, otherwise
        // return 0
        if (userStencilAvailable) {
            values.add(new Integer(actualStencilSize));
        } else {
            values.add(new Integer(0));
        }

        keys.add("compressedGeometry.majorVersionNumber");
        values.add(new Integer(GeometryDecompressor.majorVersionNumber));
        keys.add("compressedGeometry.minorVersionNumber");
        values.add(new Integer(GeometryDecompressor.minorVersionNumber));
        keys.add("compressedGeometry.minorMinorVersionNumber");
        values.add(new Integer(GeometryDecompressor.minorMinorVersionNumber));

        // Properties associated with graphics context
        keys.add("texture3DAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_3D) != 0));

        keys.add("textureColorTableSize");
        values.add(new Integer(textureColorTableSize));

        keys.add("textureEnvCombineAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_COMBINE) != 0));

        keys.add("textureCombineDot3Available");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_COMBINE_DOT3) != 0));

        keys.add("textureCombineSubtractAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_COMBINE_SUBTRACT) != 0));

        keys.add("textureCubeMapAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_CUBE_MAP) != 0));

        keys.add("textureSharpenAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_SHARPEN) != 0));

        keys.add("textureDetailAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_DETAIL) != 0));

        keys.add("textureFilter4Available");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_FILTER4) != 0));

        keys.add("textureAnisotropicFilterDegreeMax");
        values.add(new Float(anisotropicDegreeMax));

        keys.add("textureWidthMax");
        values.add(new Integer(textureWidthMax));

        keys.add("textureHeightMax");
        values.add(new Integer(textureHeightMax));

        keys.add("texture3DWidthMax");
        values.add(new Integer(texture3DWidthMax));

        keys.add("texture3DHeightMax");
        values.add(new Integer(texture3DHeightMax));

        keys.add("texture3DDepthMax");
        values.add(new Integer(texture3DDepthMax));

        keys.add("textureBoundaryWidthMax");
        values.add(new Integer(textureBoundaryWidthMax));

        keys.add("textureLodRangeAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_LOD_RANGE) != 0));

        keys.add("textureLodOffsetAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_LOD_OFFSET) != 0));

        keys.add("textureNonPowerOfTwoAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_NON_POWER_OF_TWO) != 0));

        keys.add("textureAutoMipMapGenerationAvailable");
        values.add(new Boolean((textureExtendedFeatures & TEXTURE_AUTO_MIPMAP_GENERATION) != 0));

        keys.add("textureCoordSetsMax");
        values.add(new Integer(maxTexCoordSets));

        keys.add("textureUnitStateMax");
        values.add(new Integer(maxTextureUnits));

        keys.add("textureImageUnitsMax");
        values.add(new Integer(maxTextureImageUnits));

        keys.add("textureImageUnitsVertexMax");
        values.add(new Integer(maxVertexTextureImageUnits));

        keys.add("textureImageUnitsCombinedMax");
        values.add(new Integer(maxCombinedTextureImageUnits));

        keys.add("vertexAttrsMax");
        values.add(new Integer(maxVertexAttrs));

        keys.add("shadingLanguageGLSL");
        values.add(new Boolean(shadingLanguageGLSL));

        keys.add("native.version");
        values.add(nativeGraphicsVersion);

        keys.add("native.vendor");
        values.add(nativeGraphicsVendor);

        keys.add("native.renderer");
        values.add(nativeGraphicsRenderer);

        // Now Create read-only properties object
        queryProps = new J3dQueryProps(keys, values);
    }

    /**
     * Update the view cache associated with this canvas.
     */
    void updateViewCache(boolean flag, CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) {

        assert cvc == null;
        synchronized (cvLock) {
            if (firstPaintCalled && (canvasViewCache != null)) {
                assert canvasViewCacheFrustum != null;
                // Issue 109 : choose the appropriate cvCache
                if (frustumBBox != null) {
                    canvasViewCacheFrustum.snapshot(true);
                    canvasViewCacheFrustum.computeDerivedData(flag, null, frustumBBox, doInfinite);
                } else {
                    canvasViewCache.snapshot(false);
                    canvasViewCache.computeDerivedData(flag, null, null, doInfinite);
                }
            }
        }
    }

    /**
     * Set depthBufferWriteEnableOverride flag
     */
    void setDepthBufferWriteEnableOverride(boolean flag) {
        depthBufferWriteEnableOverride = flag;
    }

    /**
     * Set depthBufferEnableOverride flag
     */
    void setDepthBufferEnableOverride(boolean flag) {
        depthBufferEnableOverride = flag;
    }

    // Static initializer for Canvas3D class
    static {
        VirtualUniverse.loadLibraries();
    }

    void resetTexture(Context ctx, int texUnitIndex) {
        // D3D also need to reset texture attributes
        this.resetTextureNative(ctx, texUnitIndex);

        if (texUnitIndex < 0) {
            texUnitIndex = 0;
        }
        texUnitState[texUnitIndex].mirror = null;
        texUnitState[texUnitIndex].texture = null;
    }

    // reset all attributes so that everything e.g. display list,
    // texture will recreate again in the next frame
    void resetRendering() {
        reset();

        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= VIEW_INFO_DIRTY;
            cvDirtyMask[1] |= VIEW_INFO_DIRTY;
        }

    }

    void reset() {
        int i;
        currentAppear = new AppearanceRetained();
        currentMaterial = new MaterialRetained();
        viewFrustum = new CachedFrustum();
        canvasDirty = 0xffff;
        lightBin = null;
        environmentSet = null;
        attributeBin = null;
        shaderBin = null;
        textureBin = null;
        renderMolecule = null;
        polygonAttributes = null;
        lineAttributes = null;
        pointAttributes = null;
        material = null;
        enableLighting = false;
        transparency = null;
        coloringAttributes = null;
        shaderProgram = null;
        texture = null;
        texAttrs = null;
        if (texUnitState != null) {
            TextureUnitStateRetained tus;
            for (i = 0; i < texUnitState.length; i++) {
                tus = texUnitState[i];
                if (tus != null) {
                    tus.texAttrs = null;
                    tus.texGen = null;
                }
            }
        }
        texCoordGeneration = null;
        renderingAttrs = null;
        appearance = null;
        appHandle = null;
        dirtyRenderMoleculeList.clear();
        displayListResourceFreeList.clear();

        dirtyDlistPerRinfoList.clear();
        textureIdResourceFreeList.clear();

        lightChanged = true;
        modelMatrix = null;
        modelClip = null;
        fog = null;
        sceneAmbient = new Color3f();

        for (i = 0; i < frameCount.length; i++) {
            frameCount[i] = -1;
        }

        for (i = 0; i < lights.length; i++) {
            lights[i] = null;
        }

        if (currentLights != null) {
            for (i = 0; i < currentLights.length; i++) {
                currentLights[i] = null;
            }
        }

        enableMask = -1;
        stateUpdateMask = 0;
        depthBufferWriteEnableOverride = false;
        depthBufferEnableOverride = false;
        depthBufferWriteEnable = true;
        vfPlanesValid = false;
        lightChanged = false;

        for (i = 0; i < curStateToUpdate.length; i++) {
            curStateToUpdate[i] = null;
        }

        // Issue 362 - need to reset display lists and ctxTimeStamp in this
        // method, so that display lists  will be recreated when canvas is
        // removed from a view and then added back into a view with another
        // canvas
        needToRebuildDisplayList = true;
        ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp();
    }

    void resetImmediateRendering() {
        canvasDirty = 0xffff;
        ra = null;

        setSceneAmbient(ctx, 0.0f, 0.0f, 0.0f);
        disableFog(ctx);
        resetRenderingAttributes(ctx, false, false);

        resetTexture(ctx, -1);
        resetTexCoordGeneration(ctx);
        resetTextureAttributes(ctx);
        texUnitState[0].texAttrs = null;
        texUnitState[0].texGen = null;

        resetPolygonAttributes(ctx);
        resetLineAttributes(ctx);
        resetPointAttributes(ctx);
        resetTransparency(ctx, RenderMolecule.SURFACE, PolygonAttributes.POLYGON_FILL, false, false);
        resetColoringAttributes(ctx, 1.0f, 1.0f, 1.0f, 1.0f, false);
        updateMaterial(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
        resetRendering();
        makeCtxCurrent();
        synchronized (dirtyMaskLock) {
            cvDirtyMask[0] |= VIEW_INFO_DIRTY;
            cvDirtyMask[1] |= VIEW_INFO_DIRTY;
        }
        needToRebuildDisplayList = true;

        ctxTimeStamp = VirtualUniverse.mc.getContextTimeStamp();
    }

    @Override
    public Point getLocationOnScreen() {
        try {
            return super.getLocationOnScreen();
        } catch (IllegalComponentStateException e) {
        }

        return new Point();
    }

    void setProjectionMatrix(Context ctx, Transform3D projTrans) {
        this.projTrans = projTrans;
        setProjectionMatrix(ctx, projTrans.mat);
    }

    void setModelViewMatrix(Context ctx, double[] viewMatrix, Transform3D mTrans) {
        setModelViewMatrix(ctx, viewMatrix, mTrans.mat);
        if (!useStereo) {
            this.modelMatrix = mTrans;
        } else {
            // TODO : This seems wrong to do only for the right eye.
            // A possible approach is to invalidate the cache at begin of
            // each eye.
            if (rightStereoPass) {
                //  Only set cache in right stereo pass, otherwise
                //  if the left stereo pass set the cache value,
                //  setModelViewMatrix() in right stereo pass will not
                //  perform in RenderMolecules.
                this.modelMatrix = mTrans;
            }
        }
    }

    void setDepthBufferWriteEnable(boolean mode) {
        depthBufferWriteEnable = mode;
        setDepthBufferWriteEnable(ctx, mode);
    }

    void setNumActiveTexUnit(int n) {
        numActiveTexUnit = n;
    }

    int getNumActiveTexUnit() {
        return numActiveTexUnit;
    }

    void setLastActiveTexUnit(int n) {
        lastActiveTexUnit = n;
    }

    int getLastActiveTexUnit() {
        return lastActiveTexUnit;
    }

    // Create the texture state array
    void createTexUnitState() {
        texUnitState = new TextureUnitStateRetained[maxAvailableTextureUnits];
        for (int t = 0; t < maxAvailableTextureUnits; t++) {
            texUnitState[t] = new TextureUnitStateRetained();
            texUnitState[t].texture = null;
            texUnitState[t].mirror = null;
        }
    }

    /**
     * Enable separate specular color if it is not overriden by the
     * property j3d.disableSeparateSpecular.
     */
    void enableSeparateSpecularColor() {
        boolean enable = !VirtualUniverse.mc.disableSeparateSpecularColor;
        updateSeparateSpecularColorEnable(ctx, enable);
    }

    // Send a createOffScreenBuffer message to Renderer (via
    // MasterControl) and wait for it to be done
    private void sendCreateOffScreenBuffer() {
        // Wait for the buffer to be created unless called from
        // a Behavior or from a Rendering thread
        if (!(Thread.currentThread() instanceof BehaviorScheduler)
                && !(Thread.currentThread() instanceof Renderer)) {

            offScreenBufferPending = true;
        }

        // Send message to Renderer thread to perform createOffScreenBuffer.
        VirtualUniverse.mc.sendCreateOffScreenBuffer(this);

        // Wait for off-screen buffer to be created
        while (offScreenBufferPending) {
            // Issue 364: create master control thread if needed
            VirtualUniverse.mc.createMasterControlThread();
            MasterControl.threadYield();
        }
    }

    // Send a destroyOffScreenBuffer message to Renderer (via
    // MasterControl) and wait for it to be done
    private void sendDestroyCtxAndOffScreenBuffer() {
        // Wait for the buffer to be destroyed unless called from
        // a Behavior or from a Rendering thread
        Thread currentThread = Thread.currentThread();
        if (!(currentThread instanceof BehaviorScheduler) && !(currentThread instanceof Renderer)) {

            offScreenBufferPending = true;
        }

        // Fix for Issue 18 and Issue 175
        // Send message to Renderer thread to perform remove Ctx and destroyOffScreenBuffer.

        VirtualUniverse.mc.sendDestroyCtxAndOffScreenBuffer(this);

        // Wait for ctx and off-screen buffer to be destroyed
        while (offScreenBufferPending) {
            // Issue 364: create master control thread if needed
            VirtualUniverse.mc.createMasterControlThread();
            MasterControl.threadYield();
        }
    }

    // Send a allocateCanvasId message to Renderer (via MasterControl) without
    // waiting for it to be done
    private void sendAllocateCanvasId() {
        // Send message to Renderer thread to allocate a canvasId
        VirtualUniverse.mc.sendAllocateCanvasId(this);
    }

    // Send a freeCanvasId message to Renderer (via MasterControl) without
    // waiting for it to be done
    private void sendFreeCanvasId() {
        // Send message to Renderer thread to free the canvasId
        VirtualUniverse.mc.sendFreeCanvasId(this);
    }

    private void removeCtx() {

        if ((screen != null) && (screen.renderer != null) && (ctx != null)) {
            VirtualUniverse.mc.postRequest(MasterControl.FREE_CONTEXT,
                    new Object[] { this, Long.valueOf(0L), drawable, ctx });
            // Fix for Issue 19
            // Wait for the context to be freed unless called from
            // a Behavior or from a Rendering thread
            Thread currentThread = Thread.currentThread();
            if (!(currentThread instanceof BehaviorScheduler) && !(currentThread instanceof Renderer)) {
                while (ctxTimeStamp != 0) {
                    MasterControl.threadYield();
                }
            }
            ctx = null;
        }
    }

    /**
     * Serialization of Canvas3D objects is not supported.
     *
     * @exception UnsupportedOperationException this method is not supported
     *
     * @since Java 3D 1.3
     */
    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {

        throw new UnsupportedOperationException(J3dI18N.getString("Canvas3D20"));
    }

    /**
     * Serialization of Canvas3D objects is not supported.
     *
     * @exception UnsupportedOperationException this method is not supported
     *
     * @since Java 3D 1.3
     */
    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {

        throw new UnsupportedOperationException(J3dI18N.getString("Canvas3D20"));
    }

    // mark that the current bin specified by the bit is already updated
    void setStateIsUpdated(int bit) {
        stateUpdateMask &= ~(1 << bit);
    }

    // mark that the bin specified by the bit needs to be updated
    void setStateToUpdate(int bit, Object bin) {
        stateUpdateMask |= 1 << bit;
        curStateToUpdate[bit] = bin;
    }

    // update LightBin, EnvironmentSet, AttributeBin & ShaderBin if neccessary
    // according to the stateUpdateMask

    static int ENV_STATE_MASK = (1 << LIGHTBIN_BIT) | (1 << ENVIRONMENTSET_BIT) | (1 << ATTRIBUTEBIN_BIT)
            | (1 << SHADERBIN_BIT);

    void updateEnvState() {

        if ((stateUpdateMask & ENV_STATE_MASK) == 0)
            return;

        if ((stateUpdateMask & (1 << LIGHTBIN_BIT)) != 0) {
            ((LightBin) curStateToUpdate[LIGHTBIN_BIT]).updateAttributes(this);
        }

        if ((stateUpdateMask & (1 << ENVIRONMENTSET_BIT)) != 0) {
            ((EnvironmentSet) curStateToUpdate[ENVIRONMENTSET_BIT]).updateAttributes(this);
        }

        if ((stateUpdateMask & (1 << ATTRIBUTEBIN_BIT)) != 0) {
            ((AttributeBin) curStateToUpdate[ATTRIBUTEBIN_BIT]).updateAttributes(this);
        }

        if ((stateUpdateMask & (1 << SHADERBIN_BIT)) != 0) {
            ((ShaderBin) curStateToUpdate[SHADERBIN_BIT]).updateAttributes(this);
        }

        // reset the state update mask for those environment state bits
        stateUpdateMask &= ~ENV_STATE_MASK;
    }

    /**
     * update state if neccessary according to the stateUpdatedMask
     */
    void updateState(int dirtyBits) {

        if (stateUpdateMask == 0)
            return;

        updateEnvState();

        if ((stateUpdateMask & (1 << TEXTUREBIN_BIT)) != 0) {
            ((TextureBin) curStateToUpdate[TEXTUREBIN_BIT]).updateAttributes(this);
        }

        if ((stateUpdateMask & (1 << RENDERMOLECULE_BIT)) != 0) {
            ((RenderMolecule) curStateToUpdate[RENDERMOLECULE_BIT]).updateAttributes(this, dirtyBits);

        }

        if ((stateUpdateMask & (1 << TRANSPARENCY_BIT)) != 0) {
            ((RenderMolecule) curStateToUpdate[RENDERMOLECULE_BIT]).updateTransparencyAttributes(this);
            stateUpdateMask &= ~(1 << TRANSPARENCY_BIT);
        }

        // reset state update mask
        stateUpdateMask = 0;
    }

    // This method updates this Texture2D for raster.
    // Note : No multi-texture is not used.
    void updateTextureForRaster(Texture2DRetained texture) {

        // Setup texture and texture attributes for texture unit 0.
        Pipeline.getPipeline().updateTextureUnitState(ctx, 0, true);
        setLastActiveTexUnit(0);
        setNumActiveTexUnit(1);

        texture.updateNative(this);
        resetTexCoordGeneration(ctx);
        resetTextureAttributes(ctx);

        for (int i = 1; i < maxTextureUnits; i++) {
            resetTexture(ctx, i);
        }

        // set the active texture unit back to 0
        activeTextureUnit(ctx, 0);

        // Force the next textureBin to reload.
        canvasDirty |= Canvas3D.TEXTUREBIN_DIRTY | Canvas3D.TEXTUREATTRIBUTES_DIRTY;
    }

    void restoreTextureBin() {

        // Need to check TextureBin's shaderBin for null
        // TextureBin can get clear() if there isn't any RM under it.
        if ((textureBin != null) && (textureBin.shaderBin != null)) {
            textureBin.updateAttributes(this);
        }
    }

    void textureFill(RasterRetained raster, Point2d winCoord, float mapZ, float alpha) {

        int winWidth = canvasViewCache.getCanvasWidth();
        int winHeight = canvasViewCache.getCanvasHeight();

        int rasterImageWidth = raster.image.width;
        int rasterImageHeight = raster.image.height;

        float texMinU = 0, texMinV = 0, texMaxU = 0, texMaxV = 0;
        float mapMinX = 0, mapMinY = 0, mapMaxX = 0, mapMaxY = 0;

        Point rasterSrcOffset = new Point();
        raster.getSrcOffset(rasterSrcOffset);

        Dimension rasterSize = new Dimension();
        raster.getSize(rasterSize);

        //        System.err.println("rasterImageWidth " + rasterImageWidth + " rasterImageHeight " + rasterImageHeight);
        //        System.err.println("rasterSrcOffset " + rasterSrcOffset + " rasterSize " + rasterSize);

        int rasterMinX = rasterSrcOffset.x;
        int rasterMaxX = rasterSrcOffset.x + rasterSize.width;
        int rasterMinY = rasterSrcOffset.y;
        int rasterMaxY = rasterSrcOffset.y + rasterSize.height;

        if ((rasterMinX >= rasterImageWidth) || (rasterMinY >= rasterImageHeight) || (rasterMaxX <= 0)
                || (rasterMaxY <= 0)) {
            return;
        }

        if (rasterMinX < 0) {
            rasterMinX = 0;
        }
        if (rasterMinY < 0) {
            rasterMinY = 0;
        }

        if (rasterMaxX > rasterImageWidth) {
            rasterMaxX = rasterImageWidth;
        }

        if (rasterMaxY > rasterImageHeight) {
            rasterMaxY = rasterImageHeight;
        }

        texMinU = (float) rasterMinX / (float) rasterImageWidth;
        texMaxU = (float) rasterMaxX / (float) rasterImageWidth;
        mapMinX = (float) winCoord.x / (float) winWidth;
        mapMaxX = (float) (winCoord.x + (rasterMaxX - rasterMinX)) / (float) winWidth;

        if (raster.image.isYUp()) {
            texMinV = (float) rasterMinY / (float) rasterImageHeight;
            texMaxV = (float) rasterMaxY / (float) rasterImageHeight;
        } else {
            //  System.err.println("In yUp is false case");
            texMinV = 1.0f - (float) rasterMaxY / (float) rasterImageHeight;
            texMaxV = 1.0f - (float) rasterMinY / (float) rasterImageHeight;
        }

        mapMinY = 1.0f - ((float) (winCoord.y + (rasterMaxY - rasterMinY)) / (float) winHeight);
        mapMaxY = 1.0f - ((float) winCoord.y / (float) winHeight);

        textureFillRaster(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY, mapMaxY, mapZ, alpha,
                raster.image.useBilinearFilter());

    }

    void textureFill(BackgroundRetained bg, int winWidth, int winHeight) {

        final int maxX = bg.image.width;
        final int maxY = bg.image.height;

        //        System.err.println("maxX " + maxX + " maxY " + maxY);

        float xzoom = (float) winWidth / maxX;
        float yzoom = (float) winHeight / maxY;
        float zoom = 0;
        float texMinU = 0, texMinV = 0, texMaxU = 0, texMaxV = 0, adjustV = 0;
        float mapMinX = 0, mapMinY = 0, mapMaxX = 0, mapMaxY = 0;
        float halfWidth = 0, halfHeight = 0;

        switch (bg.imageScaleMode) {
        case Background.SCALE_NONE:
            texMinU = 0.0f;
            texMinV = 0.0f;
            texMaxU = 1.0f;
            texMaxV = 1.0f;
            halfWidth = (float) winWidth / 2.0f;
            halfHeight = (float) winHeight / 2.0f;
            mapMinX = (float) ((0 - halfWidth) / halfWidth);
            mapMinY = (float) ((0 - halfHeight) / halfHeight);
            mapMaxX = (float) ((maxX - halfWidth) / halfWidth);
            mapMaxY = (float) ((maxY - halfHeight) / halfHeight);
            adjustV = ((float) winHeight - (float) maxY) / halfHeight;
            mapMinY += adjustV;
            mapMaxY += adjustV;
            break;
        case Background.SCALE_FIT_MIN:
            zoom = Math.min(xzoom, yzoom);
            texMinU = 0.0f;
            texMinV = 0.0f;
            texMaxU = 1.0f;
            texMaxV = 1.0f;
            mapMinX = -1.0f;
            mapMaxY = 1.0f;
            if (xzoom < yzoom) {
                mapMaxX = 1.0f;
                mapMinY = -1.0f + 2.0f * (1.0f - zoom * (float) maxY / (float) winHeight);
            } else {
                mapMaxX = -1.0f + zoom * (float) maxX / winWidth * 2;
                mapMinY = -1.0f;
            }
            break;
        case Background.SCALE_FIT_MAX:
            zoom = Math.max(xzoom, yzoom);
            mapMinX = -1.0f;
            mapMinY = -1.0f;
            mapMaxX = 1.0f;
            mapMaxY = 1.0f;
            if (xzoom < yzoom) {
                texMinU = 0.0f;
                texMinV = 0.0f;
                texMaxU = (float) winWidth / maxX / zoom;
                texMaxV = 1.0f;
            } else {
                texMinU = 0.0f;
                texMinV = 1.0f - (float) winHeight / maxY / zoom;
                texMaxU = 1.0f;
                texMaxV = 1.0f;
            }
            break;
        case Background.SCALE_FIT_ALL:
            texMinU = 0.0f;
            texMinV = 0.0f;
            texMaxU = 1.0f;
            texMaxV = 1.0f;
            mapMinX = -1.0f;
            mapMinY = -1.0f;
            mapMaxX = 1.0f;
            mapMaxY = 1.0f;
            break;
        case Background.SCALE_REPEAT:

            texMinU = 0.0f;
            texMinV = -yzoom;
            texMaxU = xzoom;
            texMaxV = 0.0f;
            mapMinX = -1.0f;
            mapMinY = -1.0f;
            mapMaxX = 1.0f;
            mapMaxY = 1.0f;
            break;
        case Background.SCALE_NONE_CENTER:
            // TODO : Why is there a zoom ?
            if (xzoom >= 1.0f) {
                texMinU = 0.0f;
                texMaxU = 1.0f;
                mapMinX = -(float) maxX / winWidth;
                mapMaxX = (float) maxX / winWidth;
            } else {
                texMinU = 0.5f - (float) winWidth / maxX / 2;
                texMaxU = 0.5f + (float) winWidth / maxX / 2;
                mapMinX = -1.0f;
                mapMaxX = 1.0f;
            }
            if (yzoom >= 1.0f) {
                texMinV = 0.0f;
                texMaxV = 1.0f;
                mapMinY = -(float) maxY / winHeight;
                mapMaxY = (float) maxY / winHeight;
            } else {
                texMinV = 0.5f - (float) winHeight / maxY / 2;
                texMaxV = 0.5f + (float) winHeight / maxY / 2;
                mapMinY = -1.0f;
                mapMaxY = 1.0f;
            }
            break;
        }

        //        System.err.println("Java 3D : mapMinX " + mapMinX + " mapMinY " + mapMinY +
        //                           " mapMaxX " + mapMaxX + " mapMaxY " + mapMaxY);
        textureFillBackground(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY, mapMaxY,
                bg.image.useBilinearFilter());

    }

    void clear(BackgroundRetained bg, int winWidth, int winHeight) {

        // Issue 239 - clear stencil if requested and available
        // Note that this is a partial solution, since we eventually want an API
        // to control this.
        boolean clearStencil = VirtualUniverse.mc.stencilClear && userStencilAvailable;

        clear(ctx, bg.color.x, bg.color.y, bg.color.z, clearStencil);

        // TODO : This is a bug on not mirror bg. Will fix this as a bug after 1.5 beta.
        // For now, as a workaround, we will check bg.image and bg.image.imageData not null.
        if ((bg.image != null) && (bg.image.imageData != null)) {
            // setup Texture pipe.
            updateTextureForRaster(bg.texture);

            textureFill(bg, winWidth, winHeight);

            // Restore texture pipe.
            restoreTextureBin();
        }
    }

    /**
     * obj is either TextureRetained or DetailTextureImage
     * if obj is DetailTextureImage then we just clear
     * the resourceCreationMask of all the formats
     * no matter it is create or not since we don't
     * remember the format information for simplicity.
     * We don't need to check duplicate value of id in the
     * table since this procedure is invoke only when id
     * of texture is -1 one time only.
     * This is always call from Renderer thread.
     */
    void addTextureResource(int id, TextureRetained obj) {
        if (id <= 0) {
            return;
        }

        if (useSharedCtx) {
            screen.renderer.addTextureResource(id, obj);
        } else {
            // This will replace the previous key if exists
            if (textureIDResourceTable.size() <= id) {
                for (int i = textureIDResourceTable.size(); i < id; i++) {
                    textureIDResourceTable.add(null);
                }
                textureIDResourceTable.add(obj);
            } else {
                textureIDResourceTable.set(id, obj);
            }

        }
    }

    // handle free resource in the FreeList
    void freeResourcesInFreeList(Context ctx) {
        Iterator<Integer> it;
        int val;

        // free resource for those canvases that
        // don't use shared ctx
        if (displayListResourceFreeList.size() > 0) {
            for (it = displayListResourceFreeList.iterator(); it.hasNext();) {
                val = it.next().intValue();
                if (val <= 0) {
                    continue;
                }
                Canvas3D.freeDisplayList(ctx, val);
            }
            displayListResourceFreeList.clear();
        }

        if (textureIdResourceFreeList.size() > 0) {
            for (it = textureIdResourceFreeList.iterator(); it.hasNext();) {
                val = it.next().intValue();
                if (val <= 0) {
                    continue;
                }
                if (val >= textureIDResourceTable.size()) {
                    System.err.println("Error in freeResourcesInFreeList : ResourceIDTableSize = "
                            + textureIDResourceTable.size() + " val = " + val);
                } else {
                    TextureRetained tex = textureIDResourceTable.get(val);
                    if (tex != null) {
                        synchronized (tex.resourceLock) {
                            tex.resourceCreationMask &= ~canvasBit;
                            if (tex.resourceCreationMask == 0) {
                                tex.freeTextureId(val);
                            }
                        }
                    }

                    textureIDResourceTable.set(val, null);
                }
                Canvas3D.freeTexture(ctx, val);
            }
            textureIdResourceFreeList.clear();
        }
    }

    void freeContextResources(Renderer rdr, boolean freeBackground, Context ctx) {
        TextureRetained tex;

        // Just return if we don't have a valid renderer or context
        if (rdr == null || ctx == null) {
            return;
        }

        if (freeBackground) {
            // Dispose of Graphics2D Texture
            if (graphics2D != null) {
                graphics2D.dispose();
            }
        }

        for (int id = textureIDResourceTable.size() - 1; id >= 0; id--) {
            tex = textureIDResourceTable.get(id);
            if (tex == null) {
                continue;
            }

            // Issue 403 : this assertion doesn't hold in some cases
            // TODO KCR : determine why this is the case
            //            assert id == ((TextureRetained)obj).objectId;

            Canvas3D.freeTexture(ctx, id);
            synchronized (tex.resourceLock) {
                tex.resourceCreationMask &= ~canvasBit;
                if (tex.resourceCreationMask == 0) {

                    tex.freeTextureId(id);
                }
            }
        }
        textureIDResourceTable.clear();

        freeAllDisplayListResources(ctx);
    }

    void freeAllDisplayListResources(Context ctx) {
        if ((view != null) && (view.renderBin != null)) {
            view.renderBin.freeAllDisplayListResources(this, ctx);
            if (useSharedCtx) {
                // We need to rebuild all other Canvas3D resource
                // shared by this Canvas3D. Since we didn't
                // remember resource in Renderer but RenderBin only.
                if ((screen != null) && (screen.renderer != null)) {
                    screen.renderer.needToRebuildDisplayList = true;
                }
            }
        }

    }

    // *****************************************************************
    // Wrappers for native methods go below here
    // *****************************************************************

    // This is the native method for creating the underlying graphics context.
    private Context createNewContext(Drawable drawable, Context shareCtx, boolean isSharedCtx, boolean offScreen) {
        return Pipeline.getPipeline().createNewContext(this, drawable, shareCtx, isSharedCtx, offScreen);
    }

    private void createQueryContext(Drawable drawable, boolean offScreen, int width, int height) {
        Pipeline.getPipeline().createQueryContext(this, drawable, offScreen, width, height);
    }

    // This is the native for creating offscreen buffer
    Drawable createOffScreenBuffer(Context ctx, int width, int height) {
        return Pipeline.getPipeline().createOffScreenBuffer(this, ctx, width, height);
    }

    void destroyOffScreenBuffer(Context ctx, Drawable drawable) {
        assert drawable != null;
        Pipeline.getPipeline().destroyOffScreenBuffer(this, ctx, drawable);
    }

    // This is the native for reading the image from the offscreen buffer
    private void readOffScreenBuffer(Context ctx, int format, int type, Object data, int width, int height) {
        Pipeline.getPipeline().readOffScreenBuffer(this, ctx, format, type, data, width, height);
    }

    // The native method for swapBuffers
    void swapBuffers(Context ctx, Drawable drawable) {
        Pipeline.getPipeline().swapBuffers(this, ctx, drawable);
    }

    // -----------------------------------------------------------------------------

    // native method for setting Material when no material is present
    void updateMaterial(Context ctx, float r, float g, float b, float a) {
        Pipeline.getPipeline().updateMaterialColor(ctx, r, g, b, a);
    }

    static void destroyContext(Drawable drawable, Context ctx) {
        Pipeline.getPipeline().destroyContext(drawable, ctx);
    }

    // This is the native method for doing accumulation.
    void accum(Context ctx, float value) {
        Pipeline.getPipeline().accum(ctx, value);
    }

    // This is the native method for doing accumulation return.
    void accumReturn(Context ctx) {
        Pipeline.getPipeline().accumReturn(ctx);
    }

    // This is the native method for clearing the accumulation buffer.
    void clearAccum(Context ctx) {
        Pipeline.getPipeline().clearAccum(ctx);
    }

    // This is the native method for getting the number of lights the underlying
    // native library can support.
    int getNumCtxLights(Context ctx) {
        return Pipeline.getPipeline().getNumCtxLights(ctx);
    }

    // Native method for decal 1st child setup
    boolean decal1stChildSetup(Context ctx) {
        return Pipeline.getPipeline().decal1stChildSetup(ctx);
    }

    // Native method for decal nth child setup
    void decalNthChildSetup(Context ctx) {
        Pipeline.getPipeline().decalNthChildSetup(ctx);
    }

    // Native method for decal reset
    void decalReset(Context ctx, boolean depthBufferEnable) {
        Pipeline.getPipeline().decalReset(ctx, depthBufferEnable);
    }

    // Native method for decal reset
    void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable) {
        Pipeline.getPipeline().ctxUpdateEyeLightingEnable(ctx, localEyeLightingEnable);
    }

    // The following three methods are used in multi-pass case

    // native method for setting blend color
    void setBlendColor(Context ctx, float red, float green, float blue, float alpha) {
        Pipeline.getPipeline().setBlendColor(ctx, red, green, blue, alpha);
    }

    // native method for setting blend func
    void setBlendFunc(Context ctx, int src, int dst) {
        Pipeline.getPipeline().setBlendFunc(ctx, src, dst);
    }

    // native method for setting fog enable flag
    void setFogEnableFlag(Context ctx, boolean enableFlag) {
        Pipeline.getPipeline().setFogEnableFlag(ctx, enableFlag);
    }

    boolean isAntialiasingSet() {
        return antialiasingSet;
    }

    // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle supported
    void setFullSceneAntialiasing(Context ctx, boolean enable) {
        Pipeline.getPipeline().setFullSceneAntialiasing(ctx, enable);
        antialiasingSet = enable;
    }

    // Native method to update separate specular color control
    void updateSeparateSpecularColorEnable(Context ctx, boolean control) {
        Pipeline.getPipeline().updateSeparateSpecularColorEnable(ctx, control);
    }

    // True under Solaris,
    // False under windows when display mode <= 8 bit
    private boolean validGraphicsMode() {
        return Pipeline.getPipeline().validGraphicsMode();
    }

    // native method for setting light enables
    void setLightEnables(Context ctx, long enableMask, int maxLights) {
        Pipeline.getPipeline().setLightEnables(ctx, enableMask, maxLights);
    }

    // native method for setting scene ambient
    void setSceneAmbient(Context ctx, float red, float green, float blue) {
        Pipeline.getPipeline().setSceneAmbient(ctx, red, green, blue);
    }

    // native method for disabling fog
    void disableFog(Context ctx) {
        Pipeline.getPipeline().disableFog(ctx);
    }

    // native method for disabling modelClip
    void disableModelClip(Context ctx) {
        Pipeline.getPipeline().disableModelClip(ctx);
    }

    // native method for setting default RenderingAttributes
    void resetRenderingAttributes(Context ctx, boolean depthBufferWriteEnableOverride,
            boolean depthBufferEnableOverride) {
        Pipeline.getPipeline().resetRenderingAttributes(ctx, depthBufferWriteEnableOverride,
                depthBufferEnableOverride);
    }

    // native method for setting default texture
    void resetTextureNative(Context ctx, int texUnitIndex) {
        Pipeline.getPipeline().resetTextureNative(ctx, texUnitIndex);
    }

    // native method for activating a particular texture unit
    void activeTextureUnit(Context ctx, int texUnitIndex) {
        Pipeline.getPipeline().activeTextureUnit(ctx, texUnitIndex);
    }

    // native method for setting default TexCoordGeneration
    void resetTexCoordGeneration(Context ctx) {
        Pipeline.getPipeline().resetTexCoordGeneration(ctx);
    }

    // native method for setting default TextureAttributes
    void resetTextureAttributes(Context ctx) {
        Pipeline.getPipeline().resetTextureAttributes(ctx);
    }

    // native method for setting default PolygonAttributes
    void resetPolygonAttributes(Context ctx) {
        Pipeline.getPipeline().resetPolygonAttributes(ctx);
    }

    // native method for setting default LineAttributes
    void resetLineAttributes(Context ctx) {
        Pipeline.getPipeline().resetLineAttributes(ctx);
    }

    // native method for setting default PointAttributes
    void resetPointAttributes(Context ctx) {
        Pipeline.getPipeline().resetPointAttributes(ctx);
    }

    // native method for setting default TransparencyAttributes
    void resetTransparency(Context ctx, int geometryType, int polygonMode, boolean lineAA, boolean pointAA) {
        Pipeline.getPipeline().resetTransparency(ctx, geometryType, polygonMode, lineAA, pointAA);
    }

    // native method for setting default ColoringAttributes
    void resetColoringAttributes(Context ctx, float r, float g, float b, float a, boolean enableLight) {
        Pipeline.getPipeline().resetColoringAttributes(ctx, r, g, b, a, enableLight);
    }

    /**
     *  This native method makes sure that the rendering for this canvas
     *  gets done now.
     */
    void syncRender(Context ctx, boolean wait) {
        Pipeline.getPipeline().syncRender(ctx, wait);
    }

    // The native method that sets this ctx to be the current one
    static boolean useCtx(Context ctx, Drawable drawable) {
        return Pipeline.getPipeline().useCtx(ctx, drawable);
    }

    // Give the Pipeline a chance to release the context. The return
    // value indicates whether the context was released.
    private boolean releaseCtx(Context ctx) {
        return Pipeline.getPipeline().releaseCtx(ctx);
    }

    void clear(Context ctx, float r, float g, float b, boolean clearStencil) {
        Pipeline.getPipeline().clear(ctx, r, g, b, clearStencil);
    }

    void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV,
            float mapMinX, float mapMaxX, float mapMinY, float mapMaxY, boolean useBiliearFilter) {
        Pipeline.getPipeline().textureFillBackground(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX,
                mapMinY, mapMaxY, useBiliearFilter);
    }

    void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, float mapMinX,
            float mapMaxX, float mapMinY, float mapMaxY, float mapZ, float alpha, boolean useBiliearFilter) {
        Pipeline.getPipeline().textureFillRaster(ctx, texMinU, texMaxU, texMinV, texMaxV, mapMinX, mapMaxX, mapMinY,
                mapMaxY, mapZ, alpha, useBiliearFilter);
    }

    void executeRasterDepth(Context ctx, float posX, float posY, float posZ, int srcOffsetX, int srcOffsetY,
            int rasterWidth, int rasterHeight, int depthWidth, int depthHeight, int depthType, Object depthData) {
        Pipeline.getPipeline().executeRasterDepth(ctx, posX, posY, posZ, srcOffsetX, srcOffsetY, rasterWidth,
                rasterHeight, depthWidth, depthHeight, depthType, depthData);
    }

    // The native method for setting the ModelView matrix.
    void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix) {
        Pipeline.getPipeline().setModelViewMatrix(ctx, viewMatrix, modelMatrix);
    }

    // The native method for setting the Projection matrix.
    void setProjectionMatrix(Context ctx, double[] projMatrix) {
        Pipeline.getPipeline().setProjectionMatrix(ctx, projMatrix);
    }

    // The native method for setting the Viewport.
    void setViewport(Context ctx, int x, int y, int width, int height) {
        Pipeline.getPipeline().resizeOffscreenLayer(this, width, height);
        Pipeline.getPipeline().setViewport(ctx, x, y, width, height);
    }

    // used for display Lists
    void newDisplayList(Context ctx, int displayListId) {
        Pipeline.getPipeline().newDisplayList(ctx, displayListId);
    }

    void endDisplayList(Context ctx) {
        Pipeline.getPipeline().endDisplayList(ctx);
    }

    void callDisplayList(Context ctx, int id, boolean isNonUniformScale) {
        Pipeline.getPipeline().callDisplayList(ctx, id, isNonUniformScale);
    }

    static void freeDisplayList(Context ctx, int id) {
        Pipeline.getPipeline().freeDisplayList(ctx, id);
    }

    static void freeTexture(Context ctx, int id) {
        Pipeline.getPipeline().freeTexture(ctx, id);
    }

    static int generateTexID(Context ctx) {
        return Pipeline.getPipeline().generateTexID(ctx);
    }

    void texturemapping(Context ctx, int px, int py, int xmin, int ymin, int xmax, int ymax, int texWidth,
            int texHeight, int rasWidth, int format, int objectId, byte[] image, int winWidth, int winHeight) {
        Pipeline.getPipeline().texturemapping(ctx, px, py, xmin, ymin, xmax, ymax, texWidth, texHeight, rasWidth,
                format, objectId, image, winWidth, winHeight);
    }

    boolean initTexturemapping(Context ctx, int texWidth, int texHeight, int objectId) {
        return Pipeline.getPipeline().initTexturemapping(ctx, texWidth, texHeight, objectId);
    }

    // Set internal render mode to one of FIELD_ALL, FIELD_LEFT or
    // FIELD_RIGHT.  Note that it is up to the caller to ensure that
    // stereo is available before setting the mode to FIELD_LEFT or
    // FIELD_RIGHT.  The boolean isTRUE for double buffered mode, FALSE
    // foe single buffering.
    void setRenderMode(Context ctx, int mode, boolean doubleBuffer) {
        Pipeline.getPipeline().setRenderMode(ctx, mode, doubleBuffer);
    }

    // Set glDepthMask.
    void setDepthBufferWriteEnable(Context ctx, boolean mode) {
        Pipeline.getPipeline().setDepthBufferWriteEnable(ctx, mode);
    }

    // Methods to get actual capabilities from Canvas3D

    boolean hasDoubleBuffer() {
        return Pipeline.getPipeline().hasDoubleBuffer(this);
    }

    boolean hasStereo() {
        return Pipeline.getPipeline().hasStereo(this);
    }

    int getStencilSize() {
        return Pipeline.getPipeline().getStencilSize(this);
    }

    boolean hasSceneAntialiasingMultisample() {
        return Pipeline.getPipeline().hasSceneAntialiasingMultisample(this);
    }

    boolean hasSceneAntialiasingAccum() {
        return Pipeline.getPipeline().hasSceneAntialiasingAccum(this);
    }

}