Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/macosx/classes/sun/font/CStrike.java
41153 views
1
/*
2
* Copyright (c) 2011, 2018, 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.awt.Rectangle;
29
import java.awt.geom.*;
30
import java.util.*;
31
32
import sun.awt.SunHints;
33
34
public final class CStrike extends PhysicalStrike {
35
36
// Creates the native strike
37
private static native long createNativeStrikePtr(long nativeFontPtr,
38
double[] glyphTx,
39
double[] invDevTxMatrix,
40
int aaHint,
41
int fmHint);
42
43
// Disposes the native strike
44
private static native void disposeNativeStrikePtr(long nativeStrikePtr);
45
46
// Creates a StrikeMetrics from the underlying native system fonts
47
private static native StrikeMetrics getFontMetrics(long nativeStrikePtr);
48
49
// Returns native struct pointers used by the Sun 2D Renderer
50
private static native void getGlyphImagePtrsNative(long nativeStrikePtr,
51
long[] glyphInfos,
52
int[] uniCodes, int len);
53
54
// Returns the advance give a glyph code. It should be used only
55
// when the glyph code belongs to the CFont passed in.
56
private static native float getNativeGlyphAdvance(long nativeStrikePtr,
57
int glyphCode);
58
59
// Returns the outline shape of a glyph
60
private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,
61
int glyphCode,
62
double x,
63
double y);
64
65
// returns the bounding rect for a glyph
66
private static native void getNativeGlyphImageBounds(long nativeStrikePtr,
67
int glyphCode,
68
Rectangle2D.Float result,
69
double x, double y);
70
71
private final CFont nativeFont;
72
private AffineTransform invDevTx;
73
private final GlyphInfoCache glyphInfoCache;
74
private final GlyphAdvanceCache glyphAdvanceCache;
75
private long nativeStrikePtr;
76
77
CStrike(final CFont font, final FontStrikeDesc inDesc) {
78
nativeFont = font;
79
desc = inDesc;
80
glyphInfoCache = new GlyphInfoCache(font, desc);
81
glyphAdvanceCache = new GlyphAdvanceCache();
82
disposer = glyphInfoCache;
83
84
// Normally the device transform should be the identity transform
85
// for screen operations. The device transform only becomes
86
// interesting when we are outputting between different dpi surfaces,
87
// like when we are printing to postscript or use retina.
88
if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) {
89
try {
90
invDevTx = inDesc.devTx.createInverse();
91
} catch (NoninvertibleTransformException ignored) {
92
// ignored, since device transforms should not be that
93
// complicated, and if they are - there is nothing we can do,
94
// so we won't worry about it.
95
}
96
}
97
}
98
99
public long getNativeStrikePtr() {
100
if (nativeStrikePtr != 0) {
101
return nativeStrikePtr;
102
}
103
104
final double[] glyphTx = new double[6];
105
desc.glyphTx.getMatrix(glyphTx);
106
107
final double[] invDevTxMatrix = new double[6];
108
if (invDevTx == null) {
109
invDevTxMatrix[0] = 1;
110
invDevTxMatrix[3] = 1;
111
} else {
112
invDevTx.getMatrix(invDevTxMatrix);
113
}
114
115
final int aaHint = desc.aaHint;
116
final int fmHint = desc.fmHint;
117
118
synchronized (this) {
119
if (nativeStrikePtr != 0) {
120
return nativeStrikePtr;
121
}
122
nativeStrikePtr =
123
createNativeStrikePtr(nativeFont.getNativeFontPtr(),
124
glyphTx, invDevTxMatrix, aaHint, fmHint);
125
}
126
127
return nativeStrikePtr;
128
}
129
130
@SuppressWarnings("deprecation")
131
protected synchronized void finalize() throws Throwable {
132
if (nativeStrikePtr != 0) {
133
disposeNativeStrikePtr(nativeStrikePtr);
134
}
135
nativeStrikePtr = 0;
136
}
137
138
139
@Override
140
public int getNumGlyphs() {
141
return nativeFont.getNumGlyphs();
142
}
143
144
@Override
145
StrikeMetrics getFontMetrics() {
146
if (strikeMetrics == null) {
147
StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());
148
if (invDevTx != null) {
149
metrics.convertToUserSpace(invDevTx);
150
}
151
metrics.convertToUserSpace(desc.glyphTx);
152
strikeMetrics = metrics;
153
}
154
return strikeMetrics;
155
}
156
157
@Override
158
float getGlyphAdvance(final int glyphCode) {
159
return getCachedNativeGlyphAdvance(glyphCode);
160
}
161
162
@Override
163
float getCodePointAdvance(final int cp) {
164
return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
165
}
166
167
@Override
168
Point2D.Float getCharMetrics(final char ch) {
169
return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch));
170
}
171
172
@Override
173
Point2D.Float getGlyphMetrics(final int glyphCode) {
174
return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f);
175
}
176
177
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
178
GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);
179
Rectangle2D r2d = gp.getBounds2D();
180
Rectangle2D.Float r2df;
181
if (r2d instanceof Rectangle2D.Float) {
182
r2df = (Rectangle2D.Float)r2d;
183
} else {
184
float x = (float)r2d.getX();
185
float y = (float)r2d.getY();
186
float w = (float)r2d.getWidth();
187
float h = (float)r2d.getHeight();
188
r2df = new Rectangle2D.Float(x, y, w, h);
189
}
190
return r2df;
191
}
192
193
// pt, result in device space
194
void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
195
Rectangle2D.Float floatRect = new Rectangle2D.Float();
196
197
if (invDevTx != null) {
198
invDevTx.transform(pt, pt);
199
}
200
201
getGlyphImageBounds(glyphCode, pt.x, pt.y, floatRect);
202
203
if (floatRect.width == 0 && floatRect.height == 0) {
204
result.setRect(0, 0, -1, -1);
205
return;
206
}
207
208
result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height);
209
}
210
211
private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) {
212
getNativeGlyphImageBounds(getNativeStrikePtr(), glyphCode, floatRect, x, y);
213
}
214
215
GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
216
return getNativeGlyphOutline(getNativeStrikePtr(), glyphCode, x, y);
217
}
218
219
// should implement, however not called though any path that is publicly exposed
220
GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
221
throw new Error("not implemented yet");
222
}
223
224
// called from the Sun2D renderer
225
long getGlyphImagePtr(int glyphCode) {
226
synchronized (glyphInfoCache) {
227
long ptr = glyphInfoCache.get(glyphCode);
228
if (ptr != 0L) return ptr;
229
230
long[] ptrs = new long[1];
231
int[] codes = new int[1];
232
codes[0] = glyphCode;
233
234
getGlyphImagePtrs(codes, ptrs, 1);
235
236
ptr = ptrs[0];
237
glyphInfoCache.put(glyphCode, ptr);
238
239
return ptr;
240
}
241
}
242
243
// called from the Sun2D renderer
244
void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
245
synchronized (glyphInfoCache) {
246
// fill the image pointer array with existing pointers
247
// from the cache
248
int missed = 0;
249
for (int i = 0; i < len; i++) {
250
int code = glyphCodes[i];
251
252
final long ptr = glyphInfoCache.get(code);
253
if (ptr != 0L) {
254
images[i] = ptr;
255
} else {
256
// zero this element out, because the caller does not
257
// promise to keep it clean
258
images[i] = 0L;
259
missed++;
260
}
261
}
262
263
if (missed == 0) {
264
return; // horray! we got away without touching native!
265
}
266
267
// all distinct glyph codes requested (partially filled)
268
final int[] filteredCodes = new int[missed];
269
// indices into filteredCodes array (totally filled)
270
final int[] filteredIndicies = new int[missed];
271
272
// scan, mark, and store the requested glyph codes again to
273
// send into native
274
int j = 0;
275
int dupes = 0;
276
for (int i = 0; i < len; i++) {
277
if (images[i] != 0L) continue; // already filled
278
279
final int code = glyphCodes[i];
280
281
// we have already promised to strike this glyph - this is
282
// a dupe
283
if (glyphInfoCache.get(code) == -1L) {
284
filteredIndicies[j] = -1;
285
dupes++;
286
j++;
287
continue;
288
}
289
290
// this is a distinct glyph we have not struck before, or
291
// promised to strike mark this one as "promise to strike"
292
// in the global cache with a -1L
293
final int k = j - dupes;
294
filteredCodes[k] = code;
295
glyphInfoCache.put(code, -1L);
296
filteredIndicies[j] = k;
297
j++;
298
}
299
300
final int filteredRunLen = j - dupes;
301
final long[] filteredImages = new long[filteredRunLen];
302
303
// bulk call to fill in the distinct glyph pointers from native
304
getFilteredGlyphImagePtrs(filteredImages, filteredCodes, filteredRunLen);
305
306
// scan the requested glyph list, and fill in pointers from our
307
// distinct glyph list which has been filled from native
308
j = 0;
309
for (int i = 0; i < len; i++) {
310
if (images[i] != 0L && images[i] != -1L) {
311
continue; // already placed
312
}
313
314
// index into filteredImages array
315
final int k = filteredIndicies[j];
316
final int code = glyphCodes[i];
317
if (k == -1L) {
318
// we should have already filled the cache with this pointer
319
images[i] = glyphInfoCache.get(code);
320
} else {
321
// fill the particular glyph code request, and store
322
// in the cache
323
final long ptr = filteredImages[k];
324
images[i] = ptr;
325
glyphInfoCache.put(code, ptr);
326
}
327
328
j++;
329
}
330
}
331
}
332
333
private void getFilteredGlyphImagePtrs(long[] glyphInfos,
334
int[] uniCodes, int len)
335
{
336
getGlyphImagePtrsNative(getNativeStrikePtr(), glyphInfos, uniCodes, len);
337
}
338
339
private float getCachedNativeGlyphAdvance(int glyphCode) {
340
synchronized(glyphAdvanceCache) {
341
float advance = glyphAdvanceCache.get(glyphCode);
342
if (advance != 0) {
343
return advance;
344
}
345
346
advance = getNativeGlyphAdvance(getNativeStrikePtr(), glyphCode);
347
glyphAdvanceCache.put(glyphCode, advance);
348
return advance;
349
}
350
}
351
352
// This class stores glyph pointers, and is indexed based on glyph codes,
353
// and negative unicode values. See the comments in
354
// CCharToGlyphMapper for more details on our glyph code strategy.
355
private static class GlyphInfoCache extends CStrikeDisposer {
356
private static final int FIRST_LAYER_SIZE = 256;
357
private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
358
359
// rdar://problem/5204197
360
private boolean disposed = false;
361
362
private final long[] firstLayerCache;
363
private SparseBitShiftingTwoLayerArray secondLayerCache;
364
private HashMap<Integer, Long> generalCache;
365
366
GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {
367
super(nativeFont, desc);
368
firstLayerCache = new long[FIRST_LAYER_SIZE];
369
}
370
371
public synchronized long get(final int index) {
372
if (index < 0) {
373
if (-index < SECOND_LAYER_SIZE) {
374
// catch common unicodes
375
if (secondLayerCache == null) {
376
return 0L;
377
}
378
return secondLayerCache.get(-index);
379
}
380
} else {
381
if (index < FIRST_LAYER_SIZE) {
382
// catch common glyphcodes
383
return firstLayerCache[index];
384
}
385
}
386
387
if (generalCache == null) {
388
return 0L;
389
}
390
final Long value = generalCache.get(Integer.valueOf(index));
391
if (value == null) {
392
return 0L;
393
}
394
return value.longValue();
395
}
396
397
public synchronized void put(final int index, final long value) {
398
if (index < 0) {
399
if (-index < SECOND_LAYER_SIZE) {
400
// catch common unicodes
401
if (secondLayerCache == null) {
402
secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128
403
}
404
secondLayerCache.put(-index, value);
405
return;
406
}
407
} else {
408
if (index < FIRST_LAYER_SIZE) {
409
// catch common glyphcodes
410
firstLayerCache[index] = value;
411
return;
412
}
413
}
414
415
if (generalCache == null) {
416
generalCache = new HashMap<Integer, Long>();
417
}
418
419
generalCache.put(Integer.valueOf(index), Long.valueOf(value));
420
}
421
422
public synchronized void dispose() {
423
// rdar://problem/5204197
424
// Note that sun.font.Font2D.getStrike() actively disposes
425
// cleared strikeRef. We need to check the disposed flag to
426
// prevent double frees of native resources.
427
if (disposed) {
428
return;
429
}
430
431
super.dispose();
432
433
// clean out the first array
434
disposeLongArray(firstLayerCache);
435
436
// clean out the two layer arrays
437
if (secondLayerCache != null) {
438
final long[][] secondLayerLongArrayArray = secondLayerCache.cache;
439
for (int i = 0; i < secondLayerLongArrayArray.length; i++) {
440
final long[] longArray = secondLayerLongArrayArray[i];
441
if (longArray != null) disposeLongArray(longArray);
442
}
443
}
444
445
// clean up everyone else
446
if (generalCache != null) {
447
final Iterator<Long> i = generalCache.values().iterator();
448
while (i.hasNext()) {
449
final long longValue = i.next().longValue();
450
if (longValue != -1 && longValue != 0) {
451
removeGlyphInfoFromCache(longValue);
452
StrikeCache.freeLongPointer(longValue);
453
}
454
}
455
}
456
457
// rdar://problem/5204197
458
// Finally, set the flag.
459
disposed = true;
460
}
461
462
private static void disposeLongArray(final long[] longArray) {
463
for (int i = 0; i < longArray.length; i++) {
464
final long ptr = longArray[i];
465
if (ptr != 0 && ptr != -1) {
466
removeGlyphInfoFromCache(ptr);
467
StrikeCache.freeLongPointer(ptr); // free's the native struct pointer
468
}
469
}
470
}
471
472
private static class SparseBitShiftingTwoLayerArray {
473
final long[][] cache;
474
final int shift;
475
final int secondLayerLength;
476
477
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
478
this.shift = shift;
479
this.cache = new long[1 << shift][];
480
this.secondLayerLength = size >> shift;
481
}
482
483
public long get(final int index) {
484
final int firstIndex = index >> shift;
485
final long[] firstLayerRow = cache[firstIndex];
486
if (firstLayerRow == null) return 0L;
487
return firstLayerRow[index - (firstIndex * (1 << shift))];
488
}
489
490
public void put(final int index, final long value) {
491
final int firstIndex = index >> shift;
492
long[] firstLayerRow = cache[firstIndex];
493
if (firstLayerRow == null) {
494
cache[firstIndex] = firstLayerRow = new long[secondLayerLength];
495
}
496
firstLayerRow[index - (firstIndex * (1 << shift))] = value;
497
}
498
}
499
}
500
501
private static class GlyphAdvanceCache {
502
private static final int FIRST_LAYER_SIZE = 256;
503
private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
504
505
private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE];
506
private SparseBitShiftingTwoLayerArray secondLayerCache;
507
private HashMap<Integer, Float> generalCache;
508
509
public synchronized float get(final int index) {
510
if (index < 0) {
511
if (-index < SECOND_LAYER_SIZE) {
512
// catch common unicodes
513
if (secondLayerCache == null) return 0;
514
return secondLayerCache.get(-index);
515
}
516
} else {
517
if (index < FIRST_LAYER_SIZE) {
518
// catch common glyphcodes
519
return firstLayerCache[index];
520
}
521
}
522
523
if (generalCache == null) return 0;
524
final Float value = generalCache.get(Integer.valueOf(index));
525
if (value == null) return 0;
526
return value.floatValue();
527
}
528
529
public synchronized void put(final int index, final float value) {
530
if (index < 0) {
531
if (-index < SECOND_LAYER_SIZE) {
532
// catch common unicodes
533
if (secondLayerCache == null) {
534
secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128
535
}
536
secondLayerCache.put(-index, value);
537
return;
538
}
539
} else {
540
if (index < FIRST_LAYER_SIZE) {
541
// catch common glyphcodes
542
firstLayerCache[index] = value;
543
return;
544
}
545
}
546
547
if (generalCache == null) {
548
generalCache = new HashMap<Integer, Float>();
549
}
550
551
generalCache.put(Integer.valueOf(index), Float.valueOf(value));
552
}
553
554
private static class SparseBitShiftingTwoLayerArray {
555
final float[][] cache;
556
final int shift;
557
final int secondLayerLength;
558
559
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
560
this.shift = shift;
561
this.cache = new float[1 << shift][];
562
this.secondLayerLength = size >> shift;
563
}
564
565
public float get(final int index) {
566
final int firstIndex = index >> shift;
567
final float[] firstLayerRow = cache[firstIndex];
568
if (firstLayerRow == null) return 0L;
569
return firstLayerRow[index - (firstIndex * (1 << shift))];
570
}
571
572
public void put(final int index, final float value) {
573
final int firstIndex = index >> shift;
574
float[] firstLayerRow = cache[firstIndex];
575
if (firstLayerRow == null) {
576
cache[firstIndex] = firstLayerRow =
577
new float[secondLayerLength];
578
}
579
firstLayerRow[index - (firstIndex * (1 << shift))] = value;
580
}
581
}
582
}
583
}
584
585