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