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 vh...@apache.org on 2002/11/18 12:47:14 UTC

cvs commit: xml-batik/samples/tests/spec/interactivity cursor4.svg

vhardy      2002/11/18 03:47:14

  Modified:    sources/org/apache/batik/bridge CursorManager.java
                        ViewBox.java
               samples/tests/spec/interactivity cursor4.svg
  Added:       samples/tests/resources/images svgCursor2.svg svgCursor3.svg
                        svgCursor4.svg
  Log:
  Added support for SVG cursors. The <cursor> element can now reference 
  SVG images. 
  
  
  
  
  
  
  Revision  Changes    Path
  1.6       +180 -77   xml-batik/sources/org/apache/batik/bridge/CursorManager.java
  
  Index: CursorManager.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/bridge/CursorManager.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- CursorManager.java	15 Nov 2002 18:44:52 -0000	1.5
  +++ CursorManager.java	18 Nov 2002 11:47:14 -0000	1.6
  @@ -16,6 +16,7 @@
   import java.awt.Rectangle;
   import java.awt.Toolkit;
   import java.awt.geom.AffineTransform;
  +import java.awt.geom.Point2D;
   import java.awt.image.BufferedImage;
   import java.awt.image.ColorModel;
   import java.awt.image.RenderedImage;
  @@ -27,8 +28,13 @@
   import java.lang.ref.SoftReference;
   
   import org.apache.batik.ext.awt.image.GraphicsUtil;
  +import org.apache.batik.ext.awt.image.PadMode;
   import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
   import org.apache.batik.ext.awt.image.renderable.Filter;
  +import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit;
  +import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
  +
  +import org.apache.batik.gvt.GraphicsNode;
   
   import org.apache.batik.css.engine.SVGCSSEngine;
   import org.apache.batik.css.engine.value.ListValue;
  @@ -42,8 +48,11 @@
   import org.apache.batik.util.SoftReferenceCache;
   
   import org.w3c.dom.Element;
  +import org.w3c.dom.Node;
  +import org.w3c.dom.svg.SVGDocument;
   import org.w3c.dom.css.CSSPrimitiveValue;
   import org.w3c.dom.css.CSSValue;
  +import org.w3c.dom.svg.SVGPreserveAspectRatio;
   
   
   /**
  @@ -78,6 +87,12 @@
           = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
   
       /**
  +     * Default preferred cursor size, used for SVG images
  +     */
  +    public static final int DEFAULT_PREFERRED_WIDTH = 32;
  +    public static final int DEFAULT_PREFERRED_HEIGHT = 32;
  +
  +    /**
        * Static initialization of the cursorMap
        */
       static {
  @@ -307,6 +322,11 @@
           // One of the cursor url resolved to a <cursor> element
           // Try to handle its image.
           String uriStr = XLinkSupport.getXLinkHref(cursorElement);
  +        if (uriStr.length() == 0) {
  +            throw new BridgeException(cursorElement, ERR_ATTRIBUTE_MISSING,
  +                                      new Object[] {"xlink:href"});
  +        }
  +
           String baseURI = XMLBaseSupport.getCascadedXMLBase(cursorElement);
           ParsedURL purl;
           if (baseURI == null) {
  @@ -337,103 +357,56 @@
   
           CursorDescriptor desc = new CursorDescriptor(purl, x, y);
   
  +        //
           // Check if there is a cursor in the cache for this url
  +        //
           Cursor cachedCursor = cursorCache.getCursor(desc);
   
           if (cachedCursor != null) {
               return cachedCursor;
           } 
           
  -        ImageTagRegistry reg = ImageTagRegistry.getRegistry();
  -        Filter f = reg.readURL(purl);
  -
  -
  -        //
  -        // Check if we got a broken image
  -        //
  -        if (f.getProperty
  -            (SVGBrokenLinkProvider.SVG_BROKEN_LINK_DOCUMENT_PROPERTY) != null) {
  -            cursorCache.clearCursor(desc);
  -            return null;
  -        } 
  -            
           //
  -        // Now, get the preferred cursor dimension
  +        // Load image into Filter f and transform hotSpot to 
  +        // cursor space.
           //
  -        Rectangle preferredSize = f.getBounds2D().getBounds();
  -        if (preferredSize == null || preferredSize.width <=0
  -            || preferredSize.height <=0 ) {
  +        Point2D.Float hotSpot = new Point2D.Float(x, y);
  +        Filter f = cursorHrefToFilter(cursorElement, 
  +                                      purl, 
  +                                      hotSpot);
  +        if (f == null) {
               cursorCache.clearCursor(desc);
               return null;
           }
  -
  -        Dimension cursorSize 
  -            = Toolkit.getDefaultToolkit().getBestCursorSize
  -            (preferredSize.width, preferredSize.height);
  -
  -        // 
  -        // Fit the rendered image into the cursor image
  -        // size and aspect ratio if it does not fit into 
  -        // the cursorSize area. Otherwise, draw the cursor 
  -        // into an image the size of the preferred cursor 
  -        // size.
  -        //
  -        Image bi = null;
  -
  -        if (cursorSize.width < preferredSize.width 
  -            ||
  -            cursorSize.height < preferredSize.height) {
  -            float rw = cursorSize.width;
  -            float rh = (cursorSize.width * preferredSize.height) / (float)preferredSize.width;
  -            
  -            if (rh > cursorSize.height) {
  -                rw *= (cursorSize.height / rh);
  -                rh = cursorSize.height;
  -            }
               
  -            RenderedImage ri = f.createScaledRendering((int)Math.round(rw),
  -                                                       (int)Math.round(rh),
  -                                                       null);
  -            
  -            if (ri instanceof Image) {
  -                bi = (Image)ri;
  -            } else {
  -                bi = renderedImageToImage(ri);
  -            }
  -
  -            // Apply the scale transform that is applied to the image
  -            x *= rw/preferredSize.width;
  -            y *= rh/preferredSize.height;
  +        // The returned Filter is guaranteed to create a 
  +        // default rendering of the desired size
  +        Rectangle cursorSize = f.getBounds2D().getBounds();
  +        RenderedImage ri = f.createScaledRendering(cursorSize.width,
  +                                                   cursorSize.height,
  +                                                   null);
  +        Image img = null;
   
  +        if (ri instanceof Image) {
  +            img = (Image)ri;
           } else {
  -            // Preferred size fits into ideal cursor size. No resize,
  -            // just draw cursor in 0, 0.
  -            BufferedImage tbi = new BufferedImage(cursorSize.width,
  -                                                  cursorSize.height,
  -                                                  BufferedImage.TYPE_INT_ARGB);
  -            RenderedImage ri = f.createScaledRendering(preferredSize.width,
  -                                                       preferredSize.height,
  -                                                       null);
  -            Graphics2D g = GraphicsUtil.createGraphics(tbi);
  -            GraphicsUtil.drawImage(g, ri);
  -            g.dispose();
  -            bi = tbi;
  +            img = renderedImageToImage(ri);
           }
   
           // Make sure the not spot does not fall out of the cursor area. If it
           // does, then clamp the coordinates to the image space.
  -        x = x < 0 ? 0 : x;
  -        y = y < 0 ? 0 : y;
  -        x = x > (cursorSize.width-1) ? cursorSize.width - 1 : x;
  -        y = y > (cursorSize.height-1) ? cursorSize.height - 1: y;
  +        hotSpot.x = hotSpot.x < 0 ? 0 : hotSpot.x;
  +        hotSpot.y = hotSpot.y < 0 ? 0 : hotSpot.y;
  +        hotSpot.x = hotSpot.x > (cursorSize.width-1) ? cursorSize.width - 1 : hotSpot.x;
  +        hotSpot.y = hotSpot.y > (cursorSize.height-1) ? cursorSize.height - 1: hotSpot.y;
   
           //
  -        // The cursor image is now into the bi image
  +        // The cursor image is now into 'img'
           //
           Cursor c = Toolkit.getDefaultToolkit()
  -            .createCustomCursor(bi, 
  -                                new Point((int)Math.round(x),
  -                                          (int)Math.round(y)),
  +            .createCustomCursor(img, 
  +                                new Point((int)Math.round(hotSpot.x),
  +                                          (int)Math.round(hotSpot.y)),
                                   purl.toString());
   
           cursorCache.putCursor(desc, c);
  @@ -441,6 +414,138 @@
       }
   
       /**
  +     * Converts the input ParsedURL into a Filter and transforms the 
  +     * input hotSpot point (in image space) to cursor space
  +     */
  +    protected Filter cursorHrefToFilter(Element cursorElement, 
  +                                        ParsedURL purl,
  +                                        Point2D hotSpot) {
  +        
  +        AffineRable8Bit f = null;
  +        String uriStr = purl.toString();
  +        Dimension cursorSize = null;
  +
  +        // Try to load as an SVG Document
  +        DocumentLoader loader = (DocumentLoader)ctx.getDocumentLoader();
  +        SVGDocument svgDoc = (SVGDocument)cursorElement.getOwnerDocument();
  +        URIResolver resolver = new URIResolver(svgDoc, loader);
  +        try {
  +            Element rootElement = null;
  +            Node n = resolver.getNode(uriStr, cursorElement);
  +            if (n.getNodeType() == n.DOCUMENT_NODE) {
  +                rootElement = ((SVGDocument)n).getRootElement();
  +            } else {
  +                throw new BridgeException 
  +                    (cursorElement, ERR_URI_IMAGE_INVALID,
  +                     new Object[] {uriStr});
  +            }
  +            GraphicsNode node = ctx.getGVTBuilder().build(ctx, rootElement);
  +
  +            //
  +            // The cursorSize define the viewport into which the 
  +            // cursor is displayed. That viewport is platform 
  +            // dependant and is not defined by the SVG content.
  +            //
  +            float width  = DEFAULT_PREFERRED_WIDTH;
  +            float height = DEFAULT_PREFERRED_HEIGHT;
  +            UnitProcessor.Context uctx 
  +                = UnitProcessor.createContext(ctx, rootElement);
  +
  +            String s = rootElement.getAttribute(SVG_WIDTH_ATTRIBUTE);
  +            if (s.length() != 0) {
  +                width = UnitProcessor.svgHorizontalLengthToUserSpace
  +                (s, SVG_WIDTH_ATTRIBUTE, uctx);
  +            }
  +            
  +            s = rootElement.getAttribute(SVG_HEIGHT_ATTRIBUTE);
  +            if (s.length() != 0) {
  +                height = UnitProcessor.svgVerticalLengthToUserSpace
  +                    (s, SVG_HEIGHT_ATTRIBUTE, uctx);
  +            }
  +
  +            cursorSize 
  +                = Toolkit.getDefaultToolkit().getBestCursorSize
  +                ((int)Math.round(width), (int)Math.round(height));
  +            
  +            // Handle the viewBox transform
  +            AffineTransform at 
  +                = ViewBox.getPreserveAspectRatioTransform(rootElement,
  +                                                          cursorSize.width,
  +                                                          cursorSize.height);
  +            Filter filter = node.getGraphicsNodeRable(true);
  +            f = new AffineRable8Bit(filter, at);
  +        } catch (BridgeException ex) {
  +            throw ex;
  +        } catch (SecurityException ex) {
  +            throw new BridgeException(cursorElement, ERR_URI_UNSECURE,
  +                                      new Object[] {uriStr});
  +        } catch (Exception ex) {
  +            /* Nothing to do */ 
  +        }
  +
  +
  +        // If f is null, it means that we are not dealing with
  +        // an SVG image. Try as a raster image.
  +        if (f == null) {
  +            ImageTagRegistry reg = ImageTagRegistry.getRegistry();
  +            Filter filter = reg.readURL(purl);
  +            if (filter == null) {
  +                return null;
  +            }
  +
  +            // Check if we got a broken image
  +            if (filter.getProperty
  +                (SVGBrokenLinkProvider.SVG_BROKEN_LINK_DOCUMENT_PROPERTY) != null) {
  +                return null;
  +            } 
  +            
  +            Rectangle preferredSize = filter.getBounds2D().getBounds();
  +            cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize
  +                (preferredSize.width, preferredSize.height);
  +            
  +            if (preferredSize != null && preferredSize.width >0
  +                && preferredSize.height > 0 ) {
  +                AffineTransform at = new AffineTransform();
  +                if (preferredSize.width > cursorSize.width 
  +                    ||
  +                    preferredSize.height > cursorSize.height) {
  +                    at = ViewBox.getPreserveAspectRatioTransform
  +                        (new float[] {0, 0, preferredSize.width, preferredSize.height},
  +                         SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN,
  +                         true,
  +                         cursorSize.width,
  +                         cursorSize.height);
  +                } 
  +                f = new AffineRable8Bit(filter, at);
  +            } else {
  +                // Invalid Size
  +                return null;
  +            }
  +        }
  +
  +
  +        //
  +        // Transform the hot spot from image space to cursor space
  +        //
  +        AffineTransform at = f.getAffine();
  +        at.transform(hotSpot, hotSpot);
  +
  +        //
  +        // In all cases, clip to the cursor boundaries
  +        //
  +        Rectangle cursorViewport 
  +            = new Rectangle(0, 0, cursorSize.width, cursorSize.height);
  +
  +        PadRable8Bit cursorImage 
  +            = new PadRable8Bit(f, cursorViewport,
  +                               PadMode.ZERO_PAD);
  +
  +        return cursorImage;
  +            
  +    }
  +
  +
  +    /**
        * Implementation helper: converts a RenderedImage to an Image
        */
       protected Image renderedImageToImage(RenderedImage ri) {
  @@ -495,8 +600,6 @@
                    &&
                    this.y == desc.y;
                    
  -            // System.out.println("isEqual : " + isEqual);
  -            // (new Exception()).printStackTrace();
               return isEqual;
           }
   
  
  
  
  1.8       +2 -2      xml-batik/sources/org/apache/batik/bridge/ViewBox.java
  
  Index: ViewBox.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/bridge/ViewBox.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- ViewBox.java	8 Nov 2001 23:02:43 -0000	1.7
  +++ ViewBox.java	18 Nov 2002 11:47:14 -0000	1.8
  @@ -285,7 +285,7 @@
        * @param w the width of the region in which the document has to fit into
        * @param h the height of the region in which the document has to fit into
        */
  -    private static
  +    public static
           AffineTransform getPreserveAspectRatioTransform(float [] vb,
                                                           short align,
                                                           boolean meet,
  
  
  
  1.1                  xml-batik/samples/tests/resources/images/svgCursor2.svg
  
  Index: svgCursor2.svg
  ===================================================================
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  
       id="body" >
      <rect x="0%" y="0%" width="200%" height="200%" fill="gold" />
      <rect x="0" y="0" width="32" height="32" fill="black" />
      <rect x="2" y="2" width="28" height="28" fill="#eeeeee" />
      <rect x="15" y="0" width="2" height="32" fill="black" />
      <rect y="15" x="0" width="32" height="2" fill="black" />
      <rect x="12" y="12" width="8" height="8" fill="crimson" />
      <rect x="5" y="20" width="22" height="10" fill="#eeeeee" />
      <text x="15.5" y="29" font-family="sans-serif" font-size="8" text-anchor="middle">SVG 2</text>
  </svg>
  
  
  1.1                  xml-batik/samples/tests/resources/images/svgCursor3.svg
  
  Index: svgCursor3.svg
  ===================================================================
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  
       id="body"  viewBox="0 0 32 32" >
      <rect x="0%" y="0%" width="200%" height="200%" fill="green" />
      <rect x="0" y="0" width="32" height="32" fill="black" />
      <rect x="2" y="2" width="28" height="28" fill="#ccccff" />
      <rect x="15" y="0" width="2" height="32" fill="black" />
      <rect y="15" x="0" width="32" height="2" fill="black" />
      <rect x="12" y="12" width="8" height="8" fill="crimson" />
      <rect x="5" y="20" width="22" height="10" fill="#ccccff" />
      <text x="15.5" y="29" font-family="sans-serif" font-size="8" text-anchor="middle">SVG 3</text>
  </svg>
  
  
  1.1                  xml-batik/samples/tests/resources/images/svgCursor4.svg
  
  Index: svgCursor4.svg
  ===================================================================
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  
       id="body"  width="300" height="300" viewBox="0 0 32 16" preserveAspectRatio="xMidYMid meet">
      <rect x="0" y="0" width="32" height="16" fill="black" />
      <rect x="2" y="2" width="28" height="12" fill="orange" />
      <rect x="15" y="0" width="2" height="16" fill="black" />
      <rect y="7" x="0" width="32" height="2" fill="black" />
      <rect x="12" y="4" width="8" height="8" fill="crimson" />
  </svg>
  
  
  1.3       +24 -3     xml-batik/samples/tests/spec/interactivity/cursor4.svg
  
  Index: cursor4.svg
  ===================================================================
  RCS file: /home/cvs/xml-batik/samples/tests/spec/interactivity/cursor4.svg,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- cursor4.svg	15 Nov 2002 18:44:52 -0000	1.2
  +++ cursor4.svg	18 Nov 2002 11:47:14 -0000	1.3
  @@ -77,8 +77,8 @@
           <rect x="0" y="0" width="100" height="100" stroke="black"/>
           <rect x="44" y="44" width="12" height="12" fill="black"/>
           <rect x="45" y="45" width="10" height="10" fill="#eeeeee" />
  -        <rect x="49" y="40" width="2" height="20" fill="black" />
  -        <rect y="49" x="40" height="2" width="20" fill="black" />
  +        <rect x="49" y="30" width="2" height="40" fill="black" />
  +        <rect y="49" x="30" height="2" width="40" fill="black" />
           <!-- <line x1="40" y1="50" x2="60" y2="50" stroke="black" />
           <line y1="40" x1="50" y2="60" x2="50" stroke="black" /> -->
           <rect x="49" y="49" width="2" height="2" fill="black"
  @@ -98,6 +98,9 @@
           <use xlink:href="#jpegImage" x="0" y="34"/>
           <use xlink:href="#unsupportedImage" x="34" y="34"/>
           <use xlink:href="#svgImage" x="68" y="34"/>
  +        <use xlink:href="#svgImage2" x="0" y="68"/>
  +        <use xlink:href="#svgImage3" x="34" y="68"/>
  +        <use xlink:href="#svgImage4" x="68" y="68"/>
       </g>
   
       <text class="label" text-anchor="middle" x="75%" y="340" >Current Target Area Cursor</text>
  @@ -127,6 +130,18 @@
               <text class="label" text-anchor="middle" y="40">SVG Image<tspan dy="1.5em" x="0">defaults to crosshair</tspan></text>
               <use xlink:href="#svgImage"  />
           </g>
  +        <g id="refsvgImage2">
  +            <text class="label" text-anchor="middle" y="40">SVG Image<tspan dy="1.5em" x="0">No viewBox/width/height</tspan></text>
  +            <use xlink:href="#svgImage2"  />
  +        </g>
  +        <g id="refsvgImage3">
  +            <text class="label" text-anchor="middle" y="40">SVG Image<tspan dy="1.5em" x="0">ViewBox, no width/height</tspan></text>
  +            <use xlink:href="#svgImage3"  />
  +        </g>
  +y        <g id="refsvgImage4">
  +            <text class="label" text-anchor="middle" y="40">SVG Image<tspan dy="1.5em" x="0">ViewBox, different aspect ratio</tspan></text>
  +            <use xlink:href="#svgImage4"  />
  +        </g>
   
       </g>
   
  @@ -137,6 +152,9 @@
           <image id="jpegImage" xlink:href="../../resources/images/jpegCursor.jpg" width="32" height="32" x="-16" y="-16"/>
           <image id="unsupportedImage" xlink:href="../../resources/images/bmpCursor.bmp" width="32" height="32" x="-16" y="-16"/>
           <image id="svgImage" xlink:href="../../resources/images/svgCursor.svg" width="32" height="32" x="-16" y="-16"/>
  +        <image id="svgImage2" xlink:href="../../resources/images/svgCursor2.svg" width="32" height="32" x="-16" y="-16"/>
  +        <image id="svgImage3" xlink:href="../../resources/images/svgCursor3.svg" width="32" height="32" x="-16" y="-16"/>
  +        <image id="svgImage4" xlink:href="../../resources/images/svgCursor4.svg" width="32" height="32" x="-16" y="-16"/>
   
           <cursor id="cursorbrokenImage" xlink:href="../../resources/images/iDontExist.png" x="16" y="16"/>
           <cursor id="cursortiffImage" xlink:href="../../resources/images/tiffCursor.tif" x="16" y="16"/>
  @@ -144,6 +162,9 @@
           <cursor id="cursorjpegImage" xlink:href="../../resources/images/jpegCursor.jpg" x="16" y="16"/>
           <cursor id="cursorunsupportedImage" xlink:href="../../resources/images/bmpCursor.bmp" x="16" y="16"/>
           <cursor id="cursorsvgImage" xlink:href="../../resources/images/svgCursor.svg" x="16" y="16"/>
  +        <cursor id="cursorsvgImage2" xlink:href="../../resources/images/svgCursor2.svg" x="16" y="16"/>
  +        <cursor id="cursorsvgImage3" xlink:href="../../resources/images/svgCursor3.svg" x="16" y="16"/>
  +        <cursor id="cursorsvgImage4" xlink:href="../../resources/images/svgCursor4.svg" x="16" y="8"/>
       </defs>
   
   </svg>
  
  
  

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