1/*
2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.java2d.pipe;
27
28import java.awt.Shape;
29import java.awt.BasicStroke;
30import java.awt.geom.PathIterator;
31import java.awt.geom.AffineTransform;
32
33import java.security.PrivilegedAction;
34import java.security.AccessController;
35import sun.security.action.GetPropertyAction;
36
37import sun.awt.geom.PathConsumer2D;
38
39/**
40 * This class abstracts a number of features for which the Java 2D
41 * implementation relies on proprietary licensed software libraries.
42 * Access to those features is now achieved by retrieving the singleton
43 * instance of this class and calling the appropriate methods on it.
44 * The 3 primary features abstracted here include:
45 * <dl>
46 * <dt>Shape createStrokedShape(Shape, [BasicStroke attributes]);
47 * <dd>This method implements the functionality of the method of the
48 * same name on the {@link BasicStroke} class.
49 * <dt>void strokeTo(Shape, [rendering parameters], PathConsumer2D);
50 * <dd>This method performs widening of the source path on the fly
51 * and sends the results to the given {@link PathConsumer2D} object.
52 * This procedure avoids having to create an intermediate Shape
53 * object to hold the results of the {@code createStrokedShape} method.
54 * The main user of this method is the Java 2D non-antialiasing renderer.
55 * <dt>AATileGenerator getAATileGenerator(Shape, [rendering parameters]);
56 * <dd>This method returns an object which can iterate over the
57 * specified bounding box and produce tiles of coverage values for
58 * antialiased rendering.  The details of the operation of the
59 * {@link AATileGenerator} object are explained in its class comments.
60 * </dl>
61 * Additionally, the following informational method supplies important
62 * data about the implementation.
63 * <dl>
64 * <dt>float getMinimumAAPenSize()
65 * <dd>This method provides information on how small the BasicStroke
66 * line width can get before dropouts occur.  Rendering with a BasicStroke
67 * is defined to never allow the line to have breaks, gaps, or dropouts
68 * even if the width is set to 0.0f, so this information allows the
69 * {@link sun.java2d.SunGraphics2D} class to detect the "thin line" case and set
70 * the rendering attributes accordingly.
71 * </dl>
72 * At startup the runtime will load a single instance of this class.
73 * It searches the classpath for a registered provider of this API
74 * and returns either the last one it finds, or the instance whose
75 * class name matches the value supplied in the System property
76 * {@code sun.java2d.renderer}.
77 * Additionally, a runtime System property flag can be set to trace
78 * all calls to methods on the {@code RenderingEngine} in use by
79 * setting the sun.java2d.renderer.trace property to any non-null value.
80 * <p>
81 * Parts of the system that need to use any of the above features should
82 * call {@code RenderingEngine.getInstance()} to obtain the properly
83 * registered (and possibly trace-enabled) version of the RenderingEngine.
84 */
85public abstract class RenderingEngine {
86    private static RenderingEngine reImpl;
87
88    /**
89     * Returns an instance of {@code RenderingEngine} as determined
90     * by the installation environment and runtime flags.
91     * <p>
92     * A specific instance of the {@code RenderingEngine} can be
93     * chosen by specifying the runtime flag:
94     * <pre>
95     *     java -Dsun.java2d.renderer=&lt;classname&gt;
96     * </pre>
97     *
98     * If no specific {@code RenderingEngine} is specified on the command
99     * line or the requested class fails to load, then the Marlin
100     * renderer will be used as the default.
101     * <p>
102     * A printout of which RenderingEngine is loaded and used can be
103     * enabled by specifying the runtime flag:
104     * <pre>
105     *     java -Dsun.java2d.renderer.verbose=true
106     * </pre>
107     * <p>
108     * Runtime tracing of the actions of the {@code RenderingEngine}
109     * can be enabled by specifying the runtime flag:
110     * <pre>
111     *     java -Dsun.java2d.renderer.trace=&lt;any string&gt;
112     * </pre>
113     * @return an instance of {@code RenderingEngine}
114     * @since 1.7
115     */
116    public static synchronized RenderingEngine getInstance() {
117        if (reImpl != null) {
118            return reImpl;
119        }
120
121        /* Look first for an app-override renderer,
122         * if not specified or present, then look for marlin.
123         */
124        GetPropertyAction gpa =
125            new GetPropertyAction("sun.java2d.renderer");
126        String reClass = AccessController.doPrivileged(gpa);
127        if (reClass != null) {
128            try {
129                Class<?> cls = Class.forName(reClass);
130                reImpl = (RenderingEngine) cls.getConstructor().newInstance();
131            } catch (ReflectiveOperationException ignored0) {
132            }
133        }
134        if (reImpl == null) {
135            final String marlinREClass = "sun.java2d.marlin.DMarlinRenderingEngine";
136            try {
137                Class<?> cls = Class.forName(marlinREClass);
138                reImpl = (RenderingEngine) cls.getConstructor().newInstance();
139            } catch (ReflectiveOperationException ignored1) {
140            }
141        }
142
143        if (reImpl == null) {
144            throw new InternalError("No RenderingEngine module found");
145        }
146
147        gpa = new GetPropertyAction("sun.java2d.renderer.verbose");
148        String verbose = AccessController.doPrivileged(gpa);
149        if (verbose != null && verbose.startsWith("t")) {
150            System.out.println("RenderingEngine = "+reImpl);
151        }
152
153        gpa = new GetPropertyAction("sun.java2d.renderer.trace");
154        String reTrace = AccessController.doPrivileged(gpa);
155        if (reTrace != null) {
156            reImpl = new Tracer(reImpl);
157        }
158
159        return reImpl;
160    }
161
162    /**
163     * Create a widened path as specified by the parameters.
164     * <p>
165     * The specified {@code src} {@link Shape} is widened according
166     * to the specified attribute parameters as per the
167     * {@link BasicStroke} specification.
168     *
169     * @param src the source path to be widened
170     * @param width the width of the widened path as per {@code BasicStroke}
171     * @param caps the end cap decorations as per {@code BasicStroke}
172     * @param join the segment join decorations as per {@code BasicStroke}
173     * @param miterlimit the miter limit as per {@code BasicStroke}
174     * @param dashes the dash length array as per {@code BasicStroke}
175     * @param dashphase the initial dash phase as per {@code BasicStroke}
176     * @return the widened path stored in a new {@code Shape} object
177     * @since 1.7
178     */
179    public abstract Shape createStrokedShape(Shape src,
180                                             float width,
181                                             int caps,
182                                             int join,
183                                             float miterlimit,
184                                             float dashes[],
185                                             float dashphase);
186
187    /**
188     * Sends the geometry for a widened path as specified by the parameters
189     * to the specified consumer.
190     * <p>
191     * The specified {@code src} {@link Shape} is widened according
192     * to the parameters specified by the {@link BasicStroke} object.
193     * Adjustments are made to the path as appropriate for the
194     * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
195     * {@code normalize} boolean parameter is true.
196     * Adjustments are made to the path as appropriate for the
197     * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
198     * {@code antialias} boolean parameter is true.
199     * <p>
200     * The geometry of the widened path is forwarded to the indicated
201     * {@link PathConsumer2D} object as it is calculated.
202     *
203     * @param src the source path to be widened
204     * @param bs the {@code BasicSroke} object specifying the
205     *           decorations to be applied to the widened path
206     * @param normalize indicates whether stroke normalization should
207     *                  be applied
208     * @param antialias indicates whether or not adjustments appropriate
209     *                  to antialiased rendering should be applied
210     * @param consumer the {@code PathConsumer2D} instance to forward
211     *                 the widened geometry to
212     * @since 1.7
213     */
214    public abstract void strokeTo(Shape src,
215                                  AffineTransform at,
216                                  BasicStroke bs,
217                                  boolean thin,
218                                  boolean normalize,
219                                  boolean antialias,
220                                  PathConsumer2D consumer);
221
222    /**
223     * Construct an antialiased tile generator for the given shape with
224     * the given rendering attributes and store the bounds of the tile
225     * iteration in the bbox parameter.
226     * The {@code at} parameter specifies a transform that should affect
227     * both the shape and the {@code BasicStroke} attributes.
228     * The {@code clip} parameter specifies the current clip in effect
229     * in device coordinates and can be used to prune the data for the
230     * operation, but the renderer is not required to perform any
231     * clipping.
232     * If the {@code BasicStroke} parameter is null then the shape
233     * should be filled as is, otherwise the attributes of the
234     * {@code BasicStroke} should be used to specify a draw operation.
235     * The {@code thin} parameter indicates whether or not the
236     * transformed {@code BasicStroke} represents coordinates smaller
237     * than the minimum resolution of the antialiasing rasterizer as
238     * specified by the {@code getMinimumAAPenWidth()} method.
239     * <p>
240     * Upon returning, this method will fill the {@code bbox} parameter
241     * with 4 values indicating the bounds of the iteration of the
242     * tile generator.
243     * The iteration order of the tiles will be as specified by the
244     * pseudo-code:
245     * <pre>
246     *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
247     *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
248     *         }
249     *     }
250     * </pre>
251     * If there is no output to be rendered, this method may return
252     * null.
253     *
254     * @param s the shape to be rendered (fill or draw)
255     * @param at the transform to be applied to the shape and the
256     *           stroke attributes
257     * @param clip the current clip in effect in device coordinates
258     * @param bs if non-null, a {@code BasicStroke} whose attributes
259     *           should be applied to this operation
260     * @param thin true if the transformed stroke attributes are smaller
261     *             than the minimum dropout pen width
262     * @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
263     *                  {@code RenderingHint} is in effect
264     * @param bbox returns the bounds of the iteration
265     * @return the {@code AATileGenerator} instance to be consulted
266     *         for tile coverages, or null if there is no output to render
267     * @since 1.7
268     */
269    public abstract AATileGenerator getAATileGenerator(Shape s,
270                                                       AffineTransform at,
271                                                       Region clip,
272                                                       BasicStroke bs,
273                                                       boolean thin,
274                                                       boolean normalize,
275                                                       int bbox[]);
276
277    /**
278     * Construct an antialiased tile generator for the given parallelogram
279     * store the bounds of the tile iteration in the bbox parameter.
280     * The parallelogram is specified as a starting point and 2 delta
281     * vectors that indicate the slopes of the 2 pairs of sides of the
282     * parallelogram.
283     * The 4 corners of the parallelogram are defined by the 4 points:
284     * <ul>
285     * <li> {@code x}, {@code y}
286     * <li> {@code x+dx1}, {@code y+dy1}
287     * <li> {@code x+dx1+dx2}, {@code y+dy1+dy2}
288     * <li> {@code x+dx2}, {@code y+dy2}
289     * </ul>
290     * The {@code lw1} and {@code lw2} parameters provide a specification
291     * for an optionally stroked parallelogram if they are positive numbers.
292     * The {@code lw1} parameter is the ratio of the length of the {@code dx1},
293     * {@code dx2} delta vector to half of the line width in that same
294     * direction.
295     * The {@code lw2} parameter provides the same ratio for the other delta
296     * vector.
297     * If {@code lw1} and {@code lw2} are both greater than zero, then
298     * the parallelogram figure is doubled by both expanding and contracting
299     * each delta vector by its corresponding {@code lw} value.
300     * If either {@code lw1} or {@code lw2} are also greater than 1, then
301     * the inner (contracted) parallelogram disappears and the figure is
302     * simply a single expanded parallelogram.
303     * The {@code clip} parameter specifies the current clip in effect
304     * in device coordinates and can be used to prune the data for the
305     * operation, but the renderer is not required to perform any
306     * clipping.
307     * <p>
308     * Upon returning, this method will fill the {@code bbox} parameter
309     * with 4 values indicating the bounds of the iteration of the
310     * tile generator.
311     * The iteration order of the tiles will be as specified by the
312     * pseudo-code:
313     * <pre>
314     *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
315     *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
316     *         }
317     *     }
318     * </pre>
319     * If there is no output to be rendered, this method may return
320     * null.
321     *
322     * @param x the X coordinate of the first corner of the parallelogram
323     * @param y the Y coordinate of the first corner of the parallelogram
324     * @param dx1 the X coordinate delta of the first leg of the parallelogram
325     * @param dy1 the Y coordinate delta of the first leg of the parallelogram
326     * @param dx2 the X coordinate delta of the second leg of the parallelogram
327     * @param dy2 the Y coordinate delta of the second leg of the parallelogram
328     * @param lw1 the line width ratio for the first leg of the parallelogram
329     * @param lw2 the line width ratio for the second leg of the parallelogram
330     * @param clip the current clip in effect in device coordinates
331     * @param bbox returns the bounds of the iteration
332     * @return the {@code AATileGenerator} instance to be consulted
333     *         for tile coverages, or null if there is no output to render
334     * @since 1.7
335     */
336    public abstract AATileGenerator getAATileGenerator(double x, double y,
337                                                       double dx1, double dy1,
338                                                       double dx2, double dy2,
339                                                       double lw1, double lw2,
340                                                       Region clip,
341                                                       int bbox[]);
342
343    /**
344     * Returns the minimum pen width that the antialiasing rasterizer
345     * can represent without dropouts occurring.
346     * @since 1.7
347     */
348    public abstract float getMinimumAAPenSize();
349
350    /**
351     * Utility method to feed a {@link PathConsumer2D} object from a
352     * given {@link PathIterator}.
353     * This method deals with the details of running the iterator and
354     * feeding the consumer a segment at a time.
355     */
356    public static void feedConsumer(PathIterator pi, PathConsumer2D consumer) {
357        float coords[] = new float[6];
358        while (!pi.isDone()) {
359            switch (pi.currentSegment(coords)) {
360            case PathIterator.SEG_MOVETO:
361                consumer.moveTo(coords[0], coords[1]);
362                break;
363            case PathIterator.SEG_LINETO:
364                consumer.lineTo(coords[0], coords[1]);
365                break;
366            case PathIterator.SEG_QUADTO:
367                consumer.quadTo(coords[0], coords[1],
368                                coords[2], coords[3]);
369                break;
370            case PathIterator.SEG_CUBICTO:
371                consumer.curveTo(coords[0], coords[1],
372                                 coords[2], coords[3],
373                                 coords[4], coords[5]);
374                break;
375            case PathIterator.SEG_CLOSE:
376                consumer.closePath();
377                break;
378            }
379            pi.next();
380        }
381    }
382
383    static class Tracer extends RenderingEngine {
384        RenderingEngine target;
385        String name;
386
387        public Tracer(RenderingEngine target) {
388            this.target = target;
389            name = target.getClass().getName();
390        }
391
392        public Shape createStrokedShape(Shape src,
393                                        float width,
394                                        int caps,
395                                        int join,
396                                        float miterlimit,
397                                        float dashes[],
398                                        float dashphase)
399        {
400            System.out.println(name+".createStrokedShape("+
401                               src.getClass().getName()+", "+
402                               "width = "+width+", "+
403                               "caps = "+caps+", "+
404                               "join = "+join+", "+
405                               "miter = "+miterlimit+", "+
406                               "dashes = "+dashes+", "+
407                               "dashphase = "+dashphase+")");
408            return target.createStrokedShape(src,
409                                             width, caps, join, miterlimit,
410                                             dashes, dashphase);
411        }
412
413        public void strokeTo(Shape src,
414                             AffineTransform at,
415                             BasicStroke bs,
416                             boolean thin,
417                             boolean normalize,
418                             boolean antialias,
419                             PathConsumer2D consumer)
420        {
421            System.out.println(name+".strokeTo("+
422                               src.getClass().getName()+", "+
423                               at+", "+
424                               bs+", "+
425                               (thin ? "thin" : "wide")+", "+
426                               (normalize ? "normalized" : "pure")+", "+
427                               (antialias ? "AA" : "non-AA")+", "+
428                               consumer.getClass().getName()+")");
429            target.strokeTo(src, at, bs, thin, normalize, antialias, consumer);
430        }
431
432        public float getMinimumAAPenSize() {
433            System.out.println(name+".getMinimumAAPenSize()");
434            return target.getMinimumAAPenSize();
435        }
436
437        public AATileGenerator getAATileGenerator(Shape s,
438                                                  AffineTransform at,
439                                                  Region clip,
440                                                  BasicStroke bs,
441                                                  boolean thin,
442                                                  boolean normalize,
443                                                  int bbox[])
444        {
445            System.out.println(name+".getAATileGenerator("+
446                               s.getClass().getName()+", "+
447                               at+", "+
448                               clip+", "+
449                               bs+", "+
450                               (thin ? "thin" : "wide")+", "+
451                               (normalize ? "normalized" : "pure")+")");
452            return target.getAATileGenerator(s, at, clip,
453                                             bs, thin, normalize,
454                                             bbox);
455        }
456        public AATileGenerator getAATileGenerator(double x, double y,
457                                                  double dx1, double dy1,
458                                                  double dx2, double dy2,
459                                                  double lw1, double lw2,
460                                                  Region clip,
461                                                  int bbox[])
462        {
463            System.out.println(name+".getAATileGenerator("+
464                               x+", "+y+", "+
465                               dx1+", "+dy1+", "+
466                               dx2+", "+dy2+", "+
467                               lw1+", "+lw2+", "+
468                               clip+")");
469            return target.getAATileGenerator(x, y,
470                                             dx1, dy1,
471                                             dx2, dy2,
472                                             lw1, lw2,
473                                             clip, bbox);
474        }
475    }
476}
477