+ * Parts of the system that need to use any of the above features should + * call {@code RenderingEngine.getInstance()} to obtain the properly + * registered (and possibly trace-enabled) version of the RenderingEngine. + */ +public abstract class RenderingEngine { + private static RenderingEngine reImpl; + + /** + * Returns an instance of {@code RenderingEngine} as determined + * by the installation environment and runtime flags. + *
+ * A specific instance of the {@code RenderingEngine} can be + * chosen by specifying the runtime flag: + *
+ * java -Dsun.java2d.renderer=<classname> + *+ * + * If no specific {@code RenderingEngine} is specified on the command + * line or the requested class fails to load, then the Marlin + * renderer will be used as the default. + *
+ * A printout of which RenderingEngine is loaded and used can be + * enabled by specifying the runtime flag: + *
+ * java -Dsun.java2d.renderer.verbose=true + *+ *
+ * Runtime tracing of the actions of the {@code RenderingEngine} + * can be enabled by specifying the runtime flag: + *
+ * java -Dsun.java2d.renderer.trace=<any string> + *+ * @return an instance of {@code RenderingEngine} + * @since 1.7 + */ + public static synchronized RenderingEngine getInstance() { + if (reImpl != null) { + return reImpl; + } + + /* Look first for an app-override renderer, + * if not specified or present, then look for marlin. + */ + GetPropertyAction gpa = + new GetPropertyAction("sun.java2d.renderer"); + String reClass = AccessController.doPrivileged(gpa); + if (reClass != null) { + try { + Class> cls = Class.forName(reClass); + reImpl = (RenderingEngine) cls.getConstructor().newInstance(); + } catch (ReflectiveOperationException ignored0) { + } + } + if (reImpl == null) { + final String marlinREClass = "sun.java2d.marlin.DMarlinRenderingEngine"; + try { + Class> cls = Class.forName(marlinREClass); + reImpl = (RenderingEngine) cls.getConstructor().newInstance(); + } catch (ReflectiveOperationException ignored1) { + } + } + + if (reImpl == null) { + throw new InternalError("No RenderingEngine module found"); + } + + gpa = new GetPropertyAction("sun.java2d.renderer.verbose"); + String verbose = AccessController.doPrivileged(gpa); + if (verbose != null && verbose.startsWith("t")) { + System.out.println("RenderingEngine = "+reImpl); + } + + gpa = new GetPropertyAction("sun.java2d.renderer.trace"); + String reTrace = AccessController.doPrivileged(gpa); + if (reTrace != null) { + reImpl = new Tracer(reImpl); + } + + return reImpl; + } + + /** + * Create a widened path as specified by the parameters. + *
+ * The specified {@code src} {@link Shape} is widened according + * to the specified attribute parameters as per the + * {@link BasicStroke} specification. + * + * @param src the source path to be widened + * @param width the width of the widened path as per {@code BasicStroke} + * @param caps the end cap decorations as per {@code BasicStroke} + * @param join the segment join decorations as per {@code BasicStroke} + * @param miterlimit the miter limit as per {@code BasicStroke} + * @param dashes the dash length array as per {@code BasicStroke} + * @param dashphase the initial dash phase as per {@code BasicStroke} + * @return the widened path stored in a new {@code Shape} object + * @since 1.7 + */ + public abstract Shape createStrokedShape(Shape src, + float width, + int caps, + int join, + float miterlimit, + float dashes[], + float dashphase); + + /** + * Sends the geometry for a widened path as specified by the parameters + * to the specified consumer. + *
+ * The specified {@code src} {@link Shape} is widened according + * to the parameters specified by the {@link BasicStroke} object. + * Adjustments are made to the path as appropriate for the + * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the + * {@code normalize} boolean parameter is true. + * Adjustments are made to the path as appropriate for the + * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the + * {@code antialias} boolean parameter is true. + *
+ * The geometry of the widened path is forwarded to the indicated + * {@link PathConsumer2D} object as it is calculated. + * + * @param src the source path to be widened + * @param bs the {@code BasicSroke} object specifying the + * decorations to be applied to the widened path + * @param normalize indicates whether stroke normalization should + * be applied + * @param antialias indicates whether or not adjustments appropriate + * to antialiased rendering should be applied + * @param consumer the {@code PathConsumer2D} instance to forward + * the widened geometry to + * @since 1.7 + */ + public abstract void strokeTo(Shape src, + AffineTransform at, + BasicStroke bs, + boolean thin, + boolean normalize, + boolean antialias, + PathConsumer2D consumer); + + /** + * Construct an antialiased tile generator for the given shape with + * the given rendering attributes and store the bounds of the tile + * iteration in the bbox parameter. + * The {@code at} parameter specifies a transform that should affect + * both the shape and the {@code BasicStroke} attributes. + * The {@code clip} parameter specifies the current clip in effect + * in device coordinates and can be used to prune the data for the + * operation, but the renderer is not required to perform any + * clipping. + * If the {@code BasicStroke} parameter is null then the shape + * should be filled as is, otherwise the attributes of the + * {@code BasicStroke} should be used to specify a draw operation. + * The {@code thin} parameter indicates whether or not the + * transformed {@code BasicStroke} represents coordinates smaller + * than the minimum resolution of the antialiasing rasterizer as + * specified by the {@code getMinimumAAPenWidth()} method. + *
+ * Upon returning, this method will fill the {@code bbox} parameter + * with 4 values indicating the bounds of the iteration of the + * tile generator. + * The iteration order of the tiles will be as specified by the + * pseudo-code: + *
+ * for (y = bbox[1]; y < bbox[3]; y += tileheight) { + * for (x = bbox[0]; x < bbox[2]; x += tilewidth) { + * } + * } + *+ * If there is no output to be rendered, this method may return + * null. + * + * @param s the shape to be rendered (fill or draw) + * @param at the transform to be applied to the shape and the + * stroke attributes + * @param clip the current clip in effect in device coordinates + * @param bs if non-null, a {@code BasicStroke} whose attributes + * should be applied to this operation + * @param thin true if the transformed stroke attributes are smaller + * than the minimum dropout pen width + * @param normalize true if the {@code VALUE_STROKE_NORMALIZE} + * {@code RenderingHint} is in effect + * @param bbox returns the bounds of the iteration + * @return the {@code AATileGenerator} instance to be consulted + * for tile coverages, or null if there is no output to render + * @since 1.7 + */ + public abstract AATileGenerator getAATileGenerator(Shape s, + AffineTransform at, + Region clip, + BasicStroke bs, + boolean thin, + boolean normalize, + int bbox[]); + + /** + * Construct an antialiased tile generator for the given parallelogram + * store the bounds of the tile iteration in the bbox parameter. + * The parallelogram is specified as a starting point and 2 delta + * vectors that indicate the slopes of the 2 pairs of sides of the + * parallelogram. + * The 4 corners of the parallelogram are defined by the 4 points: + *
+ * Upon returning, this method will fill the {@code bbox} parameter + * with 4 values indicating the bounds of the iteration of the + * tile generator. + * The iteration order of the tiles will be as specified by the + * pseudo-code: + *
+ * for (y = bbox[1]; y < bbox[3]; y += tileheight) { + * for (x = bbox[0]; x < bbox[2]; x += tilewidth) { + * } + * } + *+ * If there is no output to be rendered, this method may return + * null. + * + * @param x the X coordinate of the first corner of the parallelogram + * @param y the Y coordinate of the first corner of the parallelogram + * @param dx1 the X coordinate delta of the first leg of the parallelogram + * @param dy1 the Y coordinate delta of the first leg of the parallelogram + * @param dx2 the X coordinate delta of the second leg of the parallelogram + * @param dy2 the Y coordinate delta of the second leg of the parallelogram + * @param lw1 the line width ratio for the first leg of the parallelogram + * @param lw2 the line width ratio for the second leg of the parallelogram + * @param clip the current clip in effect in device coordinates + * @param bbox returns the bounds of the iteration + * @return the {@code AATileGenerator} instance to be consulted + * for tile coverages, or null if there is no output to render + * @since 1.7 + */ + public abstract AATileGenerator getAATileGenerator(double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2, + Region clip, + int bbox[]); + + /** + * Returns the minimum pen width that the antialiasing rasterizer + * can represent without dropouts occurring. + * @since 1.7 + */ + public abstract float getMinimumAAPenSize(); + + /** + * Utility method to feed a {@link PathConsumer2D} object from a + * given {@link PathIterator}. + * This method deals with the details of running the iterator and + * feeding the consumer a segment at a time. + */ + public static void feedConsumer(PathIterator pi, PathConsumer2D consumer) { + float coords[] = new float[6]; + while (!pi.isDone()) { + switch (pi.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + consumer.moveTo(coords[0], coords[1]); + break; + case PathIterator.SEG_LINETO: + consumer.lineTo(coords[0], coords[1]); + break; + case PathIterator.SEG_QUADTO: + consumer.quadTo(coords[0], coords[1], + coords[2], coords[3]); + break; + case PathIterator.SEG_CUBICTO: + consumer.curveTo(coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]); + break; + case PathIterator.SEG_CLOSE: + consumer.closePath(); + break; + } + pi.next(); + } + } + + static class Tracer extends RenderingEngine { + RenderingEngine target; + String name; + + public Tracer(RenderingEngine target) { + this.target = target; + name = target.getClass().getName(); + } + + public Shape createStrokedShape(Shape src, + float width, + int caps, + int join, + float miterlimit, + float dashes[], + float dashphase) + { + System.out.println(name+".createStrokedShape("+ + src.getClass().getName()+", "+ + "width = "+width+", "+ + "caps = "+caps+", "+ + "join = "+join+", "+ + "miter = "+miterlimit+", "+ + "dashes = "+dashes+", "+ + "dashphase = "+dashphase+")"); + return target.createStrokedShape(src, + width, caps, join, miterlimit, + dashes, dashphase); + } + + public void strokeTo(Shape src, + AffineTransform at, + BasicStroke bs, + boolean thin, + boolean normalize, + boolean antialias, + PathConsumer2D consumer) + { + System.out.println(name+".strokeTo("+ + src.getClass().getName()+", "+ + at+", "+ + bs+", "+ + (thin ? "thin" : "wide")+", "+ + (normalize ? "normalized" : "pure")+", "+ + (antialias ? "AA" : "non-AA")+", "+ + consumer.getClass().getName()+")"); + target.strokeTo(src, at, bs, thin, normalize, antialias, consumer); + } + + public float getMinimumAAPenSize() { + System.out.println(name+".getMinimumAAPenSize()"); + return target.getMinimumAAPenSize(); + } + + public AATileGenerator getAATileGenerator(Shape s, + AffineTransform at, + Region clip, + BasicStroke bs, + boolean thin, + boolean normalize, + int bbox[]) + { + System.out.println(name+".getAATileGenerator("+ + s.getClass().getName()+", "+ + at+", "+ + clip+", "+ + bs+", "+ + (thin ? "thin" : "wide")+", "+ + (normalize ? "normalized" : "pure")+")"); + return target.getAATileGenerator(s, at, clip, + bs, thin, normalize, + bbox); + } + public AATileGenerator getAATileGenerator(double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2, + Region clip, + int bbox[]) + { + System.out.println(name+".getAATileGenerator("+ + x+", "+y+", "+ + dx1+", "+dy1+", "+ + dx2+", "+dy2+", "+ + lw1+", "+lw2+", "+ + clip+")"); + return target.getAATileGenerator(x, y, + dx1, dy1, + dx2, dy2, + lw1, lw2, + clip, bbox); + } + } +}