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