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