1 /*
   2  * Copyright (c) 2015, 2016, 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.geom.Path2D;
  29 import java.lang.ref.SoftReference;
  30 import java.lang.ref.WeakReference;
  31 import java.util.concurrent.atomic.AtomicInteger;
  32 import static sun.java2d.marlin.ArrayCache.*;
  33 import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
  34 import static sun.java2d.marlin.MarlinUtils.logInfo;
  35 
  36 /**
  37  * This class is a renderer context dedicated to a single thread
  38  */
  39 final class RendererContext implements MarlinConst {
  40 
  41     // RendererContext creation counter
  42     private static final AtomicInteger contextCount = new AtomicInteger(1);
  43     // RendererContext statistics
  44     static final RendererStats stats = (doStats || doMonitors)
  45                                        ? RendererStats.getInstance(): null;
  46 
  47     private static final boolean USE_CACHE_HARD_REF = doStats
  48         || (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK);
  49 
  50     /**
  51      * Create a new renderer context
  52      *
  53      * @param storageQueue true = queue storage; false = thread-Local storage
  54      * @return new RendererContext instance
  55      */
  56     static RendererContext createContext(final boolean storageQueue) {
  57         final RendererContext newCtx = new RendererContext("ctx"
  58                     + Integer.toString(contextCount.getAndIncrement()),
  59                     storageQueue);
  60 
  61         if (RendererContext.stats != null) {
  62             RendererContext.stats.allContexts.add(newCtx);
  63         }
  64         return newCtx;
  65     }
  66 
  67     // context name (debugging purposes)
  68     final String name;
  69     // true = queue storage; false = thread-Local storage
  70     final boolean storageQueue;
  71     /*
  72      * Used flag indicating this thread-Local context is already used
  73      * (used to detect reentrance)
  74      */
  75     boolean usedTL = false;
  76     /*
  77      * Reference to this instance (hard, soft or weak).
  78      * @see MarlinRenderingEngine#REF_TYPE
  79      */
  80     final Object reference;
  81     // Smallest object used as Cleaner's parent reference
  82     final Object cleanerObj = new Object();
  83     // dirty flag indicating an exception occured during pipeline in pathTo()
  84     boolean dirty = false;
  85     // dynamic array caches kept using weak reference (low memory footprint)
  86     WeakReference<ArrayCachesHolder> refArrayCaches = null;
  87     // hard reference to array caches (for statistics)
  88     ArrayCachesHolder hardRefArrayCaches = null;
  89     // shared data
  90     final float[] float6 = new float[6];
  91     // shared curve (dirty) (Renderer / Stroker)
  92     final Curve curve = new Curve();
  93     // MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
  94     final NormalizingPathIterator nPCPathIterator;
  95     // MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator:
  96     final NormalizingPathIterator nPQPathIterator;
  97     // MarlinRenderingEngine.TransformingPathConsumer2D
  98     final TransformingPathConsumer2D transformerPC2D;
  99     // recycled Path2D instance
 100     Path2D.Float p2d = null;
 101     final Renderer renderer;
 102     final Stroker stroker;
 103     // Simplifies out collinear lines
 104     final CollinearSimplifier simplifier = new CollinearSimplifier();
 105     final Dasher dasher;
 106     final MarlinTileGenerator ptg;
 107     final MarlinCache cache;
 108     // flag indicating the shape is stroked (1) or filled (0)
 109     int stroking = 0;
 110 
 111     /**
 112      * Constructor
 113      *
 114      * @param name context name (debugging)
 115      * @param storageQueue true = queue storage; false = thread-Local storage
 116      */
 117     RendererContext(final String name, final boolean storageQueue) {
 118         if (logCreateContext) {
 119             MarlinUtils.logInfo("new RendererContext = " + name);
 120         }
 121 
 122         this.name = name;
 123         this.storageQueue = storageQueue;
 124 
 125         // NormalizingPathIterator instances:
 126         nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6);
 127         nPQPathIterator  = new NormalizingPathIterator.NearestPixelQuarter(float6);
 128 
 129         // MarlinRenderingEngine.TransformingPathConsumer2D
 130         transformerPC2D = new TransformingPathConsumer2D();
 131 
 132         // Renderer:
 133         cache = new MarlinCache(this);
 134         renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache
 135         ptg = new MarlinTileGenerator(renderer);
 136 
 137         stroker = new Stroker(this);
 138         dasher = new Dasher(this);
 139 
 140         // Create the reference to this instance (hard, soft or weak):
 141         switch (MarlinRenderingEngine.REF_TYPE) {
 142             default:
 143             case MarlinRenderingEngine.REF_HARD:
 144                 reference = this;
 145                 break;
 146             case MarlinRenderingEngine.REF_SOFT:
 147                 reference = new SoftReference<RendererContext>(this);
 148                 break;
 149             case MarlinRenderingEngine.REF_WEAK:
 150                 reference = new WeakReference<RendererContext>(this);
 151                 break;
 152         }
 153     }
 154 
 155     /**
 156      * Disposes this renderer context:
 157      * clean up before reusing this context
 158      */
 159     void dispose() {
 160         stroking = 0;
 161         // reset hard reference to array caches if needed:
 162         if (!USE_CACHE_HARD_REF) {
 163             hardRefArrayCaches = null;
 164         }
 165         // if context is maked as DIRTY:
 166         if (dirty) {
 167             // may happen if an exception if thrown in the pipeline processing:
 168             // force cleanup of all possible pipelined blocks (except Renderer):
 169 
 170             // NormalizingPathIterator instances:
 171             this.nPCPathIterator.dispose();
 172             this.nPQPathIterator.dispose();
 173             // Dasher:
 174             this.dasher.dispose();
 175             // Stroker:
 176             this.stroker.dispose();
 177 
 178             // mark context as CLEAN:
 179             dirty = false;
 180         }
 181     }
 182 
 183     // Array caches
 184     ArrayCachesHolder getArrayCachesHolder() {
 185         // Use hard reference first (cached resolved weak reference):
 186         ArrayCachesHolder holder = hardRefArrayCaches;
 187         if (holder == null) {
 188             // resolve reference:
 189             holder = (refArrayCaches != null)
 190                      ? refArrayCaches.get()
 191                      : null;
 192             // create a new ArrayCachesHolder if none is available
 193             if (holder == null) {
 194                 if (logCreateContext) {
 195                     MarlinUtils.logInfo("new ArrayCachesHolder for "
 196                                         + "RendererContext = " + name);
 197                 }
 198 
 199                 holder = new ArrayCachesHolder();
 200 
 201                 if (USE_CACHE_HARD_REF) {
 202                     // update hard reference:
 203                     hardRefArrayCaches = holder;
 204                 } else {
 205                     // update weak reference:
 206                     refArrayCaches = new WeakReference<ArrayCachesHolder>(holder);
 207                 }
 208             }
 209         }
 210         return holder;
 211     }
 212 
 213     // dirty byte array cache
 214     ByteArrayCache getDirtyByteArrayCache(final int length) {
 215         final int bucket = ArrayCache.getBucketDirtyBytes(length);
 216         return getArrayCachesHolder().dirtyByteArrayCaches[bucket];
 217     }
 218 
 219     byte[] getDirtyByteArray(final int length) {
 220         if (length <= MAX_DIRTY_BYTE_ARRAY_SIZE) {
 221             return getDirtyByteArrayCache(length).getArray();
 222         }
 223 
 224         if (doStats) {
 225             incOversize();
 226         }
 227 
 228         if (doLogOverSize) {
 229             logInfo("getDirtyByteArray[oversize]: length=\t" + length);
 230         }
 231 
 232         return new byte[length];
 233     }
 234 
 235     void putDirtyByteArray(final byte[] array) {
 236         final int length = array.length;
 237         // odd sized array are non-cached arrays (initial arrays)
 238         // ensure to never store initial arrays in cache:
 239         if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) {
 240             getDirtyByteArrayCache(length).putDirtyArray(array, length);
 241         }
 242     }
 243 
 244     byte[] widenDirtyByteArray(final byte[] in,
 245                                final int usedSize, final int needSize)
 246     {
 247         final int length = in.length;
 248         if (doChecks && length >= needSize) {
 249             return in;
 250         }
 251         if (doStats) {
 252             incResizeDirtyByte();
 253         }
 254 
 255         // maybe change bucket:
 256         // ensure getNewSize() > newSize:
 257         final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize));
 258 
 259         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 260 
 261         // maybe return current array:
 262         // NO clean-up of array data = DIRTY ARRAY
 263         putDirtyByteArray(in);
 264 
 265         if (doLogWidenArray) {
 266             logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t"
 267                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 268                     + needSize);
 269         }
 270         return res;
 271     }
 272 
 273     // int array cache
 274     IntArrayCache getIntArrayCache(final int length) {
 275         final int bucket = ArrayCache.getBucket(length);
 276         return getArrayCachesHolder().intArrayCaches[bucket];
 277     }
 278 
 279     int[] getIntArray(final int length) {
 280         if (length <= MAX_ARRAY_SIZE) {
 281             return getIntArrayCache(length).getArray();
 282         }
 283 
 284         if (doStats) {
 285             incOversize();
 286         }
 287 
 288         if (doLogOverSize) {
 289             logInfo("getIntArray[oversize]: length=\t" + length);
 290         }
 291 
 292         return new int[length];
 293     }
 294 
 295     // unused
 296     int[] widenIntArray(final int[] in, final int usedSize,
 297                         final int needSize, final int clearTo)
 298     {
 299         final int length = in.length;
 300         if (doChecks && length >= needSize) {
 301             return in;
 302         }
 303         if (doStats) {
 304             incResizeInt();
 305         }
 306 
 307         // maybe change bucket:
 308         // ensure getNewSize() > newSize:
 309         final int[] res = getIntArray(getNewSize(usedSize, needSize));
 310 
 311         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 312 
 313         // maybe return current array:
 314         putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo)
 315 
 316         if (doLogWidenArray) {
 317             logInfo("widenIntArray[" + res.length + "]: usedSize=\t"
 318                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 319                     + needSize);
 320         }
 321         return res;
 322     }
 323 
 324     void putIntArray(final int[] array, final int fromIndex,
 325                      final int toIndex)
 326     {
 327         final int length = array.length;
 328         // odd sized array are non-cached arrays (initial arrays)
 329         // ensure to never store initial arrays in cache:
 330         if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
 331             getIntArrayCache(length).putArray(array, length, fromIndex, toIndex);
 332         }
 333     }
 334 
 335     // dirty int array cache
 336     IntArrayCache getDirtyIntArrayCache(final int length) {
 337         final int bucket = ArrayCache.getBucket(length);
 338         return getArrayCachesHolder().dirtyIntArrayCaches[bucket];
 339     }
 340 
 341     int[] getDirtyIntArray(final int length) {
 342         if (length <= MAX_ARRAY_SIZE) {
 343             return getDirtyIntArrayCache(length).getArray();
 344         }
 345 
 346         if (doStats) {
 347             incOversize();
 348         }
 349 
 350         if (doLogOverSize) {
 351             logInfo("getDirtyIntArray[oversize]: length=\t" + length);
 352         }
 353 
 354         return new int[length];
 355     }
 356 
 357     int[] widenDirtyIntArray(final int[] in,
 358                              final int usedSize, final int needSize)
 359     {
 360         final int length = in.length;
 361         if (doChecks && length >= needSize) {
 362             return in;
 363         }
 364         if (doStats) {
 365             incResizeDirtyInt();
 366         }
 367 
 368         // maybe change bucket:
 369         // ensure getNewSize() > newSize:
 370         final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize));
 371 
 372         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 373 
 374         // maybe return current array:
 375         // NO clean-up of array data = DIRTY ARRAY
 376         putDirtyIntArray(in);
 377 
 378         if (doLogWidenArray) {
 379             logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t"
 380                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 381                     + needSize);
 382         }
 383         return res;
 384     }
 385 
 386     void putDirtyIntArray(final int[] array) {
 387         final int length = array.length;
 388         // odd sized array are non-cached arrays (initial arrays)
 389         // ensure to never store initial arrays in cache:
 390         if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
 391             getDirtyIntArrayCache(length).putDirtyArray(array, length);
 392         }
 393     }
 394 
 395     // dirty float array cache
 396     FloatArrayCache getDirtyFloatArrayCache(final int length) {
 397         final int bucket = ArrayCache.getBucket(length);
 398         return getArrayCachesHolder().dirtyFloatArrayCaches[bucket];
 399     }
 400 
 401     float[] getDirtyFloatArray(final int length) {
 402         if (length <= MAX_ARRAY_SIZE) {
 403             return getDirtyFloatArrayCache(length).getArray();
 404         }
 405 
 406         if (doStats) {
 407             incOversize();
 408         }
 409 
 410         if (doLogOverSize) {
 411             logInfo("getDirtyFloatArray[oversize]: length=\t" + length);
 412         }
 413 
 414         return new float[length];
 415     }
 416 
 417     float[] widenDirtyFloatArray(final float[] in,
 418                                  final int usedSize, final int needSize)
 419     {
 420         final int length = in.length;
 421         if (doChecks && length >= needSize) {
 422             return in;
 423         }
 424         if (doStats) {
 425             incResizeDirtyFloat();
 426         }
 427 
 428         // maybe change bucket:
 429         // ensure getNewSize() > newSize:
 430         final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize));
 431 
 432         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 433 
 434         // maybe return current array:
 435         // NO clean-up of array data = DIRTY ARRAY
 436         putDirtyFloatArray(in);
 437 
 438         if (doLogWidenArray) {
 439             logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t"
 440                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 441                     + needSize);
 442         }
 443         return res;
 444     }
 445 
 446     void putDirtyFloatArray(final float[] array) {
 447         final int length = array.length;
 448         // odd sized array are non-cached arrays (initial arrays)
 449         // ensure to never store initial arrays in cache:
 450         if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
 451             getDirtyFloatArrayCache(length).putDirtyArray(array, length);
 452         }
 453     }
 454 
 455     /* class holding all array cache instances */
 456     static final class ArrayCachesHolder {
 457         // zero-filled int array cache:
 458         final IntArrayCache[] intArrayCaches;
 459         // dirty array caches:
 460         final IntArrayCache[] dirtyIntArrayCaches;
 461         final FloatArrayCache[] dirtyFloatArrayCaches;
 462         final ByteArrayCache[] dirtyByteArrayCaches;
 463 
 464         ArrayCachesHolder() {
 465             intArrayCaches = new IntArrayCache[BUCKETS];
 466             dirtyIntArrayCaches = new IntArrayCache[BUCKETS];
 467             dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS];
 468             dirtyByteArrayCaches = new ByteArrayCache[BUCKETS];
 469 
 470             for (int i = 0; i < BUCKETS; i++) {
 471                 intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
 472                 // dirty array caches:
 473                 dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
 474                 dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]);
 475                 dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]);
 476             }
 477         }
 478     }
 479 }