1 /*
   2  * Copyright (c) 2003, 2014, 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.font;
  27 
  28 import java.lang.ref.SoftReference;
  29 import java.lang.ref.WeakReference;
  30 import java.awt.Font;
  31 import java.awt.GraphicsEnvironment;
  32 import java.awt.Rectangle;
  33 import java.awt.geom.AffineTransform;
  34 import java.awt.geom.GeneralPath;
  35 import java.awt.geom.NoninvertibleTransformException;
  36 import java.awt.geom.Point2D;
  37 import java.awt.geom.Rectangle2D;
  38 import java.util.concurrent.ConcurrentHashMap;
  39 import static sun.awt.SunHints.*;
  40 
  41 
  42 public class FileFontStrike extends PhysicalStrike {
  43 
  44     /* fffe and ffff are values we specially interpret as meaning
  45      * invisible glyphs.
  46      */
  47     static final int INVISIBLE_GLYPHS = 0x0fffe;
  48 
  49     private FileFont fileFont;
  50 
  51     /* REMIND: replace this scheme with one that installs a cache
  52      * instance of the appropriate type. It will require changes in
  53      * FontStrikeDisposer and NativeStrike etc.
  54      */
  55     private static final int UNINITIALISED = 0;
  56     private static final int INTARRAY      = 1;
  57     private static final int LONGARRAY     = 2;
  58     private static final int SEGINTARRAY   = 3;
  59     private static final int SEGLONGARRAY  = 4;
  60 
  61     private volatile int glyphCacheFormat = UNINITIALISED;
  62 
  63     /* segmented arrays are blocks of 32 */
  64     private static final int SEGSHIFT = 5;
  65     private static final int SEGSIZE  = 1 << SEGSHIFT;
  66 
  67     private boolean segmentedCache;
  68     private int[][] segIntGlyphImages;
  69     private long[][] segLongGlyphImages;
  70 
  71     /* The "metrics" information requested by clients is usually nothing
  72      * more than the horizontal advance of the character.
  73      * In most cases this advance and other metrics information is stored
  74      * in the glyph image cache.
  75      * But in some cases we do not automatically retrieve the glyph
  76      * image when the advance is requested. In those cases we want to
  77      * cache the advances since this has been shown to be important for
  78      * performance.
  79      * The segmented cache is used in cases when the single array
  80      * would be too large.
  81      */
  82     private float[] horizontalAdvances;
  83     private float[][] segHorizontalAdvances;
  84 
  85     /* Outline bounds are used when printing and when drawing outlines
  86      * to the screen. On balance the relative rarity of these cases
  87      * and the fact that getting this requires generating a path at
  88      * the scaler level means that its probably OK to store these
  89      * in a Java-level hashmap as the trade-off between time and space.
  90      * Later can revisit whether to cache these at all, or elsewhere.
  91      * Should also profile whether subsequent to getting the bounds, the
  92      * outline itself is also requested. The 1.4 implementation doesn't
  93      * cache outlines so you could generate the path twice - once to get
  94      * the bounds and again to return the outline to the client.
  95      * If the two uses are coincident then also look into caching outlines.
  96      * One simple optimisation is that we could store the last single
  97      * outline retrieved. This assumes that bounds then outline will always
  98      * be retrieved for a glyph rather than retrieving bounds for all glyphs
  99      * then outlines for all glyphs.
 100      */
 101     ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap;
 102     SoftReference<ConcurrentHashMap<Integer, Point2D.Float>>
 103         glyphMetricsMapRef;
 104 
 105     AffineTransform invertDevTx;
 106 
 107     boolean useNatives;
 108     NativeStrike[] nativeStrikes;
 109 
 110     /* Used only for communication to native layer */
 111     private int intPtSize;
 112 
 113     /* Perform global initialisation needed for Windows native rasterizer */
 114     private static native boolean initNative();
 115     private static boolean isXPorLater = false;
 116     static {
 117         if (FontUtilities.isWindows && !FontUtilities.useT2K &&
 118             !GraphicsEnvironment.isHeadless()) {
 119             isXPorLater = initNative();
 120         }
 121     }
 122 
 123     FileFontStrike(FileFont fileFont, FontStrikeDesc desc) {
 124         super(fileFont, desc);
 125         this.fileFont = fileFont;
 126 
 127         if (desc.style != fileFont.style) {
 128           /* If using algorithmic styling, the base values are
 129            * boldness = 1.0, italic = 0.0. The superclass constructor
 130            * initialises these.
 131            */
 132             if ((desc.style & Font.ITALIC) == Font.ITALIC &&
 133                 (fileFont.style & Font.ITALIC) == 0) {
 134                 algoStyle = true;
 135                 italic = 0.7f;
 136             }
 137             if ((desc.style & Font.BOLD) == Font.BOLD &&
 138                 ((fileFont.style & Font.BOLD) == 0)) {
 139                 algoStyle = true;
 140                 boldness = 1.33f;
 141             }
 142         }
 143         double[] matrix = new double[4];
 144         AffineTransform at = desc.glyphTx;
 145         at.getMatrix(matrix);
 146         if (!desc.devTx.isIdentity() &&
 147             desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
 148             try {
 149                 invertDevTx = desc.devTx.createInverse();
 150             } catch (NoninvertibleTransformException e) {
 151             }
 152         }
 153 
 154         /* Amble fonts are better rendered unhinted although there's the
 155          * inevitable fuzziness that accompanies this due to no longer
 156          * snapping stems to the pixel grid. The exception is that in B&W
 157          * mode they are worse without hinting. The down side to that is that
 158          * B&W metrics will differ which normally isn't the case, although
 159          * since AA mode is part of the measuring context that should be OK.
 160          * We don't expect Amble to be installed in the Windows fonts folder.
 161          * If we were to, then we'd also might want to disable using the
 162          * native rasteriser path which is used for LCD mode for platform
 163          * fonts. since we have no way to disable hinting by GDI.
 164          * In the case of Amble, since its 'gasp' table says to disable
 165          * hinting, I'd expect GDI to follow that, so likely it should
 166          * all be consistent even if GDI used.
 167          */
 168         boolean disableHinting = desc.aaHint != INTVAL_TEXT_ANTIALIAS_OFF &&
 169                                  fileFont.familyName.startsWith("Amble");
 170 
 171         /* If any of the values is NaN then substitute the null scaler context.
 172          * This will return null images, zero advance, and empty outlines
 173          * as no rendering need take place in this case.
 174          * We pass in the null scaler as the singleton null context
 175          * requires it. However
 176          */
 177         if (Double.isNaN(matrix[0]) || Double.isNaN(matrix[1]) ||
 178             Double.isNaN(matrix[2]) || Double.isNaN(matrix[3]) ||
 179             fileFont.getScaler() == null) {
 180             pScalerContext = NullFontScaler.getNullScalerContext();
 181         } else {
 182             pScalerContext = fileFont.getScaler().createScalerContext(matrix,
 183                                     desc.aaHint, desc.fmHint,
 184                                     boldness, italic, disableHinting);
 185         }
 186 
 187         mapper = fileFont.getMapper();
 188         int numGlyphs = mapper.getNumGlyphs();
 189 
 190         /* Always segment for fonts with > 256 glyphs, but also for smaller
 191          * fonts with non-typical sizes and transforms.
 192          * Segmenting for all non-typical pt sizes helps to minimize memory
 193          * usage when very many distinct strikes are created.
 194          * The size range of 0->5 and 37->INF for segmenting is arbitrary
 195          * but the intention is that typical GUI integer point sizes (6->36)
 196          * should not segment unless there's another reason to do so.
 197          */
 198         float ptSize = (float)matrix[3]; // interpreted only when meaningful.
 199         int iSize = intPtSize = (int)ptSize;
 200         boolean isSimpleTx = (at.getType() & complexTX) == 0;
 201         segmentedCache =
 202             (numGlyphs > SEGSIZE << 3) ||
 203             ((numGlyphs > SEGSIZE << 1) &&
 204              (!isSimpleTx || ptSize != iSize || iSize < 6 || iSize > 36));
 205 
 206         /* This can only happen if we failed to allocate memory for context.
 207          * NB: in such case we may still have some memory in java heap
 208          *     but subsequent attempt to allocate null scaler context
 209          *     may fail too (cause it is allocate in the native heap).
 210          *     It is not clear how to make this more robust but on the
 211          *     other hand getting NULL here seems to be extremely unlikely.
 212          */
 213         if (pScalerContext == 0L) {
 214             /* REMIND: when the code is updated to install cache objects
 215              * rather than using a switch this will be more efficient.
 216              */
 217             this.disposer = new FontStrikeDisposer(fileFont, desc);
 218             initGlyphCache();
 219             pScalerContext = NullFontScaler.getNullScalerContext();
 220             SunFontManager.getInstance().deRegisterBadFont(fileFont);
 221             return;
 222         }
 223         /* First, see if native code should be used to create the glyph.
 224          * GDI will return the integer metrics, not fractional metrics, which
 225          * may be requested for this strike, so we would require here that :
 226          * desc.fmHint != INTVAL_FRACTIONALMETRICS_ON
 227          * except that the advance returned by GDI is always overwritten by
 228          * the JDK rasteriser supplied one (see getGlyphImageFromWindows()).
 229          */
 230         if (FontUtilities.isWindows && isXPorLater &&
 231             !FontUtilities.useT2K &&
 232             !GraphicsEnvironment.isHeadless() &&
 233             !fileFont.useJavaRasterizer &&
 234             (desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
 235              desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) &&
 236             (matrix[1] == 0.0 && matrix[2] == 0.0 &&
 237              matrix[0] == matrix[3] &&
 238              matrix[0] >= 3.0 && matrix[0] <= 100.0) &&
 239             !((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) {
 240             useNatives = true;
 241         }
 242         else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) {
 243             /* Check its a simple scale of a pt size in the range
 244              * where native bitmaps typically exist (6-36 pts) */
 245             if (matrix[1] == 0.0 && matrix[2] == 0.0 &&
 246                 matrix[0] >= 6.0 && matrix[0] <= 36.0 &&
 247                 matrix[0] == matrix[3]) {
 248                 useNatives = true;
 249                 int numNatives = fileFont.nativeFonts.length;
 250                 nativeStrikes = new NativeStrike[numNatives];
 251                 /* Maybe initialise these strikes lazily?. But we
 252                  * know we need at least one
 253                  */
 254                 for (int i=0; i<numNatives; i++) {
 255                     nativeStrikes[i] =
 256                         new NativeStrike(fileFont.nativeFonts[i], desc, false);
 257                 }
 258             }
 259         }
 260         if (FontUtilities.isLogging() && FontUtilities.isWindows) {
 261             FontUtilities.getLogger().info
 262                 ("Strike for " + fileFont + " at size = " + intPtSize +
 263                  " use natives = " + useNatives +
 264                  " useJavaRasteriser = " + fileFont.useJavaRasterizer +
 265                  " AAHint = " + desc.aaHint +
 266                  " Has Embedded bitmaps = " +
 267                  ((TrueTypeFont)fileFont).
 268                  useEmbeddedBitmapsForSize(intPtSize));
 269         }
 270         this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext);
 271 
 272         /* Always get the image and the advance together for smaller sizes
 273          * that are likely to be important to rendering performance.
 274          * The pixel size of 48.0 can be thought of as
 275          * "maximumSizeForGetImageWithAdvance".
 276          * This should be no greater than OutlineTextRender.THRESHOLD.
 277          */
 278         double maxSz = 48.0;
 279         getImageWithAdvance =
 280             Math.abs(at.getScaleX()) <= maxSz &&
 281             Math.abs(at.getScaleY()) <= maxSz &&
 282             Math.abs(at.getShearX()) <= maxSz &&
 283             Math.abs(at.getShearY()) <= maxSz;
 284 
 285         /* Some applications request advance frequently during layout.
 286          * If we are not getting and caching the image with the advance,
 287          * there is a potentially significant performance penalty if the
 288          * advance is repeatedly requested before requesting the image.
 289          * We should at least cache the horizontal advance.
 290          * REMIND: could use info in the font, eg hmtx, to retrieve some
 291          * advances. But still want to cache it here.
 292          */
 293 
 294         if (!getImageWithAdvance) {
 295             if (!segmentedCache) {
 296                 horizontalAdvances = new float[numGlyphs];
 297                 /* use max float as uninitialised advance */
 298                 for (int i=0; i<numGlyphs; i++) {
 299                     horizontalAdvances[i] = Float.MAX_VALUE;
 300                 }
 301             } else {
 302                 int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
 303                 segHorizontalAdvances = new float[numSegments][];
 304             }
 305         }
 306     }
 307 
 308     /* A number of methods are delegated by the strike to the scaler
 309      * context which is a shared resource on a physical font.
 310      */
 311 
 312     public int getNumGlyphs() {
 313         return fileFont.getNumGlyphs();
 314     }
 315 
 316     long getGlyphImageFromNative(int glyphCode) {
 317         if (FontUtilities.isWindows) {
 318             return getGlyphImageFromWindows(glyphCode);
 319         } else {
 320             return getGlyphImageFromX11(glyphCode);
 321         }
 322     }
 323 
 324     /* There's no global state conflicts, so this method is not
 325      * presently synchronized.
 326      */
 327     private native long _getGlyphImageFromWindows(String family,
 328                                                   int style,
 329                                                   int size,
 330                                                   int glyphCode,
 331                                                   boolean fracMetrics);
 332 
 333     long getGlyphImageFromWindows(int glyphCode) {
 334         String family = fileFont.getFamilyName(null);
 335         int style = desc.style & Font.BOLD | desc.style & Font.ITALIC
 336             | fileFont.getStyle();
 337         int size = intPtSize;
 338         long ptr = _getGlyphImageFromWindows
 339             (family, style, size, glyphCode,
 340              desc.fmHint == INTVAL_FRACTIONALMETRICS_ON);
 341         if (ptr != 0) {
 342             /* Get the advance from the JDK rasterizer. This is mostly
 343              * necessary for the fractional metrics case, but there are
 344              * also some very small number (<0.25%) of marginal cases where
 345              * there is some rounding difference between windows and JDK.
 346              * After these are resolved, we can restrict this extra
 347              * work to the FM case.
 348              */
 349             float advance = getGlyphAdvance(glyphCode, false);
 350             StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset,
 351                                         advance);
 352             return ptr;
 353         } else {
 354             return fileFont.getGlyphImage(pScalerContext, glyphCode);
 355         }
 356     }
 357 
 358     /* Try the native strikes first, then try the fileFont strike */
 359     long getGlyphImageFromX11(int glyphCode) {
 360         long glyphPtr;
 361         char charCode = fileFont.glyphToCharMap[glyphCode];
 362         for (int i=0;i<nativeStrikes.length;i++) {
 363             CharToGlyphMapper mapper = fileFont.nativeFonts[i].getMapper();
 364             int gc = mapper.charToGlyph(charCode)&0xffff;
 365             if (gc != mapper.getMissingGlyphCode()) {
 366                 glyphPtr = nativeStrikes[i].getGlyphImagePtrNoCache(gc);
 367                 if (glyphPtr != 0L) {
 368                     return glyphPtr;
 369                 }
 370             }
 371         }
 372         return fileFont.getGlyphImage(pScalerContext, glyphCode);
 373     }
 374 
 375     long getGlyphImagePtr(int glyphCode) {
 376         if (glyphCode >= INVISIBLE_GLYPHS) {
 377             return StrikeCache.invisibleGlyphPtr;
 378         }
 379         long glyphPtr = 0L;
 380         if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) {
 381             return glyphPtr;
 382         } else {
 383             if (useNatives) {
 384                 glyphPtr = getGlyphImageFromNative(glyphCode);
 385                 if (glyphPtr == 0L && FontUtilities.isLogging()) {
 386                     FontUtilities.getLogger().info
 387                         ("Strike for " + fileFont +
 388                          " at size = " + intPtSize +
 389                          " couldn't get native glyph for code = " + glyphCode);
 390                  }
 391             } if (glyphPtr == 0L) {
 392                 glyphPtr = fileFont.getGlyphImage(pScalerContext,
 393                                                   glyphCode);
 394             }
 395             return setCachedGlyphPtr(glyphCode, glyphPtr);
 396         }
 397     }
 398 
 399     void getGlyphImagePtrs(int[] glyphCodes, long[] images, int  len) {
 400 
 401         for (int i=0; i<len; i++) {
 402             int glyphCode = glyphCodes[i];
 403             if (glyphCode >= INVISIBLE_GLYPHS) {
 404                 images[i] = StrikeCache.invisibleGlyphPtr;
 405                 continue;
 406             } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
 407                 continue;
 408             } else {
 409                 long glyphPtr = 0L;
 410                 if (useNatives) {
 411                     glyphPtr = getGlyphImageFromNative(glyphCode);
 412                 } if (glyphPtr == 0L) {
 413                     glyphPtr = fileFont.getGlyphImage(pScalerContext,
 414                                                       glyphCode);
 415                 }
 416                 images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
 417             }
 418         }
 419     }
 420 
 421     /* The following method is called from CompositeStrike as a special case.
 422      */
 423     int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
 424 
 425         int convertedCnt = 0;
 426 
 427         for (int i=0; i<len; i++) {
 428             int glyphCode = glyphCodes[i];
 429             if (glyphCode >>> 24 != 0) {
 430                 return convertedCnt;
 431             } else {
 432                 convertedCnt++;
 433             }
 434             if (glyphCode >= INVISIBLE_GLYPHS) {
 435                 images[i] = StrikeCache.invisibleGlyphPtr;
 436                 continue;
 437             } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
 438                 continue;
 439             } else {
 440                 long glyphPtr = 0L;
 441                 if (useNatives) {
 442                     glyphPtr = getGlyphImageFromNative(glyphCode);
 443                 }
 444                 if (glyphPtr == 0L) {
 445                     glyphPtr = fileFont.getGlyphImage(pScalerContext,
 446                                                       glyphCode);
 447                 }
 448                 images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
 449             }
 450         }
 451         return convertedCnt;
 452     }
 453 
 454     /* Only look in the cache */
 455     long getCachedGlyphPtr(int glyphCode) {
 456         try {
 457             return getCachedGlyphPtrInternal(glyphCode);
 458         } catch (Exception e) {
 459           NullFontScaler nullScaler =
 460              (NullFontScaler)FontScaler.getNullScaler();
 461           long nullSC = NullFontScaler.getNullScalerContext();
 462           return nullScaler.getGlyphImage(nullSC, glyphCode);
 463         }
 464     }
 465 
 466     private long getCachedGlyphPtrInternal(int glyphCode) {
 467         switch (glyphCacheFormat) {
 468             case INTARRAY:
 469                 return intGlyphImages[glyphCode] & INTMASK;
 470             case SEGINTARRAY:
 471                 int segIndex = glyphCode >> SEGSHIFT;
 472                 if (segIntGlyphImages[segIndex] != null) {
 473                     int subIndex = glyphCode % SEGSIZE;
 474                     return segIntGlyphImages[segIndex][subIndex] & INTMASK;
 475                 } else {
 476                     return 0L;
 477                 }
 478             case LONGARRAY:
 479                 return longGlyphImages[glyphCode];
 480             case SEGLONGARRAY:
 481                 segIndex = glyphCode >> SEGSHIFT;
 482                 if (segLongGlyphImages[segIndex] != null) {
 483                     int subIndex = glyphCode % SEGSIZE;
 484                     return segLongGlyphImages[segIndex][subIndex];
 485                 } else {
 486                     return 0L;
 487                 }
 488         }
 489         /* If reach here cache is UNINITIALISED. */
 490         return 0L;
 491     }
 492 
 493     private synchronized long setCachedGlyphPtr(int glyphCode, long glyphPtr) {
 494         try {
 495             return setCachedGlyphPtrInternal(glyphCode, glyphPtr);
 496         } catch (Exception e) {
 497             switch (glyphCacheFormat) {
 498                 case INTARRAY:
 499                 case SEGINTARRAY:
 500                     StrikeCache.freeIntPointer((int)glyphPtr);
 501                     break;
 502                 case LONGARRAY:
 503                 case SEGLONGARRAY:
 504                     StrikeCache.freeLongPointer(glyphPtr);
 505                     break;
 506              }
 507              NullFontScaler nullScaler =
 508                  (NullFontScaler)FontScaler.getNullScaler();
 509              long nullSC = NullFontScaler.getNullScalerContext();
 510              return nullScaler.getGlyphImage(nullSC, glyphCode);
 511         }
 512     }
 513 
 514     private long setCachedGlyphPtrInternal(int glyphCode, long glyphPtr) {
 515         switch (glyphCacheFormat) {
 516             case INTARRAY:
 517                 if (intGlyphImages[glyphCode] == 0) {
 518                     intGlyphImages[glyphCode] = (int)glyphPtr;
 519                     return glyphPtr;
 520                 } else {
 521                     StrikeCache.freeIntPointer((int)glyphPtr);
 522                     return intGlyphImages[glyphCode] & INTMASK;
 523                 }
 524 
 525             case SEGINTARRAY:
 526                 int segIndex = glyphCode >> SEGSHIFT;
 527                 int subIndex = glyphCode % SEGSIZE;
 528                 if (segIntGlyphImages[segIndex] == null) {
 529                     segIntGlyphImages[segIndex] = new int[SEGSIZE];
 530                 }
 531                 if (segIntGlyphImages[segIndex][subIndex] == 0) {
 532                     segIntGlyphImages[segIndex][subIndex] = (int)glyphPtr;
 533                     return glyphPtr;
 534                 } else {
 535                     StrikeCache.freeIntPointer((int)glyphPtr);
 536                     return segIntGlyphImages[segIndex][subIndex] & INTMASK;
 537                 }
 538 
 539             case LONGARRAY:
 540                 if (longGlyphImages[glyphCode] == 0L) {
 541                     longGlyphImages[glyphCode] = glyphPtr;
 542                     return glyphPtr;
 543                 } else {
 544                     StrikeCache.freeLongPointer(glyphPtr);
 545                     return longGlyphImages[glyphCode];
 546                 }
 547 
 548            case SEGLONGARRAY:
 549                 segIndex = glyphCode >> SEGSHIFT;
 550                 subIndex = glyphCode % SEGSIZE;
 551                 if (segLongGlyphImages[segIndex] == null) {
 552                     segLongGlyphImages[segIndex] = new long[SEGSIZE];
 553                 }
 554                 if (segLongGlyphImages[segIndex][subIndex] == 0L) {
 555                     segLongGlyphImages[segIndex][subIndex] = glyphPtr;
 556                     return glyphPtr;
 557                 } else {
 558                     StrikeCache.freeLongPointer(glyphPtr);
 559                     return segLongGlyphImages[segIndex][subIndex];
 560                 }
 561         }
 562 
 563         /* Reach here only when the cache is not initialised which is only
 564          * for the first glyph to be initialised in the strike.
 565          * Initialise it and recurse. Note that we are already synchronized.
 566          */
 567         initGlyphCache();
 568         return setCachedGlyphPtr(glyphCode, glyphPtr);
 569     }
 570 
 571     /* Called only from synchronized code or constructor */
 572     private synchronized void initGlyphCache() {
 573 
 574         int numGlyphs = mapper.getNumGlyphs();
 575         int tmpFormat = UNINITIALISED;
 576         if (segmentedCache) {
 577             int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
 578             if (longAddresses) {
 579                 tmpFormat = SEGLONGARRAY;
 580                 segLongGlyphImages = new long[numSegments][];
 581                 this.disposer.segLongGlyphImages = segLongGlyphImages;
 582              } else {
 583                  tmpFormat = SEGINTARRAY;
 584                  segIntGlyphImages = new int[numSegments][];
 585                  this.disposer.segIntGlyphImages = segIntGlyphImages;
 586              }
 587         } else {
 588             if (longAddresses) {
 589                 tmpFormat = LONGARRAY;
 590                 longGlyphImages = new long[numGlyphs];
 591                 this.disposer.longGlyphImages = longGlyphImages;
 592             } else {
 593                 tmpFormat = INTARRAY;
 594                 intGlyphImages = new int[numGlyphs];
 595                 this.disposer.intGlyphImages = intGlyphImages;
 596             }
 597         }
 598         glyphCacheFormat = tmpFormat;
 599     }
 600 
 601     float getGlyphAdvance(int glyphCode) {
 602         return getGlyphAdvance(glyphCode, true);
 603     }
 604 
 605     /* Metrics info is always retrieved. If the GlyphInfo address is non-zero
 606      * then metrics info there is valid and can just be copied.
 607      * This is in user space coordinates unless getUserAdv == false.
 608      * Device space advance should not be propagated out of this class.
 609      */
 610     private float getGlyphAdvance(int glyphCode, boolean getUserAdv) {
 611         float advance;
 612 
 613         if (glyphCode >= INVISIBLE_GLYPHS) {
 614             return 0f;
 615         }
 616 
 617         /* Notes on the (getUserAdv == false) case.
 618          *
 619          * Setting getUserAdv == false is internal to this class.
 620          * If there's no graphics transform we can let
 621          * getGlyphAdvance take its course, and potentially caching in
 622          * advances arrays, except for signalling that
 623          * getUserAdv == false means there is no need to create an image.
 624          * It is possible that code already calculated the user advance,
 625          * and it is desirable to take advantage of that work.
 626          * But, if there's a transform and we want device advance, we
 627          * can't use any values cached in the advances arrays - unless
 628          * first re-transform them into device space using 'desc.devTx'.
 629          * invertDevTx is null if the graphics transform is identity,
 630          * a translate, or non-invertible. The latter case should
 631          * not ever occur in the getUserAdv == false path.
 632          * In other words its either null, or the inversion of a
 633          * simple uniform scale. If its null, we can populate and
 634          * use the advance caches as normal.
 635          *
 636          * If we don't find a cached value, obtain the device advance and
 637          * return it. This will get stashed on the image by the caller and any
 638          * subsequent metrics calls will be able to use it as is the case
 639          * whenever an image is what is initially requested.
 640          *
 641          * Don't query if there's a value cached on the image, since this
 642          * getUserAdv==false code path is entered solely when none exists.
 643          */
 644         if (horizontalAdvances != null) {
 645             advance = horizontalAdvances[glyphCode];
 646             if (advance != Float.MAX_VALUE) {
 647                 if (!getUserAdv && invertDevTx != null) {
 648                     Point2D.Float metrics = new Point2D.Float(advance, 0f);
 649                     desc.devTx.deltaTransform(metrics, metrics);
 650                     return metrics.x;
 651                 } else {
 652                     return advance;
 653                 }
 654             }
 655         } else if (segmentedCache && segHorizontalAdvances != null) {
 656             int segIndex = glyphCode >> SEGSHIFT;
 657             float[] subArray = segHorizontalAdvances[segIndex];
 658             if (subArray != null) {
 659                 advance = subArray[glyphCode % SEGSIZE];
 660                 if (advance != Float.MAX_VALUE) {
 661                     if (!getUserAdv && invertDevTx != null) {
 662                         Point2D.Float metrics = new Point2D.Float(advance, 0f);
 663                         desc.devTx.deltaTransform(metrics, metrics);
 664                         return metrics.x;
 665                     } else {
 666                         return advance;
 667                     }
 668                 }
 669             }
 670         }
 671 
 672         if (!getUserAdv && invertDevTx != null) {
 673             Point2D.Float metrics = new Point2D.Float();
 674             fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
 675             return metrics.x;
 676         }
 677 
 678         if (invertDevTx != null || !getUserAdv) {
 679             /* If there is a device transform need x & y advance to
 680              * transform back into user space.
 681              */
 682             advance = getGlyphMetrics(glyphCode, getUserAdv).x;
 683         } else {
 684             long glyphPtr;
 685             if (getImageWithAdvance) {
 686                 /* A heuristic optimisation says that for most cases its
 687                  * worthwhile retrieving the image at the same time as the
 688                  * advance. So here we get the image data even if its not
 689                  * already cached.
 690                  */
 691                 glyphPtr = getGlyphImagePtr(glyphCode);
 692             } else {
 693                 glyphPtr = getCachedGlyphPtr(glyphCode);
 694             }
 695             if (glyphPtr != 0L) {
 696                 advance = StrikeCache.unsafe.getFloat
 697                     (glyphPtr + StrikeCache.xAdvanceOffset);
 698 
 699             } else {
 700                 advance = fileFont.getGlyphAdvance(pScalerContext, glyphCode);
 701             }
 702         }
 703 
 704         if (horizontalAdvances != null) {
 705             horizontalAdvances[glyphCode] = advance;
 706         } else if (segmentedCache && segHorizontalAdvances != null) {
 707             int segIndex = glyphCode >> SEGSHIFT;
 708             int subIndex = glyphCode % SEGSIZE;
 709             if (segHorizontalAdvances[segIndex] == null) {
 710                 segHorizontalAdvances[segIndex] = new float[SEGSIZE];
 711                 for (int i=0; i<SEGSIZE; i++) {
 712                      segHorizontalAdvances[segIndex][i] = Float.MAX_VALUE;
 713                 }
 714             }
 715             segHorizontalAdvances[segIndex][subIndex] = advance;
 716         }
 717         return advance;
 718     }
 719 
 720     float getCodePointAdvance(int cp) {
 721         return getGlyphAdvance(mapper.charToGlyph(cp));
 722     }
 723 
 724     /**
 725      * Result and pt are both in device space.
 726      */
 727     void getGlyphImageBounds(int glyphCode, Point2D.Float pt,
 728                              Rectangle result) {
 729 
 730         long ptr = getGlyphImagePtr(glyphCode);
 731         float topLeftX, topLeftY;
 732 
 733         /* With our current design NULL ptr is not possible
 734            but if we eventually allow scalers to return NULL pointers
 735            this check might be actually useful. */
 736         if (ptr == 0L) {
 737             result.x = (int) Math.floor(pt.x+0.5f);
 738             result.y = (int) Math.floor(pt.y+0.5f);
 739             result.width = result.height = 0;
 740             return;
 741         }
 742 
 743         topLeftX = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftXOffset);
 744         topLeftY = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftYOffset);
 745 
 746         result.x = (int)Math.floor(pt.x + topLeftX + 0.5f);
 747         result.y = (int)Math.floor(pt.y + topLeftY + 0.5f);
 748         result.width =
 749             StrikeCache.unsafe.getShort(ptr+StrikeCache.widthOffset)  &0x0ffff;
 750         result.height =
 751             StrikeCache.unsafe.getShort(ptr+StrikeCache.heightOffset) &0x0ffff;
 752 
 753         /* HRGB LCD text may have padding that is empty. This is almost always
 754          * going to be when topLeftX is -2 or less.
 755          * Try to return a tighter bounding box in that case.
 756          * If the first three bytes of every row are all zero, then
 757          * add 1 to "x" and reduce "width" by 1.
 758          */
 759         if ((desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
 760              desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR)
 761             && topLeftX <= -2.0f) {
 762             int minx = getGlyphImageMinX(ptr, result.x);
 763             if (minx > result.x) {
 764                 result.x += 1;
 765                 result.width -=1;
 766             }
 767         }
 768     }
 769 
 770     private int getGlyphImageMinX(long ptr, int origMinX) {
 771 
 772         int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);
 773         int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);
 774         int rowBytes =
 775             StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);
 776 
 777         if (rowBytes == width) {
 778             return origMinX;
 779         }
 780 
 781         long pixelData =
 782             StrikeCache.unsafe.getAddress(ptr + StrikeCache.pixelDataOffset);
 783 
 784         if (pixelData == 0L) {
 785             return origMinX;
 786         }
 787 
 788         for (int y=0;y<height;y++) {
 789             for (int x=0;x<3;x++) {
 790                 if (StrikeCache.unsafe.getByte(pixelData+y*rowBytes+x) != 0) {
 791                     return origMinX;
 792                 }
 793             }
 794         }
 795         return origMinX+1;
 796     }
 797 
 798     /* These 3 metrics methods below should be implemented to return
 799      * values in user space.
 800      */
 801     StrikeMetrics getFontMetrics() {
 802         if (strikeMetrics == null) {
 803             strikeMetrics =
 804                 fileFont.getFontMetrics(pScalerContext);
 805             if (invertDevTx != null) {
 806                 strikeMetrics.convertToUserSpace(invertDevTx);
 807             }
 808         }
 809         return strikeMetrics;
 810     }
 811 
 812     Point2D.Float getGlyphMetrics(int glyphCode) {
 813         return getGlyphMetrics(glyphCode, true);
 814     }
 815 
 816     private Point2D.Float getGlyphMetrics(int glyphCode, boolean getImage) {
 817         Point2D.Float metrics = new Point2D.Float();
 818 
 819         // !!! or do we force sgv user glyphs?
 820         if (glyphCode >= INVISIBLE_GLYPHS) {
 821             return metrics;
 822         }
 823         long glyphPtr;
 824         if (getImageWithAdvance && getImage) {
 825             /* A heuristic optimisation says that for most cases its
 826              * worthwhile retrieving the image at the same time as the
 827              * metrics. So here we get the image data even if its not
 828              * already cached.
 829              */
 830             glyphPtr = getGlyphImagePtr(glyphCode);
 831         } else {
 832              glyphPtr = getCachedGlyphPtr(glyphCode);
 833         }
 834         if (glyphPtr != 0L) {
 835             metrics = new Point2D.Float();
 836             metrics.x = StrikeCache.unsafe.getFloat
 837                 (glyphPtr + StrikeCache.xAdvanceOffset);
 838             metrics.y = StrikeCache.unsafe.getFloat
 839                 (glyphPtr + StrikeCache.yAdvanceOffset);
 840             /* advance is currently in device space, need to convert back
 841              * into user space.
 842              * This must not include the translation component. */
 843             if (invertDevTx != null) {
 844                 invertDevTx.deltaTransform(metrics, metrics);
 845             }
 846         } else {
 847             /* We sometimes cache these metrics as they are expensive to
 848              * generate for large glyphs.
 849              * We never reach this path if we obtain images with advances.
 850              * But if we do not obtain images with advances its possible that
 851              * we first obtain this information, then the image, and never
 852              * will access this value again.
 853              */
 854             Integer key = Integer.valueOf(glyphCode);
 855             Point2D.Float value = null;
 856             ConcurrentHashMap<Integer, Point2D.Float> glyphMetricsMap = null;
 857             if (glyphMetricsMapRef != null) {
 858                 glyphMetricsMap = glyphMetricsMapRef.get();
 859             }
 860             if (glyphMetricsMap != null) {
 861                 value = glyphMetricsMap.get(key);
 862                 if (value != null) {
 863                     metrics.x = value.x;
 864                     metrics.y = value.y;
 865                     /* already in user space */
 866                     return metrics;
 867                 }
 868             }
 869             if (value == null) {
 870                 fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
 871                 /* advance is currently in device space, need to convert back
 872                  * into user space.
 873                  */
 874                 if (invertDevTx != null) {
 875                     invertDevTx.deltaTransform(metrics, metrics);
 876                 }
 877                 value = new Point2D.Float(metrics.x, metrics.y);
 878                 /* We aren't synchronizing here so it is possible to
 879                  * overwrite the map with another one but this is harmless.
 880                  */
 881                 if (glyphMetricsMap == null) {
 882                     glyphMetricsMap =
 883                         new ConcurrentHashMap<Integer, Point2D.Float>();
 884                     glyphMetricsMapRef =
 885                         new SoftReference<ConcurrentHashMap<Integer,
 886                         Point2D.Float>>(glyphMetricsMap);
 887                 }
 888                 glyphMetricsMap.put(key, value);
 889             }
 890         }
 891         return metrics;
 892     }
 893 
 894     Point2D.Float getCharMetrics(char ch) {
 895         return getGlyphMetrics(mapper.charToGlyph(ch));
 896     }
 897 
 898     /* The caller of this can be trusted to return a copy of this
 899      * return value rectangle to public API. In fact frequently it
 900      * can't use this return value directly anyway.
 901      * This returns bounds in device space. Currently the only
 902      * caller is SGV and it converts back to user space.
 903      * We could change things so that this code does the conversion so
 904      * that all coords coming out of the font system are converted back
 905      * into user space even if they were measured in device space.
 906      * The same applies to the other methods that return outlines (below)
 907      * But it may make particular sense for this method that caches its
 908      * results.
 909      * There'd be plenty of exceptions, to this too, eg getGlyphPoint needs
 910      * device coords as its called from native layout and getGlyphImageBounds
 911      * is used by GlyphVector.getGlyphPixelBounds which is specified to
 912      * return device coordinates, the image pointers aren't really used
 913      * up in Java code either.
 914      */
 915     Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
 916 
 917         if (boundsMap == null) {
 918             boundsMap = new ConcurrentHashMap<Integer, Rectangle2D.Float>();
 919         }
 920 
 921         Integer key = Integer.valueOf(glyphCode);
 922         Rectangle2D.Float bounds = boundsMap.get(key);
 923 
 924         if (bounds == null) {
 925             bounds = fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
 926             boundsMap.put(key, bounds);
 927         }
 928         return bounds;
 929     }
 930 
 931     public Rectangle2D getOutlineBounds(int glyphCode) {
 932         return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
 933     }
 934 
 935     private
 936         WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;
 937 
 938     GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
 939 
 940         GeneralPath gp = null;
 941         ConcurrentHashMap<Integer, GeneralPath> outlineMap = null;
 942 
 943         if (outlineMapRef != null) {
 944             outlineMap = outlineMapRef.get();
 945             if (outlineMap != null) {
 946                 gp = outlineMap.get(glyphCode);
 947             }
 948         }
 949 
 950         if (gp == null) {
 951             gp = fileFont.getGlyphOutline(pScalerContext, glyphCode, 0, 0);
 952             if (outlineMap == null) {
 953                 outlineMap = new ConcurrentHashMap<Integer, GeneralPath>();
 954                 outlineMapRef =
 955                    new WeakReference
 956                        <ConcurrentHashMap<Integer,GeneralPath>>(outlineMap);
 957             }
 958             outlineMap.put(glyphCode, gp);
 959         }
 960         gp = (GeneralPath)gp.clone(); // mutable!
 961         if (x != 0f || y != 0f) {
 962             gp.transform(AffineTransform.getTranslateInstance(x, y));
 963         }
 964         return gp;
 965     }
 966 
 967     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
 968         return fileFont.getGlyphVectorOutline(pScalerContext,
 969                                               glyphs, glyphs.length, x, y);
 970     }
 971 
 972     protected void adjustPoint(Point2D.Float pt) {
 973         if (invertDevTx != null) {
 974             invertDevTx.deltaTransform(pt, pt);
 975         }
 976     }
 977 }