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.getCallerInfo;
  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 implements MarlinConst {
  41 
  42     private static final String className = RendererContext.class.getName();
  43     // RendererContext creation counter
  44     private static final AtomicInteger contextCount = new AtomicInteger(1);
  45     // RendererContext statistics
  46     static final RendererStats stats = (doStats || doMonitors)
  47                                        ? RendererStats.getInstance(): null;
  48 
  49     private static final boolean USE_CACHE_HARD_REF = doStats
  50         || (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK);
  51 
  52     /**
  53      * Create a new renderer context
  54      *
  55      * @return new RendererContext instance
  56      */
  57     static RendererContext createContext() {
  58         final RendererContext newCtx = new RendererContext("ctx"
  59                     + Integer.toString(contextCount.getAndIncrement()));
  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     /*
  69      * Reference to this instance (hard, soft or weak).
  70      * @see MarlinRenderingEngine#REF_TYPE
  71      */
  72     final Object reference;
  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                 }
 193 
 194                 // update weak reference:
 195                 refArrayCaches = new WeakReference<ArrayCachesHolder>(holder);
 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                     + "\tfrom=\t" + getCallerInfo(className));
 219         }
 220 
 221         return new byte[length];
 222     }
 223 
 224     void putDirtyByteArray(final byte[] array) {
 225         final int length = array.length;
 226         // odd sized array are non-cached arrays (initial arrays)
 227         // ensure to never store initial arrays in cache:
 228         if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) {
 229             getDirtyByteArrayCache(length).putDirtyArray(array, length);
 230         }
 231     }
 232 
 233     byte[] widenDirtyByteArray(final byte[] in,
 234                                final int usedSize, final int needSize)
 235     {
 236         final int length = in.length;
 237         if (doChecks && length >= needSize) {
 238             return in;
 239         }
 240         if (doStats) {
 241             incResizeDirtyByte();
 242         }
 243 
 244         // maybe change bucket:
 245         // ensure getNewSize() > newSize:
 246         final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize));
 247 
 248         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 249 
 250         // maybe return current array:
 251         // NO clean-up of array data = DIRTY ARRAY
 252         putDirtyByteArray(in);
 253 
 254         if (doLogWidenArray) {
 255             logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t"
 256                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 257                     + needSize + "\tfrom=\t" + getCallerInfo(className));
 258         }
 259         return res;
 260     }
 261 
 262     // int array cache
 263     IntArrayCache getIntArrayCache(final int length) {
 264         final int bucket = ArrayCache.getBucket(length);
 265         return getArrayCachesHolder().intArrayCaches[bucket];
 266     }
 267 
 268     int[] getIntArray(final int length) {
 269         if (length <= MAX_ARRAY_SIZE) {
 270             return getIntArrayCache(length).getArray();
 271         }
 272 
 273         if (doStats) {
 274             incOversize();
 275         }
 276 
 277         if (doLogOverSize) {
 278             logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t"
 279                     + getCallerInfo(className));
 280         }
 281 
 282         return new int[length];
 283     }
 284 
 285     // unused
 286     int[] widenIntArray(final int[] in, final int usedSize,
 287                         final int needSize, final int clearTo)
 288     {
 289         final int length = in.length;
 290         if (doChecks && length >= needSize) {
 291             return in;
 292         }
 293         if (doStats) {
 294             incResizeInt();
 295         }
 296 
 297         // maybe change bucket:
 298         // ensure getNewSize() > newSize:
 299         final int[] res = getIntArray(getNewSize(usedSize, needSize));
 300 
 301         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 302 
 303         // maybe return current array:
 304         putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo)
 305 
 306         if (doLogWidenArray) {
 307             logInfo("widenIntArray[" + res.length + "]: usedSize=\t"
 308                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 309                     + needSize + "\tfrom=\t" + getCallerInfo(className));
 310         }
 311         return res;
 312     }
 313 
 314     void putIntArray(final int[] array, final int fromIndex,
 315                      final int toIndex)
 316     {
 317         final int length = array.length;
 318         // odd sized array are non-cached arrays (initial arrays)
 319         // ensure to never store initial arrays in cache:
 320         if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
 321             getIntArrayCache(length).putArray(array, length, fromIndex, toIndex);
 322         }
 323     }
 324 
 325     // dirty int array cache
 326     IntArrayCache getDirtyIntArrayCache(final int length) {
 327         final int bucket = ArrayCache.getBucket(length);
 328         return getArrayCachesHolder().dirtyIntArrayCaches[bucket];
 329     }
 330 
 331     int[] getDirtyIntArray(final int length) {
 332         if (length <= MAX_ARRAY_SIZE) {
 333             return getDirtyIntArrayCache(length).getArray();
 334         }
 335 
 336         if (doStats) {
 337             incOversize();
 338         }
 339 
 340         if (doLogOverSize) {
 341             logInfo("getDirtyIntArray[oversize]: length=\t" + length
 342                     + "\tfrom=\t" + getCallerInfo(className));
 343         }
 344 
 345         return new int[length];
 346     }
 347 
 348     int[] widenDirtyIntArray(final int[] in,
 349                              final int usedSize, final int needSize)
 350     {
 351         final int length = in.length;
 352         if (doChecks && length >= needSize) {
 353             return in;
 354         }
 355         if (doStats) {
 356             incResizeDirtyInt();
 357         }
 358 
 359         // maybe change bucket:
 360         // ensure getNewSize() > newSize:
 361         final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize));
 362 
 363         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 364 
 365         // maybe return current array:
 366         // NO clean-up of array data = DIRTY ARRAY
 367         putDirtyIntArray(in);
 368 
 369         if (doLogWidenArray) {
 370             logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t"
 371                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 372                     + needSize + "\tfrom=\t" + getCallerInfo(className));
 373         }
 374         return res;
 375     }
 376 
 377     void putDirtyIntArray(final int[] array) {
 378         final int length = array.length;
 379         // odd sized array are non-cached arrays (initial arrays)
 380         // ensure to never store initial arrays in cache:
 381         if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
 382             getDirtyIntArrayCache(length).putDirtyArray(array, length);
 383         }
 384     }
 385 
 386     // dirty float array cache
 387     FloatArrayCache getDirtyFloatArrayCache(final int length) {
 388         final int bucket = ArrayCache.getBucket(length);
 389         return getArrayCachesHolder().dirtyFloatArrayCaches[bucket];
 390     }
 391 
 392     float[] getDirtyFloatArray(final int length) {
 393         if (length <= MAX_ARRAY_SIZE) {
 394             return getDirtyFloatArrayCache(length).getArray();
 395         }
 396 
 397         if (doStats) {
 398             incOversize();
 399         }
 400 
 401         if (doLogOverSize) {
 402             logInfo("getDirtyFloatArray[oversize]: length=\t" + length
 403                     + "\tfrom=\t" + getCallerInfo(className));
 404         }
 405 
 406         return new float[length];
 407     }
 408 
 409     float[] widenDirtyFloatArray(final float[] in,
 410                                  final int usedSize, final int needSize)
 411     {
 412         final int length = in.length;
 413         if (doChecks && length >= needSize) {
 414             return in;
 415         }
 416         if (doStats) {
 417             incResizeDirtyFloat();
 418         }
 419 
 420         // maybe change bucket:
 421         // ensure getNewSize() > newSize:
 422         final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize));
 423 
 424         System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
 425 
 426         // maybe return current array:
 427         // NO clean-up of array data = DIRTY ARRAY
 428         putDirtyFloatArray(in);
 429 
 430         if (doLogWidenArray) {
 431             logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t"
 432                     + usedSize + "\tlength=\t" + length + "\tneeded length=\t"
 433                     + needSize + "\tfrom=\t" + getCallerInfo(className));
 434         }
 435         return res;
 436     }
 437 
 438     void putDirtyFloatArray(final float[] array) {
 439         final int length = array.length;
 440         // odd sized array are non-cached arrays (initial arrays)
 441         // ensure to never store initial arrays in cache:
 442         if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
 443             getDirtyFloatArrayCache(length).putDirtyArray(array, length);
 444         }
 445     }
 446 
 447     /* class holding all array cache instances */
 448     static final class ArrayCachesHolder {
 449         // zero-filled int array cache:
 450         final IntArrayCache[] intArrayCaches;
 451         // dirty array caches:
 452         final IntArrayCache[] dirtyIntArrayCaches;
 453         final FloatArrayCache[] dirtyFloatArrayCaches;
 454         final ByteArrayCache[] dirtyByteArrayCaches;
 455 
 456         ArrayCachesHolder() {
 457             intArrayCaches = new IntArrayCache[BUCKETS];
 458             dirtyIntArrayCaches = new IntArrayCache[BUCKETS];
 459             dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS];
 460             dirtyByteArrayCaches = new ByteArrayCache[BUCKETS];
 461 
 462             for (int i = 0; i < BUCKETS; i++) {
 463                 intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
 464                 // dirty array caches:
 465                 dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
 466                 dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]);
 467                 dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]);
 468             }
 469         }
 470     }
 471 }