Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/font/GlyphList.java
41154 views
1
/*
2
* Copyright (c) 2000, 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.font.GlyphVector;
29
import java.util.concurrent.atomic.AtomicBoolean;
30
31
import sun.java2d.SurfaceData;
32
import sun.java2d.loops.FontInfo;
33
34
/*
35
* This class represents a list of actual renderable glyphs.
36
* It can be constructed from a number of text sources, representing
37
* the various ways in which a programmer can ask a Graphics2D object
38
* to render some text. Once constructed, it provides a way of iterating
39
* through the device metrics and graybits of the individual glyphs that
40
* need to be rendered to the screen.
41
*
42
* Note that this class holds pointers to native data which must be
43
* disposed. It is not marked as finalizable since it is intended
44
* to be very lightweight and finalization is a comparitively expensive
45
* procedure. The caller must specifically use try{} finally{} to
46
* manually ensure that the object is disposed after use, otherwise
47
* native data structures might be leaked.
48
*
49
* Here is a code sample for using this class:
50
*
51
* public void drawString(String str, FontInfo info, float x, float y) {
52
* GlyphList gl = GlyphList.getInstance();
53
* try {
54
* gl.setFromString(info, str, x, y);
55
* gl.startGlyphIteration();
56
* int numglyphs = gl.getNumGlyphs();
57
* for (int i = 0; i < numglyphs; i++) {
58
* gl.setGlyphIndex(i);
59
* int metrics[] = gl.getMetrics();
60
* byte bits[] = gl.getGrayBits();
61
* int glyphx = metrics[0];
62
* int glyphy = metrics[1];
63
* int glyphw = metrics[2];
64
* int glyphh = metrics[3];
65
* int off = 0;
66
* for (int j = 0; j < glyphh; j++) {
67
* for (int i = 0; i < glyphw; i++) {
68
* int dx = glyphx + i;
69
* int dy = glyphy + j;
70
* int alpha = bits[off++];
71
* drawPixel(alpha, dx, dy);
72
* }
73
* }
74
* }
75
* } finally {
76
* gl.dispose();
77
* }
78
* }
79
*/
80
public final class GlyphList {
81
private static final int MINGRAYLENGTH = 1024;
82
private static final int MAXGRAYLENGTH = 8192;
83
private static final int DEFAULT_LENGTH = 32;
84
85
int glyphindex;
86
int[] metrics;
87
byte[] graybits;
88
89
/* A reference to the strike is needed for the case when the GlyphList
90
* may be added to a queue for batch processing, (e.g. OpenGL) and we need
91
* to be completely certain that the strike is still valid when the glyphs
92
* images are later referenced. This does mean that if such code discards
93
* GlyphList and places only the data it contains on the queue, that the
94
* strike needs to be part of that data held by a strong reference.
95
* In the cases of drawString() and drawChars(), this is a single strike,
96
* although it may be a composite strike. In the case of
97
* drawGlyphVector() it may be a single strike, or a list of strikes.
98
*/
99
Object strikelist; // hold multiple strikes during rendering of complex gv
100
101
/* In normal usage, the same GlyphList will get recycled, so
102
* it makes sense to allocate arrays that will get reused along with
103
* it, rather than generating garbage. Garbage will be generated only
104
* in MP envts where multiple threads are executing. Throughput should
105
* still be higher in those cases.
106
*/
107
int len = 0;
108
int maxLen = 0;
109
int maxPosLen = 0;
110
int[] glyphData;
111
char[] chData;
112
long[] images;
113
float[] positions;
114
float x, y;
115
float gposx, gposy;
116
boolean usePositions;
117
118
/* lcdRGBOrder is used only by LCD text rendering. Its here because
119
* the Graphics may have a different hint value than the one used
120
* by a GlyphVector, so it has to be stored here - and is obtained
121
* from the right FontInfo. Another approach would have been to have
122
* install a separate pipe for that case but that's a lot of extra
123
* code when a simple boolean will suffice. The overhead to non-LCD
124
* text is a redundant boolean assign per call.
125
*/
126
boolean lcdRGBOrder;
127
128
/*
129
* lcdSubPixPos is used only by LCD text rendering. Its here because
130
* the Graphics may have a different hint value than the one used
131
* by a GlyphVector, so it has to be stored here - and is obtained
132
* from the right FontInfo. Its also needed by the code which
133
* calculates glyph positions which already needs to access this
134
* GlyphList and would otherwise need the FontInfo.
135
* This is true only if LCD text and fractional metrics hints
136
* are selected on the graphics.
137
* When this is true and the glyph positions as determined by the
138
* advances are non-integral, it requests adjustment of the positions.
139
* Setting this for surfaces which do not support it through accelerated
140
* loops may cause a slow-down as software loops are invoked instead.
141
*/
142
boolean lcdSubPixPos;
143
144
/* This scheme creates a singleton GlyphList which is checked out
145
* for use. Callers who find its checked out create one that after use
146
* is discarded. This means that in a MT-rendering environment,
147
* there's no need to synchronise except for that one instance.
148
* Fewer threads will then need to synchronise, perhaps helping
149
* throughput on a MP system. If for some reason the reusable
150
* GlyphList is checked out for a long time (or never returned?) then
151
* we would end up always creating new ones. That situation should not
152
* occur and if it did, it would just lead to some extra garbage being
153
* created.
154
*/
155
private static final GlyphList reusableGL = new GlyphList();
156
private static final AtomicBoolean inUse = new AtomicBoolean();
157
158
private ColorGlyphSurfaceData glyphSurfaceData;
159
160
void ensureCapacity(int len) {
161
/* Note len must not be -ve! only setFromChars should be capable
162
* of passing down a -ve len, and this guards against it.
163
*/
164
if (len < 0) {
165
len = 0;
166
}
167
if (usePositions && len > maxPosLen) {
168
positions = new float[len * 2 + 2];
169
maxPosLen = len;
170
}
171
172
if (maxLen == 0 || len > maxLen) {
173
glyphData = new int[len];
174
chData = new char[len];
175
images = new long[len];
176
maxLen = len;
177
}
178
}
179
180
private GlyphList() {
181
// ensureCapacity(DEFAULT_LENGTH);
182
}
183
184
// private GlyphList(int arraylen) {
185
// ensureCapacity(arraylen);
186
// }
187
188
public static GlyphList getInstance() {
189
if (inUse.compareAndSet(false, true)) {
190
return reusableGL;
191
} else {
192
return new GlyphList();
193
}
194
}
195
196
/* In some cases the caller may be able to estimate the size of
197
* array needed, and it will usually be long enough. This avoids
198
* the unnecessary reallocation that occurs if our default
199
* values are too small. This is useful because this object
200
* will be discarded so the re-allocation overhead is high.
201
*/
202
// public static GlyphList getInstance(int sz) {
203
// if (inUse.compareAndSet(false, true) {
204
// return reusableGL;
205
// } else {
206
// return new GlyphList(sz);
207
// }
208
// }
209
210
/* GlyphList is in an invalid state until setFrom* method is called.
211
* After obtaining a new GlyphList it is the caller's responsibility
212
* that one of these methods is executed before handing off the
213
* GlyphList
214
*/
215
216
public boolean setFromString(FontInfo info, String str, float x, float y) {
217
this.x = x;
218
this.y = y;
219
this.strikelist = info.fontStrike;
220
this.lcdRGBOrder = info.lcdRGBOrder;
221
this.lcdSubPixPos = info.lcdSubPixPos;
222
len = str.length();
223
ensureCapacity(len);
224
str.getChars(0, len, chData, 0);
225
return mapChars(info, len);
226
}
227
228
public boolean setFromChars(FontInfo info, char[] chars, int off, int alen,
229
float x, float y) {
230
this.x = x;
231
this.y = y;
232
this.strikelist = info.fontStrike;
233
this.lcdRGBOrder = info.lcdRGBOrder;
234
this.lcdSubPixPos = info.lcdSubPixPos;
235
len = alen;
236
if (alen < 0) {
237
len = 0;
238
} else {
239
len = alen;
240
}
241
ensureCapacity(len);
242
System.arraycopy(chars, off, chData, 0, len);
243
return mapChars(info, len);
244
}
245
246
private boolean mapChars(FontInfo info, int len) {
247
/* REMIND.Is it worthwhile for the iteration to convert
248
* chars to glyph ids to directly map to images?
249
*/
250
if (info.font2D.getMapper().charsToGlyphsNS(len, chData, glyphData)) {
251
return false;
252
}
253
info.fontStrike.getGlyphImagePtrs(glyphData, images, len);
254
glyphindex = -1;
255
return true;
256
}
257
258
259
public void setFromGlyphVector(FontInfo info, GlyphVector gv,
260
float x, float y) {
261
this.x = x;
262
this.y = y;
263
this.lcdRGBOrder = info.lcdRGBOrder;
264
this.lcdSubPixPos = info.lcdSubPixPos;
265
/* A GV may be rendered in different Graphics. It is possible it is
266
* used for one case where LCD text is available, and another where
267
* it is not. Pass in the "info". to ensure get a suitable one.
268
*/
269
StandardGlyphVector sgv = StandardGlyphVector.getStandardGV(gv, info);
270
// call before ensureCapacity :-
271
usePositions = sgv.needsPositions(info.devTx);
272
len = sgv.getNumGlyphs();
273
ensureCapacity(len);
274
strikelist = sgv.setupGlyphImages(images,
275
usePositions ? positions : null,
276
info.devTx);
277
glyphindex = -1;
278
}
279
280
public void startGlyphIteration() {
281
if (glyphindex >= 0) {
282
throw new InternalError("glyph iteration restarted");
283
}
284
if (metrics == null) {
285
metrics = new int[5];
286
}
287
/* gposx and gposy are used to accumulate the advance.
288
* Add 0.5f for consistent rounding to pixel position. */
289
gposx = x + 0.5f;
290
gposy = y + 0.5f;
291
}
292
293
/*
294
* Must be called after 'startGlyphIteration'.
295
* Returns overall bounds for glyphs starting from the next glyph
296
* in iteration till the glyph with specified index.
297
* The underlying storage for bounds is shared with metrics,
298
* so this method (and the array it returns) shouldn't be used between
299
* 'setGlyphIndex' call and matching 'getMetrics' call.
300
*/
301
public int[] getBounds(int endGlyphIndex) {
302
fillBounds(metrics, endGlyphIndex);
303
return metrics;
304
}
305
306
/* This method now assumes "state", so must be called 0->len
307
* The metrics it returns are accumulated on the fly
308
* So it could be renamed "nextGlyph()".
309
* Note that a laid out GlyphVector which has assigned glyph positions
310
* doesn't have this stricture..
311
*/
312
public void setGlyphIndex(int i) {
313
glyphindex = i;
314
if (images[i] == 0L) {
315
metrics[0] = (int)gposx;
316
metrics[1] = (int)gposy;
317
metrics[2] = 0;
318
metrics[3] = 0;
319
metrics[4] = 0;
320
return;
321
}
322
float gx =
323
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftXOffset);
324
float gy =
325
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftYOffset);
326
327
if (usePositions) {
328
metrics[0] = (int)Math.floor(positions[(i<<1)] + gposx + gx);
329
metrics[1] = (int)Math.floor(positions[(i<<1)+1] + gposy + gy);
330
} else {
331
metrics[0] = (int)Math.floor(gposx + gx);
332
metrics[1] = (int)Math.floor(gposy + gy);
333
/* gposx and gposy are used to accumulate the advance */
334
gposx += StrikeCache.unsafe.getFloat
335
(images[i]+StrikeCache.xAdvanceOffset);
336
gposy += StrikeCache.unsafe.getFloat
337
(images[i]+StrikeCache.yAdvanceOffset);
338
}
339
metrics[2] =
340
StrikeCache.unsafe.getChar(images[i]+StrikeCache.widthOffset);
341
metrics[3] =
342
StrikeCache.unsafe.getChar(images[i]+StrikeCache.heightOffset);
343
metrics[4] =
344
StrikeCache.unsafe.getChar(images[i]+StrikeCache.rowBytesOffset);
345
}
346
347
public int[] getMetrics() {
348
return metrics;
349
}
350
351
public byte[] getGrayBits() {
352
int len = metrics[4] * metrics[3];
353
if (graybits == null) {
354
graybits = new byte[Math.max(len, MINGRAYLENGTH)];
355
} else {
356
if (len > graybits.length) {
357
graybits = new byte[len];
358
}
359
}
360
if (images[glyphindex] == 0L) {
361
return graybits;
362
}
363
long pixelDataAddress =
364
StrikeCache.unsafe.getAddress(images[glyphindex] +
365
StrikeCache.pixelDataOffset);
366
367
if (pixelDataAddress == 0L) {
368
return graybits;
369
}
370
/* unsafe is supposed to be fast, but I doubt if this loop can beat
371
* a native call which does a getPrimitiveArrayCritical and a
372
* memcpy for the typical amount of image data (30-150 bytes)
373
* Consider a native method if there is a performance problem (which
374
* I haven't seen so far).
375
*/
376
for (int i=0; i<len; i++) {
377
graybits[i] = StrikeCache.unsafe.getByte(pixelDataAddress+i);
378
}
379
return graybits;
380
}
381
382
public long[] getImages() {
383
return images;
384
}
385
386
public boolean usePositions() {
387
return usePositions;
388
}
389
390
public float[] getPositions() {
391
return positions;
392
}
393
394
public float getX() {
395
return x;
396
}
397
398
public float getY() {
399
return y;
400
}
401
402
public Object getStrike() {
403
return strikelist;
404
}
405
406
public boolean isSubPixPos() {
407
return lcdSubPixPos;
408
}
409
410
public boolean isRGBOrder() {
411
return lcdRGBOrder;
412
}
413
414
/* There's a reference equality test overhead here, but it allows us
415
* to avoid synchronizing for GL's that will just be GC'd. This
416
* helps MP throughput.
417
*/
418
public void dispose() {
419
if (this == reusableGL) {
420
if (graybits != null && graybits.length > MAXGRAYLENGTH) {
421
graybits = null;
422
}
423
usePositions = false;
424
strikelist = null; // remove reference to the strike list
425
inUse.set(false);
426
}
427
}
428
429
/* The value here is for use by the rendering engine as it reflects
430
* the number of glyphs in the array to be blitted. Surrogates pairs
431
* may have two slots (the second of these being a dummy entry of the
432
* invisible glyph), whereas an application client would expect only
433
* one glyph. In other words don't propagate this value up to client code.
434
*
435
* {dlf} an application client should have _no_ expectations about the
436
* number of glyphs per char. This ultimately depends on the font
437
* technology and layout process used, which in general clients will
438
* know nothing about.
439
*/
440
public int getNumGlyphs() {
441
return len;
442
}
443
444
/* We re-do all this work as we iterate through the glyphs
445
* but it seems unavoidable without re-working the Java TextRenderers.
446
*/
447
private void fillBounds(int[] bounds, int endGlyphIndex) {
448
/* Faster to access local variables in the for loop? */
449
int xOffset = StrikeCache.topLeftXOffset;
450
int yOffset = StrikeCache.topLeftYOffset;
451
int wOffset = StrikeCache.widthOffset;
452
int hOffset = StrikeCache.heightOffset;
453
int xAdvOffset = StrikeCache.xAdvanceOffset;
454
int yAdvOffset = StrikeCache.yAdvanceOffset;
455
456
int startGlyphIndex = glyphindex + 1;
457
if (startGlyphIndex >= endGlyphIndex) {
458
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0;
459
return;
460
}
461
float bx0, by0, bx1, by1;
462
bx0 = by0 = Float.POSITIVE_INFINITY;
463
bx1 = by1 = Float.NEGATIVE_INFINITY;
464
465
int posIndex = startGlyphIndex<<1;
466
float glx = gposx;
467
float gly = gposy;
468
char gw, gh;
469
float gx, gy, gx0, gy0, gx1, gy1;
470
for (int i=startGlyphIndex; i<endGlyphIndex; i++) {
471
if (images[i] == 0L) {
472
continue;
473
}
474
gx = StrikeCache.unsafe.getFloat(images[i]+xOffset);
475
gy = StrikeCache.unsafe.getFloat(images[i]+yOffset);
476
gw = StrikeCache.unsafe.getChar(images[i]+wOffset);
477
gh = StrikeCache.unsafe.getChar(images[i]+hOffset);
478
479
if (usePositions) {
480
gx0 = positions[posIndex++] + gx + glx;
481
gy0 = positions[posIndex++] + gy + gly;
482
} else {
483
gx0 = glx + gx;
484
gy0 = gly + gy;
485
glx += StrikeCache.unsafe.getFloat(images[i]+xAdvOffset);
486
gly += StrikeCache.unsafe.getFloat(images[i]+yAdvOffset);
487
}
488
gx1 = gx0 + gw;
489
gy1 = gy0 + gh;
490
if (bx0 > gx0) bx0 = gx0;
491
if (by0 > gy0) by0 = gy0;
492
if (bx1 < gx1) bx1 = gx1;
493
if (by1 < gy1) by1 = gy1;
494
}
495
/* floor is safe and correct because all glyph widths, heights
496
* and offsets are integers
497
*/
498
bounds[0] = (int)Math.floor(bx0);
499
bounds[1] = (int)Math.floor(by0);
500
bounds[2] = (int)Math.floor(bx1);
501
bounds[3] = (int)Math.floor(by1);
502
}
503
504
public static boolean canContainColorGlyphs() {
505
return FontUtilities.isMacOSX;
506
}
507
508
public boolean isColorGlyph(int glyphIndex) {
509
int width = StrikeCache.unsafe.getChar(images[glyphIndex] +
510
StrikeCache.widthOffset);
511
int rowBytes = StrikeCache.unsafe.getChar(images[glyphIndex] +
512
StrikeCache.rowBytesOffset);
513
return rowBytes == width * 4;
514
}
515
516
public SurfaceData getColorGlyphData() {
517
if (glyphSurfaceData == null) {
518
glyphSurfaceData = new ColorGlyphSurfaceData();
519
}
520
glyphSurfaceData.setCurrentGlyph(images[glyphindex]);
521
return glyphSurfaceData;
522
}
523
}
524
525