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