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 }