You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-dev@xmlgraphics.apache.org by Antipin <an...@borlas.ru> on 2004/03/16 13:14:01 UTC

suggestion

I've got a problem. I use image, which takes big space e.g. 40000x40000 px.
and so there is a problem with the amount of memory. I've found a 
compromise. The bigger cache we use  the faster the performance will be.  
I  use  my own ImageRenderer (DRenderer.java)
and RenderedImage (ExtRenderedImage.java) instead BufferedImage. 
I suggest to use a RenderedImage in follow classes for flexibility. 


org.apache.batik.bridge.RepaintManager;
org.apache.batik.bridge.UpdateManagerEvent;
org.apache.batik.swing.gvt.GVTTreeRenderer;
org.apache.batik.swing.gvt.GVTTreeRendererEvent;
org.apache.batik.swing.gvt.JGVTComponent;
org.apache.batik.swing.svg.JSVGComponent;


----------------------DRenderer---------------------------------
package org.apache.batik.gvt.renderer;

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.renderable.*;
import java.util.*;
import java.util.List;

import borlas.utils.*;
import borlas.utils.TileCache;
import org.apache.batik.ext.awt.geom.*;
import org.apache.batik.ext.awt.image.*;
import org.apache.batik.ext.awt.image.renderable.*;
import org.apache.batik.ext.awt.image.rendered.*;
import org.apache.batik.gvt.*;
import org.apache.batik.gvt.filter.*;



public class DRenderer implements ImageRenderer{


        final static int COPY_OVERHEAD      = 1000;
        final static int COPY_LINE_OVERHEAD = 10;


        protected RenderingHints renderingHints;
        protected AffineTransform usr2dev;


        protected GraphicsNode      rootGN;
        protected Filter            rootFilter;

        protected RenderedImage image = null;

        protected int offScreenWidth;
        protected int offScreenHeight;

        protected boolean isDoubleBuffered;

        protected RectListManager dirtyAreas = new RectListManager();

        protected TileCache cache;

        protected static RenderingHints defaultRenderingHints;

        static {
                defaultRenderingHints = new RenderingHints(null);
                defaultRenderingHints.put(RenderingHints.KEY_ANTIALIASING,
                                                                  

RenderingHints.VALUE_ANTIALIAS_ON);

                defaultRenderingHints.put(RenderingHints.KEY_INTERPOLATION,
                                                                  

RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);

        }


        public void setDoubleBuffered(boolean flag){
                isDoubleBuffered = flag;
        }
        public boolean isDoubleBuffered(){
                return isDoubleBuffered;
        }

        public AffineTransform getTransform(){
                return usr2dev;
        }
        public void setTransform(AffineTransform usr2dev){
                if (this.usr2dev.equals(usr2dev))
                        return;

                if(usr2dev == null) this.usr2dev = new AffineTransform();
                else this.usr2dev = new AffineTransform(usr2dev);

        }


        public void updateOffScreen(int width, int height) {
                offScreenWidth  = width;
                offScreenHeight = height;
        }


        public void dispose() {
                rootGN     = null;
                rootFilter = null;
                renderingHints = null;
        }


        public void repaint(Shape area){

                if (area == null) return;
                List l = new ArrayList(1);
                l.add(area);
                repaint(l);
        }

        public GraphicsNode getTree(){
                return rootGN;
        }
        public void setTree(GraphicsNode rootGN){
                this.rootGN = rootGN;
                rootFilter  = null;
                image = null;

                renderingHints = new RenderingHints(defaultRenderingHints);

        }




        public DRenderer(TileCache tci){
                cache = tci;
                renderingHints = new RenderingHints(defaultRenderingHints);
                usr2dev = new AffineTransform();
        }

        public DRenderer(RenderingHints rh,
                                                   AffineTransform at,TileCache tci){

                cache = tci;
                renderingHints = new RenderingHints(rh);
                usr2dev = new AffineTransform(at);

        }



        public void flush(){
                cache.flush();
        }

        public void clearOffScreen() {
                renderGNR();
        }

        public void repaint(RectListManager rLM){

                Iterator iter = rLM.iterator();
                while (iter.hasNext()) {
                        Rectangle r = (Rectangle)iter.next();
                        dirtyAreas.add(r);
                }

                dirtyAreas.mergeRects(COPY_OVERHEAD, COPY_LINE_OVERHEAD);
                iter = dirtyAreas.iterator();
                while(iter.hasNext()){
                        Rectangle rect = (Rectangle)iter.next();
                        flush(rect);
                }

                dirtyAreas.clear();
                return;





        }



        public void flush(Rectangle r) {
                if(image == null) return;

                r = usr2dev.createTransformedShape(r).getBounds();
                int lb = xToTile(r.x);
                int rb = xToTile(r.x + r.width);
                int tb = yToTile(r.y);
                int bb = yToTile(r.y + r.height);

                for(int i = lb; i <= rb; i++){
                        for(int j = tb; j <= bb; j++){
                                cache.removeTile(image,i,j);
                        }
                }

                return;
        }

        public void flush(Collection areas) {
                Iterator iter = areas.iterator();
                while(iter.hasNext()){
                        Rectangle rect = ((Shape)iter.next()).getBounds();
                        dirtyAreas.add(rect);
                }
                dirtyAreas.mergeRects(COPY_OVERHEAD, COPY_LINE_OVERHEAD);
                iter = dirtyAreas.iterator();
                while(iter.hasNext()){
                        Rectangle rect = (Rectangle)iter.next();
                        flush(rect);
                }

                dirtyAreas.clear();

                return;
        }



        public RenderedImage getOffScreen() {
                if (rootGN == null) return null;
                return image;
        }


        public void repaint(List areas) {
                if (areas == null) return;

                if(image == null ||
                   image.getWidth() < offScreenWidth
                   || image.getHeight() < offScreenHeight
                   ){
                        renderGNR();
                }

                flush(areas);
        }

        protected int xToTile(int x){
                int off = image.getTileGridXOffset();
                int tw = image.getTileWidth();
                return (x - off)/tw;
        }
        protected int yToTile(int y){
                int off = image.getTileGridYOffset();
                int th = image.getTileHeight();
                return (y - off)/th;
        }
        protected int xTileToX(int xt){
                int off = image.getTileGridXOffset();
                int tw = image.getTileWidth();
                return xt*tw + off;

        }
        protected int yTileToY(int yt){
                int off = image.getTileGridYOffset();
                int th = image.getTileHeight();
                return yt*th + off;

        }



        protected void renderGNR() {
                if(rootGN == null) return;
                if (rootFilter == null) {
                        rootFilter = rootGN.getGraphicsNodeRable(true);
                }


                cache.flush();
                AffineTransform at, rcAT;
                at = usr2dev;
                rcAT = new AffineTransform(at.getScaleX(), at.getShearY(),
                                                                   at.getShearX(), at.getScaleY(),
                                                                   0, 0);

                RenderContext rc = new RenderContext(rcAT, null, renderingHints);


                Rectangle bounds = new Rectangle(offScreenWidth,offScreenHeight);



                RenderedImage ri = new 

ExtRenderedImage((GraphicsNodeRable8Bit)rootFilter,rc.getTransform(),rc.getRenderingHints(),bounds);

                if (ri == null){
                        image = null;
                        return;
                }

                CachableRed ret;
                ret = GraphicsUtil.wrap(ri);

                int dx = Math.round((float)at.getTranslateX());
                int dy = Math.round((float)at.getTranslateY());
                ret = new TranslateRed(ret, ret.getMinX()+dx, ret.getMinY()+dy);
                image = GraphicsUtil.convertTosRGB(ret);


        }
}
--------------------------------------------------------ExtRnderedImge---------------------------

package borlas.utils;

import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.ext.awt.image.rendered.AbstractTiledRed;
import org.apache.batik.ext.awt.image.rendered.AbstractRed;
import org.apache.batik.ext.awt.image.rendered.CachableRed;

import java.awt.AlphaComposite;
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

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


public class ExtRenderedImage extends AbstractRed {


        private AffineTransform node2dev = null;
        private GraphicsNodeRable8Bit filter  = null;
        private RenderingHints renderingHints = null;

        public ExtRenderedImage(GraphicsNodeRable8Bit rable,
                                                        AffineTransform node2dev,RenderingHints rh,

                                                           Rectangle bounds){
                super(); // We _must_ call init...


                filter = rable;
                renderingHints = rh;

                this.node2dev = node2dev;

                ColorModel cm = GraphicsUtil.sRGB_Unpre;

                int defSz = AbstractTiledRed.getDefaultTileSize();

                // Make tile(0,0) fall on the closest intersection of defaultSz.
                int tgX = defSz*(int)Math.floor(bounds.x/defSz);
                int tgY = defSz*(int)Math.floor(bounds.y/defSz);

                int tw  = (bounds.x+bounds.width)-tgX;
                if (tw > defSz) tw = defSz;
                int th  = (bounds.y+bounds.height)-tgY;
                if (th > defSz) th = defSz;
                if ((tw <= 0) || (th <= 0)) {
                        tw = 1;
                        th = 1;
                }

                // fix my sample model so it makes sense given my size.
                SampleModel sm = cm.createCompatibleSampleModel(tw, th);

                // Finish initializing our base class...
                init((CachableRed)null, bounds, cm, sm, tgX, tgY, null);
        }

        public WritableRaster copyData(WritableRaster wr) {
                genRect(wr);
                return wr;
        }

        public void genRect(WritableRaster wr) {
                // System.out.println("  Rect: " + wr.getBounds());
                BufferedImage offScreen
                        = new BufferedImage(cm,
                                                                wr.createWritableTranslatedChild(0,0),
                                                                cm.isAlphaPremultiplied(),
                                                                null);

                Graphics2D g = GraphicsUtil.createGraphics(offScreen,renderingHints);
                g.setComposite(AlphaComposite.Clear);

                g.fillRect(0, 0, wr.getWidth(), wr.getHeight());
                g.setComposite(AlphaComposite.SrcOver);
                g.translate(-wr.getMinX(), -wr.getMinY());
                // Set transform
        g.transform(node2dev);

                filter.paintRable(g);

                g.dispose();
        }
}
---------------------------------------------------------------------------------------------------
And piece of  MyCanvas that  extends JSVGComponent

        protected ImageRenderer createImageRenderer() {
                if(isDynamicDocument){
                        return new DRenderer(cache);
                }else{
                        return rendererFactory.createStaticImageRenderer();
                }
        }




        public void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D)g;


                Rectangle d = g.getClipBounds();
                Dimension dim = this.getSize();
                if(d == null){
                        d = new Rectangle(0,0,dim.width,dim.height);
                }
                g2d.setComposite(AlphaComposite.SrcOver);
                g2d.setPaint(getBackground());
                g2d.fillRect(0, 0, dim.width, dim.height);

                paintPrev(g2d);

                this.PaintBackground(g);

                if (image != null) {

                        if (paintingTransform != null) {
                                g2d.transform(paintingTransform);
                        }

                        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                                                 

RenderingHints.VALUE_ANTIALIAS_ON);

                        int lb, rb, tb, bb;

                        int numTX = image.getNumXTiles();
                        int numTY = image.getNumYTiles();
                        int mtx = image.getMinTileX();
                        int mty = image.getMinTileY();

                        lb = xToTile(d.x);
                        lb = Math.max(lb, image.getMinTileX());
                        lb = Math.min(lb, mtx + numTX - 1);

                        rb = xToTile(d.x + d.width - 1);
                        rb = Math.max(rb, image.getMinTileX());
                        rb = Math.min(rb, mtx + numTX - 1);

                        tb = yToTile(d.y);
                        tb = Math.max(tb, image.getMinTileY());
                        tb = Math.min(tb, mty + numTY - 1);

                        bb = yToTile(d.y + d.height - 1);
                        bb = Math.max(bb, image.getMinTileY());
                        bb = Math.min(bb, mty + numTY - 1);


                        for(int i = lb; i <= rb; i++){
                                for(int j = tb; j <= bb; j++){
                                        int tx = xTileToX(i);
                                        int ty = yTileToY(j);
                                        Raster raster = cache.getTile(image,i,j);
                                        if(raster == null){
                                                raster = image.getTile(i,j);
                                                if(raster == null) return;
                                                cache.addTile(image,i,j,raster);
                                        }
                                        DataBuffer dataBuffer = raster.getDataBuffer();

                                        SampleModel sampleModel = image.getSampleModel();


                                        WritableRaster wr = 

Raster.createWritableRaster(sampleModel,dataBuffer,new Point(0,0));
                                        BufferedImage bi = new BufferedImage(image.getColorModel(),wr,
                                                        image.getColorModel().isAlphaPremultiplied(),null);
                                        g2d.drawRenderedImage(bi, 

AffineTransform.getTranslateInstance(tx,ty));
                                }
                        }



                        Iterator it = overlays.iterator();
                        while (it.hasNext()) {
                                ((Overlay)it.next()).paint(g);
                        }
                }
        }



        protected int xToTile(int x){
                int off = image.getTileGridXOffset();
                int tw = image.getTileWidth();
                return (x - off)/tw;
        }
        protected int yToTile(int y){
                int off = image.getTileGridYOffset();
                int th = image.getTileHeight();
                return (y - off)/th;
        }
        protected int xTileToX(int xt){
                int off = image.getTileGridXOffset();
                int tw = image.getTileWidth();
                return xt*tw + off;

        }
        protected int yTileToY(int yt){
                int off = image.getTileGridYOffset();
                int th = image.getTileHeight();
                return yt*th + off;

        }



---------------------------------------------------------------------
To unsubscribe, e-mail: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org


Re: suggestion

Posted by Thomas DeWeese <Th...@Kodak.com>.
Antipin wrote:

> I've got a problem. I use image, which takes big space e.g. 40000x40000 px.
> and so there is a problem with the amount of memory. I've found a 
> compromise. The bigger cache we use  the faster the performance will be.  
> I  use  my own ImageRenderer (DRenderer.java)
> and RenderedImage (ExtRenderedImage.java) instead BufferedImage. 
> I suggest to use a RenderedImage in follow classes for flexibility. 

Hi Antipin,

    I am encouraged by your enthusiasm.  However I have a few questions
and notes on the suggestion.

    First off why is it trying to render the entire 40,000x40,000
image at once.  It should only render the portion that is visible.
My first guess is that you have embedded it in a normal Swing
scroll pane, in which case I strongly suggest you look at using
JSVGScrollPane that is new in Batik 1.5.1.

    Second I would note that your modification moves the rendering
of the SVG document into the Swing thread (in the paint method).
This is generally bad as the rendering can take several seconds
even just for the displayed area.

    Third you may not be aware that the StaticRenderer already
does the caching that your modification adds. This is normally
disabled for Dynamic documents because the extra time to update
the cache can be quite noticeable, however you should be able to
just replace the createImageRenderer method with one that
always returns the Static Renderer.  It is also worth noting
that the static renderer goes to fairly great extents to
try and render 'blocks' of tiles which makes a large difference
for complex documents.

    So to summarize, I think you can most/all of what you want
(and perhaps more) by using the JSVGScrollPane with a
StaticRenderer.  If you tried this solution and found it
unacceptable for some reason I would be interested in hearing why.




---------------------------------------------------------------------
To unsubscribe, e-mail: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org