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