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=<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 @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