8148886: SEGV in sun.java2d.marlin.Renderer._endRendering
Handle reentrancy in both AAShapePipe and MarlinRenderingEngine using new sun.java2d.ReentrantContextProvider implementations Reviewed-by: flar, prr
This commit is contained in:
parent
c067e0428b
commit
1036ce73ea
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
/**
|
||||
* ReentrantContext is a base class to hold thread-local data supporting
|
||||
* reentrancy in either a ThreadLocal or a ConcurrentLinkedQueue
|
||||
*
|
||||
* @see ReentrantContextProvider
|
||||
*/
|
||||
public class ReentrantContext {
|
||||
// usage stored as a byte
|
||||
byte usage = ReentrantContextProvider.USAGE_TL_INACTIVE;
|
||||
/*
|
||||
* Reference to this instance (hard, soft or weak).
|
||||
* @see ReentrantContextProvider#refType
|
||||
*/
|
||||
Reference<? extends ReentrantContext> reference = null;
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* This abstract ReentrantContextProvider helper class manages the creation,
|
||||
* storage, and retrieval of concrete ReentrantContext instances which can be
|
||||
* subclassed to hold cached contextual data.
|
||||
*
|
||||
* It supports reentrancy as every call to acquire() provides a new unique context
|
||||
* instance that must later be returned for reuse by a call to release(ctx)
|
||||
* (typically in a try/finally block).
|
||||
*
|
||||
* It has a couple of abstract implementations which store references in a queue
|
||||
* and/or thread-local storage.
|
||||
* The Providers can be configured to hold ReentrantContext instances in memory
|
||||
* using hard, soft or weak references.
|
||||
*
|
||||
* The acquire() and release() methods are used to retrieve and return the contexts.
|
||||
*
|
||||
* The {@code newContext()} method remains abstract in all implementations and
|
||||
* must be provided by the module to create a new subclass of ReentrantContext
|
||||
* with the appropriate contextual data in it.
|
||||
*
|
||||
* Sample Usage:
|
||||
* - create a subclass ReentrantContextImpl to hold the thread state:
|
||||
*
|
||||
* static final class ReentrantContextImpl extends ReentrantContext {
|
||||
* // specific cached data
|
||||
* }
|
||||
*
|
||||
* - create the appropriate ReentrantContextProvider:
|
||||
*
|
||||
* private static final ReentrantContextProvider<ReentrantContextImpl> contextProvider =
|
||||
* new ReentrantContextProviderTL<ReentrantContextImpl>(ReentrantContextProvider.REF_WEAK)
|
||||
* {
|
||||
* @Override
|
||||
* protected ReentrantContextImpl newContext() {
|
||||
* return new ReentrantContextImpl();
|
||||
* }
|
||||
* };
|
||||
* ...
|
||||
* void someMethod() {
|
||||
* ReentrantContextImpl ctx = contextProvider.acquire();
|
||||
* try {
|
||||
* // use the context
|
||||
* } finally {
|
||||
* contextProvider.release(ctx);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param <K> ReentrantContext subclass
|
||||
*
|
||||
* @see ReentrantContext
|
||||
*/
|
||||
public abstract class ReentrantContextProvider<K extends ReentrantContext>
|
||||
{
|
||||
// thread-local storage: inactive
|
||||
static final byte USAGE_TL_INACTIVE = 0;
|
||||
// thread-local storage: in use
|
||||
static final byte USAGE_TL_IN_USE = 1;
|
||||
// CLQ storage
|
||||
static final byte USAGE_CLQ = 2;
|
||||
|
||||
// hard reference
|
||||
public static final int REF_HARD = 0;
|
||||
// soft reference
|
||||
public static final int REF_SOFT = 1;
|
||||
// weak reference
|
||||
public static final int REF_WEAK = 2;
|
||||
|
||||
/* members */
|
||||
// internal reference type
|
||||
private final int refType;
|
||||
|
||||
/**
|
||||
* Create a new ReentrantContext provider using the given reference type
|
||||
* among hard, soft or weak
|
||||
*
|
||||
* @param refType reference type
|
||||
*/
|
||||
protected ReentrantContextProvider(final int refType) {
|
||||
this.refType = refType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ReentrantContext instance
|
||||
*
|
||||
* @return new ReentrantContext instance
|
||||
*/
|
||||
protected abstract K newContext();
|
||||
|
||||
/**
|
||||
* Give a ReentrantContext instance for the current thread
|
||||
*
|
||||
* @return ReentrantContext instance
|
||||
*/
|
||||
public abstract K acquire();
|
||||
|
||||
/**
|
||||
* Restore the given ReentrantContext instance for reuse
|
||||
*
|
||||
* @param ctx ReentrantContext instance
|
||||
*/
|
||||
public abstract void release(K ctx);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final Reference<K> getOrCreateReference(final K ctx) {
|
||||
if (ctx.reference == null) {
|
||||
// Create the reference:
|
||||
switch (refType) {
|
||||
case REF_HARD:
|
||||
ctx.reference = new HardReference<K>(ctx);
|
||||
break;
|
||||
case REF_SOFT:
|
||||
ctx.reference = new SoftReference<K>(ctx);
|
||||
break;
|
||||
default:
|
||||
case REF_WEAK:
|
||||
ctx.reference = new WeakReference<K>(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (Reference<K>) ctx.reference;
|
||||
}
|
||||
|
||||
/* Missing HardReference implementation */
|
||||
static final class HardReference<V> extends WeakReference<V> {
|
||||
// kept strong reference:
|
||||
private final V strongRef;
|
||||
|
||||
HardReference(final V referent) {
|
||||
// no referent needed for the parent WeakReference:
|
||||
super(null);
|
||||
this.strongRef = referent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get() {
|
||||
return strongRef;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* This ReentrantContextProvider implementation uses one ConcurrentLinkedQueue
|
||||
* to store all ReentrantContext instances (thread and its child contexts)
|
||||
*
|
||||
* Note: this implementation keeps less contexts in memory depending on the
|
||||
* concurrent active threads in contrary to a ThreadLocal provider. However,
|
||||
* it is slower in highly concurrent workloads.
|
||||
*
|
||||
* @param <K> ReentrantContext subclass
|
||||
*/
|
||||
public abstract class ReentrantContextProviderCLQ<K extends ReentrantContext>
|
||||
extends ReentrantContextProvider<K>
|
||||
{
|
||||
// ReentrantContext queue to store all contexts
|
||||
private final ConcurrentLinkedQueue<Reference<K>> ctxQueue
|
||||
= new ConcurrentLinkedQueue<Reference<K>>();
|
||||
|
||||
/**
|
||||
* Create a new ReentrantContext provider using the given reference type
|
||||
* among hard, soft or weak based using a ConcurrentLinkedQueue storage
|
||||
*
|
||||
* @param refType reference type
|
||||
*/
|
||||
public ReentrantContextProviderCLQ(final int refType) {
|
||||
super(refType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a ReentrantContext instance for the current thread
|
||||
*
|
||||
* @return ReentrantContext instance
|
||||
*/
|
||||
@Override
|
||||
public final K acquire() {
|
||||
K ctx = null;
|
||||
// Drain queue if all referent are null:
|
||||
Reference<K> ref = null;
|
||||
while ((ctx == null) && ((ref = ctxQueue.poll()) != null)) {
|
||||
ctx = ref.get();
|
||||
}
|
||||
if (ctx == null) {
|
||||
// create a new ReentrantContext if none is available
|
||||
ctx = newContext();
|
||||
ctx.usage = USAGE_CLQ;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the given ReentrantContext instance for reuse
|
||||
*
|
||||
* @param ctx ReentrantContext instance
|
||||
*/
|
||||
@Override
|
||||
public final void release(final K ctx) {
|
||||
if (ctx.usage == USAGE_CLQ) {
|
||||
ctxQueue.offer(getOrCreateReference(ctx));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
/**
|
||||
* This ReentrantContextProvider implementation uses a ThreadLocal to hold
|
||||
* the first ReentrantContext per thread and a ReentrantContextProviderCLQ to
|
||||
* store child ReentrantContext instances needed during recursion.
|
||||
*
|
||||
* Note: this implementation may keep up to one context in memory per thread.
|
||||
* Child contexts for recursive uses are stored in the queue using a WEAK
|
||||
* reference by default unless specified in the 2 argument constructor.
|
||||
*
|
||||
* @param <K> ReentrantContext subclass
|
||||
*/
|
||||
public abstract class ReentrantContextProviderTL<K extends ReentrantContext>
|
||||
extends ReentrantContextProvider<K>
|
||||
{
|
||||
// Thread-local storage:
|
||||
private final ThreadLocal<Reference<K>> ctxTL
|
||||
= new ThreadLocal<Reference<K>>();
|
||||
|
||||
// ReentrantContext CLQ provider for child contexts:
|
||||
private final ReentrantContextProviderCLQ<K> ctxProviderCLQ;
|
||||
|
||||
/**
|
||||
* Create a new ReentrantContext provider using the given reference type
|
||||
* among hard, soft or weak.
|
||||
* It uses weak reference for the child contexts.
|
||||
*
|
||||
* @param refType reference type
|
||||
*/
|
||||
public ReentrantContextProviderTL(final int refType) {
|
||||
this(refType, REF_WEAK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ReentrantContext provider using the given reference types
|
||||
* among hard, soft or weak
|
||||
*
|
||||
* @param refTypeTL reference type used by ThreadLocal
|
||||
* @param refTypeCLQ reference type used by ReentrantContextProviderCLQ
|
||||
*/
|
||||
public ReentrantContextProviderTL(final int refTypeTL, final int refTypeCLQ)
|
||||
{
|
||||
super(refTypeTL);
|
||||
|
||||
final ReentrantContextProviderTL<K> parent = this;
|
||||
|
||||
this.ctxProviderCLQ = new ReentrantContextProviderCLQ<K>(refTypeCLQ) {
|
||||
@Override
|
||||
protected K newContext() {
|
||||
return parent.newContext();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a ReentrantContext instance for the current thread
|
||||
*
|
||||
* @return ReentrantContext instance
|
||||
*/
|
||||
@Override
|
||||
public final K acquire() {
|
||||
K ctx = null;
|
||||
final Reference<K> ref = ctxTL.get();
|
||||
if (ref != null) {
|
||||
ctx = ref.get();
|
||||
}
|
||||
if (ctx == null) {
|
||||
// create a new ReentrantContext if none is available
|
||||
ctx = newContext();
|
||||
// update thread local reference:
|
||||
ctxTL.set(getOrCreateReference(ctx));
|
||||
}
|
||||
// Check reentrance:
|
||||
if (ctx.usage == USAGE_TL_INACTIVE) {
|
||||
ctx.usage = USAGE_TL_IN_USE;
|
||||
} else {
|
||||
// get or create another ReentrantContext from CLQ provider:
|
||||
ctx = ctxProviderCLQ.acquire();
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the given ReentrantContext instance for reuse
|
||||
*
|
||||
* @param ctx ReentrantContext instance
|
||||
*/
|
||||
@Override
|
||||
public final void release(final K ctx) {
|
||||
if (ctx.usage == USAGE_TL_IN_USE) {
|
||||
ctx.usage = USAGE_TL_INACTIVE;
|
||||
} else {
|
||||
ctxProviderCLQ.release(ctx);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -126,7 +126,7 @@ final class ByteArrayCache implements MarlinConst {
|
||||
}
|
||||
|
||||
if (doChecks) {
|
||||
check(array, 0, array.length, value);
|
||||
check(array, fromIndex, toIndex, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,9 +135,10 @@ final class ByteArrayCache implements MarlinConst {
|
||||
{
|
||||
if (doChecks) {
|
||||
// check zero on full array:
|
||||
for (int i = fromIndex; i < toIndex; i++) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != value) {
|
||||
logException("Invalid array value at " + i + "\n"
|
||||
logException("Invalid value at: " + i + " = " + array[i]
|
||||
+ " from: " + fromIndex + " to: " + toIndex + "\n"
|
||||
+ Arrays.toString(array), new Throwable());
|
||||
|
||||
// ensure array is correctly filled:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -127,7 +127,7 @@ final class FloatArrayCache implements MarlinConst {
|
||||
}
|
||||
|
||||
if (doChecks) {
|
||||
check(array, 0, array.length, value);
|
||||
check(array, fromIndex, toIndex, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,9 +136,10 @@ final class FloatArrayCache implements MarlinConst {
|
||||
{
|
||||
if (doChecks) {
|
||||
// check zero on full array:
|
||||
for (int i = fromIndex; i < toIndex; i++) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != value) {
|
||||
logException("Invalid array value at " + i + "\n"
|
||||
logException("Invalid value at: " + i + " = " + array[i]
|
||||
+ " from: " + fromIndex + " to: " + toIndex + "\n"
|
||||
+ Arrays.toString(array), new Throwable());
|
||||
|
||||
// ensure array is correctly filled:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -126,7 +126,7 @@ final class IntArrayCache implements MarlinConst {
|
||||
}
|
||||
|
||||
if (doChecks) {
|
||||
check(array, 0, array.length, value);
|
||||
check(array, fromIndex, toIndex, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,9 +135,10 @@ final class IntArrayCache implements MarlinConst {
|
||||
{
|
||||
if (doChecks) {
|
||||
// check zero on full array:
|
||||
for (int i = fromIndex; i < toIndex; i++) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != value) {
|
||||
logException("Invalid array value at " + i + "\n"
|
||||
logException("Invalid value at: " + i + " = " + array[i]
|
||||
+ " from: " + fromIndex + " to: " + toIndex + "\n"
|
||||
+ Arrays.toString(array), new Throwable());
|
||||
|
||||
// ensure array is correctly filled:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 2016, 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
|
||||
@ -590,8 +590,8 @@ public final class MarlinCache implements MarlinConst {
|
||||
alphaRow[to + 1] = 0;
|
||||
}
|
||||
if (doChecks) {
|
||||
IntArrayCache.check(blkFlags, 0, blkFlags.length, 0);
|
||||
IntArrayCache.check(alphaRow, 0, alphaRow.length, 0);
|
||||
IntArrayCache.check(blkFlags, blkW, blkE, 0);
|
||||
IntArrayCache.check(alphaRow, from, px1 - bboxX0, 0);
|
||||
}
|
||||
|
||||
if (doMonitors) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 2016, 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
|
||||
@ -30,11 +30,12 @@ import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.lang.ref.Reference;
|
||||
import java.security.AccessController;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import static sun.java2d.marlin.MarlinUtils.logInfo;
|
||||
import sun.awt.geom.PathConsumer2D;
|
||||
import sun.java2d.ReentrantContextProvider;
|
||||
import sun.java2d.ReentrantContextProviderCLQ;
|
||||
import sun.java2d.ReentrantContextProviderTL;
|
||||
import sun.java2d.pipe.AATileGenerator;
|
||||
import sun.java2d.pipe.Region;
|
||||
import sun.java2d.pipe.RenderingEngine;
|
||||
@ -882,46 +883,50 @@ public class MarlinRenderingEngine extends RenderingEngine
|
||||
// use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext
|
||||
private static final boolean useThreadLocal;
|
||||
|
||||
// hard reference
|
||||
static final int REF_HARD = 0;
|
||||
// soft reference
|
||||
static final int REF_SOFT = 1;
|
||||
// weak reference
|
||||
static final int REF_WEAK = 2;
|
||||
|
||||
// reference type stored in either TL or CLQ
|
||||
static final int REF_TYPE;
|
||||
|
||||
// Per-thread RendererContext
|
||||
private static final ThreadLocal<Object> rdrCtxThreadLocal;
|
||||
// RendererContext queue when ThreadLocal is disabled
|
||||
private static final ConcurrentLinkedQueue<Object> rdrCtxQueue;
|
||||
private static final ReentrantContextProvider<RendererContext> rdrCtxProvider;
|
||||
|
||||
// Static initializer to use TL or CLQ mode
|
||||
static {
|
||||
// CLQ mode by default:
|
||||
useThreadLocal = MarlinProperties.isUseThreadLocal();
|
||||
rdrCtxThreadLocal = (useThreadLocal) ? new ThreadLocal<Object>()
|
||||
: null;
|
||||
rdrCtxQueue = (!useThreadLocal) ? new ConcurrentLinkedQueue<Object>()
|
||||
: null;
|
||||
|
||||
// Soft reference by default:
|
||||
String refType = AccessController.doPrivileged(
|
||||
final String refType = AccessController.doPrivileged(
|
||||
new GetPropertyAction("sun.java2d.renderer.useRef",
|
||||
"soft"));
|
||||
switch (refType) {
|
||||
default:
|
||||
case "soft":
|
||||
REF_TYPE = REF_SOFT;
|
||||
REF_TYPE = ReentrantContextProvider.REF_SOFT;
|
||||
break;
|
||||
case "weak":
|
||||
REF_TYPE = REF_WEAK;
|
||||
REF_TYPE = ReentrantContextProvider.REF_WEAK;
|
||||
break;
|
||||
case "hard":
|
||||
REF_TYPE = REF_HARD;
|
||||
REF_TYPE = ReentrantContextProvider.REF_HARD;
|
||||
break;
|
||||
}
|
||||
|
||||
if (useThreadLocal) {
|
||||
rdrCtxProvider = new ReentrantContextProviderTL<RendererContext>(REF_TYPE)
|
||||
{
|
||||
@Override
|
||||
protected RendererContext newContext() {
|
||||
return RendererContext.createContext();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
rdrCtxProvider = new ReentrantContextProviderCLQ<RendererContext>(REF_TYPE)
|
||||
{
|
||||
@Override
|
||||
protected RendererContext newContext() {
|
||||
return RendererContext.createContext();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean settingsLogged = !enableLogs;
|
||||
@ -936,13 +941,13 @@ public class MarlinRenderingEngine extends RenderingEngine
|
||||
String refType;
|
||||
switch (REF_TYPE) {
|
||||
default:
|
||||
case REF_HARD:
|
||||
case ReentrantContextProvider.REF_HARD:
|
||||
refType = "hard";
|
||||
break;
|
||||
case REF_SOFT:
|
||||
case ReentrantContextProvider.REF_SOFT:
|
||||
refType = "soft";
|
||||
break;
|
||||
case REF_WEAK:
|
||||
case ReentrantContextProvider.REF_WEAK:
|
||||
refType = "weak";
|
||||
break;
|
||||
}
|
||||
@ -1025,22 +1030,7 @@ public class MarlinRenderingEngine extends RenderingEngine
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
static RendererContext getRendererContext() {
|
||||
RendererContext rdrCtx = null;
|
||||
final Object ref = (useThreadLocal) ? rdrCtxThreadLocal.get()
|
||||
: rdrCtxQueue.poll();
|
||||
if (ref != null) {
|
||||
// resolve reference:
|
||||
rdrCtx = (REF_TYPE == REF_HARD) ? ((RendererContext) ref)
|
||||
: ((Reference<RendererContext>) ref).get();
|
||||
}
|
||||
// create a new RendererContext if none is available
|
||||
if (rdrCtx == null) {
|
||||
rdrCtx = RendererContext.createContext();
|
||||
if (useThreadLocal) {
|
||||
// update thread local reference:
|
||||
rdrCtxThreadLocal.set(rdrCtx.reference);
|
||||
}
|
||||
}
|
||||
final RendererContext rdrCtx = rdrCtxProvider.acquire();
|
||||
if (doMonitors) {
|
||||
RendererContext.stats.mon_pre_getAATileGenerator.start();
|
||||
}
|
||||
@ -1057,8 +1047,6 @@ public class MarlinRenderingEngine extends RenderingEngine
|
||||
if (doMonitors) {
|
||||
RendererContext.stats.mon_pre_getAATileGenerator.stop();
|
||||
}
|
||||
if (!useThreadLocal) {
|
||||
rdrCtxQueue.offer(rdrCtx.reference);
|
||||
}
|
||||
rdrCtxProvider.release(rdrCtx);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -26,9 +26,10 @@
|
||||
package sun.java2d.marlin;
|
||||
|
||||
import java.awt.geom.Path2D;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import sun.java2d.ReentrantContext;
|
||||
import sun.java2d.ReentrantContextProvider;
|
||||
import static sun.java2d.marlin.ArrayCache.*;
|
||||
import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
|
||||
import static sun.java2d.marlin.MarlinUtils.logInfo;
|
||||
@ -36,7 +37,7 @@ import static sun.java2d.marlin.MarlinUtils.logInfo;
|
||||
/**
|
||||
* This class is a renderer context dedicated to a single thread
|
||||
*/
|
||||
final class RendererContext implements MarlinConst {
|
||||
final class RendererContext extends ReentrantContext implements MarlinConst {
|
||||
|
||||
// RendererContext creation counter
|
||||
private static final AtomicInteger contextCount = new AtomicInteger(1);
|
||||
@ -45,7 +46,7 @@ final class RendererContext implements MarlinConst {
|
||||
? RendererStats.getInstance(): null;
|
||||
|
||||
private static final boolean USE_CACHE_HARD_REF = doStats
|
||||
|| (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK);
|
||||
|| (MarlinRenderingEngine.REF_TYPE == ReentrantContextProvider.REF_WEAK);
|
||||
|
||||
/**
|
||||
* Create a new renderer context
|
||||
@ -55,6 +56,7 @@ final class RendererContext implements MarlinConst {
|
||||
static RendererContext createContext() {
|
||||
final RendererContext newCtx = new RendererContext("ctx"
|
||||
+ Integer.toString(contextCount.getAndIncrement()));
|
||||
|
||||
if (RendererContext.stats != null) {
|
||||
RendererContext.stats.allContexts.add(newCtx);
|
||||
}
|
||||
@ -63,11 +65,6 @@ final class RendererContext implements MarlinConst {
|
||||
|
||||
// context name (debugging purposes)
|
||||
final String name;
|
||||
/*
|
||||
* Reference to this instance (hard, soft or weak).
|
||||
* @see MarlinRenderingEngine#REF_TYPE
|
||||
*/
|
||||
final Object reference;
|
||||
// Smallest object used as Cleaner's parent reference
|
||||
final Object cleanerObj = new Object();
|
||||
// dirty flag indicating an exception occured during pipeline in pathTo()
|
||||
@ -101,7 +98,7 @@ final class RendererContext implements MarlinConst {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param name
|
||||
* @param name context name (debugging)
|
||||
*/
|
||||
RendererContext(final String name) {
|
||||
if (logCreateContext) {
|
||||
@ -124,20 +121,6 @@ final class RendererContext implements MarlinConst {
|
||||
|
||||
stroker = new Stroker(this);
|
||||
dasher = new Dasher(this);
|
||||
|
||||
// Create the reference to this instance (hard, soft or weak):
|
||||
switch (MarlinRenderingEngine.REF_TYPE) {
|
||||
default:
|
||||
case MarlinRenderingEngine.REF_HARD:
|
||||
reference = this;
|
||||
break;
|
||||
case MarlinRenderingEngine.REF_SOFT:
|
||||
reference = new SoftReference<RendererContext>(this);
|
||||
break;
|
||||
case MarlinRenderingEngine.REF_WEAK:
|
||||
reference = new WeakReference<RendererContext>(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -27,7 +27,7 @@ package sun.java2d.marlin;
|
||||
|
||||
public final class Version {
|
||||
|
||||
private static final String version = "marlin-0.7.3-Unsafe-OpenJDK";
|
||||
private static final String version = "marlin-0.7.3.2-Unsafe-OpenJDK";
|
||||
|
||||
public static String getVersion() {
|
||||
return version;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2016, 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
|
||||
@ -28,7 +28,11 @@ import java.awt.BasicStroke;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import sun.awt.SunHints;
|
||||
import sun.java2d.ReentrantContext;
|
||||
import sun.java2d.ReentrantContextProvider;
|
||||
import sun.java2d.ReentrantContextProviderTL;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
|
||||
/**
|
||||
@ -38,28 +42,31 @@ import sun.java2d.SunGraphics2D;
|
||||
* This class sets up the Generator and computes the alpha tiles
|
||||
* and then passes them on to a CompositePipe object for painting.
|
||||
*/
|
||||
public class AAShapePipe
|
||||
public final class AAShapePipe
|
||||
implements ShapeDrawPipe, ParallelogramPipe
|
||||
{
|
||||
static RenderingEngine renderengine = RenderingEngine.getInstance();
|
||||
static final RenderingEngine renderengine = RenderingEngine.getInstance();
|
||||
|
||||
// Per-thread TileState (~1K very small so do not use any Weak Reference)
|
||||
private static final ThreadLocal<TileState> tileStateThreadLocal =
|
||||
new ThreadLocal<TileState>() {
|
||||
@Override
|
||||
protected TileState initialValue() {
|
||||
return new TileState();
|
||||
}
|
||||
};
|
||||
private static final ReentrantContextProvider<TileState> tileStateProvider =
|
||||
new ReentrantContextProviderTL<TileState>(
|
||||
ReentrantContextProvider.REF_HARD)
|
||||
{
|
||||
@Override
|
||||
protected TileState newContext() {
|
||||
return new TileState();
|
||||
}
|
||||
};
|
||||
|
||||
CompositePipe outpipe;
|
||||
final CompositePipe outpipe;
|
||||
|
||||
public AAShapePipe(CompositePipe pipe) {
|
||||
outpipe = pipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(SunGraphics2D sg, Shape s) {
|
||||
BasicStroke bs;
|
||||
final BasicStroke bs;
|
||||
|
||||
if (sg.stroke instanceof BasicStroke) {
|
||||
bs = (BasicStroke) sg.stroke;
|
||||
@ -71,10 +78,12 @@ public class AAShapePipe
|
||||
renderPath(sg, s, bs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(SunGraphics2D sg, Shape s) {
|
||||
renderPath(sg, s, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillParallelogram(SunGraphics2D sg,
|
||||
double ux1, double uy1,
|
||||
double ux2, double uy2,
|
||||
@ -82,21 +91,23 @@ public class AAShapePipe
|
||||
double dx1, double dy1,
|
||||
double dx2, double dy2)
|
||||
{
|
||||
Region clip = sg.getCompClip();
|
||||
final TileState ts = tileStateThreadLocal.get();
|
||||
final int[] abox = ts.abox;
|
||||
final TileState ts = tileStateProvider.acquire();
|
||||
try {
|
||||
final int[] abox = ts.abox;
|
||||
|
||||
AATileGenerator aatg =
|
||||
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
|
||||
clip, abox);
|
||||
if (aatg == null) {
|
||||
// Nothing to render
|
||||
return;
|
||||
final AATileGenerator aatg =
|
||||
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
|
||||
sg.getCompClip(), abox);
|
||||
if (aatg != null) {
|
||||
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
|
||||
aatg, abox, ts);
|
||||
}
|
||||
} finally {
|
||||
tileStateProvider.release(ts);
|
||||
}
|
||||
|
||||
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawParallelogram(SunGraphics2D sg,
|
||||
double ux1, double uy1,
|
||||
double ux2, double uy2,
|
||||
@ -105,52 +116,61 @@ public class AAShapePipe
|
||||
double dx2, double dy2,
|
||||
double lw1, double lw2)
|
||||
{
|
||||
Region clip = sg.getCompClip();
|
||||
final TileState ts = tileStateThreadLocal.get();
|
||||
final int[] abox = ts.abox;
|
||||
final TileState ts = tileStateProvider.acquire();
|
||||
try {
|
||||
final int[] abox = ts.abox;
|
||||
|
||||
AATileGenerator aatg =
|
||||
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1, lw2,
|
||||
clip, abox);
|
||||
if (aatg == null) {
|
||||
// Nothing to render
|
||||
return;
|
||||
final AATileGenerator aatg =
|
||||
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1,
|
||||
lw2, sg.getCompClip(), abox);
|
||||
if (aatg != null) {
|
||||
// Note that bbox is of the original shape, not the wide path.
|
||||
// This is appropriate for handing to Paint methods...
|
||||
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
|
||||
aatg, abox, ts);
|
||||
}
|
||||
} finally {
|
||||
tileStateProvider.release(ts);
|
||||
}
|
||||
|
||||
// Note that bbox is of the original shape, not the wide path.
|
||||
// This is appropriate for handing to Paint methods...
|
||||
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
|
||||
}
|
||||
|
||||
public void renderPath(SunGraphics2D sg, Shape s, BasicStroke bs) {
|
||||
boolean adjust = (bs != null &&
|
||||
final boolean adjust = (bs != null &&
|
||||
sg.strokeHint != SunHints.INTVAL_STROKE_PURE);
|
||||
boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
|
||||
final boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
|
||||
|
||||
Region clip = sg.getCompClip();
|
||||
final TileState ts = tileStateThreadLocal.get();
|
||||
final int[] abox = ts.abox;
|
||||
final TileState ts = tileStateProvider.acquire();
|
||||
try {
|
||||
final int[] abox = ts.abox;
|
||||
|
||||
AATileGenerator aatg =
|
||||
renderengine.getAATileGenerator(s, sg.transform, clip,
|
||||
bs, thin, adjust, abox);
|
||||
if (aatg == null) {
|
||||
// Nothing to render
|
||||
return;
|
||||
final AATileGenerator aatg =
|
||||
renderengine.getAATileGenerator(s, sg.transform, sg.getCompClip(),
|
||||
bs, thin, adjust, abox);
|
||||
if (aatg != null) {
|
||||
renderTiles(sg, s, aatg, abox, ts);
|
||||
}
|
||||
} finally {
|
||||
tileStateProvider.release(ts);
|
||||
}
|
||||
|
||||
renderTiles(sg, s, aatg, abox, ts);
|
||||
}
|
||||
|
||||
public void renderTiles(SunGraphics2D sg, Shape s,
|
||||
AATileGenerator aatg, int abox[], TileState ts)
|
||||
final AATileGenerator aatg,
|
||||
final int[] abox, final TileState ts)
|
||||
{
|
||||
Object context = null;
|
||||
try {
|
||||
// reentrance: outpipe may also use AAShapePipe:
|
||||
context = outpipe.startSequence(sg, s,
|
||||
ts.computeDevBox(abox),
|
||||
abox);
|
||||
|
||||
// copy of int[] abox as local variables for performance:
|
||||
final int x0 = abox[0];
|
||||
final int y0 = abox[1];
|
||||
final int x1 = abox[2];
|
||||
final int y1 = abox[3];
|
||||
|
||||
final int tw = aatg.getTileWidth();
|
||||
final int th = aatg.getTileHeight();
|
||||
|
||||
@ -158,16 +178,15 @@ public class AAShapePipe
|
||||
final byte[] alpha = ts.getAlphaTile(tw * th);
|
||||
byte[] atile;
|
||||
|
||||
for (int y = abox[1]; y < abox[3]; y += th) {
|
||||
int h = Math.min(th, abox[3] - y);
|
||||
for (int y = y0; y < y1; y += th) {
|
||||
final int h = Math.min(th, y1 - y);
|
||||
|
||||
for (int x = abox[0]; x < abox[2]; x += tw) {
|
||||
int w = Math.min(tw, abox[2] - x);
|
||||
for (int x = x0; x < x1; x += tw) {
|
||||
final int w = Math.min(tw, x1 - x);
|
||||
|
||||
int a = aatg.getTypicalAlpha();
|
||||
if (a == 0x00 ||
|
||||
outpipe.needTile(context, x, y, w, h) == false)
|
||||
{
|
||||
final int a = aatg.getTypicalAlpha();
|
||||
|
||||
if (a == 0x00 || !outpipe.needTile(context, x, y, w, h)) {
|
||||
aatg.nextTile();
|
||||
outpipe.skipTile(context, x, y);
|
||||
continue;
|
||||
@ -180,8 +199,7 @@ public class AAShapePipe
|
||||
aatg.getAlpha(alpha, 0, tw);
|
||||
}
|
||||
|
||||
outpipe.renderPathTile(context, atile, 0, tw,
|
||||
x, y, w, h);
|
||||
outpipe.renderPathTile(context, atile, 0, tw, x, y, w, h);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -193,7 +211,7 @@ public class AAShapePipe
|
||||
}
|
||||
|
||||
// Tile state used by AAShapePipe
|
||||
static final class TileState {
|
||||
static final class TileState extends ReentrantContext {
|
||||
// cached tile (32 x 32 tile by default)
|
||||
private byte[] theTile = new byte[32 * 32];
|
||||
// dirty aabox array
|
||||
@ -240,5 +258,4 @@ public class AAShapePipe
|
||||
return box;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
205
jdk/test/sun/java2d/marlin/CrashPaintTest.java
Normal file
205
jdk/test/sun/java2d/marlin/CrashPaintTest.java
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Paint;
|
||||
import java.awt.PaintContext;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.TexturePaint;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8148886
|
||||
* @summary Verifies that Marlin supports reentrant operations (ThreadLocal)
|
||||
* like in custom Paint or custom Composite
|
||||
* @run main CrashPaintTest
|
||||
*/
|
||||
public class CrashPaintTest {
|
||||
|
||||
static final boolean SAVE_IMAGE = false;
|
||||
|
||||
public static void main(String argv[]) {
|
||||
Locale.setDefault(Locale.US);
|
||||
|
||||
// initialize j.u.l Looger:
|
||||
final Logger log = Logger.getLogger("sun.java2d.marlin");
|
||||
log.addHandler(new Handler() {
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
Throwable th = record.getThrown();
|
||||
// detect any Throwable:
|
||||
if (th != null) {
|
||||
System.out.println("Test failed:\n" + record.getMessage());
|
||||
th.printStackTrace(System.out);
|
||||
|
||||
throw new RuntimeException("Test failed: ", th);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
});
|
||||
|
||||
// enable Marlin logging & internal checks:
|
||||
System.setProperty("sun.java2d.renderer.log", "true");
|
||||
System.setProperty("sun.java2d.renderer.useLogger", "true");
|
||||
System.setProperty("sun.java2d.renderer.doChecks", "true");
|
||||
|
||||
// Force using thread-local storage:
|
||||
System.setProperty("sun.java2d.renderer.useThreadLocal", "true");
|
||||
// Force smaller pixelsize to force using array caches:
|
||||
System.setProperty("sun.java2d.renderer.pixelsize", "256");
|
||||
|
||||
final int width = 300;
|
||||
final int height = 300;
|
||||
|
||||
final BufferedImage image = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
final Graphics2D g2d = (Graphics2D) image.getGraphics();
|
||||
try {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
g2d.setBackground(Color.WHITE);
|
||||
g2d.clearRect(0, 0, width, height);
|
||||
|
||||
final Ellipse2D.Double ellipse
|
||||
= new Ellipse2D.Double(0, 0, width, height);
|
||||
|
||||
final Paint paint = new CustomPaint(100);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
final long start = System.nanoTime();
|
||||
g2d.setPaint(paint);
|
||||
g2d.fill(ellipse);
|
||||
|
||||
g2d.setColor(Color.GREEN);
|
||||
g2d.draw(ellipse);
|
||||
|
||||
final long time = System.nanoTime() - start;
|
||||
System.out.println("paint: duration= " + (1e-6 * time) + " ms.");
|
||||
}
|
||||
|
||||
if (SAVE_IMAGE) {
|
||||
try {
|
||||
final File file = new File("CrashPaintTest.png");
|
||||
System.out.println("Writing file: "
|
||||
+ file.getAbsolutePath());
|
||||
ImageIO.write(image, "PNG", file);
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Writing file failure:");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Check image on few pixels:
|
||||
final Raster raster = image.getData();
|
||||
|
||||
// 170, 175 = blue
|
||||
checkPixel(raster, 170, 175, Color.BLUE.getRGB());
|
||||
// 50, 50 = blue
|
||||
checkPixel(raster, 50, 50, Color.BLUE.getRGB());
|
||||
|
||||
// 190, 110 = pink
|
||||
checkPixel(raster, 190, 110, Color.PINK.getRGB());
|
||||
// 280, 210 = pink
|
||||
checkPixel(raster, 280, 210, Color.PINK.getRGB());
|
||||
|
||||
} finally {
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkPixel(final Raster raster,
|
||||
final int x, final int y,
|
||||
final int expected) {
|
||||
|
||||
final int[] rgb = (int[]) raster.getDataElements(x, y, null);
|
||||
|
||||
if (rgb[0] != expected) {
|
||||
throw new IllegalStateException("bad pixel at (" + x + ", " + y
|
||||
+ ") = " + rgb[0] + " expected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomPaint extends TexturePaint {
|
||||
private int size;
|
||||
|
||||
CustomPaint(final int size) {
|
||||
super(new BufferedImage(size, size,
|
||||
BufferedImage.TYPE_INT_ARGB),
|
||||
new Rectangle2D.Double(0, 0, size, size)
|
||||
);
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaintContext createContext(ColorModel cm,
|
||||
Rectangle deviceBounds,
|
||||
Rectangle2D userBounds,
|
||||
AffineTransform at,
|
||||
RenderingHints hints) {
|
||||
|
||||
// Fill bufferedImage using
|
||||
final Graphics2D g2d = (Graphics2D) getImage().getGraphics();
|
||||
try {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setBackground(Color.PINK);
|
||||
g2d.clearRect(0, 0, size, size);
|
||||
|
||||
g2d.setColor(Color.BLUE);
|
||||
g2d.drawRect(0, 0, size, size);
|
||||
|
||||
g2d.fillOval(size / 10, size / 10,
|
||||
size * 8 / 10, size * 8 / 10);
|
||||
|
||||
} finally {
|
||||
g2d.dispose();
|
||||
}
|
||||
|
||||
return super.createContext(cm, deviceBounds, userBounds, at, hints);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user