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