History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/font/FontDesignMetrics.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/font/FontDesignMetrics.java+590−0
@@ -0,0 +1,590 @@
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
26package sun.font;
27
28import java.lang.ref.ReferenceQueue;
29import java.lang.ref.SoftReference;
30
31import java.awt.FontMetrics;
32import java.awt.Font;
33import java.awt.GraphicsEnvironment;
34import java.awt.geom.AffineTransform;
35import java.awt.geom.NoninvertibleTransformException;
36import java.awt.font.FontRenderContext;
37import java.awt.font.TextLayout;
38
39import java.io.IOException;
40import java.io.ObjectInputStream;
41import java.io.ObjectOutputStream;
42
43import java.util.concurrent.ConcurrentHashMap;
44
45import sun.java2d.Disposer;
46import 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
107public 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}