Package org.apache.batik.gvt.renderer

Source Code of org.apache.batik.gvt.renderer.StaticRenderer

/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.        *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in  *
* the LICENSE file.                                                         *
*****************************************************************************/

package org.apache.batik.gvt.renderer;

import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.CompositeGraphicsNode;
import org.apache.batik.gvt.GraphicsNodeRenderContext;
import org.apache.batik.gvt.TextPainter;
import org.apache.batik.gvt.Selector;
import org.apache.batik.gvt.Selectable;
import org.apache.batik.gvt.filter.GraphicsNodeRable;
import org.apache.batik.gvt.filter.GraphicsNodeRableFactory;

import org.apache.batik.gvt.filter.ConcreteGraphicsNodeRableFactory;
import org.apache.batik.gvt.filter.GraphicsNodeRable8Bit;

import org.apache.batik.gvt.text.ConcreteTextSelector;

import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.ext.awt.image.PadMode;
import org.apache.batik.ext.awt.image.rendered.CachableRed;
import org.apache.batik.ext.awt.image.rendered.PadRed;
import org.apache.batik.ext.awt.image.rendered.TranslateRed;

import java.util.Iterator;
import java.util.Stack;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.Raster;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
import java.awt.image.renderable.RenderContext;

/**
* Simple implementation of the Renderer that simply does static
* rendering in an offscreen buffer image.
*
* @author <a href="mailto:vincent.hardy@eng.sun.com>Vincent Hardy</a>
* @version $Id: StaticRenderer.java,v 1.13 2001/05/17 23:06:39 deweese Exp $
*/
public class StaticRenderer implements ImageRenderer {
    /**
     * Error messages
     */
    private static final String ILLEGAL_ARGUMENT_NULL_OFFSCREEN =
        "offScreen should not be null";
    private static final String ILLEGAL_ARGUMENT_ZERO_WIDTH_OR_HEIGHT =
        "offScreen should have positive width/height";

    /**
     * Tree this Renderer paints.
     */
    protected GraphicsNode      rootGN;
    protected GraphicsNodeRable rootGNR;
    protected CachableRed       rootCR;

    /**
     * Flag for double buffering.
     */
    private boolean isDoubleBuffered = false;

    /**
     * The Selector instance which listens to TextSelection gestures.
     */
    private Selector textSelector = null;

    /**
     * Offscreen image where the Renderer does its rendering
     */
    protected WritableRaster currentBaseRaster;
    protected WritableRaster currentRaster;
    protected BufferedImage  currentOffScreen;

    protected WritableRaster workingBaseRaster;
    protected WritableRaster workingRaster;
    protected BufferedImage  workingOffScreen;

    protected int offScreenWidth;
    protected int offScreenHeight;

    /**
     * Passed to the GVT tree to describe the rendering environment
     */
    protected GraphicsNodeRenderContext nodeRenderContext;

    /**
     * @param offScreen image where the Renderer should do its rendering
     * @param rc a GraphicsNodeRenderContext which this renderer should use
     */
    public StaticRenderer(GraphicsNodeRenderContext rc){
        setRenderContext(rc);
    }

    /**
     * @param offScreen image where the Renderer should do its rendering
     */
    public StaticRenderer(){

        RenderingHints hints = new RenderingHints(null);
        hints.put(RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);

        hints.put(RenderingHints.KEY_INTERPOLATION,
                  RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        FontRenderContext fontRenderContext =
            new FontRenderContext(new AffineTransform(), true, true);

        TextPainter textPainter = new StrokingTextPainter();

        GraphicsNodeRableFactory gnrFactory =
            new ConcreteGraphicsNodeRableFactory();

        this.nodeRenderContext =
            new GraphicsNodeRenderContext(new AffineTransform(),
                                          null,
                                          hints,
                                          fontRenderContext,
                                          textPainter,
                                          gnrFactory);
    }


    /**
     * Disposes all resources of this renderer.
     */
    public void dispose() {
        rootGN  = null;
        rootGNR = null;
        rootCR  = null;

        workingOffScreen = null;
        workingBaseRaster = null;
        workingRaster = null;

        currentOffScreen = null;
        currentBaseRaster = null;
        currentRaster = null;

        nodeRenderContext = null;
    }

    /**
     * This associates the given GVT Tree with this renderer.
     * Any previous tree association is forgotten.
     * Not certain if this should be just GraphicsNode, or CanvasGraphicsNode.
     */
    public void setTree(GraphicsNode rootGN){
        this.rootGN = rootGN;
        rootGNR = null;
        rootCR  = null;

        workingOffScreen = null;
        workingBaseRaster = null;
        workingRaster = null;

        currentOffScreen = null;
        currentBaseRaster = null;
        currentRaster = null;
    }

    /**
     * @return the GVT tree associated with this renderer
     */
    public GraphicsNode getTree(){
        return rootGN;
    }

    /**
     * @param rc a GraphicsNodeRenderContext which the Renderer should use
     *           for its rendering
     */
    public void setRenderContext(GraphicsNodeRenderContext rc) {
        this.nodeRenderContext = rc;
        rootGNR    = null;
        rootCR     = null;

        workingOffScreen = null;
        workingBaseRaster = null;
        workingRaster = null;

        currentOffScreen = null;
        currentBaseRaster = null;
        currentRaster = null;
    }

    /**
     * @return the GraphicsNodeRenderContext which the Renderer uses
     *           for its rendering
     */
    public GraphicsNodeRenderContext getRenderContext() {
        return nodeRenderContext;
    }

    /**
     * Sets the transform from the current user space (as defined by
     * the top node of the GVT tree, to the associated device space.
     *
     * @param usr2dev the new user space to device space transform. If null,
     *        the identity transform will be set.
     */
    public void setTransform(AffineTransform usr2dev){
        if(usr2dev == null)
            usr2dev = new AffineTransform();

        // Update the RenderContext in the nodeRenderContext
        nodeRenderContext.setTransform(usr2dev);
        rootCR = null;
    }

    /**
     * Returns a copy of the transform from the current user space (as
     * defined by the top node of the GVT tree) to the device space (1
     * unit = 1/72nd of an inch / 1 pixel, roughly speaking
     */
    public AffineTransform getTransform(){
        return nodeRenderContext.getTransform();
    }

    /**
     * Returns true if the Renderer is currently doubleBuffering is
     * rendering requests.  If it is then getOffscreen will only
     * return completed renderings (or null if nothing is available). 
     */
    public boolean isDoubleBuffered(){
        return isDoubleBuffered;
    }

    /**
     * Turns on/off double buffering in renderer.  Turning off
     * double buffering makes it possible to see the ongoing results
     * of a render operation.
     *
     * @param isDoubleBuffered the new value for double buffering
     */
    public void setDoubleBuffered(boolean isDoubleBuffered){
        this.isDoubleBuffered = isDoubleBuffered;
        if (isDoubleBuffered) {
            // Now double buffering, so make sure they can't see work buffers.
            currentOffScreen  = null;
            currentBaseRaster = null;
            currentRaster     = null;
        } else {
            // No longer double buffering so join work and current buffers.
            currentOffScreen  = workingOffScreen;
            currentBaseRaster = workingBaseRaster;
            currentRaster     = workingRaster;
        }
    }


    /**
     * Update the size of the image to be returned by getOffScreen.
     * Note that this change will not be reflected by calls to
     * getOffscreen until either clearOffscreen has completed (when
     * isDoubleBuffered is false) or reapint has completed (when
     * isDoubleBuffered is true). 
     *
     */
    public void updateOffScreen(int width, int height) {
        offScreenWidth  = width;
        offScreenHeight = height;
    }

    /**
     * Returns the current offscreen image.
     *
     * The exact symantics of this vary base on the value of
     * isDoubleBuffered.  If isDoubleBuffered is false this will
     * return the image currently being worked on as soon as it is
     * available.
     *
     * if isDoubleBuffered is false this will return the most recently
     * completed result of repaint.
     */
    public BufferedImage getOffScreen() {
        if (rootGN == null)
            return null;

        return currentOffScreen;
    }

    /**
     * Sets up and clears the current offscreen buffer.
     *
     * When not double buffering one should call this method before
     * calling getOffscreen to get the offscreen being drawn into.
     * This ensures the buffer is up to date and doesn't contain junk.
     *
     * When double buffering this call can effectively be skipped,
     * since getOffscreen will only refect the new rendering after
     * repaint completes. 
     */
    public void clearOffScreen() {

        WritableRaster syncRaster;
        ColorModel     cm;

        updateWorkingBuffers();
        if ((rootCR == null)           ||
            (workingBaseRaster == null))
            return;
       
        cm         = rootCR.getColorModel();
        syncRaster = workingBaseRaster;

        // Ensure only one thread works on baseRaster at a time...
        synchronized (syncRaster) {
            BufferedImage bi = new BufferedImage
                (cm, workingBaseRaster, cm.isAlphaPremultiplied(), null);
            Graphics2D g2d = bi.createGraphics();
            g2d.setComposite(AlphaComposite.Clear);
            g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight());
            g2d.dispose();
        }
    }

    /**
     * Repaints the associated GVT tree under 'area'.
     *
     * If double buffered is true if this method completes cleanly it
     * will set the result of the repaint as the image returned by
     * getOffscreen
     *
     * @param area region to be repainted, in the current user space
     * coordinate system. 
     */
    public void repaint(Shape area) throws InterruptedException {
        if (area == null)
            return;

        // long t0 = System.currentTimeMillis();

        CachableRed cr;
        WritableRaster syncRaster;
        WritableRaster copyRaster;

        // While we are synchronized pull all the relavent info out
        // of member variables into local variables.
        updateWorkingBuffers();
        if ((rootCR == null)           ||
            (workingBaseRaster == null))
            return;

        cr = rootCR;
        syncRaster = workingBaseRaster;
        copyRaster = workingRaster;

        Rectangle srcR = rootCR.getBounds();
        Rectangle dstR = workingRaster.getBounds();
        if ((dstR.x < srcR.x) ||
            (dstR.y < srcR.y) ||
            (dstR.x+dstR.width  > srcR.x+srcR.width) ||
            (dstR.y+dstR.height > srcR.y+srcR.height))
            cr = new PadRed(cr, dstR, PadMode.ZERO_PAD, null);

        // Ensure only one thread works on baseRaster at a time...
        synchronized (syncRaster) {
            cr.copyData(copyRaster);
        }

        if (!Thread.currentThread().isInterrupted()) {
            // Swap the buffers if the rendering completed cleanly.
            BufferedImage tmpBI = workingOffScreen;

            workingBaseRaster = currentBaseRaster;
            workingRaster     = currentRaster;
            workingOffScreen  = currentOffScreen;

            currentRaster     = copyRaster;
            currentBaseRaster = syncRaster;
            currentOffScreen  = tmpBI;
        }
    }

    /**
     * Internal method used to synchronize local state in response to
     * various set methods. 
     */
    protected void updateWorkingBuffers() {
        if (rootGNR == null) {
            rootGNR = new GraphicsNodeRable8Bit(rootGN,
                                                nodeRenderContext);
            rootCR = null;
        }

        if (rootCR == null) {
            workingBaseRaster = null;
            workingRaster     = null;
            workingOffScreen  = null;

            AffineTransform at, rcAT;
            at = nodeRenderContext.getTransform();
            rcAT = new AffineTransform(at.getScaleX(), at.getShearY(),
                                       at.getShearX(), at.getScaleY(),
                                       0, 0);

            RenderContext rc = new RenderContext
                (rcAT, null,
                 nodeRenderContext.getRenderingHints());
           
            RenderedImage ri = rootGNR.createRendering(rc);
            if (ri == null)
                return;

            rootCR = GraphicsUtil.wrap(ri);
            int dx = Math.round((float)at.getTranslateX()+0.5f);
            int dy = Math.round((float)at.getTranslateY()+0.5f);
            rootCR = new TranslateRed(rootCR, rootCR.getMinX()+dx,
                                      rootCR.getMinY()+dy);
            rootCR = GraphicsUtil.convertTosRGB(rootCR);

        }

        SampleModel sm = rootCR.getSampleModel();
        int         w  = offScreenWidth;
        int         h  = offScreenHeight;

        int tw = sm.getWidth();
        int th = sm.getHeight();
        w = (((w+tw-1)/tw)+1)*tw;
        h = (((h+th-1)/th)+1)*th;

        if ((workingBaseRaster == null) ||
            (workingBaseRaster.getWidth()  < w) ||
            (workingBaseRaster.getHeight() < h)) {

            sm = sm.createCompatibleSampleModel(w, h);
           
            workingBaseRaster
                = Raster.createWritableRaster(sm, new Point(0,0));
        }

        int tgx = -rootCR.getTileGridXOffset();
        int tgy = -rootCR.getTileGridYOffset();
        int xt, yt;
        if (tgx>=0) xt = tgx/tw;
        else        xt = (tgx-tw+1)/tw;
        if (tgy>=0) yt = tgy/th;
        else        yt = (tgy-th+1)/th;

        int xloc = xt*tw - tgx;
        int yloc = yt*th - tgy;
       
        // System.out.println("Info: [" +
        //                    xloc + "," + yloc + "] [" +
        //                    tgx  + "," + tgy  + "] [" +
        //                    xt   + "," + yt   + "] [" +
        //                    tw   + "," + th   + "]");
        // This raster should be aligned with cr's tile grid.
        workingRaster = workingBaseRaster.createWritableChild
          (0, 0, w, h, xloc, yloc, null);

        workingOffScreen =  new BufferedImage
          (rootCR.getColorModel(),
           workingRaster.createWritableChild (0, 0, offScreenWidth,
                                           offScreenHeight, 0, 0, null),
           rootCR.getColorModel().isAlphaPremultiplied(), null);


        if (!isDoubleBuffered) {
            currentOffScreen  = workingOffScreen;
            currentBaseRaster = workingBaseRaster;
            currentRaster     = workingRaster;
        }
    }
}
TOP

Related Classes of org.apache.batik.gvt.renderer.StaticRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.