/* * $RCSfile: Viewport.java,v $ * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program 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. * * This program 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 at /legal/license.txt). * * 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 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.perseus.model; import com.sun.perseus.j2d.Transform; /** * A <code>Viewport</code> describes a <code>ModelNode</code> into which * rendering may happen. * * <p>A <code>ViewportNode</code> has a width and height * which a child <code>SVG</code> uses to compute the viewbox to viewport * transform.</p> * * <p>In addition, a <code>Viewport</code> has a user transform limited to * its scale and translation components.</p> * * <p>The <code>Viewport</code> is the root of any SVG fragment hierarchy. * </p> * * @version $Id: Viewport.java,v 1.9 2006/06/29 10:47:36 ln156897 Exp $ */ public abstract class Viewport extends CompositeNode implements Transformable { /** * Default width for viewports. */ public static final int DEFAULT_VIEWPORT_WIDTH = 100; /** * Default height for viewports */ public static final int DEFAULT_VIEWPORT_HEIGHT = 100; /** * As in the SVG 1.1 specification */ public static final int ZOOM_PAN_MAGNIFY = 0; /** * As in the SVG 1.1 specification */ public static final int ZOOM_PAN_DISABLE = 1; /** * As in the SVG 1.1 specification */ public static final int ZOOM_PAN_UNKNOWN = 2; /** * Viewport width */ protected int width = DEFAULT_VIEWPORT_WIDTH; /** * Viewport height */ protected int height = DEFAULT_VIEWPORT_HEIGHT; /** * The Transform applied to this node. */ protected Transform transform = new Transform(null); /** * The inverse of the Transform applied to this node. */ protected Transform inverseTxf = new Transform(null); /** * The zoomAndPan setting */ protected int zoomAndPan = ZOOM_PAN_MAGNIFY; /** * Default constructor */ public Viewport() { } /** * Returns the <code>ModelNode</code>, if any, hit by the * point at coordinate x/y. * * @param pt the x/y coordinate. Should never be null and be * of size two. If not, the behavior is unspecified. * The coordinates are in viewport space. * @return the <tt>ModelNode</tt> hit at the given point or null * if none was hit. */ public ModelNode nodeHitAt(final float[] pt) { // Check for a hit on children return nodeHitAt(getLastChildNode(), pt); } /** * @return The zoomAndPan setting for this viewport (read-only) */ public int getZoomAndPan() { return zoomAndPan; } /** * @param newZoomAndPan the new value for the zoom and pan setting */ public void setZoomAndPan(final int newZoomAndPan) { if (newZoomAndPan != ZOOM_PAN_MAGNIFY && newZoomAndPan != ZOOM_PAN_DISABLE && newZoomAndPan != ZOOM_PAN_UNKNOWN) { throw new IllegalArgumentException(); } if (newZoomAndPan == zoomAndPan) { return; } modifyingNode(); this.zoomAndPan = newZoomAndPan; modifiedNode(); } ///////////////////// /** * @return the viewport width */ public int getWidth() { return this.width; } /** * @return the viewport height */ public int getHeight() { return this.height; } /** * Sets the viewport size * * @param newWidth the new viewport width. Should be greater than 0 * @param newHeight the new viewport height. Should be greater than 0. */ public void setSize(final int newWidth, final int newHeight) { if (newWidth < 0 || newHeight < 0) { throw new IllegalArgumentException(); } if (newWidth == width && newHeight == height) { return; } modifyingNode(); this.width = newWidth; this.height = newHeight; recomputeTransformState(null); computeCanRenderWidthBit(width); computeCanRenderHeightBit(height); modifiedNode(); } /** * @return this node's cached transform. */ public Transform getTransformState() { return transform; } /** * @return this node's cached inverse transform. */ public Transform getInverseTransformState() { if (((canRenderState & CAN_RENDER_NON_INVERTIBLE_TXF_BIT) == 0)) { if (inverseTxf == null) { inverseTxf = new Transform(null); try { inverseTxf = (Transform) transform.inverse(inverseTxf); } catch (Exception e) { // If we get an exception, then we have a real error // condition, because we just checked that the // transform was invertible. throw new Error(); } } } else { inverseTxf = null; } return inverseTxf; } /** * Recomputes the transform cache, if one exists. This should recursively * call recomputeTransformState on children node or expanded content, if * any. * * By default, because a ModelNode has no transform and no cached transform, * this only does a pass down. * * @param parentTransform the Transform applied to this node's parent. */ protected void recomputeTransformState(final Transform parentTransform) { if (parentTransform != null) { throw new IllegalArgumentException(); } computeCanRenderTransformBit(transform); inverseTxf = null; recomputeTransformState(transform, getFirstChildNode()); } /** * @param newTransform The new <code>Transformable</code>'s transform. */ public void setTransform(final Transform newTransform) { if (ElementNode.equal(newTransform, this.transform)) { return; } modifyingNode(); this.transform = newTransform; recomputeTransformState(null); modifiedNode(); } /** * @return This <code>Transformable</code>'s transform. */ public Transform getTransform() { return transform; } /** * Appends this node's transform, if it is not null. * * @param tx the <code>Transform</code> to apply additional node * transforms to. This may be null. * @param workTx a <code>Transform</code> which can be re-used if a * new <code>Transform</code> needs to be created and workTx * is not the same instance as tx. * @return a transform with this node's transform added. */ protected Transform appendTransform(Transform tx, final Transform workTx) { if (transform == null) { return tx; } tx = recycleTransform(tx, workTx); tx.mMultiply(transform); return tx; } /** * Debug helper * * @return a textual description of this viewport object */ /* public String toString() { return "[Viewport(zoomPan=" + zoomAndPan + ", width=" + width + " height=" + height + " txf=" + transform + "]"; } */ }