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