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/GlyphLayout.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/font/GlyphLayout.java+668−0
@@ -0,0 +1,668 @@
1+/*
2+ * Copyright (c) 2003, 2008, 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+/*
27+ *
28+ * (C) Copyright IBM Corp. 1999-2003 - All Rights Reserved
29+ *
30+ * The original version of this source code and documentation is
31+ * copyrighted and owned by IBM. These materials are provided
32+ * under terms of a License Agreement between IBM and Sun.
33+ * This technology is protected by multiple US and International
34+ * patents. This notice and attribution to IBM may not be removed.
35+ */
36+
37+/*
38+ * GlyphLayout is used to process a run of text into a run of run of
39+ * glyphs, optionally with position and char mapping info.
40+ *
41+ * The text has already been processed for numeric shaping and bidi.
42+ * The run of text that layout works on has a single bidi level. It
43+ * also has a single font/style. Some operations need context to work
44+ * on (shaping, script resolution) so context for the text run text is
45+ * provided. It is assumed that the text array contains sufficient
46+ * context, and the offset and count delimit the portion of the text
47+ * that needs to actually be processed.
48+ *
49+ * The font might be a composite font. Layout generally requires
50+ * tables from a single physical font to operate, and so it must
51+ * resolve the 'single' font run into runs of physical fonts.
52+ *
53+ * Some characters are supported by several fonts of a composite, and
54+ * in order to properly emulate the glyph substitution behavior of a
55+ * single physical font, these characters might need to be mapped to
56+ * different physical fonts. The script code that is assigned
57+ * characters normally considered 'common script' can be used to
58+ * resolve which physical font to use for these characters. The input
59+ * to the char to glyph mapper (which assigns physical fonts as it
60+ * processes the glyphs) should include the script code, and the
61+ * mapper should operate on runs of a single script.
62+ *
63+ * To perform layout, call get() to get a new (or reuse an old)
64+ * GlyphLayout, call layout on it, then call done(GlyphLayout) when
65+ * finished. There's no particular problem if you don't call done,
66+ * but it assists in reuse of the GlyphLayout.
67+ */
68+
69+package sun.font;
70+
71+import java.lang.ref.SoftReference;
72+import java.awt.Font;
73+import java.awt.font.FontRenderContext;
74+import java.awt.font.GlyphVector;
75+import java.awt.geom.AffineTransform;
76+import java.awt.geom.NoninvertibleTransformException;
77+import java.awt.geom.Point2D;
78+import java.util.ArrayList;
79+import java.util.concurrent.ConcurrentHashMap;
80+
81+import static java.lang.Character.*;
82+
83+public final class GlyphLayout {
84+ // data for glyph vector
85+ private GVData _gvdata;
86+
87+ // cached glyph layout data for reuse
88+ private static volatile GlyphLayout cache; // reusable
89+
90+ private LayoutEngineFactory _lef; // set when get is called, unset when done is called
91+ private TextRecord _textRecord; // the text we're working on, used by iterators
92+ private ScriptRun _scriptRuns; // iterator over script runs
93+ private FontRunIterator _fontRuns; // iterator over physical fonts in a composite
94+ private int _ercount;
95+ private ArrayList _erecords;
96+ private Point2D.Float _pt;
97+ private FontStrikeDesc _sd;
98+ private float[] _mat;
99+ private int _typo_flags;
100+ private int _offset;
101+
102+ public static final class LayoutEngineKey {
103+ private Font2D font;
104+ private int script;
105+ private int lang;
106+
107+ LayoutEngineKey() {
108+ }
109+
110+ LayoutEngineKey(Font2D font, int script, int lang) {
111+ init(font, script, lang);
112+ }
113+
114+ void init(Font2D font, int script, int lang) {
115+ this.font = font;
116+ this.script = script;
117+ this.lang = lang;
118+ }
119+
120+ LayoutEngineKey copy() {
121+ return new LayoutEngineKey(font, script, lang);
122+ }
123+
124+ Font2D font() {
125+ return font;
126+ }
127+
128+ int script() {
129+ return script;
130+ }
131+
132+ int lang() {
133+ return lang;
134+ }
135+
136+ public boolean equals(Object rhs) {
137+ if (this == rhs) return true;
138+ if (rhs == null) return false;
139+ try {
140+ LayoutEngineKey that = (LayoutEngineKey)rhs;
141+ return this.script == that.script &&
142+ this.lang == that.lang &&
143+ this.font.equals(that.font);
144+ }
145+ catch (ClassCastException e) {
146+ return false;
147+ }
148+ }
149+
150+ public int hashCode() {
151+ return script ^ lang ^ font.hashCode();
152+ }
153+ }
154+
155+ public static interface LayoutEngineFactory {
156+ /**
157+ * Given a font, script, and language, determine a layout engine to use.
158+ */
159+ public LayoutEngine getEngine(Font2D font, int script, int lang);
160+
161+ /**
162+ * Given a key, determine a layout engine to use.
163+ */
164+ public LayoutEngine getEngine(LayoutEngineKey key);
165+ }
166+
167+ public static interface LayoutEngine {
168+ /**
169+ * Given a strike descriptor, text, rtl flag, and starting point, append information about
170+ * glyphs, positions, and character indices to the glyphvector data, and advance the point.
171+ *
172+ * If the GVData does not have room for the glyphs, throws an IndexOutOfBoundsException and
173+ * leave pt and the gvdata unchanged.
174+ */
175+ public void layout(FontStrikeDesc sd, float[] mat, int gmask,
176+ int baseIndex, TextRecord text, int typo_flags, Point2D.Float pt, GVData data);
177+ }
178+
179+ /**
180+ * Return a new instance of GlyphLayout, using the provided layout engine factory.
181+ * If null, the system layout engine factory will be used.
182+ */
183+ public static GlyphLayout get(LayoutEngineFactory lef) {
184+ if (lef == null) {
185+ lef = SunLayoutEngine.instance();
186+ }
187+ GlyphLayout result = null;
188+ synchronized(GlyphLayout.class) {
189+ if (cache != null) {
190+ result = cache;
191+ cache = null;
192+ }
193+ }
194+ if (result == null) {
195+ result = new GlyphLayout();
196+ }
197+ result._lef = lef;
198+ return result;
199+ }
200+
201+ /**
202+ * Return the old instance of GlyphLayout when you are done. This enables reuse
203+ * of GlyphLayout objects.
204+ */
205+ public static void done(GlyphLayout gl) {
206+ gl._lef = null;
207+ cache = gl; // object reference assignment is thread safe, it says here...
208+ }
209+
210+ private static final class SDCache {
211+ public Font key_font;
212+ public FontRenderContext key_frc;
213+
214+ public AffineTransform dtx;
215+ public AffineTransform invdtx;
216+ public AffineTransform gtx;
217+ public Point2D.Float delta;
218+ public FontStrikeDesc sd;
219+
220+ private SDCache(Font font, FontRenderContext frc) {
221+ key_font = font;
222+ key_frc = frc;
223+
224+ // !!! add getVectorTransform and hasVectorTransform to frc? then
225+ // we could just skip this work...
226+
227+ dtx = frc.getTransform();
228+ dtx.setTransform(dtx.getScaleX(), dtx.getShearY(),
229+ dtx.getShearX(), dtx.getScaleY(),
230+ 0, 0);
231+ if (!dtx.isIdentity()) {
232+ try {
233+ invdtx = dtx.createInverse();
234+ }
235+ catch (NoninvertibleTransformException e) {
236+ throw new InternalError();
237+ }
238+ }
239+
240+ float ptSize = font.getSize2D();
241+ if (font.isTransformed()) {
242+ gtx = font.getTransform();
243+ gtx.scale(ptSize, ptSize);
244+ delta = new Point2D.Float((float)gtx.getTranslateX(),
245+ (float)gtx.getTranslateY());
246+ gtx.setTransform(gtx.getScaleX(), gtx.getShearY(),
247+ gtx.getShearX(), gtx.getScaleY(),
248+ 0, 0);
249+ gtx.preConcatenate(dtx);
250+ } else {
251+ delta = ZERO_DELTA;
252+ gtx = new AffineTransform(dtx);
253+ gtx.scale(ptSize, ptSize);
254+ }
255+
256+ /* Similar logic to that used in SunGraphics2D.checkFontInfo().
257+ * Whether a grey (AA) strike is needed is size dependent if
258+ * AA mode is 'gasp'.
259+ */
260+ int aa =
261+ FontStrikeDesc.getAAHintIntVal(frc.getAntiAliasingHint(),
262+ FontUtilities.getFont2D(font),
263+ (int)Math.abs(ptSize));
264+ int fm = FontStrikeDesc.getFMHintIntVal
265+ (frc.getFractionalMetricsHint());
266+ sd = new FontStrikeDesc(dtx, gtx, font.getStyle(), aa, fm);
267+ }
268+
269+ private static final Point2D.Float ZERO_DELTA = new Point2D.Float();
270+
271+ private static
272+ SoftReference<ConcurrentHashMap<SDKey, SDCache>> cacheRef;
273+
274+ private static final class SDKey {
275+ private final Font font;
276+ private final FontRenderContext frc;
277+ private final int hash;
278+
279+ SDKey(Font font, FontRenderContext frc) {
280+ this.font = font;
281+ this.frc = frc;
282+ this.hash = font.hashCode() ^ frc.hashCode();
283+ }
284+
285+ public int hashCode() {
286+ return hash;
287+ }
288+
289+ public boolean equals(Object o) {
290+ try {
291+ SDKey rhs = (SDKey)o;
292+ return
293+ hash == rhs.hash &&
294+ font.equals(rhs.font) &&
295+ frc.equals(rhs.frc);
296+ }
297+ catch (ClassCastException e) {
298+ }
299+ return false;
300+ }
301+ }
302+
303+ public static SDCache get(Font font, FontRenderContext frc) {
304+
305+ // It is possible a translation component will be in the FRC.
306+ // It doesn't affect us except adversely as we would consider
307+ // FRC's which are really the same to be different. If we
308+ // detect a translation component, then we need to exclude it
309+ // by creating a new transform which excludes the translation.
310+ if (frc.isTransformed()) {
311+ AffineTransform transform = frc.getTransform();
312+ if (transform.getTranslateX() != 0 ||
313+ transform.getTranslateY() != 0) {
314+ transform = new AffineTransform(transform.getScaleX(),
315+ transform.getShearY(),
316+ transform.getShearX(),
317+ transform.getScaleY(),
318+ 0, 0);
319+ frc = new FontRenderContext(transform,
320+ frc.getAntiAliasingHint(),
321+ frc.getFractionalMetricsHint()
322+ );
323+ }
324+ }
325+
326+ SDKey key = new SDKey(font, frc); // garbage, yuck...
327+ ConcurrentHashMap<SDKey, SDCache> cache = null;
328+ SDCache res = null;
329+ if (cacheRef != null) {
330+ cache = cacheRef.get();
331+ if (cache != null) {
332+ res = cache.get(key);
333+ }
334+ }
335+ if (res == null) {
336+ res = new SDCache(font, frc);
337+ if (cache == null) {
338+ cache = new ConcurrentHashMap<SDKey, SDCache>(10);
339+ cacheRef = new
340+ SoftReference<ConcurrentHashMap<SDKey, SDCache>>(cache);
341+ } else if (cache.size() >= 512) {
342+ cache.clear();
343+ }
344+ cache.put(key, res);
345+ }
346+ return res;
347+ }
348+ }
349+
350+ /**
351+ * Create a glyph vector.
352+ * @param font the font to use
353+ * @param frc the font render context
354+ * @param text the text, including optional context before start and after start + count
355+ * @param offset the start of the text to lay out
356+ * @param count the length of the text to lay out
357+ * @param flags bidi and context flags {@see #java.awt.Font}
358+ * @param result a StandardGlyphVector to modify, can be null
359+ * @return the layed out glyphvector, if result was passed in, it is returned
360+ */
361+ public StandardGlyphVector layout(Font font, FontRenderContext frc,
362+ char[] text, int offset, int count,
363+ int flags, StandardGlyphVector result)
364+ {
365+ if (text == null || offset < 0 || count < 0 || (count > text.length - offset)) {
366+ throw new IllegalArgumentException();
367+ }
368+
369+ init(count);
370+
371+ // need to set after init
372+ // go through the back door for this
373+ if (font.hasLayoutAttributes()) {
374+ AttributeValues values = ((AttributeMap)font.getAttributes()).getValues();
375+ if (values.getKerning() != 0) _typo_flags |= 0x1;
376+ if (values.getLigatures() != 0) _typo_flags |= 0x2;
377+ }
378+
379+ _offset = offset;
380+
381+ // use cache now - can we use the strike cache for this?
382+
383+ SDCache txinfo = SDCache.get(font, frc);
384+ _mat[0] = (float)txinfo.gtx.getScaleX();
385+ _mat[1] = (float)txinfo.gtx.getShearY();
386+ _mat[2] = (float)txinfo.gtx.getShearX();
387+ _mat[3] = (float)txinfo.gtx.getScaleY();
388+ _pt.setLocation(txinfo.delta);
389+
390+ int lim = offset + count;
391+
392+ int min = 0;
393+ int max = text.length;
394+ if (flags != 0) {
395+ if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) {
396+ _typo_flags |= 0x80000000; // RTL
397+ }
398+
399+ if ((flags & Font.LAYOUT_NO_START_CONTEXT) != 0) {
400+ min = offset;
401+ }
402+
403+ if ((flags & Font.LAYOUT_NO_LIMIT_CONTEXT) != 0) {
404+ max = lim;
405+ }
406+ }
407+
408+ int lang = -1; // default for now
409+
410+ Font2D font2D = FontUtilities.getFont2D(font);
411+
412+ _textRecord.init(text, offset, lim, min, max);
413+ int start = offset;
414+ if (font2D instanceof CompositeFont) {
415+ _scriptRuns.init(text, offset, count); // ??? how to handle 'common' chars
416+ _fontRuns.init((CompositeFont)font2D, text, offset, lim);
417+ while (_scriptRuns.next()) {
418+ int limit = _scriptRuns.getScriptLimit();
419+ int script = _scriptRuns.getScriptCode();
420+ while (_fontRuns.next(script, limit)) {
421+ Font2D pfont = _fontRuns.getFont();
422+ /* layout can't deal with NativeFont instances. The
423+ * native font is assumed to know of a suitable non-native
424+ * substitute font. This currently works because
425+ * its consistent with the way NativeFonts delegate
426+ * in other cases too.
427+ */
428+ if (pfont instanceof NativeFont) {
429+ pfont = ((NativeFont)pfont).getDelegateFont();
430+ }
431+ int gmask = _fontRuns.getGlyphMask();
432+ int pos = _fontRuns.getPos();
433+ nextEngineRecord(start, pos, script, lang, pfont, gmask);
434+ start = pos;
435+ }
436+ }
437+ } else {
438+ _scriptRuns.init(text, offset, count); // ??? don't worry about 'common' chars
439+ while (_scriptRuns.next()) {
440+ int limit = _scriptRuns.getScriptLimit();
441+ int script = _scriptRuns.getScriptCode();
442+ nextEngineRecord(start, limit, script, lang, font2D, 0);
443+ start = limit;
444+ }
445+ }
446+
447+ int ix = 0;
448+ int stop = _ercount;
449+ int dir = 1;
450+
451+ if (_typo_flags < 0) { // RTL
452+ ix = stop - 1;
453+ stop = -1;
454+ dir = -1;
455+ }
456+
457+ // _sd.init(dtx, gtx, font.getStyle(), frc.isAntiAliased(), frc.usesFractionalMetrics());
458+ _sd = txinfo.sd;
459+ for (;ix != stop; ix += dir) {
460+ EngineRecord er = (EngineRecord)_erecords.get(ix);
461+ for (;;) {
462+ try {
463+ er.layout();
464+ break;
465+ }
466+ catch (IndexOutOfBoundsException e) {
467+ _gvdata.grow();
468+ }
469+ }
470+ }
471+
472+ // if (txinfo.invdtx != null) {
473+ // _gvdata.adjustPositions(txinfo.invdtx);
474+ // }
475+
476+ StandardGlyphVector gv = _gvdata.createGlyphVector(font, frc, result);
477+ // System.err.println("Layout returns: " + gv);
478+ return gv;
479+ }
480+
481+ //
482+ // private methods
483+ //
484+
485+ private GlyphLayout() {
486+ this._gvdata = new GVData();
487+ this._textRecord = new TextRecord();
488+ this._scriptRuns = new ScriptRun();
489+ this._fontRuns = new FontRunIterator();
490+ this._erecords = new ArrayList(10);
491+ this._pt = new Point2D.Float();
492+ this._sd = new FontStrikeDesc();
493+ this._mat = new float[4];
494+ }
495+
496+ private void init(int capacity) {
497+ this._typo_flags = 0;
498+ this._ercount = 0;
499+ this._gvdata.init(capacity);
500+ }
501+
502+ private void nextEngineRecord(int start, int limit, int script, int lang, Font2D font, int gmask) {
503+ EngineRecord er = null;
504+ if (_ercount == _erecords.size()) {
505+ er = new EngineRecord();
506+ _erecords.add(er);
507+ } else {
508+ er = (EngineRecord)_erecords.get(_ercount);
509+ }
510+ er.init(start, limit, font, script, lang, gmask);
511+ ++_ercount;
512+ }
513+
514+ /**
515+ * Storage for layout to build glyph vector data, then generate a real GlyphVector
516+ */
517+ public static final class GVData {
518+ public int _count; // number of glyphs, >= number of chars
519+ public int _flags;
520+ public int[] _glyphs;
521+ public float[] _positions;
522+ public int[] _indices;
523+
524+ private static final int UNINITIALIZED_FLAGS = -1;
525+
526+ public void init(int size) {
527+ _count = 0;
528+ _flags = UNINITIALIZED_FLAGS;
529+
530+ if (_glyphs == null || _glyphs.length < size) {
531+ if (size < 20) {
532+ size = 20;
533+ }
534+ _glyphs = new int[size];
535+ _positions = new float[size * 2 + 2];
536+ _indices = new int[size];
537+ }
538+ }
539+
540+ public void grow() {
541+ grow(_glyphs.length / 4); // always grows because min length is 20
542+ }
543+
544+ public void grow(int delta) {
545+ int size = _glyphs.length + delta;
546+ int[] nglyphs = new int[size];
547+ System.arraycopy(_glyphs, 0, nglyphs, 0, _count);
548+ _glyphs = nglyphs;
549+
550+ float[] npositions = new float[size * 2 + 2];
551+ System.arraycopy(_positions, 0, npositions, 0, _count * 2 + 2);
552+ _positions = npositions;
553+
554+ int[] nindices = new int[size];
555+ System.arraycopy(_indices, 0, nindices, 0, _count);
556+ _indices = nindices;
557+ }
558+
559+ public void adjustPositions(AffineTransform invdtx) {
560+ invdtx.transform(_positions, 0, _positions, 0, _count);
561+ }
562+
563+ public StandardGlyphVector createGlyphVector(Font font, FontRenderContext frc, StandardGlyphVector result) {
564+
565+ // !!! default initialization until we let layout engines do it
566+ if (_flags == UNINITIALIZED_FLAGS) {
567+ _flags = 0;
568+
569+ if (_count > 1) { // if only 1 glyph assume LTR
570+ boolean ltr = true;
571+ boolean rtl = true;
572+
573+ int rtlix = _count; // rtl index
574+ for (int i = 0; i < _count && (ltr || rtl); ++i) {
575+ int cx = _indices[i];
576+
577+ ltr = ltr && (cx == i);
578+ rtl = rtl && (cx == --rtlix);
579+ }
580+
581+ if (rtl) _flags |= GlyphVector.FLAG_RUN_RTL;
582+ if (!rtl && !ltr) _flags |= GlyphVector.FLAG_COMPLEX_GLYPHS;
583+ }
584+
585+ // !!! layout engines need to tell us whether they performed
586+ // position adjustments. currently they don't tell us, so
587+ // we must assume they did
588+ _flags |= GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS;
589+ }
590+
591+ int[] glyphs = new int[_count];
592+ System.arraycopy(_glyphs, 0, glyphs, 0, _count);
593+
594+ float[] positions = null;
595+ if ((_flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {
596+ positions = new float[_count * 2 + 2];
597+ System.arraycopy(_positions, 0, positions, 0, positions.length);
598+ }
599+
600+ int[] indices = null;
601+ if ((_flags & GlyphVector.FLAG_COMPLEX_GLYPHS) != 0) {
602+ indices = new int[_count];
603+ System.arraycopy(_indices, 0, indices, 0, _count);
604+ }
605+
606+ if (result == null) {
607+ result = new StandardGlyphVector(font, frc, glyphs, positions, indices, _flags);
608+ } else {
609+ result.initGlyphVector(font, frc, glyphs, positions, indices, _flags);
610+ }
611+
612+ return result;
613+ }
614+ }
615+
616+ /**
617+ * Utility class to keep track of script runs, which may have to be reordered rtl when we're
618+ * finished.
619+ */
620+ private final class EngineRecord {
621+ private int start;
622+ private int limit;
623+ private int gmask;
624+ private int eflags;
625+ private LayoutEngineKey key;
626+ private LayoutEngine engine;
627+
628+ EngineRecord() {
629+ key = new LayoutEngineKey();
630+ }
631+
632+ void init(int start, int limit, Font2D font, int script, int lang, int gmask) {
633+ this.start = start;
634+ this.limit = limit;
635+ this.gmask = gmask;
636+ this.key.init(font, script, lang);
637+ this.eflags = 0;
638+
639+ // only request canonical substitution if we have combining marks
640+ for (int i = start; i < limit; ++i) {
641+ int ch = _textRecord.text[i];
642+ if (isHighSurrogate((char)ch) &&
643+ i < limit - 1 &&
644+ isLowSurrogate(_textRecord.text[i+1])) {
645+ // rare case
646+ ch = toCodePoint((char)ch,_textRecord.text[++i]); // inc
647+ }
648+ int gc = getType(ch);
649+ if (gc == NON_SPACING_MARK ||
650+ gc == ENCLOSING_MARK ||
651+ gc == COMBINING_SPACING_MARK) { // could do range test also
652+
653+ this.eflags = 0x4;
654+ break;
655+ }
656+ }
657+
658+ this.engine = _lef.getEngine(key); // flags?
659+ }
660+
661+ void layout() {
662+ _textRecord.start = start;
663+ _textRecord.limit = limit;
664+ engine.layout(_sd, _mat, gmask, start - _offset, _textRecord,
665+ _typo_flags | eflags, _pt, _gvdata);
666+ }
667+ }
668+}