1 /*
2  * Copyright (c) 2007, 2021, 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         @SuppressWarnings("removal")
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         @SuppressWarnings("removal")
149         String verbose = AccessController.doPrivileged(gpa);
150         if (verbose != null && verbose.startsWith("t")) {
151             System.out.println("RenderingEngine = "+reImpl);
152         }
153 
154         gpa = new GetPropertyAction("sun.java2d.renderer.trace");
155         @SuppressWarnings("removal")
156         String reTrace = AccessController.doPrivileged(gpa);
157         if (reTrace != null) {
158             reImpl = new Tracer(reImpl);
159         }
160 
161         return reImpl;
162     }
163 
164     /**
165      * Create a widened path as specified by the parameters.
166      * <p>
167      * The specified {@code src} {@link Shape} is widened according
168      * to the specified attribute parameters as per the
169      * {@link BasicStroke} specification.
170      *
171      * @param src the source path to be widened
172      * @param width the width of the widened path as per {@code BasicStroke}
173      * @param caps the end cap decorations as per {@code BasicStroke}
174      * @param join the segment join decorations as per {@code BasicStroke}
175      * @param miterlimit the miter limit as per {@code BasicStroke}
176      * @param dashes the dash length array as per {@code BasicStroke}
177      * @param dashphase the initial dash phase as per {@code BasicStroke}
178      * @return the widened path stored in a new {@code Shape} object
179      * @since 1.7
180      */
createStrokedShape(Shape src, float width, int caps, int join, float miterlimit, float[] dashes, float dashphase)181     public abstract Shape createStrokedShape(Shape src,
182                                              float width,
183                                              int caps,
184                                              int join,
185                                              float miterlimit,
186                                              float[] dashes,
187                                              float dashphase);
188 
189     /**
190      * Sends the geometry for a widened path as specified by the parameters
191      * to the specified consumer.
192      * <p>
193      * The specified {@code src} {@link Shape} is widened according
194      * to the parameters specified by the {@link BasicStroke} object.
195      * Adjustments are made to the path as appropriate for the
196      * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
197      * {@code normalize} boolean parameter is true.
198      * Adjustments are made to the path as appropriate for the
199      * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
200      * {@code antialias} boolean parameter is true.
201      * <p>
202      * The geometry of the widened path is forwarded to the indicated
203      * {@link PathConsumer2D} object as it is calculated.
204      *
205      * @param src the source path to be widened
206      * @param at the transform to be applied to the shape and the
207      *           stroke attributes
208      * @param bs the {@code BasicStroke} object specifying the
209      *           decorations to be applied to the widened path
210      * @param thin true if the transformed stroke attributes are smaller
211      *             than the minimum dropout pen width
212      * @param normalize indicates whether stroke normalization should
213      *                  be applied
214      * @param antialias indicates whether or not adjustments appropriate
215      *                  to antialiased rendering should be applied
216      * @param consumer the {@code PathConsumer2D} instance to forward
217      *                 the widened geometry to
218      * @since 1.7
219      */
strokeTo(Shape src, AffineTransform at, BasicStroke bs, boolean thin, boolean normalize, boolean antialias, PathConsumer2D consumer)220     public abstract void strokeTo(Shape src,
221                                   AffineTransform at,
222                                   BasicStroke bs,
223                                   boolean thin,
224                                   boolean normalize,
225                                   boolean antialias,
226                                   PathConsumer2D consumer);
227 
228     /**
229      * Sends the geometry for a widened path as specified by the parameters
230      * to the specified consumer.
231      * <p>
232      * The specified {@code src} {@link Shape} is widened according
233      * to the parameters specified by the {@link BasicStroke} object.
234      * The clip region can be optionally given to let the renderer only
235      * send geometries overlapping the clip region.
236      * Adjustments are made to the path as appropriate for the
237      * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
238      * {@code normalize} boolean parameter is true.
239      * Adjustments are made to the path as appropriate for the
240      * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
241      * {@code antialias} boolean parameter is true.
242      * <p>
243      * The geometry of the widened path is forwarded to the indicated
244      * {@link PathConsumer2D} object as it is calculated.
245      *
246      * @param src the source path to be widened
247      * @param at the transform to be applied to the shape and the
248      *           stroke attributes
249      * @param clip the current clip in effect in device coordinates
250      * @param bs the {@code BasicStroke} object specifying the
251      *           decorations to be applied to the widened path
252      * @param thin true if the transformed stroke attributes are smaller
253      *             than the minimum dropout pen width
254      * @param normalize indicates whether stroke normalization should
255      *                  be applied
256      * @param antialias indicates whether or not adjustments appropriate
257      *                  to antialiased rendering should be applied
258      * @param consumer the {@code PathConsumer2D} instance to forward
259      *                 the widened geometry to
260      * @since 17
261      */
strokeTo(Shape src, AffineTransform at, Region clip, BasicStroke bs, boolean thin, boolean normalize, boolean antialias, final PathConsumer2D consumer)262     public void strokeTo(Shape src,
263                          AffineTransform at,
264                          Region clip,
265                          BasicStroke bs,
266                          boolean thin,
267                          boolean normalize,
268                          boolean antialias,
269                          final PathConsumer2D consumer)
270     {
271         // As default implementation, call the strokeTo() method without the clip region.
272         strokeTo(src, at, bs, thin, normalize, antialias, consumer);
273     }
274 
275     /**
276      * Construct an antialiased tile generator for the given shape with
277      * the given rendering attributes and store the bounds of the tile
278      * iteration in the bbox parameter.
279      * The {@code at} parameter specifies a transform that should affect
280      * both the shape and the {@code BasicStroke} attributes.
281      * The {@code clip} parameter specifies the current clip in effect
282      * in device coordinates and can be used to prune the data for the
283      * operation, but the renderer is not required to perform any
284      * clipping.
285      * If the {@code BasicStroke} parameter is null then the shape
286      * should be filled as is, otherwise the attributes of the
287      * {@code BasicStroke} should be used to specify a draw operation.
288      * The {@code thin} parameter indicates whether or not the
289      * transformed {@code BasicStroke} represents coordinates smaller
290      * than the minimum resolution of the antialiasing rasterizer as
291      * specified by the {@code getMinimumAAPenWidth()} method.
292      * <p>
293      * Upon returning, this method will fill the {@code bbox} parameter
294      * with 4 values indicating the bounds of the iteration of the
295      * tile generator.
296      * The iteration order of the tiles will be as specified by the
297      * pseudo-code:
298      * <pre>
299      *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
300      *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
301      *         }
302      *     }
303      * </pre>
304      * If there is no output to be rendered, this method may return
305      * null.
306      *
307      * @param s the shape to be rendered (fill or draw)
308      * @param at the transform to be applied to the shape and the
309      *           stroke attributes
310      * @param clip the current clip in effect in device coordinates
311      * @param bs if non-null, a {@code BasicStroke} whose attributes
312      *           should be applied to this operation
313      * @param thin true if the transformed stroke attributes are smaller
314      *             than the minimum dropout pen width
315      * @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
316      *                  {@code RenderingHint} is in effect
317      * @param bbox returns the bounds of the iteration
318      * @return the {@code AATileGenerator} instance to be consulted
319      *         for tile coverages, or null if there is no output to render
320      * @since 1.7
321      */
getAATileGenerator(Shape s, AffineTransform at, Region clip, BasicStroke bs, boolean thin, boolean normalize, int[] bbox)322     public abstract AATileGenerator getAATileGenerator(Shape s,
323                                                        AffineTransform at,
324                                                        Region clip,
325                                                        BasicStroke bs,
326                                                        boolean thin,
327                                                        boolean normalize,
328                                                        int[] bbox);
329 
330     /**
331      * Construct an antialiased tile generator for the given parallelogram
332      * store the bounds of the tile iteration in the bbox parameter.
333      * The parallelogram is specified as a starting point and 2 delta
334      * vectors that indicate the slopes of the 2 pairs of sides of the
335      * parallelogram.
336      * The 4 corners of the parallelogram are defined by the 4 points:
337      * <ul>
338      * <li> {@code x}, {@code y}
339      * <li> {@code x+dx1}, {@code y+dy1}
340      * <li> {@code x+dx1+dx2}, {@code y+dy1+dy2}
341      * <li> {@code x+dx2}, {@code y+dy2}
342      * </ul>
343      * The {@code lw1} and {@code lw2} parameters provide a specification
344      * for an optionally stroked parallelogram if they are positive numbers.
345      * The {@code lw1} parameter is the ratio of the length of the {@code dx1},
346      * {@code dx2} delta vector to half of the line width in that same
347      * direction.
348      * The {@code lw2} parameter provides the same ratio for the other delta
349      * vector.
350      * If {@code lw1} and {@code lw2} are both greater than zero, then
351      * the parallelogram figure is doubled by both expanding and contracting
352      * each delta vector by its corresponding {@code lw} value.
353      * If either {@code lw1} or {@code lw2} are also greater than 1, then
354      * the inner (contracted) parallelogram disappears and the figure is
355      * simply a single expanded parallelogram.
356      * The {@code clip} parameter specifies the current clip in effect
357      * in device coordinates and can be used to prune the data for the
358      * operation, but the renderer is not required to perform any
359      * clipping.
360      * <p>
361      * Upon returning, this method will fill the {@code bbox} parameter
362      * with 4 values indicating the bounds of the iteration of the
363      * tile generator.
364      * The iteration order of the tiles will be as specified by the
365      * pseudo-code:
366      * <pre>
367      *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
368      *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
369      *         }
370      *     }
371      * </pre>
372      * If there is no output to be rendered, this method may return
373      * null.
374      *
375      * @param x the X coordinate of the first corner of the parallelogram
376      * @param y the Y coordinate of the first corner of the parallelogram
377      * @param dx1 the X coordinate delta of the first leg of the parallelogram
378      * @param dy1 the Y coordinate delta of the first leg of the parallelogram
379      * @param dx2 the X coordinate delta of the second leg of the parallelogram
380      * @param dy2 the Y coordinate delta of the second leg of the parallelogram
381      * @param lw1 the line width ratio for the first leg of the parallelogram
382      * @param lw2 the line width ratio for the second leg of the parallelogram
383      * @param clip the current clip in effect in device coordinates
384      * @param bbox returns the bounds of the iteration
385      * @return the {@code AATileGenerator} instance to be consulted
386      *         for tile coverages, or null if there is no output to render
387      * @since 1.7
388      */
getAATileGenerator(double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2, Region clip, int[] bbox)389     public abstract AATileGenerator getAATileGenerator(double x, double y,
390                                                        double dx1, double dy1,
391                                                        double dx2, double dy2,
392                                                        double lw1, double lw2,
393                                                        Region clip,
394                                                        int[] bbox);
395 
396     /**
397      * Returns the minimum pen width that the antialiasing rasterizer
398      * can represent without dropouts occurring.
399      * @since 1.7
400      */
getMinimumAAPenSize()401     public abstract float getMinimumAAPenSize();
402 
403     /**
404      * Utility method to feed a {@link PathConsumer2D} object from a
405      * given {@link PathIterator}.
406      * This method deals with the details of running the iterator and
407      * feeding the consumer a segment at a time.
408      */
feedConsumer(PathIterator pi, PathConsumer2D consumer)409     public static void feedConsumer(PathIterator pi, PathConsumer2D consumer) {
410         float[] coords = new float[6];
411         while (!pi.isDone()) {
412             switch (pi.currentSegment(coords)) {
413             case PathIterator.SEG_MOVETO:
414                 consumer.moveTo(coords[0], coords[1]);
415                 break;
416             case PathIterator.SEG_LINETO:
417                 consumer.lineTo(coords[0], coords[1]);
418                 break;
419             case PathIterator.SEG_QUADTO:
420                 consumer.quadTo(coords[0], coords[1],
421                                 coords[2], coords[3]);
422                 break;
423             case PathIterator.SEG_CUBICTO:
424                 consumer.curveTo(coords[0], coords[1],
425                                  coords[2], coords[3],
426                                  coords[4], coords[5]);
427                 break;
428             case PathIterator.SEG_CLOSE:
429                 consumer.closePath();
430                 break;
431             }
432             pi.next();
433         }
434     }
435 
436     static class Tracer extends RenderingEngine {
437         RenderingEngine target;
438         String name;
439 
Tracer(RenderingEngine target)440         public Tracer(RenderingEngine target) {
441             this.target = target;
442             name = target.getClass().getName();
443         }
444 
createStrokedShape(Shape src, float width, int caps, int join, float miterlimit, float[] dashes, float dashphase)445         public Shape createStrokedShape(Shape src,
446                                         float width,
447                                         int caps,
448                                         int join,
449                                         float miterlimit,
450                                         float[] dashes,
451                                         float dashphase)
452         {
453             System.out.println(name+".createStrokedShape("+
454                                src.getClass().getName()+", "+
455                                "width = "+width+", "+
456                                "caps = "+caps+", "+
457                                "join = "+join+", "+
458                                "miter = "+miterlimit+", "+
459                                "dashes = "+dashes+", "+
460                                "dashphase = "+dashphase+")");
461             return target.createStrokedShape(src,
462                                              width, caps, join, miterlimit,
463                                              dashes, dashphase);
464         }
465 
strokeTo(Shape src, AffineTransform at, BasicStroke bs, boolean thin, boolean normalize, boolean antialias, PathConsumer2D consumer)466         public void strokeTo(Shape src,
467                              AffineTransform at,
468                              BasicStroke bs,
469                              boolean thin,
470                              boolean normalize,
471                              boolean antialias,
472                              PathConsumer2D consumer)
473         {
474             System.out.println(name+".strokeTo("+
475                                src.getClass().getName()+", "+
476                                at+", "+
477                                bs+", "+
478                                (thin ? "thin" : "wide")+", "+
479                                (normalize ? "normalized" : "pure")+", "+
480                                (antialias ? "AA" : "non-AA")+", "+
481                                consumer.getClass().getName()+")");
482             target.strokeTo(src, at, bs, thin, normalize, antialias, consumer);
483         }
484 
strokeTo(Shape src, AffineTransform at, Region clip, BasicStroke bs, boolean thin, boolean normalize, boolean antialias, PathConsumer2D consumer)485         public void strokeTo(Shape src,
486                              AffineTransform at,
487                              Region clip,
488                              BasicStroke bs,
489                              boolean thin,
490                              boolean normalize,
491                              boolean antialias,
492                              PathConsumer2D consumer)
493         {
494             System.out.println(name+".strokeTo("+
495                                src.getClass().getName()+", "+
496                                at+", "+
497                                clip+", "+
498                                bs+", "+
499                                (thin ? "thin" : "wide")+", "+
500                                (normalize ? "normalized" : "pure")+", "+
501                                (antialias ? "AA" : "non-AA")+", "+
502                                consumer.getClass().getName()+")");
503             target.strokeTo(src, at, clip, bs, thin, normalize, antialias, consumer);
504         }
505 
506 
getMinimumAAPenSize()507         public float getMinimumAAPenSize() {
508             System.out.println(name+".getMinimumAAPenSize()");
509             return target.getMinimumAAPenSize();
510         }
511 
getAATileGenerator(Shape s, AffineTransform at, Region clip, BasicStroke bs, boolean thin, boolean normalize, int[] bbox)512         public AATileGenerator getAATileGenerator(Shape s,
513                                                   AffineTransform at,
514                                                   Region clip,
515                                                   BasicStroke bs,
516                                                   boolean thin,
517                                                   boolean normalize,
518                                                   int[] bbox)
519         {
520             System.out.println(name+".getAATileGenerator("+
521                                s.getClass().getName()+", "+
522                                at+", "+
523                                clip+", "+
524                                bs+", "+
525                                (thin ? "thin" : "wide")+", "+
526                                (normalize ? "normalized" : "pure")+")");
527             return target.getAATileGenerator(s, at, clip,
528                                              bs, thin, normalize,
529                                              bbox);
530         }
getAATileGenerator(double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2, Region clip, int[] bbox)531         public AATileGenerator getAATileGenerator(double x, double y,
532                                                   double dx1, double dy1,
533                                                   double dx2, double dy2,
534                                                   double lw1, double lw2,
535                                                   Region clip,
536                                                   int[] bbox)
537         {
538             System.out.println(name+".getAATileGenerator("+
539                                x+", "+y+", "+
540                                dx1+", "+dy1+", "+
541                                dx2+", "+dy2+", "+
542                                lw1+", "+lw2+", "+
543                                clip+")");
544             return target.getAATileGenerator(x, y,
545                                              dx1, dy1,
546                                              dx2, dy2,
547                                              lw1, lw2,
548                                              clip, bbox);
549         }
550     }
551 }
552