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