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