Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/font/FileFontStrike.java
38829 views
1
/*
2
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.font;
27
28
import java.lang.ref.SoftReference;
29
import java.lang.ref.WeakReference;
30
import java.awt.Font;
31
import java.awt.GraphicsEnvironment;
32
import java.awt.Rectangle;
33
import java.awt.geom.AffineTransform;
34
import java.awt.geom.GeneralPath;
35
import java.awt.geom.NoninvertibleTransformException;
36
import java.awt.geom.Point2D;
37
import java.awt.geom.Rectangle2D;
38
import java.util.concurrent.ConcurrentHashMap;
39
import static sun.awt.SunHints.*;
40
41
42
public class FileFontStrike extends PhysicalStrike {
43
44
/* fffe and ffff are values we specially interpret as meaning
45
* invisible glyphs.
46
*/
47
static final int INVISIBLE_GLYPHS = 0x0fffe;
48
49
private FileFont fileFont;
50
51
/* REMIND: replace this scheme with one that installs a cache
52
* instance of the appropriate type. It will require changes in
53
* FontStrikeDisposer and NativeStrike etc.
54
*/
55
private static final int UNINITIALISED = 0;
56
private static final int INTARRAY = 1;
57
private static final int LONGARRAY = 2;
58
private static final int SEGINTARRAY = 3;
59
private static final int SEGLONGARRAY = 4;
60
61
private volatile int glyphCacheFormat = UNINITIALISED;
62
63
/* segmented arrays are blocks of 32 */
64
private static final int SEGSHIFT = 5;
65
private static final int SEGSIZE = 1 << SEGSHIFT;
66
67
private boolean segmentedCache;
68
private int[][] segIntGlyphImages;
69
private long[][] segLongGlyphImages;
70
71
/* The "metrics" information requested by clients is usually nothing
72
* more than the horizontal advance of the character.
73
* In most cases this advance and other metrics information is stored
74
* in the glyph image cache.
75
* But in some cases we do not automatically retrieve the glyph
76
* image when the advance is requested. In those cases we want to
77
* cache the advances since this has been shown to be important for
78
* performance.
79
* The segmented cache is used in cases when the single array
80
* would be too large.
81
*/
82
private float[] horizontalAdvances;
83
private float[][] segHorizontalAdvances;
84
85
/* Outline bounds are used when printing and when drawing outlines
86
* to the screen. On balance the relative rarity of these cases
87
* and the fact that getting this requires generating a path at
88
* the scaler level means that its probably OK to store these
89
* in a Java-level hashmap as the trade-off between time and space.
90
* Later can revisit whether to cache these at all, or elsewhere.
91
* Should also profile whether subsequent to getting the bounds, the
92
* outline itself is also requested. The 1.4 implementation doesn't
93
* cache outlines so you could generate the path twice - once to get
94
* the bounds and again to return the outline to the client.
95
* If the two uses are coincident then also look into caching outlines.
96
* One simple optimisation is that we could store the last single
97
* outline retrieved. This assumes that bounds then outline will always
98
* be retrieved for a glyph rather than retrieving bounds for all glyphs
99
* then outlines for all glyphs.
100
*/
101
ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap;
102
SoftReference<ConcurrentHashMap<Integer, Point2D.Float>>
103
glyphMetricsMapRef;
104
105
AffineTransform invertDevTx;
106
107
boolean useNatives;
108
NativeStrike[] nativeStrikes;
109
110
/* Used only for communication to native layer */
111
private int intPtSize;
112
113
/* Perform global initialisation needed for Windows native rasterizer */
114
private static native boolean initNative();
115
private static boolean isXPorLater = false;
116
static {
117
if (FontUtilities.isWindows && !FontUtilities.useT2K &&
118
!GraphicsEnvironment.isHeadless()) {
119
isXPorLater = initNative();
120
}
121
}
122
123
FileFontStrike(FileFont fileFont, FontStrikeDesc desc) {
124
super(fileFont, desc);
125
this.fileFont = fileFont;
126
127
if (desc.style != fileFont.style) {
128
/* If using algorithmic styling, the base values are
129
* boldness = 1.0, italic = 0.0. The superclass constructor
130
* initialises these.
131
*/
132
if ((desc.style & Font.ITALIC) == Font.ITALIC &&
133
(fileFont.style & Font.ITALIC) == 0) {
134
algoStyle = true;
135
italic = 0.7f;
136
}
137
if ((desc.style & Font.BOLD) == Font.BOLD &&
138
((fileFont.style & Font.BOLD) == 0)) {
139
algoStyle = true;
140
boldness = 1.33f;
141
}
142
}
143
double[] matrix = new double[4];
144
AffineTransform at = desc.glyphTx;
145
at.getMatrix(matrix);
146
if (!desc.devTx.isIdentity() &&
147
desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
148
try {
149
invertDevTx = desc.devTx.createInverse();
150
} catch (NoninvertibleTransformException e) {
151
}
152
}
153
154
/* 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.useT2K &&
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
312
public int getNumGlyphs() {
313
return fileFont.getNumGlyphs();
314
}
315
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
*/
327
private native long _getGlyphImageFromWindows(String family,
328
int style,
329
int size,
330
int glyphCode,
331
boolean fracMetrics,
332
int fontDataSize);
333
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 */
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
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
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
*/
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 */
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
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
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
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 */
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
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
*/
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
728
float getCodePointAdvance(int cp) {
729
return getGlyphAdvance(mapper.charToGlyph(cp));
730
}
731
732
/**
733
* Result and pt are both in device space.
734
*/
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);
746
result.y = (int) Math.floor(pt.y);
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);
755
result.y = (int)Math.floor(pt.y + topLeftY);
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, (int)result.x);
771
if (minx > result.x) {
772
result.x += 1;
773
result.width -=1;
774
}
775
}
776
}
777
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
*/
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
820
Point2D.Float getGlyphMetrics(int glyphCode) {
821
return getGlyphMetrics(glyphCode, true);
822
}
823
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
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 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
*/
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
939
public Rectangle2D getOutlineBounds(int glyphCode) {
940
return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
941
}
942
943
private
944
WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;
945
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 = (GeneralPath)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
975
GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
976
return fileFont.getGlyphVectorOutline(pScalerContext,
977
glyphs, glyphs.length, x, y);
978
}
979
980
protected void adjustPoint(Point2D.Float pt) {
981
if (invertDevTx != null) {
982
invertDevTx.deltaTransform(pt, pt);
983
}
984
}
985
}
986
987