1 /*
   2  * Copyright 2003-2008 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.font;
  27 
  28 import java.awt.GraphicsConfiguration;
  29 import java.awt.GraphicsEnvironment;
  30 import java.lang.ref.Reference;
  31 import java.lang.ref.ReferenceQueue;
  32 import java.lang.ref.SoftReference;
  33 import java.lang.ref.WeakReference;
  34 
  35 import sun.java2d.Disposer;
  36 import sun.java2d.pipe.BufferedContext;
  37 import sun.java2d.pipe.RenderQueue;
  38 import sun.java2d.pipe.hw.AccelGraphicsConfig;
  39 import sun.misc.Unsafe;
  40 
  41 /**
  42 
  43 A FontStrike is the keeper of scaled glyph image data which is expensive
  44 to compute so needs to be cached.
  45 So long as that data may be being used it cannot be invalidated.
  46 Yet we also need to limit the amount of native memory and number of
  47 strike objects in use.
  48 For scaleability and ease of use, a key goal is multi-threaded read
  49 access to a strike, so that it may be shared by multiple client objects,
  50 potentially executing on different threads, with no special reference
  51 counting or "check-out/check-in" requirements which would pass on the
  52 burden of keeping track of strike references to the SG2D and other clients.
  53 
  54 A cache of strikes is maintained via Reference objects.
  55 This helps in two ways :
  56 1. The VM will free references when memory is low or they have not been
  57 used in a long time.
  58 2. Reference queues provide a way to get notification of this so we can
  59 free native memory resources.
  60 
  61  */
  62 
  63 public final class StrikeCache {
  64 
  65     static final Unsafe unsafe = Unsafe.getUnsafe();
  66 
  67     static ReferenceQueue refQueue = Disposer.getQueue();
  68 
  69     /* Reference objects may have their referents cleared when GC chooses.
  70      * During application client start-up there is typically at least one
  71      * GC which causes the hotspot VM to clear soft (not just weak) references
  72      * Thus not only is there a GC pause, but the work done do rasterise
  73      * glyphs that are fairly certain to be needed again almost immediately
  74      * is thrown away. So for performance reasons a simple optimisation is to
  75      * keep up to 8 strong references to strikes to reduce the chance of
  76      * GC'ing strikes that have been used recently. Note that this may not
  77      * suffice in Solaris UTF-8 locales where a single composite strike may be
  78      * composed of 15 individual strikes, plus the composite strike.
  79      * And this assumes the new architecture doesn't maintain strikes for
  80      * natively accessed bitmaps. It may be worth "tuning" the number of
  81      * strikes kept around for the platform or locale.
  82      * Since no attempt is made to ensure uniqueness or ensure synchronized
  83      * access there is no guarantee that this cache will ensure that unique
  84      * strikes are cached. Every time a strike is looked up it is added
  85      * to the current index in this cache. All this cache has to do to be
  86      * worthwhile is prevent excessive cache flushing of strikes that are
  87      * referenced frequently. The logic that adds references here could be
  88      * tweaked to keep only strikes  that represent untransformed, screen
  89      * sizes as that's the typical performance case.
  90      */
  91     static int MINSTRIKES = 8; // can be overridden by property
  92     static int recentStrikeIndex = 0;
  93     static FontStrike[] recentStrikes;
  94     static boolean cacheRefTypeWeak;
  95 
  96     /*
  97      * Native sizes and offsets for glyph cache
  98      * There are 10 values.
  99      */
 100     static int nativeAddressSize;
 101     static int glyphInfoSize;
 102     static int xAdvanceOffset;
 103     static int yAdvanceOffset;
 104     static int boundsOffset;
 105     static int widthOffset;
 106     static int heightOffset;
 107     static int rowBytesOffset;
 108     static int topLeftXOffset;
 109     static int topLeftYOffset;
 110     static int pixelDataOffset;
 111     static long invisibleGlyphPtr;
 112 
 113     /* Native method used to return information used for unsafe
 114      * access to native data.
 115      * return values as follows:-
 116      * arr[0] = size of an address/pointer.
 117      * arr[1] = size of a GlyphInfo
 118      * arr[2] = offset of advanceX
 119      * arr[3] = offset of advanceY
 120      * arr[4] = offset of width
 121      * arr[5] = offset of height
 122      * arr[6] = offset of rowBytes
 123      * arr[7] = offset of topLeftX
 124      * arr[8] = offset of topLeftY
 125      * arr[9] = offset of pixel data.
 126      * arr[10] = address of a GlyphImageRef representing the invisible glyph
 127      */
 128     static native void getGlyphCacheDescription(long[] infoArray);
 129 
 130     static {
 131 
 132         long[] nativeInfo = new long[11];
 133         getGlyphCacheDescription(nativeInfo);
 134         //Can also get address size from Unsafe class :-
 135         //nativeAddressSize = unsafe.addressSize();
 136         nativeAddressSize = (int)nativeInfo[0];
 137         glyphInfoSize     = (int)nativeInfo[1];
 138         xAdvanceOffset    = (int)nativeInfo[2];
 139         yAdvanceOffset    = (int)nativeInfo[3];
 140         widthOffset       = (int)nativeInfo[4];
 141         heightOffset      = (int)nativeInfo[5];
 142         rowBytesOffset    = (int)nativeInfo[6];
 143         topLeftXOffset    = (int)nativeInfo[7];
 144         topLeftYOffset    = (int)nativeInfo[8];
 145         pixelDataOffset   = (int)nativeInfo[9];
 146         invisibleGlyphPtr = nativeInfo[10];
 147         if (nativeAddressSize < 4) {
 148             throw new InternalError("Unexpected address size for font data: " +
 149                                     nativeAddressSize);
 150         }
 151 
 152         java.security.AccessController.doPrivileged(
 153                                     new java.security.PrivilegedAction() {
 154             public Object run() {
 155 
 156                /* Allow a client to override the reference type used to
 157                 * cache strikes. The default is "soft" which hints to keep
 158                 * the strikes around. This property allows the client to
 159                 * override this to "weak" which hint to the GC to free
 160                 * memory more agressively.
 161                 */
 162                String refType =
 163                    System.getProperty("sun.java2d.font.reftype", "soft");
 164                cacheRefTypeWeak = refType.equals("weak");
 165 
 166                 String minStrikesStr =
 167                     System.getProperty("sun.java2d.font.minstrikes");
 168                 if (minStrikesStr != null) {
 169                     try {
 170                         MINSTRIKES = Integer.parseInt(minStrikesStr);
 171                         if (MINSTRIKES <= 0) {
 172                             MINSTRIKES = 1;
 173                         }
 174                     } catch (NumberFormatException e) {
 175                     }
 176                 }
 177 
 178                 recentStrikes = new FontStrike[MINSTRIKES];
 179 
 180                 return null;
 181             }
 182         });
 183     }
 184 
 185 
 186     static void refStrike(FontStrike strike) {
 187         int index = recentStrikeIndex;
 188         recentStrikes[index] = strike;
 189         index++;
 190         if (index == MINSTRIKES) {
 191             index = 0;
 192         }
 193         recentStrikeIndex = index;
 194     }
 195 
 196     private static final void doDispose(FontStrikeDisposer disposer) {
 197         if (disposer.intGlyphImages != null) {
 198             freeIntMemory(disposer.intGlyphImages,
 199                     disposer.pScalerContext);
 200         } else if (disposer.longGlyphImages != null) {
 201             freeLongMemory(disposer.longGlyphImages,
 202                     disposer.pScalerContext);
 203         } else if (disposer.segIntGlyphImages != null) {
 204             /* NB Now making multiple JNI calls in this case.
 205              * But assuming that there's a reasonable amount of locality
 206              * rather than sparse references then it should be OK.
 207              */
 208             for (int i=0; i<disposer.segIntGlyphImages.length; i++) {
 209                 if (disposer.segIntGlyphImages[i] != null) {
 210                     freeIntMemory(disposer.segIntGlyphImages[i],
 211                             disposer.pScalerContext);
 212                     /* native will only free the scaler context once */
 213                     disposer.pScalerContext = 0L;
 214                     disposer.segIntGlyphImages[i] = null;
 215                 }
 216             }
 217             /* This may appear inefficient but it should only be invoked
 218              * for a strike that never was asked to rasterise a glyph.
 219              */
 220             if (disposer.pScalerContext != 0L) {
 221                 freeIntMemory(new int[0], disposer.pScalerContext);
 222             }
 223         } else if (disposer.segLongGlyphImages != null) {
 224             for (int i=0; i<disposer.segLongGlyphImages.length; i++) {
 225                 if (disposer.segLongGlyphImages[i] != null) {
 226                     freeLongMemory(disposer.segLongGlyphImages[i],
 227                             disposer.pScalerContext);
 228                     disposer.pScalerContext = 0L;
 229                     disposer.segLongGlyphImages[i] = null;
 230                 }
 231             }
 232             if (disposer.pScalerContext != 0L) {
 233                 freeLongMemory(new long[0], disposer.pScalerContext);
 234             }
 235         } else if (disposer.pScalerContext != 0L) {
 236             /* Rarely a strike may have been created that never cached
 237              * any glyphs. In this case we still want to free the scaler
 238              * context.
 239              */
 240             if (longAddresses()) {
 241                 freeLongMemory(new long[0], disposer.pScalerContext);
 242             } else {
 243                 freeIntMemory(new int[0], disposer.pScalerContext);
 244             }
 245         }
 246     }
 247 
 248     private static boolean longAddresses() {
 249         return nativeAddressSize == 8;
 250     }
 251 
 252     static void disposeStrike(final FontStrikeDisposer disposer) {
 253         // we need to execute the strike disposal on the rendering thread
 254         // because they may be accessed on that thread at the time of the
 255         // disposal (for example, when the accel. cache is invalidated)
 256 
 257         // REMIND: this look a bit heavyweight, but should be ok
 258         // because strike disposal is a relatively infrequent operation,
 259         // more worrisome is the necessity of getting a GC here.
 260         RenderQueue rq = null;
 261         GraphicsEnvironment ge =
 262             GraphicsEnvironment.getLocalGraphicsEnvironment();
 263         if (!ge.isHeadless()) {
 264             GraphicsConfiguration gc =
 265                 ge.getDefaultScreenDevice().getDefaultConfiguration();
 266             if (gc instanceof AccelGraphicsConfig) {
 267                 AccelGraphicsConfig agc = (AccelGraphicsConfig)gc;
 268                 BufferedContext bc = agc.getContext();
 269                 if (bc != null) {
 270                     rq = bc.getRenderQueue();
 271                 }
 272             }
 273         }
 274         if (rq != null) {
 275             rq.lock();
 276             try {
 277                 rq.flushAndInvokeNow(new Runnable() {
 278                     public void run() {
 279                         doDispose(disposer);
 280                     }
 281                 });
 282             } finally {
 283                 rq.unlock();
 284             }
 285         } else {
 286             doDispose(disposer);
 287         }
 288     }
 289 
 290     static native void freeIntPointer(int ptr);
 291     static native void freeLongPointer(long ptr);
 292     private static native void freeIntMemory(int[] glyphPtrs, long pContext);
 293     private static native void freeLongMemory(long[] glyphPtrs, long pContext);
 294 
 295 
 296     public static Reference getStrikeRef(FontStrike strike) {
 297         return getStrikeRef(strike, cacheRefTypeWeak);
 298     }
 299 
 300     public static Reference getStrikeRef(FontStrike strike, boolean weak) {
 301         /* Some strikes may have no disposer as there's nothing
 302          * for them to free, as they allocated no native resource
 303          * eg, if they did not allocate resources because of a problem,
 304          * or they never hold native resources. So they create no disposer.
 305          * But any strike that reaches here that has a null disposer is
 306          * a potential memory leak.
 307          */
 308         if (strike.disposer == null) {
 309             if (weak) {
 310                 return new WeakReference(strike);
 311             } else {
 312                 return new SoftReference(strike);
 313             }
 314         }
 315 
 316         if (weak) {
 317             return new WeakDisposerRef(strike);
 318         } else {
 319             return new SoftDisposerRef(strike);
 320         }
 321     }
 322 
 323     static interface DisposableStrike {
 324         FontStrikeDisposer getDisposer();
 325     }
 326 
 327     static class SoftDisposerRef
 328         extends SoftReference implements DisposableStrike {
 329 
 330         private FontStrikeDisposer disposer;
 331 
 332         public FontStrikeDisposer getDisposer() {
 333             return disposer;
 334         }
 335 
 336         SoftDisposerRef(FontStrike strike) {
 337             super(strike, StrikeCache.refQueue);
 338             disposer = strike.disposer;
 339             Disposer.addReference(this, disposer);
 340         }
 341     }
 342 
 343     static class WeakDisposerRef
 344         extends WeakReference implements DisposableStrike {
 345 
 346         private FontStrikeDisposer disposer;
 347 
 348         public FontStrikeDisposer getDisposer() {
 349             return disposer;
 350         }
 351 
 352         WeakDisposerRef(FontStrike strike) {
 353             super(strike, StrikeCache.refQueue);
 354             disposer = strike.disposer;
 355             Disposer.addReference(this, disposer);
 356         }
 357     }
 358 
 359 }