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+
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+}