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
69package sun.font;
70
71import java.lang.ref.SoftReference;
72import java.awt.Font;
73import java.awt.font.FontRenderContext;
74import java.awt.font.GlyphVector;
75import java.awt.geom.AffineTransform;
76import java.awt.geom.NoninvertibleTransformException;
77import java.awt.geom.Point2D;
78import java.util.ArrayList;
79import java.util.concurrent.ConcurrentHashMap;
80
81import static java.lang.Character.*;
82
83public 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}