jdk/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java
changeset 47126 188ef162f019
equal deleted inserted replaced
45093:c42dc7b58b4d 47126:188ef162f019
       
     1 /*
       
     2  * Copyright (c) 2007, 2017, 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.marlin;
       
    27 
       
    28 import java.awt.BasicStroke;
       
    29 import java.awt.Shape;
       
    30 import java.awt.geom.AffineTransform;
       
    31 import java.awt.geom.Path2D;
       
    32 import java.awt.geom.PathIterator;
       
    33 import java.security.AccessController;
       
    34 import static sun.java2d.marlin.MarlinUtils.logInfo;
       
    35 import sun.java2d.ReentrantContextProvider;
       
    36 import sun.java2d.ReentrantContextProviderCLQ;
       
    37 import sun.java2d.ReentrantContextProviderTL;
       
    38 import sun.java2d.pipe.AATileGenerator;
       
    39 import sun.java2d.pipe.Region;
       
    40 import sun.java2d.pipe.RenderingEngine;
       
    41 import sun.security.action.GetPropertyAction;
       
    42 
       
    43 /**
       
    44  * Marlin RendererEngine implementation (derived from Pisces)
       
    45  */
       
    46 public final class DMarlinRenderingEngine extends RenderingEngine
       
    47                                           implements MarlinConst
       
    48 {
       
    49     private static enum NormMode {
       
    50         ON_WITH_AA {
       
    51             @Override
       
    52             PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
       
    53                                                     final PathIterator src)
       
    54             {
       
    55                 // NormalizingPathIterator NearestPixelCenter:
       
    56                 return rdrCtx.nPCPathIterator.init(src);
       
    57             }
       
    58         },
       
    59         ON_NO_AA{
       
    60             @Override
       
    61             PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
       
    62                                                     final PathIterator src)
       
    63             {
       
    64                 // NearestPixel NormalizingPathIterator:
       
    65                 return rdrCtx.nPQPathIterator.init(src);
       
    66             }
       
    67         },
       
    68         OFF{
       
    69             @Override
       
    70             PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
       
    71                                                     final PathIterator src)
       
    72             {
       
    73                 // return original path iterator if normalization is disabled:
       
    74                 return src;
       
    75             }
       
    76         };
       
    77 
       
    78         abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx,
       
    79                                                          PathIterator src);
       
    80     }
       
    81 
       
    82     private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
       
    83 
       
    84     static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
       
    85     static final double LOWER_BND = -UPPER_BND;
       
    86 
       
    87     /**
       
    88      * Public constructor
       
    89      */
       
    90     public DMarlinRenderingEngine() {
       
    91         super();
       
    92         logSettings(DMarlinRenderingEngine.class.getName());
       
    93     }
       
    94 
       
    95     /**
       
    96      * Create a widened path as specified by the parameters.
       
    97      * <p>
       
    98      * The specified {@code src} {@link Shape} is widened according
       
    99      * to the specified attribute parameters as per the
       
   100      * {@link BasicStroke} specification.
       
   101      *
       
   102      * @param src the source path to be widened
       
   103      * @param width the width of the widened path as per {@code BasicStroke}
       
   104      * @param caps the end cap decorations as per {@code BasicStroke}
       
   105      * @param join the segment join decorations as per {@code BasicStroke}
       
   106      * @param miterlimit the miter limit as per {@code BasicStroke}
       
   107      * @param dashes the dash length array as per {@code BasicStroke}
       
   108      * @param dashphase the initial dash phase as per {@code BasicStroke}
       
   109      * @return the widened path stored in a new {@code Shape} object
       
   110      * @since 1.7
       
   111      */
       
   112     @Override
       
   113     public Shape createStrokedShape(Shape src,
       
   114                                     float width,
       
   115                                     int caps,
       
   116                                     int join,
       
   117                                     float miterlimit,
       
   118                                     float[] dashes,
       
   119                                     float dashphase)
       
   120     {
       
   121         final DRendererContext rdrCtx = getRendererContext();
       
   122         try {
       
   123             // initialize a large copyable Path2D to avoid a lot of array growing:
       
   124             final Path2D.Double p2d = rdrCtx.getPath2D();
       
   125 
       
   126             strokeTo(rdrCtx,
       
   127                      src,
       
   128                      null,
       
   129                      width,
       
   130                      NormMode.OFF,
       
   131                      caps,
       
   132                      join,
       
   133                      miterlimit,
       
   134                      dashes,
       
   135                      dashphase,
       
   136                      rdrCtx.transformerPC2D.wrapPath2d(p2d)
       
   137                     );
       
   138 
       
   139             // Use Path2D copy constructor (trim)
       
   140             return new Path2D.Double(p2d);
       
   141 
       
   142         } finally {
       
   143             // recycle the DRendererContext instance
       
   144             returnRendererContext(rdrCtx);
       
   145         }
       
   146     }
       
   147 
       
   148     /**
       
   149      * Sends the geometry for a widened path as specified by the parameters
       
   150      * to the specified consumer.
       
   151      * <p>
       
   152      * The specified {@code src} {@link Shape} is widened according
       
   153      * to the parameters specified by the {@link BasicStroke} object.
       
   154      * Adjustments are made to the path as appropriate for the
       
   155      * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
       
   156      * {@code normalize} boolean parameter is true.
       
   157      * Adjustments are made to the path as appropriate for the
       
   158      * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
       
   159      * {@code antialias} boolean parameter is true.
       
   160      * <p>
       
   161      * The geometry of the widened path is forwarded to the indicated
       
   162      * {@link DPathConsumer2D} object as it is calculated.
       
   163      *
       
   164      * @param src the source path to be widened
       
   165      * @param bs the {@code BasicSroke} object specifying the
       
   166      *           decorations to be applied to the widened path
       
   167      * @param normalize indicates whether stroke normalization should
       
   168      *                  be applied
       
   169      * @param antialias indicates whether or not adjustments appropriate
       
   170      *                  to antialiased rendering should be applied
       
   171      * @param consumer the {@code DPathConsumer2D} instance to forward
       
   172      *                 the widened geometry to
       
   173      * @since 1.7
       
   174      */
       
   175     @Override
       
   176     public void strokeTo(Shape src,
       
   177                          AffineTransform at,
       
   178                          BasicStroke bs,
       
   179                          boolean thin,
       
   180                          boolean normalize,
       
   181                          boolean antialias,
       
   182                          final sun.awt.geom.PathConsumer2D consumer)
       
   183     {
       
   184         final NormMode norm = (normalize) ?
       
   185                 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
       
   186                 : NormMode.OFF;
       
   187 
       
   188         final DRendererContext rdrCtx = getRendererContext();
       
   189         try {
       
   190             strokeTo(rdrCtx, src, at, bs, thin, norm, antialias,
       
   191                      rdrCtx.p2dAdapter.init(consumer));
       
   192         } finally {
       
   193             // recycle the DRendererContext instance
       
   194             returnRendererContext(rdrCtx);
       
   195         }
       
   196     }
       
   197 
       
   198     final void strokeTo(final DRendererContext rdrCtx,
       
   199                         Shape src,
       
   200                         AffineTransform at,
       
   201                         BasicStroke bs,
       
   202                         boolean thin,
       
   203                         NormMode normalize,
       
   204                         boolean antialias,
       
   205                         DPathConsumer2D pc2d)
       
   206     {
       
   207         double lw;
       
   208         if (thin) {
       
   209             if (antialias) {
       
   210                 lw = userSpaceLineWidth(at, MIN_PEN_SIZE);
       
   211             } else {
       
   212                 lw = userSpaceLineWidth(at, 1.0d);
       
   213             }
       
   214         } else {
       
   215             lw = bs.getLineWidth();
       
   216         }
       
   217         strokeTo(rdrCtx,
       
   218                  src,
       
   219                  at,
       
   220                  lw,
       
   221                  normalize,
       
   222                  bs.getEndCap(),
       
   223                  bs.getLineJoin(),
       
   224                  bs.getMiterLimit(),
       
   225                  bs.getDashArray(),
       
   226                  bs.getDashPhase(),
       
   227                  pc2d);
       
   228     }
       
   229 
       
   230     private final double userSpaceLineWidth(AffineTransform at, double lw) {
       
   231 
       
   232         double widthScale;
       
   233 
       
   234         if (at == null) {
       
   235             widthScale = 1.0d;
       
   236         } else if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM  |
       
   237                                     AffineTransform.TYPE_GENERAL_SCALE)) != 0) {
       
   238             widthScale = Math.sqrt(at.getDeterminant());
       
   239         } else {
       
   240             // First calculate the "maximum scale" of this transform.
       
   241             double A = at.getScaleX();       // m00
       
   242             double C = at.getShearX();       // m01
       
   243             double B = at.getShearY();       // m10
       
   244             double D = at.getScaleY();       // m11
       
   245 
       
   246             /*
       
   247              * Given a 2 x 2 affine matrix [ A B ] such that
       
   248              *                             [ C D ]
       
   249              * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
       
   250              * find the maximum magnitude (norm) of the vector v'
       
   251              * with the constraint (x^2 + y^2 = 1).
       
   252              * The equation to maximize is
       
   253              *     |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
       
   254              * or  |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
       
   255              * Since sqrt is monotonic we can maximize |v'|^2
       
   256              * instead and plug in the substitution y = sqrt(1 - x^2).
       
   257              * Trigonometric equalities can then be used to get
       
   258              * rid of most of the sqrt terms.
       
   259              */
       
   260 
       
   261             double EA = A*A + B*B;          // x^2 coefficient
       
   262             double EB = 2.0d * (A*C + B*D); // xy coefficient
       
   263             double EC = C*C + D*D;          // y^2 coefficient
       
   264 
       
   265             /*
       
   266              * There is a lot of calculus omitted here.
       
   267              *
       
   268              * Conceptually, in the interests of understanding the
       
   269              * terms that the calculus produced we can consider
       
   270              * that EA and EC end up providing the lengths along
       
   271              * the major axes and the hypot term ends up being an
       
   272              * adjustment for the additional length along the off-axis
       
   273              * angle of rotated or sheared ellipses as well as an
       
   274              * adjustment for the fact that the equation below
       
   275              * averages the two major axis lengths.  (Notice that
       
   276              * the hypot term contains a part which resolves to the
       
   277              * difference of these two axis lengths in the absence
       
   278              * of rotation.)
       
   279              *
       
   280              * In the calculus, the ratio of the EB and (EA-EC) terms
       
   281              * ends up being the tangent of 2*theta where theta is
       
   282              * the angle that the long axis of the ellipse makes
       
   283              * with the horizontal axis.  Thus, this equation is
       
   284              * calculating the length of the hypotenuse of a triangle
       
   285              * along that axis.
       
   286              */
       
   287 
       
   288             double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
       
   289             // sqrt omitted, compare to squared limits below.
       
   290             double widthsquared = ((EA + EC + hypot) / 2.0d);
       
   291 
       
   292             widthScale = Math.sqrt(widthsquared);
       
   293         }
       
   294 
       
   295         return (lw / widthScale);
       
   296     }
       
   297 
       
   298     final void strokeTo(final DRendererContext rdrCtx,
       
   299                         Shape src,
       
   300                         AffineTransform at,
       
   301                         double width,
       
   302                         NormMode norm,
       
   303                         int caps,
       
   304                         int join,
       
   305                         float miterlimit,
       
   306                         float[] dashes,
       
   307                         float dashphase,
       
   308                         DPathConsumer2D pc2d)
       
   309     {
       
   310         // We use strokerat so that in Stroker and Dasher we can work only
       
   311         // with the pre-transformation coordinates. This will repeat a lot of
       
   312         // computations done in the path iterator, but the alternative is to
       
   313         // work with transformed paths and compute untransformed coordinates
       
   314         // as needed. This would be faster but I do not think the complexity
       
   315         // of working with both untransformed and transformed coordinates in
       
   316         // the same code is worth it.
       
   317         // However, if a path's width is constant after a transformation,
       
   318         // we can skip all this untransforming.
       
   319 
       
   320         // As pathTo() will check transformed coordinates for invalid values
       
   321         // (NaN / Infinity) to ignore such points, it is necessary to apply the
       
   322         // transformation before the path processing.
       
   323         AffineTransform strokerat = null;
       
   324 
       
   325         int dashLen = -1;
       
   326         boolean recycleDashes = false;
       
   327         double[] dashesD = null;
       
   328 
       
   329         // Ensure converting dashes to double precision:
       
   330         if (dashes != null) {
       
   331             recycleDashes = true;
       
   332             dashLen = dashes.length;
       
   333             dashesD = rdrCtx.dasher.copyDashArray(dashes);
       
   334         }
       
   335 
       
   336         if (at != null && !at.isIdentity()) {
       
   337             final double a = at.getScaleX();
       
   338             final double b = at.getShearX();
       
   339             final double c = at.getShearY();
       
   340             final double d = at.getScaleY();
       
   341             final double det = a * d - c * b;
       
   342 
       
   343             if (Math.abs(det) <= (2.0d * Double.MIN_VALUE)) {
       
   344                 // this rendering engine takes one dimensional curves and turns
       
   345                 // them into 2D shapes by giving them width.
       
   346                 // However, if everything is to be passed through a singular
       
   347                 // transformation, these 2D shapes will be squashed down to 1D
       
   348                 // again so, nothing can be drawn.
       
   349 
       
   350                 // Every path needs an initial moveTo and a pathDone. If these
       
   351                 // are not there this causes a SIGSEGV in libawt.so (at the time
       
   352                 // of writing of this comment (September 16, 2010)). Actually,
       
   353                 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
       
   354                 // but the pathDone is definitely needed.
       
   355                 pc2d.moveTo(0.0d, 0.0d);
       
   356                 pc2d.pathDone();
       
   357                 return;
       
   358             }
       
   359 
       
   360             // If the transform is a constant multiple of an orthogonal transformation
       
   361             // then every length is just multiplied by a constant, so we just
       
   362             // need to transform input paths to stroker and tell stroker
       
   363             // the scaled width. This condition is satisfied if
       
   364             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
       
   365             // leave a bit of room for error.
       
   366             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
       
   367                 final double scale =  Math.sqrt(a*a + c*c);
       
   368 
       
   369                 if (dashesD != null) {
       
   370                     for (int i = 0; i < dashLen; i++) {
       
   371                         dashesD[i] *= scale;
       
   372                     }
       
   373                     dashphase *= scale;
       
   374                 }
       
   375                 width *= scale;
       
   376 
       
   377                 // by now strokerat == null. Input paths to
       
   378                 // stroker (and maybe dasher) will have the full transform at
       
   379                 // applied to them and nothing will happen to the output paths.
       
   380             } else {
       
   381                 strokerat = at;
       
   382 
       
   383                 // by now strokerat == at. Input paths to
       
   384                 // stroker (and maybe dasher) will have the full transform at
       
   385                 // applied to them, then they will be normalized, and then
       
   386                 // the inverse of *only the non translation part of at* will
       
   387                 // be applied to the normalized paths. This won't cause problems
       
   388                 // in stroker, because, suppose at = T*A, where T is just the
       
   389                 // translation part of at, and A is the rest. T*A has already
       
   390                 // been applied to Stroker/Dasher's input. Then Ainv will be
       
   391                 // applied. Ainv*T*A is not equal to T, but it is a translation,
       
   392                 // which means that none of stroker's assumptions about its
       
   393                 // input will be violated. After all this, A will be applied
       
   394                 // to stroker's output.
       
   395             }
       
   396         } else {
       
   397             // either at is null or it's the identity. In either case
       
   398             // we don't transform the path.
       
   399             at = null;
       
   400         }
       
   401 
       
   402         if (USE_SIMPLIFIER) {
       
   403             // Use simplifier after stroker before Renderer
       
   404             // to remove collinear segments (notably due to cap square)
       
   405             pc2d = rdrCtx.simplifier.init(pc2d);
       
   406         }
       
   407 
       
   408         final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
       
   409         pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
       
   410 
       
   411         pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
       
   412 
       
   413         if (dashesD != null) {
       
   414             pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
       
   415                                       recycleDashes);
       
   416         }
       
   417         pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
       
   418 
       
   419         final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
       
   420                                          src.getPathIterator(at));
       
   421 
       
   422         pathTo(rdrCtx, pi, pc2d);
       
   423 
       
   424         /*
       
   425          * Pipeline seems to be:
       
   426          * shape.getPathIterator(at)
       
   427          * -> (NormalizingPathIterator)
       
   428          * -> (inverseDeltaTransformConsumer)
       
   429          * -> (Dasher)
       
   430          * -> Stroker
       
   431          * -> (deltaTransformConsumer)
       
   432          *
       
   433          * -> (CollinearSimplifier) to remove redundant segments
       
   434          *
       
   435          * -> pc2d = Renderer (bounding box)
       
   436          */
       
   437     }
       
   438 
       
   439     private static boolean nearZero(final double num) {
       
   440         return Math.abs(num) < 2.0d * Math.ulp(num);
       
   441     }
       
   442 
       
   443     abstract static class NormalizingPathIterator implements PathIterator {
       
   444 
       
   445         private PathIterator src;
       
   446 
       
   447         // the adjustment applied to the current position.
       
   448         private double curx_adjust, cury_adjust;
       
   449         // the adjustment applied to the last moveTo position.
       
   450         private double movx_adjust, movy_adjust;
       
   451 
       
   452         private final double[] tmp;
       
   453 
       
   454         NormalizingPathIterator(final double[] tmp) {
       
   455             this.tmp = tmp;
       
   456         }
       
   457 
       
   458         final NormalizingPathIterator init(final PathIterator src) {
       
   459             this.src = src;
       
   460             return this; // fluent API
       
   461         }
       
   462 
       
   463         /**
       
   464          * Disposes this path iterator:
       
   465          * clean up before reusing this instance
       
   466          */
       
   467         final void dispose() {
       
   468             // free source PathIterator:
       
   469             this.src = null;
       
   470         }
       
   471 
       
   472         @Override
       
   473         public final int currentSegment(final double[] coords) {
       
   474             int lastCoord;
       
   475             final int type = src.currentSegment(coords);
       
   476 
       
   477             switch(type) {
       
   478                 case PathIterator.SEG_MOVETO:
       
   479                 case PathIterator.SEG_LINETO:
       
   480                     lastCoord = 0;
       
   481                     break;
       
   482                 case PathIterator.SEG_QUADTO:
       
   483                     lastCoord = 2;
       
   484                     break;
       
   485                 case PathIterator.SEG_CUBICTO:
       
   486                     lastCoord = 4;
       
   487                     break;
       
   488                 case PathIterator.SEG_CLOSE:
       
   489                     // we don't want to deal with this case later. We just exit now
       
   490                     curx_adjust = movx_adjust;
       
   491                     cury_adjust = movy_adjust;
       
   492                     return type;
       
   493                 default:
       
   494                     throw new InternalError("Unrecognized curve type");
       
   495             }
       
   496 
       
   497             // normalize endpoint
       
   498             double coord, x_adjust, y_adjust;
       
   499 
       
   500             coord = coords[lastCoord];
       
   501             x_adjust = normCoord(coord); // new coord
       
   502             coords[lastCoord] = x_adjust;
       
   503             x_adjust -= coord;
       
   504 
       
   505             coord = coords[lastCoord + 1];
       
   506             y_adjust = normCoord(coord); // new coord
       
   507             coords[lastCoord + 1] = y_adjust;
       
   508             y_adjust -= coord;
       
   509 
       
   510             // now that the end points are done, normalize the control points
       
   511             switch(type) {
       
   512                 case PathIterator.SEG_MOVETO:
       
   513                     movx_adjust = x_adjust;
       
   514                     movy_adjust = y_adjust;
       
   515                     break;
       
   516                 case PathIterator.SEG_LINETO:
       
   517                     break;
       
   518                 case PathIterator.SEG_QUADTO:
       
   519                     coords[0] += (curx_adjust + x_adjust) / 2.0d;
       
   520                     coords[1] += (cury_adjust + y_adjust) / 2.0d;
       
   521                     break;
       
   522                 case PathIterator.SEG_CUBICTO:
       
   523                     coords[0] += curx_adjust;
       
   524                     coords[1] += cury_adjust;
       
   525                     coords[2] += x_adjust;
       
   526                     coords[3] += y_adjust;
       
   527                     break;
       
   528                 case PathIterator.SEG_CLOSE:
       
   529                     // handled earlier
       
   530                 default:
       
   531             }
       
   532             curx_adjust = x_adjust;
       
   533             cury_adjust = y_adjust;
       
   534             return type;
       
   535         }
       
   536 
       
   537         abstract double normCoord(final double coord);
       
   538 
       
   539         @Override
       
   540         public final int currentSegment(final float[] coords) {
       
   541             final double[] _tmp = tmp; // dirty
       
   542             int type = this.currentSegment(_tmp);
       
   543             for (int i = 0; i < 6; i++) {
       
   544                 coords[i] = (float)_tmp[i];
       
   545             }
       
   546             return type;
       
   547         }
       
   548 
       
   549         @Override
       
   550         public final int getWindingRule() {
       
   551             return src.getWindingRule();
       
   552         }
       
   553 
       
   554         @Override
       
   555         public final boolean isDone() {
       
   556             if (src.isDone()) {
       
   557                 // Dispose this instance:
       
   558                 dispose();
       
   559                 return true;
       
   560             }
       
   561             return false;
       
   562         }
       
   563 
       
   564         @Override
       
   565         public final void next() {
       
   566             src.next();
       
   567         }
       
   568 
       
   569         static final class NearestPixelCenter
       
   570                                 extends NormalizingPathIterator
       
   571         {
       
   572             NearestPixelCenter(final double[] tmp) {
       
   573                 super(tmp);
       
   574             }
       
   575 
       
   576             @Override
       
   577             double normCoord(final double coord) {
       
   578                 // round to nearest pixel center
       
   579                 return Math.floor(coord) + 0.5d;
       
   580             }
       
   581         }
       
   582 
       
   583         static final class NearestPixelQuarter
       
   584                                 extends NormalizingPathIterator
       
   585         {
       
   586             NearestPixelQuarter(final double[] tmp) {
       
   587                 super(tmp);
       
   588             }
       
   589 
       
   590             @Override
       
   591             double normCoord(final double coord) {
       
   592                 // round to nearest (0.25, 0.25) pixel quarter
       
   593                 return Math.floor(coord + 0.25d) + 0.25d;
       
   594             }
       
   595         }
       
   596     }
       
   597 
       
   598     private static void pathTo(final DRendererContext rdrCtx, final PathIterator pi,
       
   599                                final DPathConsumer2D pc2d)
       
   600     {
       
   601         // mark context as DIRTY:
       
   602         rdrCtx.dirty = true;
       
   603 
       
   604         final double[] coords = rdrCtx.double6;
       
   605 
       
   606         pathToLoop(coords, pi, pc2d);
       
   607 
       
   608         // mark context as CLEAN:
       
   609         rdrCtx.dirty = false;
       
   610     }
       
   611 
       
   612     private static void pathToLoop(final double[] coords, final PathIterator pi,
       
   613                                    final DPathConsumer2D pc2d)
       
   614     {
       
   615         // ported from DuctusRenderingEngine.feedConsumer() but simplified:
       
   616         // - removed skip flag = !subpathStarted
       
   617         // - removed pathClosed (ie subpathStarted not set to false)
       
   618         boolean subpathStarted = false;
       
   619 
       
   620         for (; !pi.isDone(); pi.next()) {
       
   621             switch (pi.currentSegment(coords)) {
       
   622             case PathIterator.SEG_MOVETO:
       
   623                 /* Checking SEG_MOVETO coordinates if they are out of the
       
   624                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
       
   625                  * and Infinity values. Skipping next path segment in case of
       
   626                  * invalid data.
       
   627                  */
       
   628                 if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
       
   629                     coords[1] < UPPER_BND && coords[1] > LOWER_BND)
       
   630                 {
       
   631                     pc2d.moveTo(coords[0], coords[1]);
       
   632                     subpathStarted = true;
       
   633                 }
       
   634                 break;
       
   635             case PathIterator.SEG_LINETO:
       
   636                 /* Checking SEG_LINETO coordinates if they are out of the
       
   637                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
       
   638                  * and Infinity values. Ignoring current path segment in case
       
   639                  * of invalid data. If segment is skipped its endpoint
       
   640                  * (if valid) is used to begin new subpath.
       
   641                  */
       
   642                 if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
       
   643                     coords[1] < UPPER_BND && coords[1] > LOWER_BND)
       
   644                 {
       
   645                     if (subpathStarted) {
       
   646                         pc2d.lineTo(coords[0], coords[1]);
       
   647                     } else {
       
   648                         pc2d.moveTo(coords[0], coords[1]);
       
   649                         subpathStarted = true;
       
   650                     }
       
   651                 }
       
   652                 break;
       
   653             case PathIterator.SEG_QUADTO:
       
   654                 // Quadratic curves take two points
       
   655                 /* Checking SEG_QUADTO coordinates if they are out of the
       
   656                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
       
   657                  * and Infinity values. Ignoring current path segment in case
       
   658                  * of invalid endpoints's data. Equivalent to the SEG_LINETO
       
   659                  * if endpoint coordinates are valid but there are invalid data
       
   660                  * among other coordinates
       
   661                  */
       
   662                 if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
       
   663                     coords[3] < UPPER_BND && coords[3] > LOWER_BND)
       
   664                 {
       
   665                     if (subpathStarted) {
       
   666                         if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
       
   667                             coords[1] < UPPER_BND && coords[1] > LOWER_BND)
       
   668                         {
       
   669                             pc2d.quadTo(coords[0], coords[1],
       
   670                                         coords[2], coords[3]);
       
   671                         } else {
       
   672                             pc2d.lineTo(coords[2], coords[3]);
       
   673                         }
       
   674                     } else {
       
   675                         pc2d.moveTo(coords[2], coords[3]);
       
   676                         subpathStarted = true;
       
   677                     }
       
   678                 }
       
   679                 break;
       
   680             case PathIterator.SEG_CUBICTO:
       
   681                 // Cubic curves take three points
       
   682                 /* Checking SEG_CUBICTO coordinates if they are out of the
       
   683                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
       
   684                  * and Infinity values. Ignoring current path segment in case
       
   685                  * of invalid endpoints's data. Equivalent to the SEG_LINETO
       
   686                  * if endpoint coordinates are valid but there are invalid data
       
   687                  * among other coordinates
       
   688                  */
       
   689                 if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
       
   690                     coords[5] < UPPER_BND && coords[5] > LOWER_BND)
       
   691                 {
       
   692                     if (subpathStarted) {
       
   693                         if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
       
   694                             coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
       
   695                             coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
       
   696                             coords[3] < UPPER_BND && coords[3] > LOWER_BND)
       
   697                         {
       
   698                             pc2d.curveTo(coords[0], coords[1],
       
   699                                          coords[2], coords[3],
       
   700                                          coords[4], coords[5]);
       
   701                         } else {
       
   702                             pc2d.lineTo(coords[4], coords[5]);
       
   703                         }
       
   704                     } else {
       
   705                         pc2d.moveTo(coords[4], coords[5]);
       
   706                         subpathStarted = true;
       
   707                     }
       
   708                 }
       
   709                 break;
       
   710             case PathIterator.SEG_CLOSE:
       
   711                 if (subpathStarted) {
       
   712                     pc2d.closePath();
       
   713                     // do not set subpathStarted to false
       
   714                     // in case of missing moveTo() after close()
       
   715                 }
       
   716                 break;
       
   717             default:
       
   718             }
       
   719         }
       
   720         pc2d.pathDone();
       
   721     }
       
   722 
       
   723     /**
       
   724      * Construct an antialiased tile generator for the given shape with
       
   725      * the given rendering attributes and store the bounds of the tile
       
   726      * iteration in the bbox parameter.
       
   727      * The {@code at} parameter specifies a transform that should affect
       
   728      * both the shape and the {@code BasicStroke} attributes.
       
   729      * The {@code clip} parameter specifies the current clip in effect
       
   730      * in device coordinates and can be used to prune the data for the
       
   731      * operation, but the renderer is not required to perform any
       
   732      * clipping.
       
   733      * If the {@code BasicStroke} parameter is null then the shape
       
   734      * should be filled as is, otherwise the attributes of the
       
   735      * {@code BasicStroke} should be used to specify a draw operation.
       
   736      * The {@code thin} parameter indicates whether or not the
       
   737      * transformed {@code BasicStroke} represents coordinates smaller
       
   738      * than the minimum resolution of the antialiasing rasterizer as
       
   739      * specified by the {@code getMinimumAAPenWidth()} method.
       
   740      * <p>
       
   741      * Upon returning, this method will fill the {@code bbox} parameter
       
   742      * with 4 values indicating the bounds of the iteration of the
       
   743      * tile generator.
       
   744      * The iteration order of the tiles will be as specified by the
       
   745      * pseudo-code:
       
   746      * <pre>
       
   747      *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
       
   748      *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
       
   749      *         }
       
   750      *     }
       
   751      * </pre>
       
   752      * If there is no output to be rendered, this method may return
       
   753      * null.
       
   754      *
       
   755      * @param s the shape to be rendered (fill or draw)
       
   756      * @param at the transform to be applied to the shape and the
       
   757      *           stroke attributes
       
   758      * @param clip the current clip in effect in device coordinates
       
   759      * @param bs if non-null, a {@code BasicStroke} whose attributes
       
   760      *           should be applied to this operation
       
   761      * @param thin true if the transformed stroke attributes are smaller
       
   762      *             than the minimum dropout pen width
       
   763      * @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
       
   764      *                  {@code RenderingHint} is in effect
       
   765      * @param bbox returns the bounds of the iteration
       
   766      * @return the {@code AATileGenerator} instance to be consulted
       
   767      *         for tile coverages, or null if there is no output to render
       
   768      * @since 1.7
       
   769      */
       
   770     @Override
       
   771     public AATileGenerator getAATileGenerator(Shape s,
       
   772                                               AffineTransform at,
       
   773                                               Region clip,
       
   774                                               BasicStroke bs,
       
   775                                               boolean thin,
       
   776                                               boolean normalize,
       
   777                                               int[] bbox)
       
   778     {
       
   779         MarlinTileGenerator ptg = null;
       
   780         DRenderer r = null;
       
   781 
       
   782         final DRendererContext rdrCtx = getRendererContext();
       
   783         try {
       
   784             // Test if at is identity:
       
   785             final AffineTransform _at = (at != null && !at.isIdentity()) ? at
       
   786                                         : null;
       
   787 
       
   788             final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
       
   789 
       
   790             if (bs == null) {
       
   791                 // fill shape:
       
   792                 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
       
   793                                                  s.getPathIterator(_at));
       
   794 
       
   795                 // note: Winding rule may be EvenOdd ONLY for fill operations !
       
   796                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
       
   797                                          clip.getWidth(), clip.getHeight(),
       
   798                                          pi.getWindingRule());
       
   799 
       
   800                 // TODO: subdivide quad/cubic curves into monotonic curves ?
       
   801                 pathTo(rdrCtx, pi, r);
       
   802             } else {
       
   803                 // draw shape with given stroke:
       
   804                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
       
   805                                          clip.getWidth(), clip.getHeight(),
       
   806                                          PathIterator.WIND_NON_ZERO);
       
   807 
       
   808                 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
       
   809             }
       
   810             if (r.endRendering()) {
       
   811                 ptg = rdrCtx.ptg.init();
       
   812                 ptg.getBbox(bbox);
       
   813                 // note: do not returnRendererContext(rdrCtx)
       
   814                 // as it will be called later by MarlinTileGenerator.dispose()
       
   815                 r = null;
       
   816             }
       
   817         } finally {
       
   818             if (r != null) {
       
   819                 // dispose renderer and recycle the RendererContext instance:
       
   820                 r.dispose();
       
   821             }
       
   822         }
       
   823 
       
   824         // Return null to cancel AA tile generation (nothing to render)
       
   825         return ptg;
       
   826     }
       
   827 
       
   828     @Override
       
   829     public final AATileGenerator getAATileGenerator(double x, double y,
       
   830                                                     double dx1, double dy1,
       
   831                                                     double dx2, double dy2,
       
   832                                                     double lw1, double lw2,
       
   833                                                     Region clip,
       
   834                                                     int[] bbox)
       
   835     {
       
   836         // REMIND: Deal with large coordinates!
       
   837         double ldx1, ldy1, ldx2, ldy2;
       
   838         boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d);
       
   839 
       
   840         if (innerpgram) {
       
   841             ldx1 = dx1 * lw1;
       
   842             ldy1 = dy1 * lw1;
       
   843             ldx2 = dx2 * lw2;
       
   844             ldy2 = dy2 * lw2;
       
   845             x -= (ldx1 + ldx2) / 2.0d;
       
   846             y -= (ldy1 + ldy2) / 2.0d;
       
   847             dx1 += ldx1;
       
   848             dy1 += ldy1;
       
   849             dx2 += ldx2;
       
   850             dy2 += ldy2;
       
   851             if (lw1 > 1.0d && lw2 > 1.0d) {
       
   852                 // Inner parallelogram was entirely consumed by stroke...
       
   853                 innerpgram = false;
       
   854             }
       
   855         } else {
       
   856             ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
       
   857         }
       
   858 
       
   859         MarlinTileGenerator ptg = null;
       
   860         DRenderer r = null;
       
   861 
       
   862         final DRendererContext rdrCtx = getRendererContext();
       
   863         try {
       
   864             r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
       
   865                                          clip.getWidth(), clip.getHeight(),
       
   866                                          DRenderer.WIND_EVEN_ODD);
       
   867 
       
   868             r.moveTo( x,  y);
       
   869             r.lineTo( (x+dx1),  (y+dy1));
       
   870             r.lineTo( (x+dx1+dx2),  (y+dy1+dy2));
       
   871             r.lineTo( (x+dx2),  (y+dy2));
       
   872             r.closePath();
       
   873 
       
   874             if (innerpgram) {
       
   875                 x += ldx1 + ldx2;
       
   876                 y += ldy1 + ldy2;
       
   877                 dx1 -= 2.0d * ldx1;
       
   878                 dy1 -= 2.0d * ldy1;
       
   879                 dx2 -= 2.0d * ldx2;
       
   880                 dy2 -= 2.0d * ldy2;
       
   881                 r.moveTo( x,  y);
       
   882                 r.lineTo( (x+dx1),  (y+dy1));
       
   883                 r.lineTo( (x+dx1+dx2),  (y+dy1+dy2));
       
   884                 r.lineTo( (x+dx2),  (y+dy2));
       
   885                 r.closePath();
       
   886             }
       
   887             r.pathDone();
       
   888 
       
   889             if (r.endRendering()) {
       
   890                 ptg = rdrCtx.ptg.init();
       
   891                 ptg.getBbox(bbox);
       
   892                 // note: do not returnRendererContext(rdrCtx)
       
   893                 // as it will be called later by MarlinTileGenerator.dispose()
       
   894                 r = null;
       
   895             }
       
   896         } finally {
       
   897             if (r != null) {
       
   898                 // dispose renderer and recycle the RendererContext instance:
       
   899                 r.dispose();
       
   900             }
       
   901         }
       
   902 
       
   903         // Return null to cancel AA tile generation (nothing to render)
       
   904         return ptg;
       
   905     }
       
   906 
       
   907     /**
       
   908      * Returns the minimum pen width that the antialiasing rasterizer
       
   909      * can represent without dropouts occuring.
       
   910      * @since 1.7
       
   911      */
       
   912     @Override
       
   913     public float getMinimumAAPenSize() {
       
   914         return MIN_PEN_SIZE;
       
   915     }
       
   916 
       
   917     static {
       
   918         if (PathIterator.WIND_NON_ZERO != DRenderer.WIND_NON_ZERO ||
       
   919             PathIterator.WIND_EVEN_ODD != DRenderer.WIND_EVEN_ODD ||
       
   920             BasicStroke.JOIN_MITER != DStroker.JOIN_MITER ||
       
   921             BasicStroke.JOIN_ROUND != DStroker.JOIN_ROUND ||
       
   922             BasicStroke.JOIN_BEVEL != DStroker.JOIN_BEVEL ||
       
   923             BasicStroke.CAP_BUTT != DStroker.CAP_BUTT ||
       
   924             BasicStroke.CAP_ROUND != DStroker.CAP_ROUND ||
       
   925             BasicStroke.CAP_SQUARE != DStroker.CAP_SQUARE)
       
   926         {
       
   927             throw new InternalError("mismatched renderer constants");
       
   928         }
       
   929     }
       
   930 
       
   931     // --- DRendererContext handling ---
       
   932     // use ThreadLocal or ConcurrentLinkedQueue to get one DRendererContext
       
   933     private static final boolean USE_THREAD_LOCAL;
       
   934 
       
   935     // reference type stored in either TL or CLQ
       
   936     static final int REF_TYPE;
       
   937 
       
   938     // Per-thread DRendererContext
       
   939     private static final ReentrantContextProvider<DRendererContext> RDR_CTX_PROVIDER;
       
   940 
       
   941     // Static initializer to use TL or CLQ mode
       
   942     static {
       
   943         USE_THREAD_LOCAL = MarlinProperties.isUseThreadLocal();
       
   944 
       
   945         // Soft reference by default:
       
   946         final String refType = AccessController.doPrivileged(
       
   947                             new GetPropertyAction("sun.java2d.renderer.useRef",
       
   948                             "soft"));
       
   949 
       
   950         // Java 1.6 does not support strings in switch:
       
   951         if ("hard".equalsIgnoreCase(refType)) {
       
   952             REF_TYPE = ReentrantContextProvider.REF_HARD;
       
   953         } else if ("weak".equalsIgnoreCase(refType)) {
       
   954             REF_TYPE = ReentrantContextProvider.REF_WEAK;
       
   955         } else {
       
   956             REF_TYPE = ReentrantContextProvider.REF_SOFT;
       
   957         }
       
   958 
       
   959         if (USE_THREAD_LOCAL) {
       
   960             RDR_CTX_PROVIDER = new ReentrantContextProviderTL<DRendererContext>(REF_TYPE)
       
   961                 {
       
   962                     @Override
       
   963                     protected DRendererContext newContext() {
       
   964                         return DRendererContext.createContext();
       
   965                     }
       
   966                 };
       
   967         } else {
       
   968             RDR_CTX_PROVIDER = new ReentrantContextProviderCLQ<DRendererContext>(REF_TYPE)
       
   969                 {
       
   970                     @Override
       
   971                     protected DRendererContext newContext() {
       
   972                         return DRendererContext.createContext();
       
   973                     }
       
   974                 };
       
   975         }
       
   976     }
       
   977 
       
   978     private static boolean SETTINGS_LOGGED = !ENABLE_LOGS;
       
   979 
       
   980     private static void logSettings(final String reClass) {
       
   981         // log information at startup
       
   982         if (SETTINGS_LOGGED) {
       
   983             return;
       
   984         }
       
   985         SETTINGS_LOGGED = true;
       
   986 
       
   987         String refType;
       
   988         switch (REF_TYPE) {
       
   989             default:
       
   990             case ReentrantContextProvider.REF_HARD:
       
   991                 refType = "hard";
       
   992                 break;
       
   993             case ReentrantContextProvider.REF_SOFT:
       
   994                 refType = "soft";
       
   995                 break;
       
   996             case ReentrantContextProvider.REF_WEAK:
       
   997                 refType = "weak";
       
   998                 break;
       
   999         }
       
  1000 
       
  1001         logInfo("=========================================================="
       
  1002                 + "=====================");
       
  1003 
       
  1004         logInfo("Marlin software rasterizer           = ENABLED");
       
  1005         logInfo("Version                              = ["
       
  1006                 + Version.getVersion() + "]");
       
  1007         logInfo("sun.java2d.renderer                  = "
       
  1008                 + reClass);
       
  1009         logInfo("sun.java2d.renderer.useThreadLocal   = "
       
  1010                 + USE_THREAD_LOCAL);
       
  1011         logInfo("sun.java2d.renderer.useRef           = "
       
  1012                 + refType);
       
  1013 
       
  1014         logInfo("sun.java2d.renderer.edges            = "
       
  1015                 + MarlinConst.INITIAL_EDGES_COUNT);
       
  1016         logInfo("sun.java2d.renderer.pixelsize        = "
       
  1017                 + MarlinConst.INITIAL_PIXEL_DIM);
       
  1018 
       
  1019         logInfo("sun.java2d.renderer.subPixel_log2_X  = "
       
  1020                 + MarlinConst.SUBPIXEL_LG_POSITIONS_X);
       
  1021         logInfo("sun.java2d.renderer.subPixel_log2_Y  = "
       
  1022                 + MarlinConst.SUBPIXEL_LG_POSITIONS_Y);
       
  1023 
       
  1024         logInfo("sun.java2d.renderer.tileSize_log2    = "
       
  1025                 + MarlinConst.TILE_H_LG);
       
  1026         logInfo("sun.java2d.renderer.tileWidth_log2   = "
       
  1027                 + MarlinConst.TILE_W_LG);
       
  1028         logInfo("sun.java2d.renderer.blockSize_log2   = "
       
  1029                 + MarlinConst.BLOCK_SIZE_LG);
       
  1030 
       
  1031         // RLE / blockFlags settings
       
  1032 
       
  1033         logInfo("sun.java2d.renderer.forceRLE         = "
       
  1034                 + MarlinProperties.isForceRLE());
       
  1035         logInfo("sun.java2d.renderer.forceNoRLE       = "
       
  1036                 + MarlinProperties.isForceNoRLE());
       
  1037         logInfo("sun.java2d.renderer.useTileFlags     = "
       
  1038                 + MarlinProperties.isUseTileFlags());
       
  1039         logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
       
  1040                 + MarlinProperties.isUseTileFlagsWithHeuristics());
       
  1041         logInfo("sun.java2d.renderer.rleMinWidth      = "
       
  1042                 + MarlinCache.RLE_MIN_WIDTH);
       
  1043 
       
  1044         // optimisation parameters
       
  1045         logInfo("sun.java2d.renderer.useSimplifier    = "
       
  1046                 + MarlinConst.USE_SIMPLIFIER);
       
  1047 
       
  1048         // debugging parameters
       
  1049         logInfo("sun.java2d.renderer.doStats          = "
       
  1050                 + MarlinConst.DO_STATS);
       
  1051         logInfo("sun.java2d.renderer.doMonitors       = "
       
  1052                 + MarlinConst.DO_MONITORS);
       
  1053         logInfo("sun.java2d.renderer.doChecks         = "
       
  1054                 + MarlinConst.DO_CHECKS);
       
  1055 
       
  1056         // logging parameters
       
  1057         logInfo("sun.java2d.renderer.useLogger        = "
       
  1058                 + MarlinConst.USE_LOGGER);
       
  1059         logInfo("sun.java2d.renderer.logCreateContext = "
       
  1060                 + MarlinConst.LOG_CREATE_CONTEXT);
       
  1061         logInfo("sun.java2d.renderer.logUnsafeMalloc  = "
       
  1062                 + MarlinConst.LOG_UNSAFE_MALLOC);
       
  1063 
       
  1064         // quality settings
       
  1065         logInfo("sun.java2d.renderer.cubic_dec_d2     = "
       
  1066                 + MarlinProperties.getCubicDecD2());
       
  1067         logInfo("sun.java2d.renderer.cubic_inc_d1     = "
       
  1068                 + MarlinProperties.getCubicIncD1());
       
  1069         logInfo("sun.java2d.renderer.quad_dec_d2      = "
       
  1070                 + MarlinProperties.getQuadDecD2());
       
  1071 
       
  1072         logInfo("Renderer settings:");
       
  1073         logInfo("CUB_DEC_BND  = " + DRenderer.CUB_DEC_BND);
       
  1074         logInfo("CUB_INC_BND  = " + DRenderer.CUB_INC_BND);
       
  1075         logInfo("QUAD_DEC_BND = " + DRenderer.QUAD_DEC_BND);
       
  1076 
       
  1077         logInfo("INITIAL_EDGES_CAPACITY               = "
       
  1078                 + MarlinConst.INITIAL_EDGES_CAPACITY);
       
  1079         logInfo("INITIAL_CROSSING_COUNT               = "
       
  1080                 + DRenderer.INITIAL_CROSSING_COUNT);
       
  1081 
       
  1082         logInfo("=========================================================="
       
  1083                 + "=====================");
       
  1084     }
       
  1085 
       
  1086     /**
       
  1087      * Get the DRendererContext instance dedicated to the current thread
       
  1088      * @return DRendererContext instance
       
  1089      */
       
  1090     @SuppressWarnings({"unchecked"})
       
  1091     static DRendererContext getRendererContext() {
       
  1092         final DRendererContext rdrCtx = RDR_CTX_PROVIDER.acquire();
       
  1093         if (DO_MONITORS) {
       
  1094             rdrCtx.stats.mon_pre_getAATileGenerator.start();
       
  1095         }
       
  1096         return rdrCtx;
       
  1097     }
       
  1098 
       
  1099     /**
       
  1100      * Reset and return the given DRendererContext instance for reuse
       
  1101      * @param rdrCtx DRendererContext instance
       
  1102      */
       
  1103     static void returnRendererContext(final DRendererContext rdrCtx) {
       
  1104         rdrCtx.dispose();
       
  1105 
       
  1106         if (DO_MONITORS) {
       
  1107             rdrCtx.stats.mon_pre_getAATileGenerator.stop();
       
  1108         }
       
  1109         RDR_CTX_PROVIDER.release(rdrCtx);
       
  1110     }
       
  1111 }