Index: sources/org/apache/batik/bridge/SVGGElementBridge.java =================================================================== --- sources/org/apache/batik/bridge/SVGGElementBridge.java (revision 280419) +++ sources/org/apache/batik/bridge/SVGGElementBridge.java (working copy) @@ -77,6 +77,16 @@ if (r != null) gn.setBackgroundEnable(r); + String s=e.getAttributeNS("http://xml.apache.org/batik/ext", "static"); + if (s.length() != 0) { + // System.err.println("Static: " + s); + if (s.toLowerCase().equals("true")) { + gn.setStatic(true); + } else { + gn.setStatic(false); + } + } + return gn; } @@ -108,6 +118,25 @@ } /** + * Invoked when an MutationEvent of type 'DOMAttrModified' is fired. + */ + public void handleDOMAttrModifiedEvent(MutationEvent evt) { + super.handleDOMAttrModifiedEvent(evt); + String attrName = evt.getAttrName(); + org.w3c.dom.Attr attr = (org.w3c.dom.Attr)evt.getRelatedNode(); + String attrNS = attr.getNamespaceURI(); + if (attrName.equals("static") && + attrNS.equals("http://xml.apache.org/batik/ext")) { + String s = evt.getNewValue(); + if (s.toLowerCase().equals("true")) { + ((CompositeGraphicsNode)node).setStatic(true); + } else { + ((CompositeGraphicsNode)node).setStatic(false); + } + } + } + + /** * Invoked when an MutationEvent of type 'DOMNodeInserted' is fired. */ public void handleElementAdded(CompositeGraphicsNode gn, Index: sources/org/apache/batik/bridge/SVGUseElementBridge.java =================================================================== --- sources/org/apache/batik/bridge/SVGUseElementBridge.java (revision 280419) +++ sources/org/apache/batik/bridge/SVGUseElementBridge.java (working copy) @@ -211,6 +211,16 @@ // 'visibility' gn.setVisible(CSSUtilities.convertVisibility(e)); + String s= localRefElement.getAttributeNS + ("http://xml.apache.org/batik/ext", "static"); + if (s.length() != 0) { + if (s.toLowerCase().equals("true")) { + gn.setStatic(true); + } else { + gn.setStatic(false); + } + } + RenderingHints hints = null; hints = CSSUtilities.convertColorRendering(e, hints); if (hints != null) @@ -403,20 +413,27 @@ public void handleDOMAttrModifiedEvent(MutationEvent evt) { String attrName = evt.getAttrName(); Node evtNode = evt.getRelatedNode(); - - if ((evtNode.getNamespaceURI() == null) && + String attrNS = evtNode.getNamespaceURI(); + if ((attrNS == null) && (attrName.equals(SVG_X_ATTRIBUTE) || attrName.equals(SVG_Y_ATTRIBUTE) || attrName.equals(SVG_TRANSFORM_ATTRIBUTE))) { node.setTransform(computeTransform(e, ctx)); handleGeometryChanged(); - } else if (((evtNode.getNamespaceURI() == null) && - (attrName.equals(SVG_WIDTH_ATTRIBUTE) || - attrName.equals(SVG_HEIGHT_ATTRIBUTE))) || - (( XLinkSupport.XLINK_NAMESPACE_URI.equals - (evtNode.getNamespaceURI()) ) && + } else if (((attrNS == null) && + (attrName.equals(SVG_WIDTH_ATTRIBUTE) || + attrName.equals(SVG_HEIGHT_ATTRIBUTE))) || + (( XLinkSupport.XLINK_NAMESPACE_URI.equals(attrNS)) && SVG_HREF_ATTRIBUTE.equals(evtNode.getLocalName()))) { buildCompositeGraphicsNode(ctx, e, (CompositeGraphicsNode)node); + } else if (attrName.equals("static") && + attrNS.equals("http://xml.apache.org/batik/ext")) { + String s = evt.getNewValue(); + if (s.toLowerCase().equals("true")) { + ((CompositeGraphicsNode)node).setStatic(true); + } else { + ((CompositeGraphicsNode)node).setStatic(false); + } } } } Index: sources/org/apache/batik/gvt/AbstractGraphicsNode.java =================================================================== --- sources/org/apache/batik/gvt/AbstractGraphicsNode.java (revision 280419) +++ sources/org/apache/batik/gvt/AbstractGraphicsNode.java (working copy) @@ -22,6 +22,7 @@ import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Shape; +import java.awt.image.RenderedImage; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; @@ -34,12 +35,15 @@ import javax.swing.event.EventListenerList; import org.apache.batik.ext.awt.RenderingHintsKeyExt; +import org.apache.batik.ext.awt.image.GraphicsUtil; +import org.apache.batik.ext.awt.image.rendered.TileCacheRed; import org.apache.batik.ext.awt.image.renderable.ClipRable; import org.apache.batik.ext.awt.image.renderable.Filter; import org.apache.batik.gvt.event.GraphicsNodeChangeEvent; import org.apache.batik.gvt.event.GraphicsNodeChangeListener; import org.apache.batik.gvt.filter.GraphicsNodeRable; import org.apache.batik.gvt.filter.GraphicsNodeRable8Bit; +import org.apache.batik.gvt.filter.GraphicsNodeRed8Bit; import org.apache.batik.gvt.filter.Mask; import org.apache.batik.util.HaltingThread; @@ -81,6 +85,15 @@ protected boolean isVisible = true; /** + * This flag bit indicates whether or not this graphics node is static + * indicating that it should render it's contents to an offscreen buffer. + */ + public boolean isStatic = false; + protected RenderedImage staticRI; + protected double staticScaleX, staticScaleY; + + + /** * The clipping filter for this graphics node. */ protected ClipRable clip; @@ -179,6 +192,15 @@ this.pointerEventType = pointerEventType; } + public void setStatic(boolean isStatic) { + if (this.isStatic != isStatic) { + staticRI = null; + staticScaleX = 0; + staticScaleY = 0; + this.isStatic = isStatic; + } + } + /** * Sets the transform of this node. * @@ -273,6 +295,9 @@ } public void setClip(ClipRable newClipper) { + if ((newClipper == null) && (this.clip == null)) + return; // No change still no clip. + fireGraphicsNodeChangeStarted(); invalidateGeometryCache(); this.clip = newClipper; @@ -344,6 +369,9 @@ * @param newMask the new mask of this node */ public void setMask(Mask newMask) { + if ((newMask == null) && (this.mask == null)) + return; // No change still no mask. + fireGraphicsNodeChangeStarted(); invalidateGeometryCache(); this.mask = newMask; @@ -363,6 +391,9 @@ * @param newFilter the new filter of this node */ public void setFilter(Filter newFilter) { + if ((newFilter == null) && (this.filter == null)) + return; // No change still no filter. + fireGraphicsNodeChangeStarted(); invalidateGeometryCache(); this.filter = newFilter; @@ -482,6 +513,7 @@ boolean useOffscreen = isOffscreenBufferNeeded(); useOffscreen |= antialiasedClip; + useOffscreen |= isStatic; if (!useOffscreen) { // Render on this canvas. @@ -516,11 +548,38 @@ g2d.setClip(null); } - Rectangle2D filterBounds = filteredImage.getBounds2D(); - g2d.clip(filterBounds); + Rectangle2D filterB = filteredImage.getBounds2D(); + if (isStatic) { - org.apache.batik.ext.awt.image.GraphicsUtil.drawImage - (g2d, filteredImage); + AffineTransform at = g2d.getTransform(); + double mat[] = new double[6]; + at.getMatrix(mat); + double scaleX = Math.sqrt(mat[0]*mat[0] + mat[2]*mat[2]); + double scaleY = Math.sqrt(mat[1]*mat[1] + mat[3]*mat[3]); + + if ((staticRI == null) || + (scaleX != staticScaleX) || + (scaleY != staticScaleY)) { + staticScaleX = scaleX; + staticScaleY = scaleY; + + AffineTransform newAT = new AffineTransform(); + newAT.scale(staticScaleX, staticScaleY); + RenderedImage ri = new GraphicsNodeRed8Bit + (this, newAT, true, g2d.getRenderingHints()); + ri = new TileCacheRed(GraphicsUtil.wrap(ri), 128, 128); + // System.err.println("RI: " + ri); + staticRI = ri; + } + + // System.err.println("Drawing BI"); + g2d.scale(1/staticScaleX, 1/staticScaleY); + GraphicsUtil.drawImage(g2d, staticRI); + g2d.setTransform(at); + } else { + g2d.clip(filterB); + GraphicsUtil.drawImage(g2d, filteredImage); + } } } @@ -717,6 +776,11 @@ parent.invalidateGeometryCache(); } bounds = null; + // if (staticRI != null) + // System.err.println("Flushing static: " + this); + staticRI = null; + staticScaleX = 0; + staticScaleY = 0; } /** Index: sources/org/apache/batik/gvt/filter/GraphicsNodeRed8Bit.java =================================================================== --- sources/org/apache/batik/gvt/filter/GraphicsNodeRed8Bit.java (revision 280419) +++ sources/org/apache/batik/gvt/filter/GraphicsNodeRed8Bit.java (working copy) @@ -92,8 +92,8 @@ 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 tgX = bounds.x; + int tgY = bounds.y; int tw = (bounds.x+bounds.width)-tgX; if (tw > defSz) tw = defSz; Index: sources/org/apache/batik/ext/awt/image/rendered/AbstractTiledRed.java =================================================================== --- sources/org/apache/batik/ext/awt/image/rendered/AbstractTiledRed.java (revision 280419) +++ sources/org/apache/batik/ext/awt/image/rendered/AbstractTiledRed.java (working copy) @@ -353,8 +353,8 @@ if ((tx1 < tx0) || (ty1 < ty0)) return; - // System.out.println("WR: " + wrR); - // System.out.println("ME: " + bounds); + // System.err.println("WR: " + wrR); + // System.err.println("ME: " + bounds + " TW: " + tileWidth); int insideTx0 = tx0; int insideTx1 = tx1; @@ -392,6 +392,7 @@ if ((xtiles > 0) && (ytiles > 0)) occupied = new boolean[xtiles*ytiles]; + // Tracks 'edge' tiles (not body tiles) boolean [] got = new boolean[2*(tx1-tx0+1) + 2*(ty1-ty0+1)]; int idx = 0; int numFound = 0; @@ -417,9 +418,13 @@ } } - // System.out.println("Found: " + numFound + " out of " + - // ((tx1-tx0+1)*(ty1-ty0+1))); + // System.err.println("Found: " + numFound + " out of " + + // ((tx1-tx0+1)*(ty1-ty0+1)) + " This: " + this); + // new Exception("Found: " + numFound + + // " out of " + ((tx1-tx0+1)*(ty1-ty0+1)) + + // " This: " + this).printStackTrace(); + // Compute the stuff from the middle in the largest possible Chunks. if ((xtiles > 0) && (ytiles > 0)) { TileBlock block = new TileBlock Index: sources/org/apache/batik/ext/awt/image/GraphicsUtil.java =================================================================== --- sources/org/apache/batik/ext/awt/image/GraphicsUtil.java (revision 280419) +++ sources/org/apache/batik/ext/awt/image/GraphicsUtil.java (working copy) @@ -109,7 +109,6 @@ continue; } else if (cr instanceof TranslateRed) { TranslateRed tr = (TranslateRed)cr; - // System.out.println("testing Translate"); int dx = tr.getDeltaX(); int dy = tr.getDeltaY(); if (at == null) @@ -163,13 +162,24 @@ // Scaling down so do it before color conversion. double determinant = at.getDeterminant(); - if (!at.isIdentity() && (determinant <= 1.0)) { - if (at.getType() != AffineTransform.TYPE_TRANSLATION) + if (!at.isIdentity() && (determinant <= 1.000001)) { + // Check if this is just a translate Affine. + boolean isTranslate=false; + double mat[] = new double[6]; + at.getMatrix(mat); + if ((determinant >= 0.999999) && (determinant <= 1.000001) && + (mat[0] >= 0.999999) && (mat[0] <= 1.000001) && + (mat[3] >= 0.999999) && (mat[3] <= 1.000001) && + (mat[1] >=-0.000001) && (mat[1] <= 0.000001) && + (mat[2] >=-0.000001) && (mat[2] <= 0.000001)) { + isTranslate = true; + } + if (isTranslate) { + int xloc = cr.getMinX() + (int)Math.round(at.getTranslateX()); + int yloc = cr.getMinY() + (int)Math.round(at.getTranslateY()); + cr = new TranslateRed(cr, xloc, yloc); + } else { cr = new AffineRed(cr, at, g2d.getRenderingHints()); - else { - int xloc = cr.getMinX() + (int)at.getTranslateX(); - int yloc = cr.getMinY() + (int)at.getTranslateY(); - cr = new TranslateRed(cr, xloc, yloc); } } @@ -191,8 +201,9 @@ cr = FormatRed.construct(cr, drawCM); // Scaling up so do it after color conversion. - if (!at.isIdentity() && (determinant > 1.0)) + if (!at.isIdentity() && (determinant > 1.000001)) { cr = new AffineRed(cr, at, g2d.getRenderingHints()); + } // Now CR is in device space, so clear the g2d transform. g2d.setTransform(IDENTITY); Index: samples/solitaire/towers.svg =================================================================== --- samples/solitaire/towers.svg (revision 280419) +++ samples/solitaire/towers.svg (working copy) @@ -4,6 +4,7 @@ @@ -247,7 +248,7 @@ ]]> - + @@ -502,14 +503,14 @@ -