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=<classname> 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=<any string> 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