Path: blob/master/src/java.desktop/share/classes/sun/java2d/pipe/RenderingEngine.java
41159 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.pipe;2627import java.awt.Shape;28import java.awt.BasicStroke;29import java.awt.geom.PathIterator;30import java.awt.geom.AffineTransform;3132import java.security.AccessController;33import sun.security.action.GetPropertyAction;3435import sun.awt.geom.PathConsumer2D;3637/**38* This class abstracts a number of features for which the Java 2D39* implementation relies on proprietary licensed software libraries.40* Access to those features is now achieved by retrieving the singleton41* instance of this class and calling the appropriate methods on it.42* The 3 primary features abstracted here include:43* <dl>44* <dt>Shape createStrokedShape(Shape, [BasicStroke attributes]);45* <dd>This method implements the functionality of the method of the46* same name on the {@link BasicStroke} class.47* <dt>void strokeTo(Shape, [rendering parameters], PathConsumer2D);48* <dd>This method performs widening of the source path on the fly49* and sends the results to the given {@link PathConsumer2D} object.50* This procedure avoids having to create an intermediate Shape51* object to hold the results of the {@code createStrokedShape} method.52* The main user of this method is the Java 2D non-antialiasing renderer.53* <dt>AATileGenerator getAATileGenerator(Shape, [rendering parameters]);54* <dd>This method returns an object which can iterate over the55* specified bounding box and produce tiles of coverage values for56* antialiased rendering. The details of the operation of the57* {@link AATileGenerator} object are explained in its class comments.58* </dl>59* Additionally, the following informational method supplies important60* data about the implementation.61* <dl>62* <dt>float getMinimumAAPenSize()63* <dd>This method provides information on how small the BasicStroke64* line width can get before dropouts occur. Rendering with a BasicStroke65* is defined to never allow the line to have breaks, gaps, or dropouts66* even if the width is set to 0.0f, so this information allows the67* {@link sun.java2d.SunGraphics2D} class to detect the "thin line" case and set68* the rendering attributes accordingly.69* </dl>70* At startup the runtime will load a single instance of this class.71* It searches the classpath for a registered provider of this API72* and returns either the last one it finds, or the instance whose73* class name matches the value supplied in the System property74* {@code sun.java2d.renderer}.75* Additionally, a runtime System property flag can be set to trace76* all calls to methods on the {@code RenderingEngine} in use by77* setting the sun.java2d.renderer.trace property to any non-null value.78* <p>79* Parts of the system that need to use any of the above features should80* call {@code RenderingEngine.getInstance()} to obtain the properly81* registered (and possibly trace-enabled) version of the RenderingEngine.82*/83public abstract class RenderingEngine {84private static RenderingEngine reImpl;8586/**87* Returns an instance of {@code RenderingEngine} as determined88* by the installation environment and runtime flags.89* <p>90* A specific instance of the {@code RenderingEngine} can be91* chosen by specifying the runtime flag:92* <pre>93* java -Dsun.java2d.renderer=<classname>94* </pre>95*96* If no specific {@code RenderingEngine} is specified on the command97* line or the requested class fails to load, then the Marlin98* renderer will be used as the default.99* <p>100* A printout of which RenderingEngine is loaded and used can be101* enabled by specifying the runtime flag:102* <pre>103* java -Dsun.java2d.renderer.verbose=true104* </pre>105* <p>106* Runtime tracing of the actions of the {@code RenderingEngine}107* can be enabled by specifying the runtime flag:108* <pre>109* java -Dsun.java2d.renderer.trace=<any string>110* </pre>111* @return an instance of {@code RenderingEngine}112* @since 1.7113*/114public static synchronized RenderingEngine getInstance() {115if (reImpl != null) {116return reImpl;117}118119/* Look first for an app-override renderer,120* if not specified or present, then look for marlin.121*/122GetPropertyAction gpa =123new GetPropertyAction("sun.java2d.renderer");124@SuppressWarnings("removal")125String reClass = AccessController.doPrivileged(gpa);126if (reClass != null) {127try {128Class<?> cls = Class.forName(reClass);129reImpl = (RenderingEngine) cls.getConstructor().newInstance();130} catch (ReflectiveOperationException ignored0) {131}132}133if (reImpl == null) {134final String marlinREClass = "sun.java2d.marlin.DMarlinRenderingEngine";135try {136Class<?> cls = Class.forName(marlinREClass);137reImpl = (RenderingEngine) cls.getConstructor().newInstance();138} catch (ReflectiveOperationException ignored1) {139}140}141142if (reImpl == null) {143throw new InternalError("No RenderingEngine module found");144}145146gpa = new GetPropertyAction("sun.java2d.renderer.verbose");147@SuppressWarnings("removal")148String verbose = AccessController.doPrivileged(gpa);149if (verbose != null && verbose.startsWith("t")) {150System.out.println("RenderingEngine = "+reImpl);151}152153gpa = new GetPropertyAction("sun.java2d.renderer.trace");154@SuppressWarnings("removal")155String reTrace = AccessController.doPrivileged(gpa);156if (reTrace != null) {157reImpl = new Tracer(reImpl);158}159160return reImpl;161}162163/**164* Create a widened path as specified by the parameters.165* <p>166* The specified {@code src} {@link Shape} is widened according167* to the specified attribute parameters as per the168* {@link BasicStroke} specification.169*170* @param src the source path to be widened171* @param width the width of the widened path as per {@code BasicStroke}172* @param caps the end cap decorations as per {@code BasicStroke}173* @param join the segment join decorations as per {@code BasicStroke}174* @param miterlimit the miter limit as per {@code BasicStroke}175* @param dashes the dash length array as per {@code BasicStroke}176* @param dashphase the initial dash phase as per {@code BasicStroke}177* @return the widened path stored in a new {@code Shape} object178* @since 1.7179*/180public abstract Shape createStrokedShape(Shape src,181float width,182int caps,183int join,184float miterlimit,185float[] dashes,186float dashphase);187188/**189* Sends the geometry for a widened path as specified by the parameters190* to the specified consumer.191* <p>192* The specified {@code src} {@link Shape} is widened according193* to the parameters specified by the {@link BasicStroke} object.194* Adjustments are made to the path as appropriate for the195* {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the196* {@code normalize} boolean parameter is true.197* Adjustments are made to the path as appropriate for the198* {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the199* {@code antialias} boolean parameter is true.200* <p>201* The geometry of the widened path is forwarded to the indicated202* {@link PathConsumer2D} object as it is calculated.203*204* @param src the source path to be widened205* @param at the transform to be applied to the shape and the206* stroke attributes207* @param bs the {@code BasicStroke} object specifying the208* decorations to be applied to the widened path209* @param thin true if the transformed stroke attributes are smaller210* than the minimum dropout pen width211* @param normalize indicates whether stroke normalization should212* be applied213* @param antialias indicates whether or not adjustments appropriate214* to antialiased rendering should be applied215* @param consumer the {@code PathConsumer2D} instance to forward216* the widened geometry to217* @since 1.7218*/219public abstract void strokeTo(Shape src,220AffineTransform at,221BasicStroke bs,222boolean thin,223boolean normalize,224boolean antialias,225PathConsumer2D consumer);226227/**228* Sends the geometry for a widened path as specified by the parameters229* to the specified consumer.230* <p>231* The specified {@code src} {@link Shape} is widened according232* to the parameters specified by the {@link BasicStroke} object.233* The clip region can be optionally given to let the renderer only234* send geometries overlapping the clip region.235* Adjustments are made to the path as appropriate for the236* {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the237* {@code normalize} boolean parameter is true.238* Adjustments are made to the path as appropriate for the239* {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the240* {@code antialias} boolean parameter is true.241* <p>242* The geometry of the widened path is forwarded to the indicated243* {@link PathConsumer2D} object as it is calculated.244*245* @param src the source path to be widened246* @param at the transform to be applied to the shape and the247* stroke attributes248* @param clip the current clip in effect in device coordinates249* @param bs the {@code BasicStroke} object specifying the250* decorations to be applied to the widened path251* @param thin true if the transformed stroke attributes are smaller252* than the minimum dropout pen width253* @param normalize indicates whether stroke normalization should254* be applied255* @param antialias indicates whether or not adjustments appropriate256* to antialiased rendering should be applied257* @param consumer the {@code PathConsumer2D} instance to forward258* the widened geometry to259* @since 17260*/261public void strokeTo(Shape src,262AffineTransform at,263Region clip,264BasicStroke bs,265boolean thin,266boolean normalize,267boolean antialias,268final PathConsumer2D consumer)269{270// As default implementation, call the strokeTo() method without the clip region.271strokeTo(src, at, bs, thin, normalize, antialias, consumer);272}273274/**275* Construct an antialiased tile generator for the given shape with276* the given rendering attributes and store the bounds of the tile277* iteration in the bbox parameter.278* The {@code at} parameter specifies a transform that should affect279* both the shape and the {@code BasicStroke} attributes.280* The {@code clip} parameter specifies the current clip in effect281* in device coordinates and can be used to prune the data for the282* operation, but the renderer is not required to perform any283* clipping.284* If the {@code BasicStroke} parameter is null then the shape285* should be filled as is, otherwise the attributes of the286* {@code BasicStroke} should be used to specify a draw operation.287* The {@code thin} parameter indicates whether or not the288* transformed {@code BasicStroke} represents coordinates smaller289* than the minimum resolution of the antialiasing rasterizer as290* specified by the {@code getMinimumAAPenWidth()} method.291* <p>292* Upon returning, this method will fill the {@code bbox} parameter293* with 4 values indicating the bounds of the iteration of the294* tile generator.295* The iteration order of the tiles will be as specified by the296* pseudo-code:297* <pre>298* for (y = bbox[1]; y < bbox[3]; y += tileheight) {299* for (x = bbox[0]; x < bbox[2]; x += tilewidth) {300* }301* }302* </pre>303* If there is no output to be rendered, this method may return304* null.305*306* @param s the shape to be rendered (fill or draw)307* @param at the transform to be applied to the shape and the308* stroke attributes309* @param clip the current clip in effect in device coordinates310* @param bs if non-null, a {@code BasicStroke} whose attributes311* should be applied to this operation312* @param thin true if the transformed stroke attributes are smaller313* than the minimum dropout pen width314* @param normalize true if the {@code VALUE_STROKE_NORMALIZE}315* {@code RenderingHint} is in effect316* @param bbox returns the bounds of the iteration317* @return the {@code AATileGenerator} instance to be consulted318* for tile coverages, or null if there is no output to render319* @since 1.7320*/321public abstract AATileGenerator getAATileGenerator(Shape s,322AffineTransform at,323Region clip,324BasicStroke bs,325boolean thin,326boolean normalize,327int[] bbox);328329/**330* Construct an antialiased tile generator for the given parallelogram331* store the bounds of the tile iteration in the bbox parameter.332* The parallelogram is specified as a starting point and 2 delta333* vectors that indicate the slopes of the 2 pairs of sides of the334* parallelogram.335* The 4 corners of the parallelogram are defined by the 4 points:336* <ul>337* <li> {@code x}, {@code y}338* <li> {@code x+dx1}, {@code y+dy1}339* <li> {@code x+dx1+dx2}, {@code y+dy1+dy2}340* <li> {@code x+dx2}, {@code y+dy2}341* </ul>342* The {@code lw1} and {@code lw2} parameters provide a specification343* for an optionally stroked parallelogram if they are positive numbers.344* The {@code lw1} parameter is the ratio of the length of the {@code dx1},345* {@code dx2} delta vector to half of the line width in that same346* direction.347* The {@code lw2} parameter provides the same ratio for the other delta348* vector.349* If {@code lw1} and {@code lw2} are both greater than zero, then350* the parallelogram figure is doubled by both expanding and contracting351* each delta vector by its corresponding {@code lw} value.352* If either {@code lw1} or {@code lw2} are also greater than 1, then353* the inner (contracted) parallelogram disappears and the figure is354* simply a single expanded parallelogram.355* The {@code clip} parameter specifies the current clip in effect356* in device coordinates and can be used to prune the data for the357* operation, but the renderer is not required to perform any358* clipping.359* <p>360* Upon returning, this method will fill the {@code bbox} parameter361* with 4 values indicating the bounds of the iteration of the362* tile generator.363* The iteration order of the tiles will be as specified by the364* pseudo-code:365* <pre>366* for (y = bbox[1]; y < bbox[3]; y += tileheight) {367* for (x = bbox[0]; x < bbox[2]; x += tilewidth) {368* }369* }370* </pre>371* If there is no output to be rendered, this method may return372* null.373*374* @param x the X coordinate of the first corner of the parallelogram375* @param y the Y coordinate of the first corner of the parallelogram376* @param dx1 the X coordinate delta of the first leg of the parallelogram377* @param dy1 the Y coordinate delta of the first leg of the parallelogram378* @param dx2 the X coordinate delta of the second leg of the parallelogram379* @param dy2 the Y coordinate delta of the second leg of the parallelogram380* @param lw1 the line width ratio for the first leg of the parallelogram381* @param lw2 the line width ratio for the second leg of the parallelogram382* @param clip the current clip in effect in device coordinates383* @param bbox returns the bounds of the iteration384* @return the {@code AATileGenerator} instance to be consulted385* for tile coverages, or null if there is no output to render386* @since 1.7387*/388public abstract AATileGenerator getAATileGenerator(double x, double y,389double dx1, double dy1,390double dx2, double dy2,391double lw1, double lw2,392Region clip,393int[] bbox);394395/**396* Returns the minimum pen width that the antialiasing rasterizer397* can represent without dropouts occurring.398* @since 1.7399*/400public abstract float getMinimumAAPenSize();401402/**403* Utility method to feed a {@link PathConsumer2D} object from a404* given {@link PathIterator}.405* This method deals with the details of running the iterator and406* feeding the consumer a segment at a time.407*/408public static void feedConsumer(PathIterator pi, PathConsumer2D consumer) {409float[] coords = new float[6];410while (!pi.isDone()) {411switch (pi.currentSegment(coords)) {412case PathIterator.SEG_MOVETO:413consumer.moveTo(coords[0], coords[1]);414break;415case PathIterator.SEG_LINETO:416consumer.lineTo(coords[0], coords[1]);417break;418case PathIterator.SEG_QUADTO:419consumer.quadTo(coords[0], coords[1],420coords[2], coords[3]);421break;422case PathIterator.SEG_CUBICTO:423consumer.curveTo(coords[0], coords[1],424coords[2], coords[3],425coords[4], coords[5]);426break;427case PathIterator.SEG_CLOSE:428consumer.closePath();429break;430}431pi.next();432}433}434435static class Tracer extends RenderingEngine {436RenderingEngine target;437String name;438439public Tracer(RenderingEngine target) {440this.target = target;441name = target.getClass().getName();442}443444public Shape createStrokedShape(Shape src,445float width,446int caps,447int join,448float miterlimit,449float[] dashes,450float dashphase)451{452System.out.println(name+".createStrokedShape("+453src.getClass().getName()+", "+454"width = "+width+", "+455"caps = "+caps+", "+456"join = "+join+", "+457"miter = "+miterlimit+", "+458"dashes = "+dashes+", "+459"dashphase = "+dashphase+")");460return target.createStrokedShape(src,461width, caps, join, miterlimit,462dashes, dashphase);463}464465public void strokeTo(Shape src,466AffineTransform at,467BasicStroke bs,468boolean thin,469boolean normalize,470boolean antialias,471PathConsumer2D consumer)472{473System.out.println(name+".strokeTo("+474src.getClass().getName()+", "+475at+", "+476bs+", "+477(thin ? "thin" : "wide")+", "+478(normalize ? "normalized" : "pure")+", "+479(antialias ? "AA" : "non-AA")+", "+480consumer.getClass().getName()+")");481target.strokeTo(src, at, bs, thin, normalize, antialias, consumer);482}483484public void strokeTo(Shape src,485AffineTransform at,486Region clip,487BasicStroke bs,488boolean thin,489boolean normalize,490boolean antialias,491PathConsumer2D consumer)492{493System.out.println(name+".strokeTo("+494src.getClass().getName()+", "+495at+", "+496clip+", "+497bs+", "+498(thin ? "thin" : "wide")+", "+499(normalize ? "normalized" : "pure")+", "+500(antialias ? "AA" : "non-AA")+", "+501consumer.getClass().getName()+")");502target.strokeTo(src, at, clip, bs, thin, normalize, antialias, consumer);503}504505506public float getMinimumAAPenSize() {507System.out.println(name+".getMinimumAAPenSize()");508return target.getMinimumAAPenSize();509}510511public AATileGenerator getAATileGenerator(Shape s,512AffineTransform at,513Region clip,514BasicStroke bs,515boolean thin,516boolean normalize,517int[] bbox)518{519System.out.println(name+".getAATileGenerator("+520s.getClass().getName()+", "+521at+", "+522clip+", "+523bs+", "+524(thin ? "thin" : "wide")+", "+525(normalize ? "normalized" : "pure")+")");526return target.getAATileGenerator(s, at, clip,527bs, thin, normalize,528bbox);529}530public AATileGenerator getAATileGenerator(double x, double y,531double dx1, double dy1,532double dx2, double dy2,533double lw1, double lw2,534Region clip,535int[] bbox)536{537System.out.println(name+".getAATileGenerator("+538x+", "+y+", "+539dx1+", "+dy1+", "+540dx2+", "+dy2+", "+541lw1+", "+lw2+", "+542clip+")");543return target.getAATileGenerator(x, y,544dx1, dy1,545dx2, dy2,546lw1, lw2,547clip, bbox);548}549}550}551552553