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.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 
getNumGlyphs()277     public int getNumGlyphs() {
278         return fileFont.getNumGlyphs();
279     }
280 
getGlyphImageFromNative(int glyphCode)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      */
_getGlyphImageFromWindows(String family, int style, int size, int glyphCode, boolean fracMetrics, int fontDataSize)292     private native long _getGlyphImageFromWindows(String family,
293                                                   int style,
294                                                   int size,
295                                                   int glyphCode,
296                                                   boolean fracMetrics,
297                                                   int fontDataSize);
298 
getGlyphImageFromWindows(int glyphCode)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 */
getGlyphImageFromX11(int glyphCode)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 
getGlyphImagePtr(int glyphCode)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 
getGlyphImagePtrs(int[] glyphCodes, long[] images, int len)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      */
getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len)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 */
getCachedGlyphPtr(int glyphCode)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 
getCachedGlyphPtrInternal(int glyphCode)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 
setCachedGlyphPtr(int glyphCode, long glyphPtr)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 
setCachedGlyphPtrInternal(int glyphCode, long glyphPtr)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 */
initGlyphCache()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 
getGlyphAdvance(int glyphCode)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      */
getGlyphAdvance(int glyphCode, boolean getUserAdv)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 
getCodePointAdvance(int cp)693     float getCodePointAdvance(int cp) {
694         return getGlyphAdvance(mapper.charToGlyph(cp));
695     }
696 
697     /**
698      * Result and pt are both in device space.
699      */
getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result)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 
getGlyphImageMinX(long ptr, int origMinX)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      */
getFontMetrics()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 
getGlyphMetrics(int glyphCode)785     Point2D.Float getGlyphMetrics(int glyphCode) {
786         return getGlyphMetrics(glyphCode, true);
787     }
788 
getGlyphMetrics(int glyphCode, boolean getImage)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 
getCharMetrics(char ch)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      */
getGlyphOutlineBounds(int glyphCode)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 
getOutlineBounds(int glyphCode)904     public Rectangle2D getOutlineBounds(int glyphCode) {
905         return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
906     }
907 
908     private
909         WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;
910 
getGlyphOutline(int glyphCode, float x, float y)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 
getGlyphVectorOutline(int[] glyphs, float x, float y)940     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
941         return fileFont.getGlyphVectorOutline(pScalerContext,
942                                               glyphs, glyphs.length, x, y);
943     }
944 
adjustPoint(Point2D.Float pt)945     protected void adjustPoint(Point2D.Float pt) {
946         if (invertDevTx != null) {
947             invertDevTx.deltaTransform(pt, pt);
948         }
949     }
950 }
951