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/FontDesignMetrics.java
38829 views
1
/*
2
* Copyright (c) 1997, 2006, 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.ReferenceQueue;
29
import java.lang.ref.SoftReference;
30
31
import java.awt.FontMetrics;
32
import java.awt.Font;
33
import java.awt.GraphicsEnvironment;
34
import java.awt.geom.AffineTransform;
35
import java.awt.geom.NoninvertibleTransformException;
36
import java.awt.font.FontRenderContext;
37
import java.awt.font.TextLayout;
38
39
import java.io.IOException;
40
import java.io.ObjectInputStream;
41
import java.io.ObjectOutputStream;
42
43
import java.util.concurrent.ConcurrentHashMap;
44
45
import sun.java2d.Disposer;
46
import sun.java2d.DisposerRecord;
47
48
/*
49
* This class provides a summary of the glyph measurements for a Font
50
* and a set of hints that guide their display. It provides more metrics
51
* information for the Font than the java.awt.FontMetrics class. There
52
* is also some redundancy with that class.
53
* <p>
54
* The design metrics for a Font are obtained from Font.getDesignMetrics().
55
* The FontDesignMetrics object returned will be independent of the
56
* point size of the Font.
57
* Most users are familiar with the idea of using <i>point size</i> to
58
* specify the size of glyphs in a font. This point size defines a
59
* measurement between the baseline of one line to the baseline of the
60
* following line in a single spaced text document. The point size is
61
* based on <i>typographic points</i>, approximately 1/72 of an inch.
62
* <p>
63
* The Java2D API adopts the convention that one point is equivalent
64
* to one unit in user coordinates. When using a normalized transform
65
* for converting user space coordinates to device space coordinates (see
66
* GraphicsConfiguration.getDefaultTransform() and
67
* GraphicsConfiguration.getNormalizingTransform()), 72 user space units
68
* equal 1 inch in device space. In this case one point is 1/72 of an inch.
69
* <p>
70
* The FontDesignMetrics class expresses font metrics in terms of arbitrary
71
* <i>typographic units</i> (not points) chosen by the font supplier
72
* and used in the underlying platform font representations. These units are
73
* defined by dividing the em-square into a grid. The em-sqaure is the
74
* theoretical square whose dimensions are the full body height of the
75
* font. A typographic unit is the smallest measurable unit in the
76
* em-square. The number of units-per-em is determined by the font
77
* designer. The greater the units-per-em, the greater the precision
78
* in metrics. For example, Type 1 fonts divide the em-square into a
79
* 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 2048
80
* grid. The scale of these units can be obtained by calling
81
* getUnitsPerEm().
82
* <p>
83
* Typographic units are relative -- their absolute size changes as the
84
* size of the of the em-square changes. An em-square is 9 points high
85
* in a 9-point font. Because typographic units are relative to the
86
* em-square, a given location on a glyph will have the same coordinates
87
* in typographic units regardless of the point size.
88
* <p>
89
* Converting typographic units to pixels requires computing pixels-per-em
90
* (ppem). This can be computed as:
91
* <pre>
92
ppem = device_resolution * (inches-per-point) * pointSize
93
* </pre>
94
* where device resolution could be measured in pixels/inch and the point
95
* size of a font is effectively points/em. Using a normalized transform
96
* from user space to device space (see above), results in 1/72 inch/point.
97
* In this case, ppem is equal to the point size on a 72 dpi monitor, so
98
* that an N point font displays N pixels high. In general,
99
* <pre>
100
pixel_units = typographic_units * (ppem / units_per_em)
101
* </pre>
102
* @see java.awt.Font
103
* @see java.awt.GraphicsConfiguration#getDefaultTransform
104
* @see java.awt.GraphicsConfiguration#getNormalizingTransform
105
*/
106
107
public final class FontDesignMetrics extends FontMetrics {
108
109
static final long serialVersionUID = 4480069578560887773L;
110
111
private static final float UNKNOWN_WIDTH = -1;
112
private static final int CURRENT_VERSION = 1;
113
114
// height, ascent, descent, leading are reported to the client
115
// as an integer this value is added to the true fp value to
116
// obtain a value which is usually going to result in a round up
117
// to the next integer except for very marginal cases.
118
private static float roundingUpValue = 0.95f;
119
120
// These fields are all part of the old serialization representation
121
private Font font;
122
private float ascent;
123
private float descent;
124
private float leading;
125
private float maxAdvance;
126
private double[] matrix;
127
private int[] cache; // now unused, still here only for serialization
128
// End legacy serialization fields
129
130
private int serVersion = 0; // If 1 in readObject, these fields are on the input stream:
131
private boolean isAntiAliased;
132
private boolean usesFractionalMetrics;
133
private AffineTransform frcTx;
134
135
private transient float[] advCache; // transient since values could change across runtimes
136
private transient int height = -1;
137
138
private transient FontRenderContext frc;
139
140
private transient double[] devmatrix = null;
141
142
private transient FontStrike fontStrike;
143
144
private static FontRenderContext DEFAULT_FRC = null;
145
146
private static FontRenderContext getDefaultFrc() {
147
148
if (DEFAULT_FRC == null) {
149
AffineTransform tx;
150
if (GraphicsEnvironment.isHeadless()) {
151
tx = new AffineTransform();
152
} else {
153
tx = GraphicsEnvironment
154
.getLocalGraphicsEnvironment()
155
.getDefaultScreenDevice()
156
.getDefaultConfiguration()
157
.getDefaultTransform();
158
}
159
DEFAULT_FRC = new FontRenderContext(tx, false, false);
160
}
161
return DEFAULT_FRC;
162
}
163
164
/* Strongly cache up to 5 most recently requested FontMetrics objects,
165
* and softly cache as many as GC allows. In practice this means we
166
* should keep references around until memory gets low.
167
* We key the cache either by a Font or a combination of the Font and
168
* and FRC. A lot of callers use only the font so although there's code
169
* duplication, we allow just a font to be a key implying a default FRC.
170
* Also we put the references on a queue so that if they do get nulled
171
* out we can clear the keys from the table.
172
*/
173
private static class KeyReference extends SoftReference
174
implements DisposerRecord, Disposer.PollDisposable {
175
176
static ReferenceQueue queue = Disposer.getQueue();
177
178
Object key;
179
180
KeyReference(Object key, Object value) {
181
super(value, queue);
182
this.key = key;
183
Disposer.addReference(this, this);
184
}
185
186
/* It is possible that since this reference object has been
187
* enqueued, that a new metrics has been put into the table
188
* for the same key value. So we'll test to see if the table maps
189
* to THIS reference. If its a new one, we'll leave it alone.
190
* It is possible that a new entry comes in after our test, but
191
* it is unlikely and if this were a problem we would need to
192
* synchronize all 'put' and 'remove' accesses to the cache which
193
* I would prefer not to do.
194
*/
195
public void dispose() {
196
if (metricsCache.get(key) == this) {
197
metricsCache.remove(key);
198
}
199
}
200
}
201
202
private static class MetricsKey {
203
Font font;
204
FontRenderContext frc;
205
int hash;
206
207
MetricsKey() {
208
}
209
210
MetricsKey(Font font, FontRenderContext frc) {
211
init(font, frc);
212
}
213
214
void init(Font font, FontRenderContext frc) {
215
this.font = font;
216
this.frc = frc;
217
this.hash = font.hashCode() + frc.hashCode();
218
}
219
220
public boolean equals(Object key) {
221
if (!(key instanceof MetricsKey)) {
222
return false;
223
}
224
return
225
font.equals(((MetricsKey)key).font) &&
226
frc.equals(((MetricsKey)key).frc);
227
}
228
229
public int hashCode() {
230
return hash;
231
}
232
233
/* Synchronize access to this on the class */
234
static final MetricsKey key = new MetricsKey();
235
}
236
237
/* All accesses to a CHM do not in general need to be synchronized,
238
* as incomplete operations on another thread would just lead to
239
* harmless cache misses.
240
*/
241
private static final ConcurrentHashMap<Object, KeyReference>
242
metricsCache = new ConcurrentHashMap<Object, KeyReference>();
243
244
private static final int MAXRECENT = 5;
245
private static final FontDesignMetrics[]
246
recentMetrics = new FontDesignMetrics[MAXRECENT];
247
private static int recentIndex = 0;
248
249
public static FontDesignMetrics getMetrics(Font font) {
250
return getMetrics(font, getDefaultFrc());
251
}
252
253
public static FontDesignMetrics getMetrics(Font font,
254
FontRenderContext frc) {
255
256
257
/* When using alternate composites, can't cache based just on
258
* the java.awt.Font. Since this is rarely used and we can still
259
* cache the physical fonts, its not a problem to just return a
260
* new instance in this case.
261
* Note that currently Swing native L&F composites are not handled
262
* by this code as they use the metrics of the physical anyway.
263
*/
264
SunFontManager fm = SunFontManager.getInstance();
265
if (fm.maybeUsingAlternateCompositeFonts() &&
266
FontUtilities.getFont2D(font) instanceof CompositeFont) {
267
return new FontDesignMetrics(font, frc);
268
}
269
270
FontDesignMetrics m = null;
271
KeyReference r;
272
273
/* There are 2 possible keys used to perform lookups in metricsCache.
274
* If the FRC is set to all defaults, we just use the font as the key.
275
* If the FRC is non-default in any way, we construct a hybrid key
276
* that combines the font and FRC.
277
*/
278
boolean usefontkey = frc.equals(getDefaultFrc());
279
280
if (usefontkey) {
281
r = metricsCache.get(font);
282
} else /* use hybrid key */ {
283
// NB synchronization is not needed here because of updates to
284
// the metrics cache but is needed for the shared key.
285
synchronized (MetricsKey.class) {
286
MetricsKey.key.init(font, frc);
287
r = metricsCache.get(MetricsKey.key);
288
}
289
}
290
291
if (r != null) {
292
m = (FontDesignMetrics)r.get();
293
}
294
295
if (m == null) {
296
/* either there was no reference, or it was cleared. Need a new
297
* metrics instance. The key to use in the map is a new
298
* MetricsKey instance when we've determined the FRC is
299
* non-default. Its constructed from local vars so we are
300
* thread-safe - no need to worry about the shared key changing.
301
*/
302
m = new FontDesignMetrics(font, frc);
303
if (usefontkey) {
304
metricsCache.put(font, new KeyReference(font, m));
305
} else /* use hybrid key */ {
306
MetricsKey newKey = new MetricsKey(font, frc);
307
metricsCache.put(newKey, new KeyReference(newKey, m));
308
}
309
}
310
311
/* Here's where we keep the recent metrics */
312
for (int i=0; i<recentMetrics.length; i++) {
313
if (recentMetrics[i]==m) {
314
return m;
315
}
316
}
317
318
synchronized (recentMetrics) {
319
recentMetrics[recentIndex++] = m;
320
if (recentIndex == MAXRECENT) {
321
recentIndex = 0;
322
}
323
}
324
return m;
325
}
326
327
/*
328
* Constructs a new FontDesignMetrics object for the given Font.
329
* Its private to enable caching - call getMetrics() instead.
330
* @param font a Font object.
331
*/
332
333
private FontDesignMetrics(Font font) {
334
335
this(font, getDefaultFrc());
336
}
337
338
/* private to enable caching - call getMetrics() instead. */
339
private FontDesignMetrics(Font font, FontRenderContext frc) {
340
super(font);
341
this.font = font;
342
this.frc = frc;
343
344
this.isAntiAliased = frc.isAntiAliased();
345
this.usesFractionalMetrics = frc.usesFractionalMetrics();
346
347
frcTx = frc.getTransform();
348
349
matrix = new double[4];
350
initMatrixAndMetrics();
351
352
initAdvCache();
353
}
354
355
private void initMatrixAndMetrics() {
356
357
Font2D font2D = FontUtilities.getFont2D(font);
358
fontStrike = font2D.getStrike(font, frc);
359
StrikeMetrics metrics = fontStrike.getFontMetrics();
360
this.ascent = metrics.getAscent();
361
this.descent = metrics.getDescent();
362
this.leading = metrics.getLeading();
363
this.maxAdvance = metrics.getMaxAdvance();
364
365
devmatrix = new double[4];
366
frcTx.getMatrix(devmatrix);
367
}
368
369
private void initAdvCache() {
370
advCache = new float[256];
371
// 0 is a valid metric so force it to -1
372
for (int i = 0; i < 256; i++) {
373
advCache[i] = UNKNOWN_WIDTH;
374
}
375
}
376
377
private void readObject(ObjectInputStream in) throws IOException,
378
ClassNotFoundException {
379
380
in.defaultReadObject();
381
if (serVersion != CURRENT_VERSION) {
382
frc = getDefaultFrc();
383
isAntiAliased = frc.isAntiAliased();
384
usesFractionalMetrics = frc.usesFractionalMetrics();
385
frcTx = frc.getTransform();
386
}
387
else {
388
frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics);
389
}
390
391
// when deserialized, members are set to their default values for their type--
392
// not to the values assigned during initialization before the constructor
393
// body!
394
height = -1;
395
396
cache = null;
397
398
initMatrixAndMetrics();
399
initAdvCache();
400
}
401
402
private void writeObject(ObjectOutputStream out) throws IOException {
403
404
cache = new int[256];
405
for (int i=0; i < 256; i++) {
406
cache[i] = -1;
407
}
408
serVersion = CURRENT_VERSION;
409
410
out.defaultWriteObject();
411
412
cache = null;
413
}
414
415
private float handleCharWidth(int ch) {
416
return fontStrike.getCodePointAdvance(ch); // x-component of result only
417
}
418
419
// Uses advCache to get character width
420
// It is incorrect to call this method for ch > 255
421
private float getLatinCharWidth(char ch) {
422
423
float w = advCache[ch];
424
if (w == UNKNOWN_WIDTH) {
425
w = handleCharWidth(ch);
426
advCache[ch] = w;
427
}
428
return w;
429
}
430
431
432
/* Override of FontMetrics.getFontRenderContext() */
433
public FontRenderContext getFontRenderContext() {
434
return frc;
435
}
436
437
public int charWidth(char ch) {
438
// default metrics for compatibility with legacy code
439
float w;
440
if (ch < 0x100) {
441
w = getLatinCharWidth(ch);
442
}
443
else {
444
w = handleCharWidth(ch);
445
}
446
return (int)(0.5 + w);
447
}
448
449
public int charWidth(int ch) {
450
if (!Character.isValidCodePoint(ch)) {
451
ch = 0xffff;
452
}
453
454
float w = handleCharWidth(ch);
455
456
return (int)(0.5 + w);
457
}
458
459
public int stringWidth(String str) {
460
461
float width = 0;
462
if (font.hasLayoutAttributes()) {
463
/* TextLayout throws IAE for null, so throw NPE explicitly */
464
if (str == null) {
465
throw new NullPointerException("str is null");
466
}
467
if (str.length() == 0) {
468
return 0;
469
}
470
width = new TextLayout(str, font, frc).getAdvance();
471
} else {
472
int length = str.length();
473
for (int i=0; i < length; i++) {
474
char ch = str.charAt(i);
475
if (ch < 0x100) {
476
width += getLatinCharWidth(ch);
477
} else if (FontUtilities.isNonSimpleChar(ch)) {
478
width = new TextLayout(str, font, frc).getAdvance();
479
break;
480
} else {
481
width += handleCharWidth(ch);
482
}
483
}
484
}
485
486
return (int) (0.5 + width);
487
}
488
489
public int charsWidth(char data[], int off, int len) {
490
491
float width = 0;
492
if (font.hasLayoutAttributes()) {
493
if (len == 0) {
494
return 0;
495
}
496
String str = new String(data, off, len);
497
width = new TextLayout(str, font, frc).getAdvance();
498
} else {
499
/* Explicit test needed to satisfy superclass spec */
500
if (len < 0) {
501
throw new IndexOutOfBoundsException("len="+len);
502
}
503
int limit = off + len;
504
for (int i=off; i < limit; i++) {
505
char ch = data[i];
506
if (ch < 0x100) {
507
width += getLatinCharWidth(ch);
508
} else if (FontUtilities.isNonSimpleChar(ch)) {
509
String str = new String(data, off, len);
510
width = new TextLayout(str, font, frc).getAdvance();
511
break;
512
} else {
513
width += handleCharWidth(ch);
514
}
515
}
516
}
517
518
return (int) (0.5 + width);
519
}
520
521
/**
522
* Gets the advance widths of the first 256 characters in the
523
* <code>Font</code>. The advance is the
524
* distance from the leftmost point to the rightmost point on the
525
* character's baseline. Note that the advance of a
526
* <code>String</code> is not necessarily the sum of the advances
527
* of its characters.
528
* @return an array storing the advance widths of the
529
* characters in the <code>Font</code>
530
* described by this <code>FontMetrics</code> object.
531
*/
532
// More efficient than base class implementation - reuses existing cache
533
public int[] getWidths() {
534
int[] widths = new int[256];
535
for (char ch = 0 ; ch < 256 ; ch++) {
536
float w = advCache[ch];
537
if (w == UNKNOWN_WIDTH) {
538
w = advCache[ch] = handleCharWidth(ch);
539
}
540
widths[ch] = (int) (0.5 + w);
541
}
542
return widths;
543
}
544
545
public int getMaxAdvance() {
546
return (int)(0.99f + this.maxAdvance);
547
}
548
549
/*
550
* Returns the typographic ascent of the font. This is the maximum distance
551
* glyphs in this font extend above the base line (measured in typographic
552
* units).
553
*/
554
public int getAscent() {
555
return (int)(roundingUpValue + this.ascent);
556
}
557
558
/*
559
* Returns the typographic descent of the font. This is the maximum distance
560
* glyphs in this font extend below the base line.
561
*/
562
public int getDescent() {
563
return (int)(roundingUpValue + this.descent);
564
}
565
566
public int getLeading() {
567
// nb this ensures the sum of the results of the public methods
568
// for leading, ascent & descent sum to height.
569
// if the calculations in any other methods change this needs
570
// to be changed too.
571
// the 0.95 value used here and in the other methods allows some
572
// tiny fraction of leeway before rouding up. A higher value (0.99)
573
// caused some excessive rounding up.
574
return
575
(int)(roundingUpValue + descent + leading) -
576
(int)(roundingUpValue + descent);
577
}
578
579
// height is calculated as the sum of two separately rounded up values
580
// because typically clients use ascent to determine the y location to
581
// pass to drawString etc and we need to ensure that the height has enough
582
// space below the baseline to fully contain any descender.
583
public int getHeight() {
584
585
if (height < 0) {
586
height = getAscent() + (int)(roundingUpValue + descent + leading);
587
}
588
return height;
589
}
590
}
591
592