Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java
38918 views
/*1* Copyright (c) 2007, 2016, 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.marlin;2627import java.awt.BasicStroke;28import java.awt.Shape;29import java.awt.geom.AffineTransform;30import java.awt.geom.Path2D;31import java.awt.geom.PathIterator;32import java.security.AccessController;33import static sun.java2d.marlin.MarlinUtils.logInfo;34import sun.awt.geom.PathConsumer2D;35import sun.java2d.ReentrantContextProvider;36import sun.java2d.ReentrantContextProviderCLQ;37import sun.java2d.ReentrantContextProviderTL;38import sun.java2d.pipe.AATileGenerator;39import sun.java2d.pipe.Region;40import sun.java2d.pipe.RenderingEngine;41import sun.security.action.GetPropertyAction;4243/**44* Marlin RendererEngine implementation (derived from Pisces)45*/46public class MarlinRenderingEngine extends RenderingEngine47implements MarlinConst48{49private static enum NormMode {ON_WITH_AA, ON_NO_AA, OFF}5051private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;5253/**54* Public constructor55*/56public MarlinRenderingEngine() {57super();58logSettings(MarlinRenderingEngine.class.getName());59}6061/**62* Create a widened path as specified by the parameters.63* <p>64* The specified {@code src} {@link Shape} is widened according65* to the specified attribute parameters as per the66* {@link BasicStroke} specification.67*68* @param src the source path to be widened69* @param width the width of the widened path as per {@code BasicStroke}70* @param caps the end cap decorations as per {@code BasicStroke}71* @param join the segment join decorations as per {@code BasicStroke}72* @param miterlimit the miter limit as per {@code BasicStroke}73* @param dashes the dash length array as per {@code BasicStroke}74* @param dashphase the initial dash phase as per {@code BasicStroke}75* @return the widened path stored in a new {@code Shape} object76* @since 1.777*/78@Override79public Shape createStrokedShape(Shape src,80float width,81int caps,82int join,83float miterlimit,84float dashes[],85float dashphase)86{87final RendererContext rdrCtx = getRendererContext();88try {89// initialize a large copyable Path2D to avoid a lot of array growing:90final Path2D.Float p2d =91(rdrCtx.p2d == null) ?92(rdrCtx.p2d = new Path2D.Float(Path2D.WIND_NON_ZERO,93INITIAL_MEDIUM_ARRAY))94: rdrCtx.p2d;95// reset96p2d.reset();9798strokeTo(rdrCtx,99src,100null,101width,102NormMode.OFF,103caps,104join,105miterlimit,106dashes,107dashphase,108rdrCtx.transformerPC2D.wrapPath2d(p2d)109);110111// Use Path2D copy constructor (trim)112return new Path2D.Float(p2d);113114} finally {115// recycle the RendererContext instance116returnRendererContext(rdrCtx);117}118}119120/**121* Sends the geometry for a widened path as specified by the parameters122* to the specified consumer.123* <p>124* The specified {@code src} {@link Shape} is widened according125* to the parameters specified by the {@link BasicStroke} object.126* Adjustments are made to the path as appropriate for the127* {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize}128* boolean parameter is true.129* Adjustments are made to the path as appropriate for the130* {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias}131* boolean parameter is true.132* <p>133* The geometry of the widened path is forwarded to the indicated134* {@link PathConsumer2D} object as it is calculated.135*136* @param src the source path to be widened137* @param bs the {@code BasicSroke} object specifying the138* decorations to be applied to the widened path139* @param normalize indicates whether stroke normalization should140* be applied141* @param antialias indicates whether or not adjustments appropriate142* to antialiased rendering should be applied143* @param consumer the {@code PathConsumer2D} instance to forward144* the widened geometry to145* @since 1.7146*/147@Override148public void strokeTo(Shape src,149AffineTransform at,150BasicStroke bs,151boolean thin,152boolean normalize,153boolean antialias,154final PathConsumer2D consumer)155{156final NormMode norm = (normalize) ?157((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)158: NormMode.OFF;159160final RendererContext rdrCtx = getRendererContext();161try {162strokeTo(rdrCtx, src, at, bs, thin, norm, antialias, consumer);163} finally {164// recycle the RendererContext instance165returnRendererContext(rdrCtx);166}167}168169final void strokeTo(final RendererContext rdrCtx,170Shape src,171AffineTransform at,172BasicStroke bs,173boolean thin,174NormMode normalize,175boolean antialias,176PathConsumer2D pc2d)177{178float lw;179if (thin) {180if (antialias) {181lw = userSpaceLineWidth(at, MIN_PEN_SIZE);182} else {183lw = userSpaceLineWidth(at, 1.0f);184}185} else {186lw = bs.getLineWidth();187}188strokeTo(rdrCtx,189src,190at,191lw,192normalize,193bs.getEndCap(),194bs.getLineJoin(),195bs.getMiterLimit(),196bs.getDashArray(),197bs.getDashPhase(),198pc2d);199}200201private final float userSpaceLineWidth(AffineTransform at, float lw) {202203float widthScale;204205if (at == null) {206widthScale = 1.0f;207} else if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM |208AffineTransform.TYPE_GENERAL_SCALE)) != 0) {209widthScale = (float)Math.sqrt(at.getDeterminant());210} else {211// First calculate the "maximum scale" of this transform.212double A = at.getScaleX(); // m00213double C = at.getShearX(); // m01214double B = at.getShearY(); // m10215double D = at.getScaleY(); // m11216217/*218* Given a 2 x 2 affine matrix [ A B ] such that219* [ C D ]220* v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to221* find the maximum magnitude (norm) of the vector v'222* with the constraint (x^2 + y^2 = 1).223* The equation to maximize is224* |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)225* or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).226* Since sqrt is monotonic we can maximize |v'|^2227* instead and plug in the substitution y = sqrt(1 - x^2).228* Trigonometric equalities can then be used to get229* rid of most of the sqrt terms.230*/231232double EA = A*A + B*B; // x^2 coefficient233double EB = 2.0*(A*C + B*D); // xy coefficient234double EC = C*C + D*D; // y^2 coefficient235236/*237* There is a lot of calculus omitted here.238*239* Conceptually, in the interests of understanding the240* terms that the calculus produced we can consider241* that EA and EC end up providing the lengths along242* the major axes and the hypot term ends up being an243* adjustment for the additional length along the off-axis244* angle of rotated or sheared ellipses as well as an245* adjustment for the fact that the equation below246* averages the two major axis lengths. (Notice that247* the hypot term contains a part which resolves to the248* difference of these two axis lengths in the absence249* of rotation.)250*251* In the calculus, the ratio of the EB and (EA-EC) terms252* ends up being the tangent of 2*theta where theta is253* the angle that the long axis of the ellipse makes254* with the horizontal axis. Thus, this equation is255* calculating the length of the hypotenuse of a triangle256* along that axis.257*/258259double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));260// sqrt omitted, compare to squared limits below.261double widthsquared = ((EA + EC + hypot)/2.0);262263widthScale = (float)Math.sqrt(widthsquared);264}265266return (lw / widthScale);267}268269final void strokeTo(final RendererContext rdrCtx,270Shape src,271AffineTransform at,272float width,273NormMode normalize,274int caps,275int join,276float miterlimit,277float dashes[],278float dashphase,279PathConsumer2D pc2d)280{281// We use strokerat and outat so that in Stroker and Dasher we can work only282// with the pre-transformation coordinates. This will repeat a lot of283// computations done in the path iterator, but the alternative is to284// work with transformed paths and compute untransformed coordinates285// as needed. This would be faster but I do not think the complexity286// of working with both untransformed and transformed coordinates in287// the same code is worth it.288// However, if a path's width is constant after a transformation,289// we can skip all this untransforming.290291// If normalization is off we save some transformations by not292// transforming the input to pisces. Instead, we apply the293// transformation after the path processing has been done.294// We can't do this if normalization is on, because it isn't a good295// idea to normalize before the transformation is applied.296AffineTransform strokerat = null;297AffineTransform outat = null;298299PathIterator pi;300int dashLen = -1;301boolean recycleDashes = false;302303if (at != null && !at.isIdentity()) {304final double a = at.getScaleX();305final double b = at.getShearX();306final double c = at.getShearY();307final double d = at.getScaleY();308final double det = a * d - c * b;309310if (Math.abs(det) <= (2f * Float.MIN_VALUE)) {311// this rendering engine takes one dimensional curves and turns312// them into 2D shapes by giving them width.313// However, if everything is to be passed through a singular314// transformation, these 2D shapes will be squashed down to 1D315// again so, nothing can be drawn.316317// Every path needs an initial moveTo and a pathDone. If these318// are not there this causes a SIGSEGV in libawt.so (at the time319// of writing of this comment (September 16, 2010)). Actually,320// I am not sure if the moveTo is necessary to avoid the SIGSEGV321// but the pathDone is definitely needed.322pc2d.moveTo(0f, 0f);323pc2d.pathDone();324return;325}326327// If the transform is a constant multiple of an orthogonal transformation328// then every length is just multiplied by a constant, so we just329// need to transform input paths to stroker and tell stroker330// the scaled width. This condition is satisfied if331// a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we332// leave a bit of room for error.333if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {334final float scale = (float) Math.sqrt(a*a + c*c);335if (dashes != null) {336recycleDashes = true;337dashLen = dashes.length;338final float[] newDashes;339if (dashLen <= INITIAL_ARRAY) {340newDashes = rdrCtx.dasher.dashes_initial;341} else {342if (doStats) {343RendererContext.stats.stat_array_dasher_dasher344.add(dashLen);345}346newDashes = rdrCtx.getDirtyFloatArray(dashLen);347}348System.arraycopy(dashes, 0, newDashes, 0, dashLen);349dashes = newDashes;350for (int i = 0; i < dashLen; i++) {351dashes[i] = scale * dashes[i];352}353dashphase = scale * dashphase;354}355width = scale * width;356pi = getNormalizingPathIterator(rdrCtx, normalize,357src.getPathIterator(at));358359// by now strokerat == null && outat == null. Input paths to360// stroker (and maybe dasher) will have the full transform at361// applied to them and nothing will happen to the output paths.362} else {363if (normalize != NormMode.OFF) {364strokerat = at;365pi = getNormalizingPathIterator(rdrCtx, normalize,366src.getPathIterator(at));367368// by now strokerat == at && outat == null. Input paths to369// stroker (and maybe dasher) will have the full transform at370// applied to them, then they will be normalized, and then371// the inverse of *only the non translation part of at* will372// be applied to the normalized paths. This won't cause problems373// in stroker, because, suppose at = T*A, where T is just the374// translation part of at, and A is the rest. T*A has already375// been applied to Stroker/Dasher's input. Then Ainv will be376// applied. Ainv*T*A is not equal to T, but it is a translation,377// which means that none of stroker's assumptions about its378// input will be violated. After all this, A will be applied379// to stroker's output.380} else {381outat = at;382pi = src.getPathIterator(null);383// outat == at && strokerat == null. This is because if no384// normalization is done, we can just apply all our385// transformations to stroker's output.386}387}388} else {389// either at is null or it's the identity. In either case390// we don't transform the path.391pi = getNormalizingPathIterator(rdrCtx, normalize,392src.getPathIterator(null));393}394395if (useSimplifier) {396// Use simplifier after stroker before Renderer397// to remove collinear segments (notably due to cap square)398pc2d = rdrCtx.simplifier.init(pc2d);399}400401// by now, at least one of outat and strokerat will be null. Unless at is not402// a constant multiple of an orthogonal transformation, they will both be403// null. In other cases, outat == at if normalization is off, and if404// normalization is on, strokerat == at.405final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;406pc2d = transformerPC2D.transformConsumer(pc2d, outat);407pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);408409pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);410411if (dashes != null) {412if (!recycleDashes) {413dashLen = dashes.length;414}415pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,416recycleDashes);417}418pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);419pathTo(rdrCtx, pi, pc2d);420421/*422* Pipeline seems to be:423* shape.getPathIterator424* -> NormalizingPathIterator425* -> inverseDeltaTransformConsumer426* -> Dasher427* -> Stroker428* -> deltaTransformConsumer OR transformConsumer429*430* -> CollinearSimplifier to remove redundant segments431*432* -> pc2d = Renderer (bounding box)433*/434}435436private static boolean nearZero(final double num) {437return Math.abs(num) < 2.0 * Math.ulp(num);438}439440PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,441final NormMode mode,442final PathIterator src)443{444switch (mode) {445case ON_WITH_AA:446// NormalizingPathIterator NearestPixelCenter:447return rdrCtx.nPCPathIterator.init(src);448case ON_NO_AA:449// NearestPixel NormalizingPathIterator:450return rdrCtx.nPQPathIterator.init(src);451case OFF:452// return original path iterator if normalization is disabled:453return src;454default:455throw new InternalError("Unrecognized normalization mode");456}457}458459abstract static class NormalizingPathIterator implements PathIterator {460461private PathIterator src;462463// the adjustment applied to the current position.464private float curx_adjust, cury_adjust;465// the adjustment applied to the last moveTo position.466private float movx_adjust, movy_adjust;467468private final float[] tmp;469470NormalizingPathIterator(final float[] tmp) {471this.tmp = tmp;472}473474final NormalizingPathIterator init(final PathIterator src) {475this.src = src;476return this; // fluent API477}478479/**480* Disposes this path iterator:481* clean up before reusing this instance482*/483final void dispose() {484// free source PathIterator:485this.src = null;486}487488@Override489public final int currentSegment(final float[] coords) {490if (doMonitors) {491RendererContext.stats.mon_npi_currentSegment.start();492}493int lastCoord;494final int type = src.currentSegment(coords);495496switch(type) {497case PathIterator.SEG_MOVETO:498case PathIterator.SEG_LINETO:499lastCoord = 0;500break;501case PathIterator.SEG_QUADTO:502lastCoord = 2;503break;504case PathIterator.SEG_CUBICTO:505lastCoord = 4;506break;507case PathIterator.SEG_CLOSE:508// we don't want to deal with this case later. We just exit now509curx_adjust = movx_adjust;510cury_adjust = movy_adjust;511512if (doMonitors) {513RendererContext.stats.mon_npi_currentSegment.stop();514}515return type;516default:517throw new InternalError("Unrecognized curve type");518}519520// TODO: handle NaN, Inf and overflow521522// normalize endpoint523float coord, x_adjust, y_adjust;524525coord = coords[lastCoord];526x_adjust = normCoord(coord); // new coord527coords[lastCoord] = x_adjust;528x_adjust -= coord;529530coord = coords[lastCoord + 1];531y_adjust = normCoord(coord); // new coord532coords[lastCoord + 1] = y_adjust;533y_adjust -= coord;534535// now that the end points are done, normalize the control points536switch(type) {537case PathIterator.SEG_MOVETO:538movx_adjust = x_adjust;539movy_adjust = y_adjust;540break;541case PathIterator.SEG_LINETO:542break;543case PathIterator.SEG_QUADTO:544coords[0] += (curx_adjust + x_adjust) / 2f;545coords[1] += (cury_adjust + y_adjust) / 2f;546break;547case PathIterator.SEG_CUBICTO:548coords[0] += curx_adjust;549coords[1] += cury_adjust;550coords[2] += x_adjust;551coords[3] += y_adjust;552break;553case PathIterator.SEG_CLOSE:554// handled earlier555default:556}557curx_adjust = x_adjust;558cury_adjust = y_adjust;559560if (doMonitors) {561RendererContext.stats.mon_npi_currentSegment.stop();562}563return type;564}565566abstract float normCoord(final float coord);567568@Override569public final int currentSegment(final double[] coords) {570final float[] _tmp = tmp; // dirty571int type = this.currentSegment(_tmp);572for (int i = 0; i < 6; i++) {573coords[i] = _tmp[i];574}575return type;576}577578@Override579public final int getWindingRule() {580return src.getWindingRule();581}582583@Override584public final boolean isDone() {585if (src.isDone()) {586// Dispose this instance:587dispose();588return true;589}590return false;591}592593@Override594public final void next() {595src.next();596}597598static final class NearestPixelCenter599extends NormalizingPathIterator600{601NearestPixelCenter(final float[] tmp) {602super(tmp);603}604605@Override606float normCoord(final float coord) {607// round to nearest pixel center608return FloatMath.floor_f(coord) + 0.5f;609}610}611612static final class NearestPixelQuarter613extends NormalizingPathIterator614{615NearestPixelQuarter(final float[] tmp) {616super(tmp);617}618619@Override620float normCoord(final float coord) {621// round to nearest (0.25, 0.25) pixel quarter622return FloatMath.floor_f(coord + 0.25f) + 0.25f;623}624}625}626627private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,628final PathConsumer2D pc2d)629{630// mark context as DIRTY:631rdrCtx.dirty = true;632633final float[] coords = rdrCtx.float6;634635pathToLoop(coords, pi, pc2d);636637// mark context as CLEAN:638rdrCtx.dirty = false;639}640641private static void pathToLoop(final float[] coords, final PathIterator pi,642final PathConsumer2D pc2d)643{644for (; !pi.isDone(); pi.next()) {645switch (pi.currentSegment(coords)) {646case PathIterator.SEG_MOVETO:647pc2d.moveTo(coords[0], coords[1]);648continue;649case PathIterator.SEG_LINETO:650pc2d.lineTo(coords[0], coords[1]);651continue;652case PathIterator.SEG_QUADTO:653pc2d.quadTo(coords[0], coords[1],654coords[2], coords[3]);655continue;656case PathIterator.SEG_CUBICTO:657pc2d.curveTo(coords[0], coords[1],658coords[2], coords[3],659coords[4], coords[5]);660continue;661case PathIterator.SEG_CLOSE:662pc2d.closePath();663continue;664default:665}666}667pc2d.pathDone();668}669670/**671* Construct an antialiased tile generator for the given shape with672* the given rendering attributes and store the bounds of the tile673* iteration in the bbox parameter.674* The {@code at} parameter specifies a transform that should affect675* both the shape and the {@code BasicStroke} attributes.676* The {@code clip} parameter specifies the current clip in effect677* in device coordinates and can be used to prune the data for the678* operation, but the renderer is not required to perform any679* clipping.680* If the {@code BasicStroke} parameter is null then the shape681* should be filled as is, otherwise the attributes of the682* {@code BasicStroke} should be used to specify a draw operation.683* The {@code thin} parameter indicates whether or not the684* transformed {@code BasicStroke} represents coordinates smaller685* than the minimum resolution of the antialiasing rasterizer as686* specified by the {@code getMinimumAAPenWidth()} method.687* <p>688* Upon returning, this method will fill the {@code bbox} parameter689* with 4 values indicating the bounds of the iteration of the690* tile generator.691* The iteration order of the tiles will be as specified by the692* pseudo-code:693* <pre>694* for (y = bbox[1]; y < bbox[3]; y += tileheight) {695* for (x = bbox[0]; x < bbox[2]; x += tilewidth) {696* }697* }698* </pre>699* If there is no output to be rendered, this method may return700* null.701*702* @param s the shape to be rendered (fill or draw)703* @param at the transform to be applied to the shape and the704* stroke attributes705* @param clip the current clip in effect in device coordinates706* @param bs if non-null, a {@code BasicStroke} whose attributes707* should be applied to this operation708* @param thin true if the transformed stroke attributes are smaller709* than the minimum dropout pen width710* @param normalize true if the {@code VALUE_STROKE_NORMALIZE}711* {@code RenderingHint} is in effect712* @param bbox returns the bounds of the iteration713* @return the {@code AATileGenerator} instance to be consulted714* for tile coverages, or null if there is no output to render715* @since 1.7716*/717@Override718public AATileGenerator getAATileGenerator(Shape s,719AffineTransform at,720Region clip,721BasicStroke bs,722boolean thin,723boolean normalize,724int bbox[])725{726MarlinTileGenerator ptg = null;727Renderer r = null;728729final RendererContext rdrCtx = getRendererContext();730try {731// Test if at is identity:732final AffineTransform _at = (at != null && !at.isIdentity()) ? at733: null;734735final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;736737if (bs == null) {738// fill shape:739final PathIterator pi = getNormalizingPathIterator(rdrCtx, norm,740s.getPathIterator(_at));741742r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),743clip.getWidth(), clip.getHeight(),744pi.getWindingRule());745746// TODO: subdivide quad/cubic curves into monotonic curves ?747pathTo(rdrCtx, pi, r);748} else {749// draw shape with given stroke:750r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),751clip.getWidth(), clip.getHeight(),752PathIterator.WIND_NON_ZERO);753754strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);755}756if (r.endRendering()) {757ptg = rdrCtx.ptg.init();758ptg.getBbox(bbox);759// note: do not returnRendererContext(rdrCtx)760// as it will be called later by MarlinTileGenerator.dispose()761r = null;762}763} finally {764if (r != null) {765// dispose renderer:766r.dispose();767// recycle the RendererContext instance768MarlinRenderingEngine.returnRendererContext(rdrCtx);769}770}771772// Return null to cancel AA tile generation (nothing to render)773return ptg;774}775776@Override777public final AATileGenerator getAATileGenerator(double x, double y,778double dx1, double dy1,779double dx2, double dy2,780double lw1, double lw2,781Region clip,782int bbox[])783{784// REMIND: Deal with large coordinates!785double ldx1, ldy1, ldx2, ldy2;786boolean innerpgram = (lw1 > 0.0 && lw2 > 0.0);787788if (innerpgram) {789ldx1 = dx1 * lw1;790ldy1 = dy1 * lw1;791ldx2 = dx2 * lw2;792ldy2 = dy2 * lw2;793x -= (ldx1 + ldx2) / 2.0;794y -= (ldy1 + ldy2) / 2.0;795dx1 += ldx1;796dy1 += ldy1;797dx2 += ldx2;798dy2 += ldy2;799if (lw1 > 1.0 && lw2 > 1.0) {800// Inner parallelogram was entirely consumed by stroke...801innerpgram = false;802}803} else {804ldx1 = ldy1 = ldx2 = ldy2 = 0.0;805}806807MarlinTileGenerator ptg = null;808Renderer r = null;809810final RendererContext rdrCtx = getRendererContext();811try {812r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),813clip.getWidth(), clip.getHeight(),814Renderer.WIND_EVEN_ODD);815816r.moveTo((float) x, (float) y);817r.lineTo((float) (x+dx1), (float) (y+dy1));818r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));819r.lineTo((float) (x+dx2), (float) (y+dy2));820r.closePath();821822if (innerpgram) {823x += ldx1 + ldx2;824y += ldy1 + ldy2;825dx1 -= 2.0 * ldx1;826dy1 -= 2.0 * ldy1;827dx2 -= 2.0 * ldx2;828dy2 -= 2.0 * ldy2;829r.moveTo((float) x, (float) y);830r.lineTo((float) (x+dx1), (float) (y+dy1));831r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));832r.lineTo((float) (x+dx2), (float) (y+dy2));833r.closePath();834}835r.pathDone();836837if (r.endRendering()) {838ptg = rdrCtx.ptg.init();839ptg.getBbox(bbox);840// note: do not returnRendererContext(rdrCtx)841// as it will be called later by MarlinTileGenerator.dispose()842r = null;843}844} finally {845if (r != null) {846// dispose renderer:847r.dispose();848// recycle the RendererContext instance849MarlinRenderingEngine.returnRendererContext(rdrCtx);850}851}852853// Return null to cancel AA tile generation (nothing to render)854return ptg;855}856857/**858* Returns the minimum pen width that the antialiasing rasterizer859* can represent without dropouts occuring.860* @since 1.7861*/862@Override863public float getMinimumAAPenSize() {864return MIN_PEN_SIZE;865}866867static {868if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO ||869PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD ||870BasicStroke.JOIN_MITER != Stroker.JOIN_MITER ||871BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND ||872BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL ||873BasicStroke.CAP_BUTT != Stroker.CAP_BUTT ||874BasicStroke.CAP_ROUND != Stroker.CAP_ROUND ||875BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE)876{877throw new InternalError("mismatched renderer constants");878}879}880881// --- RendererContext handling ---882// use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext883private static final boolean useThreadLocal;884885// reference type stored in either TL or CLQ886static final int REF_TYPE;887888// Per-thread RendererContext889private static final ReentrantContextProvider<RendererContext> rdrCtxProvider;890891// Static initializer to use TL or CLQ mode892static {893useThreadLocal = MarlinProperties.isUseThreadLocal();894895// Soft reference by default:896final String refType = AccessController.doPrivileged(897new GetPropertyAction("sun.java2d.renderer.useRef",898"soft"));899switch (refType) {900default:901case "soft":902REF_TYPE = ReentrantContextProvider.REF_SOFT;903break;904case "weak":905REF_TYPE = ReentrantContextProvider.REF_WEAK;906break;907case "hard":908REF_TYPE = ReentrantContextProvider.REF_HARD;909break;910}911912if (useThreadLocal) {913rdrCtxProvider = new ReentrantContextProviderTL<RendererContext>(REF_TYPE)914{915@Override916protected RendererContext newContext() {917return RendererContext.createContext();918}919};920} else {921rdrCtxProvider = new ReentrantContextProviderCLQ<RendererContext>(REF_TYPE)922{923@Override924protected RendererContext newContext() {925return RendererContext.createContext();926}927};928}929}930931private static boolean settingsLogged = !enableLogs;932933private static void logSettings(final String reClass) {934// log information at startup935if (settingsLogged) {936return;937}938settingsLogged = true;939940String refType;941switch (REF_TYPE) {942default:943case ReentrantContextProvider.REF_HARD:944refType = "hard";945break;946case ReentrantContextProvider.REF_SOFT:947refType = "soft";948break;949case ReentrantContextProvider.REF_WEAK:950refType = "weak";951break;952}953954logInfo("=========================================================="955+ "=====================");956957logInfo("Marlin software rasterizer = ENABLED");958logInfo("Version = ["959+ Version.getVersion() + "]");960logInfo("sun.java2d.renderer = "961+ reClass);962logInfo("sun.java2d.renderer.useThreadLocal = "963+ useThreadLocal);964logInfo("sun.java2d.renderer.useRef = "965+ refType);966967logInfo("sun.java2d.renderer.pixelsize = "968+ MarlinConst.INITIAL_PIXEL_DIM);969logInfo("sun.java2d.renderer.subPixel_log2_X = "970+ MarlinConst.SUBPIXEL_LG_POSITIONS_X);971logInfo("sun.java2d.renderer.subPixel_log2_Y = "972+ MarlinConst.SUBPIXEL_LG_POSITIONS_Y);973logInfo("sun.java2d.renderer.tileSize_log2 = "974+ MarlinConst.TILE_SIZE_LG);975976logInfo("sun.java2d.renderer.blockSize_log2 = "977+ MarlinConst.BLOCK_SIZE_LG);978979logInfo("sun.java2d.renderer.blockSize_log2 = "980+ MarlinConst.BLOCK_SIZE_LG);981982// RLE / blockFlags settings983984logInfo("sun.java2d.renderer.forceRLE = "985+ MarlinProperties.isForceRLE());986logInfo("sun.java2d.renderer.forceNoRLE = "987+ MarlinProperties.isForceNoRLE());988logInfo("sun.java2d.renderer.useTileFlags = "989+ MarlinProperties.isUseTileFlags());990logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "991+ MarlinProperties.isUseTileFlagsWithHeuristics());992logInfo("sun.java2d.renderer.rleMinWidth = "993+ MarlinCache.RLE_MIN_WIDTH);994995// optimisation parameters996logInfo("sun.java2d.renderer.useSimplifier = "997+ MarlinConst.useSimplifier);998999// debugging parameters1000logInfo("sun.java2d.renderer.doStats = "1001+ MarlinConst.doStats);1002logInfo("sun.java2d.renderer.doMonitors = "1003+ MarlinConst.doMonitors);1004logInfo("sun.java2d.renderer.doChecks = "1005+ MarlinConst.doChecks);10061007// logging parameters1008logInfo("sun.java2d.renderer.useLogger = "1009+ MarlinConst.useLogger);1010logInfo("sun.java2d.renderer.logCreateContext = "1011+ MarlinConst.logCreateContext);1012logInfo("sun.java2d.renderer.logUnsafeMalloc = "1013+ MarlinConst.logUnsafeMalloc);10141015// quality settings1016logInfo("Renderer settings:");1017logInfo("CUB_COUNT_LG = " + Renderer.CUB_COUNT_LG);1018logInfo("CUB_DEC_BND = " + Renderer.CUB_DEC_BND);1019logInfo("CUB_INC_BND = " + Renderer.CUB_INC_BND);1020logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);10211022logInfo("=========================================================="1023+ "=====================");1024}10251026/**1027* Get the RendererContext instance dedicated to the current thread1028* @return RendererContext instance1029*/1030@SuppressWarnings({"unchecked"})1031static RendererContext getRendererContext() {1032final RendererContext rdrCtx = rdrCtxProvider.acquire();1033if (doMonitors) {1034RendererContext.stats.mon_pre_getAATileGenerator.start();1035}1036return rdrCtx;1037}10381039/**1040* Reset and return the given RendererContext instance for reuse1041* @param rdrCtx RendererContext instance1042*/1043static void returnRendererContext(final RendererContext rdrCtx) {1044rdrCtx.dispose();10451046if (doMonitors) {1047RendererContext.stats.mon_pre_getAATileGenerator.stop();1048}1049rdrCtxProvider.release(rdrCtx);1050}1051}105210531054