blob: 1a3e698b564814046b17004b1d024a3680cdf2a8 [file] [log] [blame]
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.awt.geom.Path2D;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
import sun.java2d.ReentrantContext;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator;
import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
/**
* This class is a renderer context dedicated to a single thread
*/
final class DRendererContext extends ReentrantContext implements IRendererContext {
// RendererContext creation counter
private static final AtomicInteger CTX_COUNT = new AtomicInteger(1);
/**
* Create a new renderer context
*
* @return new RendererContext instance
*/
static DRendererContext createContext() {
return new DRendererContext("ctx"
+ Integer.toString(CTX_COUNT.getAndIncrement()));
}
// Smallest object used as Cleaner's parent reference
private final Object cleanerObj;
// dirty flag indicating an exception occured during pipeline in pathTo()
boolean dirty = false;
// shared data
final double[] double6 = new double[6];
// shared curve (dirty) (Renderer / Stroker)
final DCurve curve = new DCurve();
// MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
final NormalizingPathIterator nPCPathIterator;
// MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator:
final NormalizingPathIterator nPQPathIterator;
// MarlinRenderingEngine.TransformingPathConsumer2D
final DTransformingPathConsumer2D transformerPC2D;
// recycled Path2D instance (weak)
private WeakReference<Path2D.Double> refPath2D = null;
final DRenderer renderer;
final DStroker stroker;
// Simplifies out collinear lines
final DCollinearSimplifier simplifier = new DCollinearSimplifier();
// Simplifies path
final DPathSimplifier pathSimplifier = new DPathSimplifier();
final DDasher dasher;
final MarlinTileGenerator ptg;
final MarlinCache cache;
// flag indicating the shape is stroked (1) or filled (0)
int stroking = 0;
// flag indicating to clip the shape
boolean doClip = false;
// flag indicating if the path is closed or not (in advance) to handle properly caps
boolean closedPath = false;
// clip rectangle (ymin, ymax, xmin, xmax):
final double[] clipRect = new double[4];
// clip inverse scale (mean) to adjust length checks
double clipInvScale = 0.0d;
// CurveBasicMonotonizer instance
final CurveBasicMonotonizer monotonizer;
// CurveClipSplitter instance
final CurveClipSplitter curveClipSplitter;
// Array caches:
/* clean int[] cache (zero-filled) = 5 refs */
private final IntArrayCache cleanIntCache = new IntArrayCache(true, 5);
/* dirty int[] cache = 5 refs */
private final IntArrayCache dirtyIntCache = new IntArrayCache(false, 5);
/* dirty double[] cache = 4 refs (2 polystack) */
private final DoubleArrayCache dirtyDoubleCache = new DoubleArrayCache(false, 4);
/* dirty byte[] cache = 2 ref (2 polystack) */
private final ByteArrayCache dirtyByteCache = new ByteArrayCache(false, 2);
// RendererContext statistics
final RendererStats stats;
final PathConsumer2DAdapter p2dAdapter = new PathConsumer2DAdapter();
/**
* Constructor
*
* @param name context name (debugging)
*/
DRendererContext(final String name) {
if (LOG_CREATE_CONTEXT) {
MarlinUtils.logInfo("new RendererContext = " + name);
}
this.cleanerObj = new Object();
// create first stats (needed by newOffHeapArray):
if (DO_STATS || DO_MONITORS) {
stats = RendererStats.createInstance(cleanerObj, name);
// push cache stats:
stats.cacheStats = new CacheStats[] { cleanIntCache.stats,
dirtyIntCache.stats, dirtyDoubleCache.stats, dirtyByteCache.stats
};
} else {
stats = null;
}
// NormalizingPathIterator instances:
nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6);
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(double6);
// curve monotonizer & clip subdivider (before transformerPC2D init)
monotonizer = new CurveBasicMonotonizer(this);
curveClipSplitter = new CurveClipSplitter(this);
// MarlinRenderingEngine.TransformingPathConsumer2D
transformerPC2D = new DTransformingPathConsumer2D(this);
// Renderer:
cache = new MarlinCache(this);
renderer = new DRenderer(this); // needs MarlinCache from rdrCtx.cache
ptg = new MarlinTileGenerator(stats, renderer, cache);
stroker = new DStroker(this);
dasher = new DDasher(this);
}
/**
* Disposes this renderer context:
* clean up before reusing this context
*/
void dispose() {
if (DO_STATS) {
if (stats.totalOffHeap > stats.totalOffHeapMax) {
stats.totalOffHeapMax = stats.totalOffHeap;
}
stats.totalOffHeap = 0L;
}
stroking = 0;
doClip = false;
closedPath = false;
clipInvScale = 0.0d;
// if context is maked as DIRTY:
if (dirty) {
// may happen if an exception if thrown in the pipeline processing:
// force cleanup of all possible pipelined blocks (except Renderer):
// NormalizingPathIterator instances:
this.nPCPathIterator.dispose();
this.nPQPathIterator.dispose();
// Dasher:
this.dasher.dispose();
// Stroker:
this.stroker.dispose();
// mark context as CLEAN:
dirty = false;
}
}
Path2D.Double getPath2D() {
// resolve reference:
Path2D.Double p2d = (refPath2D != null) ? refPath2D.get() : null;
// create a new Path2D ?
if (p2d == null) {
p2d = new Path2D.Double(WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
// update weak reference:
refPath2D = new WeakReference<Path2D.Double>(p2d);
}
// reset the path anyway:
p2d.reset();
return p2d;
}
@Override
public RendererStats stats() {
return stats;
}
@Override
public OffHeapArray newOffHeapArray(final long initialSize) {
if (DO_STATS) {
stats.totalOffHeapInitial += initialSize;
}
return new OffHeapArray(cleanerObj, initialSize);
}
@Override
public IntArrayCache.Reference newCleanIntArrayRef(final int initialSize) {
return cleanIntCache.createRef(initialSize);
}
IntArrayCache.Reference newDirtyIntArrayRef(final int initialSize) {
return dirtyIntCache.createRef(initialSize);
}
DoubleArrayCache.Reference newDirtyDoubleArrayRef(final int initialSize) {
return dirtyDoubleCache.createRef(initialSize);
}
ByteArrayCache.Reference newDirtyByteArrayRef(final int initialSize) {
return dirtyByteCache.createRef(initialSize);
}
static final class PathConsumer2DAdapter implements DPathConsumer2D {
private sun.awt.geom.PathConsumer2D out;
PathConsumer2DAdapter() {}
PathConsumer2DAdapter init(sun.awt.geom.PathConsumer2D out) {
this.out = out;
return this;
}
@Override
public void moveTo(double x0, double y0) {
out.moveTo((float)x0, (float)y0);
}
@Override
public void lineTo(double x1, double y1) {
out.lineTo((float)x1, (float)y1);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
out.curveTo((float)x1, (float)y1,
(float)x2, (float)y2,
(float)x3, (float)y3);
}
@Override
public void quadTo(double x1, double y1, double x2, double y2) {
out.quadTo((float)x1, (float)y1, (float)x2, (float)y2);
}
@Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
}
}