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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]