0N/A/*
3909N/A * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.font;
0N/A
430N/Aimport java.awt.GraphicsConfiguration;
430N/Aimport java.awt.GraphicsEnvironment;
0N/Aimport java.lang.ref.Reference;
0N/Aimport java.lang.ref.ReferenceQueue;
0N/Aimport java.lang.ref.SoftReference;
0N/Aimport java.lang.ref.WeakReference;
2370N/Aimport java.util.*;
0N/A
0N/Aimport sun.java2d.Disposer;
430N/Aimport sun.java2d.pipe.BufferedContext;
430N/Aimport sun.java2d.pipe.RenderQueue;
430N/Aimport sun.java2d.pipe.hw.AccelGraphicsConfig;
0N/Aimport sun.misc.Unsafe;
0N/A
0N/A/**
0N/A
0N/AA FontStrike is the keeper of scaled glyph image data which is expensive
0N/Ato compute so needs to be cached.
0N/ASo long as that data may be being used it cannot be invalidated.
0N/AYet we also need to limit the amount of native memory and number of
0N/Astrike objects in use.
0N/AFor scaleability and ease of use, a key goal is multi-threaded read
0N/Aaccess to a strike, so that it may be shared by multiple client objects,
0N/Apotentially executing on different threads, with no special reference
0N/Acounting or "check-out/check-in" requirements which would pass on the
0N/Aburden of keeping track of strike references to the SG2D and other clients.
0N/A
0N/AA cache of strikes is maintained via Reference objects.
0N/AThis helps in two ways :
0N/A1. The VM will free references when memory is low or they have not been
0N/Aused in a long time.
0N/A2. Reference queues provide a way to get notification of this so we can
0N/Afree native memory resources.
0N/A
0N/A */
0N/A
0N/Apublic final class StrikeCache {
0N/A
0N/A static final Unsafe unsafe = Unsafe.getUnsafe();
0N/A
0N/A static ReferenceQueue refQueue = Disposer.getQueue();
0N/A
2370N/A static ArrayList<GlyphDisposedListener> disposeListeners = new ArrayList<GlyphDisposedListener>(1);
2370N/A
2370N/A
0N/A /* Reference objects may have their referents cleared when GC chooses.
0N/A * During application client start-up there is typically at least one
0N/A * GC which causes the hotspot VM to clear soft (not just weak) references
0N/A * Thus not only is there a GC pause, but the work done do rasterise
0N/A * glyphs that are fairly certain to be needed again almost immediately
0N/A * is thrown away. So for performance reasons a simple optimisation is to
0N/A * keep up to 8 strong references to strikes to reduce the chance of
0N/A * GC'ing strikes that have been used recently. Note that this may not
0N/A * suffice in Solaris UTF-8 locales where a single composite strike may be
0N/A * composed of 15 individual strikes, plus the composite strike.
0N/A * And this assumes the new architecture doesn't maintain strikes for
0N/A * natively accessed bitmaps. It may be worth "tuning" the number of
0N/A * strikes kept around for the platform or locale.
0N/A * Since no attempt is made to ensure uniqueness or ensure synchronized
0N/A * access there is no guarantee that this cache will ensure that unique
0N/A * strikes are cached. Every time a strike is looked up it is added
0N/A * to the current index in this cache. All this cache has to do to be
0N/A * worthwhile is prevent excessive cache flushing of strikes that are
0N/A * referenced frequently. The logic that adds references here could be
0N/A * tweaked to keep only strikes that represent untransformed, screen
0N/A * sizes as that's the typical performance case.
0N/A */
0N/A static int MINSTRIKES = 8; // can be overridden by property
0N/A static int recentStrikeIndex = 0;
0N/A static FontStrike[] recentStrikes;
0N/A static boolean cacheRefTypeWeak;
0N/A
0N/A /*
0N/A * Native sizes and offsets for glyph cache
0N/A * There are 10 values.
0N/A */
0N/A static int nativeAddressSize;
0N/A static int glyphInfoSize;
0N/A static int xAdvanceOffset;
0N/A static int yAdvanceOffset;
0N/A static int boundsOffset;
0N/A static int widthOffset;
0N/A static int heightOffset;
0N/A static int rowBytesOffset;
0N/A static int topLeftXOffset;
0N/A static int topLeftYOffset;
0N/A static int pixelDataOffset;
2370N/A static int cacheCellOffset;
2370N/A static int managedOffset;
0N/A static long invisibleGlyphPtr;
0N/A
0N/A /* Native method used to return information used for unsafe
0N/A * access to native data.
0N/A * return values as follows:-
0N/A * arr[0] = size of an address/pointer.
0N/A * arr[1] = size of a GlyphInfo
0N/A * arr[2] = offset of advanceX
0N/A * arr[3] = offset of advanceY
0N/A * arr[4] = offset of width
0N/A * arr[5] = offset of height
0N/A * arr[6] = offset of rowBytes
0N/A * arr[7] = offset of topLeftX
0N/A * arr[8] = offset of topLeftY
0N/A * arr[9] = offset of pixel data.
0N/A * arr[10] = address of a GlyphImageRef representing the invisible glyph
0N/A */
0N/A static native void getGlyphCacheDescription(long[] infoArray);
0N/A
0N/A static {
0N/A
2370N/A long[] nativeInfo = new long[13];
0N/A getGlyphCacheDescription(nativeInfo);
0N/A //Can also get address size from Unsafe class :-
0N/A //nativeAddressSize = unsafe.addressSize();
0N/A nativeAddressSize = (int)nativeInfo[0];
0N/A glyphInfoSize = (int)nativeInfo[1];
0N/A xAdvanceOffset = (int)nativeInfo[2];
0N/A yAdvanceOffset = (int)nativeInfo[3];
0N/A widthOffset = (int)nativeInfo[4];
0N/A heightOffset = (int)nativeInfo[5];
0N/A rowBytesOffset = (int)nativeInfo[6];
0N/A topLeftXOffset = (int)nativeInfo[7];
0N/A topLeftYOffset = (int)nativeInfo[8];
0N/A pixelDataOffset = (int)nativeInfo[9];
0N/A invisibleGlyphPtr = nativeInfo[10];
2370N/A cacheCellOffset = (int) nativeInfo[11];
2370N/A managedOffset = (int) nativeInfo[12];
2370N/A
0N/A if (nativeAddressSize < 4) {
0N/A throw new InternalError("Unexpected address size for font data: " +
0N/A nativeAddressSize);
0N/A }
0N/A
0N/A java.security.AccessController.doPrivileged(
0N/A new java.security.PrivilegedAction() {
0N/A public Object run() {
0N/A
0N/A /* Allow a client to override the reference type used to
0N/A * cache strikes. The default is "soft" which hints to keep
0N/A * the strikes around. This property allows the client to
0N/A * override this to "weak" which hint to the GC to free
0N/A * memory more agressively.
0N/A */
0N/A String refType =
0N/A System.getProperty("sun.java2d.font.reftype", "soft");
0N/A cacheRefTypeWeak = refType.equals("weak");
0N/A
0N/A String minStrikesStr =
0N/A System.getProperty("sun.java2d.font.minstrikes");
0N/A if (minStrikesStr != null) {
0N/A try {
0N/A MINSTRIKES = Integer.parseInt(minStrikesStr);
0N/A if (MINSTRIKES <= 0) {
0N/A MINSTRIKES = 1;
0N/A }
0N/A } catch (NumberFormatException e) {
0N/A }
0N/A }
0N/A
0N/A recentStrikes = new FontStrike[MINSTRIKES];
0N/A
0N/A return null;
0N/A }
0N/A });
0N/A }
0N/A
0N/A
0N/A static void refStrike(FontStrike strike) {
0N/A int index = recentStrikeIndex;
0N/A recentStrikes[index] = strike;
0N/A index++;
0N/A if (index == MINSTRIKES) {
0N/A index = 0;
0N/A }
0N/A recentStrikeIndex = index;
0N/A }
0N/A
430N/A private static final void doDispose(FontStrikeDisposer disposer) {
0N/A if (disposer.intGlyphImages != null) {
2370N/A freeCachedIntMemory(disposer.intGlyphImages,
430N/A disposer.pScalerContext);
0N/A } else if (disposer.longGlyphImages != null) {
2370N/A freeCachedLongMemory(disposer.longGlyphImages,
430N/A disposer.pScalerContext);
0N/A } else if (disposer.segIntGlyphImages != null) {
0N/A /* NB Now making multiple JNI calls in this case.
0N/A * But assuming that there's a reasonable amount of locality
0N/A * rather than sparse references then it should be OK.
0N/A */
0N/A for (int i=0; i<disposer.segIntGlyphImages.length; i++) {
0N/A if (disposer.segIntGlyphImages[i] != null) {
2370N/A freeCachedIntMemory(disposer.segIntGlyphImages[i],
430N/A disposer.pScalerContext);
0N/A /* native will only free the scaler context once */
0N/A disposer.pScalerContext = 0L;
0N/A disposer.segIntGlyphImages[i] = null;
0N/A }
0N/A }
0N/A /* This may appear inefficient but it should only be invoked
0N/A * for a strike that never was asked to rasterise a glyph.
0N/A */
0N/A if (disposer.pScalerContext != 0L) {
2370N/A freeCachedIntMemory(new int[0], disposer.pScalerContext);
0N/A }
0N/A } else if (disposer.segLongGlyphImages != null) {
0N/A for (int i=0; i<disposer.segLongGlyphImages.length; i++) {
0N/A if (disposer.segLongGlyphImages[i] != null) {
2370N/A freeCachedLongMemory(disposer.segLongGlyphImages[i],
430N/A disposer.pScalerContext);
0N/A disposer.pScalerContext = 0L;
0N/A disposer.segLongGlyphImages[i] = null;
0N/A }
0N/A }
0N/A if (disposer.pScalerContext != 0L) {
2370N/A freeCachedLongMemory(new long[0], disposer.pScalerContext);
0N/A }
979N/A } else if (disposer.pScalerContext != 0L) {
979N/A /* Rarely a strike may have been created that never cached
979N/A * any glyphs. In this case we still want to free the scaler
979N/A * context.
979N/A */
1686N/A if (longAddresses()) {
2370N/A freeCachedLongMemory(new long[0], disposer.pScalerContext);
979N/A } else {
2370N/A freeCachedIntMemory(new int[0], disposer.pScalerContext);
979N/A }
0N/A }
0N/A }
0N/A
1686N/A private static boolean longAddresses() {
1686N/A return nativeAddressSize == 8;
1686N/A }
1686N/A
430N/A static void disposeStrike(final FontStrikeDisposer disposer) {
430N/A // we need to execute the strike disposal on the rendering thread
430N/A // because they may be accessed on that thread at the time of the
430N/A // disposal (for example, when the accel. cache is invalidated)
430N/A
1886N/A // Whilst this is a bit heavyweight, in most applications
1886N/A // strike disposal is a relatively infrequent operation, so it
1886N/A // doesn't matter. But in some tests that use vast numbers
1886N/A // of strikes, the switching back and forth is measurable.
1886N/A // So the "pollRemove" call is added to batch up the work.
1886N/A // If we are polling we know we've already been called back
1886N/A // and can directly dispose the record.
1886N/A // Also worrisome is the necessity of getting a GC here.
1886N/A
1886N/A if (Disposer.pollingQueue) {
1886N/A doDispose(disposer);
1886N/A return;
1886N/A }
1886N/A
430N/A RenderQueue rq = null;
430N/A GraphicsEnvironment ge =
430N/A GraphicsEnvironment.getLocalGraphicsEnvironment();
430N/A if (!ge.isHeadless()) {
430N/A GraphicsConfiguration gc =
430N/A ge.getDefaultScreenDevice().getDefaultConfiguration();
430N/A if (gc instanceof AccelGraphicsConfig) {
430N/A AccelGraphicsConfig agc = (AccelGraphicsConfig)gc;
430N/A BufferedContext bc = agc.getContext();
430N/A if (bc != null) {
430N/A rq = bc.getRenderQueue();
430N/A }
430N/A }
430N/A }
430N/A if (rq != null) {
430N/A rq.lock();
430N/A try {
430N/A rq.flushAndInvokeNow(new Runnable() {
430N/A public void run() {
430N/A doDispose(disposer);
1886N/A Disposer.pollRemove();
430N/A }
430N/A });
430N/A } finally {
430N/A rq.unlock();
430N/A }
430N/A } else {
430N/A doDispose(disposer);
430N/A }
430N/A }
430N/A
0N/A static native void freeIntPointer(int ptr);
0N/A static native void freeLongPointer(long ptr);
0N/A private static native void freeIntMemory(int[] glyphPtrs, long pContext);
0N/A private static native void freeLongMemory(long[] glyphPtrs, long pContext);
0N/A
2370N/A private static void freeCachedIntMemory(int[] glyphPtrs, long pContext) {
2370N/A synchronized(disposeListeners) {
2370N/A if (disposeListeners.size() > 0) {
2370N/A ArrayList<Long> gids = null;
2370N/A
2370N/A for (int i = 0; i < glyphPtrs.length; i++) {
3611N/A if (glyphPtrs[i] != 0 && unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
2370N/A
2370N/A if (gids == null) {
2370N/A gids = new ArrayList<Long>();
2370N/A }
2370N/A gids.add((long) glyphPtrs[i]);
2370N/A }
2370N/A }
2370N/A
2370N/A if (gids != null) {
3611N/A // Any reference by the disposers to the native glyph ptrs
3611N/A // must be done before this returns.
2370N/A notifyDisposeListeners(gids);
2370N/A }
2370N/A }
2370N/A }
2370N/A
2370N/A freeIntMemory(glyphPtrs, pContext);
2370N/A }
2370N/A
2370N/A private static void freeCachedLongMemory(long[] glyphPtrs, long pContext) {
2370N/A synchronized(disposeListeners) {
2370N/A if (disposeListeners.size() > 0) {
2370N/A ArrayList<Long> gids = null;
2370N/A
2370N/A for (int i=0; i < glyphPtrs.length; i++) {
2370N/A if (glyphPtrs[i] != 0
3611N/A && unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
2370N/A
2370N/A if (gids == null) {
2370N/A gids = new ArrayList<Long>();
2370N/A }
2370N/A gids.add((long) glyphPtrs[i]);
2370N/A }
2370N/A }
2370N/A
2370N/A if (gids != null) {
3611N/A // Any reference by the disposers to the native glyph ptrs
3611N/A // must be done before this returns.
2370N/A notifyDisposeListeners(gids);
2370N/A }
2370N/A }
2370N/A }
2370N/A
2370N/A freeLongMemory(glyphPtrs, pContext);
2370N/A }
2370N/A
2370N/A public static void addGlyphDisposedListener(GlyphDisposedListener listener) {
2370N/A synchronized(disposeListeners) {
2370N/A disposeListeners.add(listener);
2370N/A }
2370N/A }
2370N/A
2370N/A private static void notifyDisposeListeners(ArrayList<Long> glyphs) {
2370N/A for (GlyphDisposedListener listener : disposeListeners) {
2370N/A listener.glyphDisposed(glyphs);
2370N/A }
2370N/A }
0N/A
0N/A public static Reference getStrikeRef(FontStrike strike) {
0N/A return getStrikeRef(strike, cacheRefTypeWeak);
0N/A }
0N/A
0N/A public static Reference getStrikeRef(FontStrike strike, boolean weak) {
0N/A /* Some strikes may have no disposer as there's nothing
0N/A * for them to free, as they allocated no native resource
0N/A * eg, if they did not allocate resources because of a problem,
0N/A * or they never hold native resources. So they create no disposer.
0N/A * But any strike that reaches here that has a null disposer is
0N/A * a potential memory leak.
0N/A */
0N/A if (strike.disposer == null) {
0N/A if (weak) {
0N/A return new WeakReference(strike);
0N/A } else {
0N/A return new SoftReference(strike);
0N/A }
0N/A }
0N/A
0N/A if (weak) {
0N/A return new WeakDisposerRef(strike);
0N/A } else {
0N/A return new SoftDisposerRef(strike);
0N/A }
0N/A }
0N/A
0N/A static interface DisposableStrike {
0N/A FontStrikeDisposer getDisposer();
0N/A }
0N/A
0N/A static class SoftDisposerRef
0N/A extends SoftReference implements DisposableStrike {
0N/A
0N/A private FontStrikeDisposer disposer;
0N/A
0N/A public FontStrikeDisposer getDisposer() {
0N/A return disposer;
0N/A }
0N/A
0N/A SoftDisposerRef(FontStrike strike) {
0N/A super(strike, StrikeCache.refQueue);
0N/A disposer = strike.disposer;
0N/A Disposer.addReference(this, disposer);
0N/A }
0N/A }
0N/A
0N/A static class WeakDisposerRef
0N/A extends WeakReference implements DisposableStrike {
0N/A
0N/A private FontStrikeDisposer disposer;
0N/A
0N/A public FontStrikeDisposer getDisposer() {
0N/A return disposer;
0N/A }
0N/A
0N/A WeakDisposerRef(FontStrike strike) {
0N/A super(strike, StrikeCache.refQueue);
0N/A disposer = strike.disposer;
0N/A Disposer.addReference(this, disposer);
0N/A }
0N/A }
0N/A
0N/A}