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