1 /*
2  * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   - Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  *
11  *   - Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  *   - Neither the name of Oracle nor the names of its
16  *     contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * This source code is provided to illustrate the usage of a given feature
34  * or technique and has been deliberately simplified. Additional steps
35  * required for a production-quality application, such as security checks,
36  * input validation and proper error handling, might not be present in
37  * this sample code.
38  */
39 
40 
41 /*
42  * (C) Copyright IBM Corp. 2003, All Rights Reserved.
43  * This technology is protected by multiple US and International
44  * patents. This notice and attribution to IBM may not be removed.
45  */
46 
47 package j2dbench.tests.text;
48 
49 import java.awt.Font;
50 import java.awt.FontMetrics;
51 import java.awt.Rectangle;
52 import java.awt.Shape;
53 import java.awt.font.GlyphMetrics;
54 import java.awt.font.GlyphVector;
55 import java.awt.font.TextHitInfo;
56 import java.awt.font.TextLayout;
57 import java.awt.geom.AffineTransform;
58 import java.awt.geom.Rectangle2D;
59 import java.text.Bidi;
60 import java.util.ArrayList;
61 
62 import j2dbench.Group;
63 import j2dbench.Result;
64 import j2dbench.TestEnvironment;
65 
66 public abstract class TextMeasureTests extends TextTests {
67     static Group measureroot;
68     static Group measuretestroot;
69 
init()70     public static void init() {
71         measureroot = new Group(textroot, "Measuring", "Measuring Benchmarks");
72         measuretestroot = new Group(measureroot, "tests", "Measuring Tests");
73 
74         new StringWidth();
75         new StringBounds();
76         new CharsWidth();
77         new CharsBounds();
78         new FontCanDisplay();
79 
80         if (hasGraphics2D) {
81             new GVWidth();
82             new GVLogicalBounds();
83             new GVVisualBounds();
84             new GVPixelBounds();
85             new GVOutline();
86             new GVGlyphLogicalBounds();
87             new GVGlyphVisualBounds();
88             new GVGlyphPixelBounds();
89             new GVGlyphOutline();
90             new GVGlyphTransform();
91             new GVGlyphMetrics();
92 
93             new TLAdvance();
94             new TLAscent();
95             new TLBounds();
96             new TLGetCaretInfo();
97             new TLGetNextHit();
98             new TLGetCaretShape();
99             new TLGetLogicalHighlightShape();
100             new TLHitTest();
101             new TLOutline();
102 
103         /*
104             new FontLineMetrics();
105             new FontStringBounds();
106         */
107         }
108     }
109 
TextMeasureTests(Group parent, String nodeName, String description)110     public TextMeasureTests(Group parent, String nodeName, String description) {
111         super(parent, nodeName, description);
112     }
113 
114     static class SWContext extends TextContext {
115         FontMetrics fm;
116 
init(TestEnvironment env, Result results)117         public void init(TestEnvironment env, Result results) {
118             super.init(env, results);
119             fm = graphics.getFontMetrics(font);
120         }
121     }
122 
createContext()123     public Context createContext() {
124         return new SWContext();
125     }
126 
127     public static class StringWidth extends TextMeasureTests {
StringWidth()128         public StringWidth() {
129             super(measuretestroot, "stringWidth", "Measuring String Width");
130         }
131 
runTest(Object ctx, int numReps)132         public void runTest(Object ctx, int numReps) {
133             SWContext swctx = (SWContext)ctx;
134             String text = swctx.text;
135             FontMetrics fm = swctx.fm;
136             int wid = 0;
137             do {
138                 wid += fm.stringWidth(text);
139             } while (--numReps >= 0);
140         }
141     }
142 
143     public static class StringBounds extends TextMeasureTests {
StringBounds()144         public StringBounds() {
145             super(measuretestroot, "stringBounds", "Measuring String Bounds");
146         }
147 
runTest(Object ctx, int numReps)148         public void runTest(Object ctx, int numReps) {
149             SWContext swctx = (SWContext)ctx;
150             String text = swctx.text;
151             FontMetrics fm = swctx.fm;
152             int wid = 0;
153             Rectangle r = null;
154             do {
155                 r = null;
156                 int dx = fm.stringWidth(text);
157                 int dy = fm.getAscent() + fm.getDescent() + fm.getLeading();
158                 int x = 0;
159                 int y = -fm.getAscent();
160                 r = new Rectangle(x, y, dx, dy);
161             } while (--numReps >= 0);
162         }
163     }
164 
165     public static class CharsWidth extends TextMeasureTests {
CharsWidth()166         public CharsWidth() {
167             super(measuretestroot, "charsWidth", "Measuring Chars Width");
168         }
169 
runTest(Object ctx, int numReps)170         public void runTest(Object ctx, int numReps) {
171             SWContext swctx = (SWContext)ctx;
172             FontMetrics fm = swctx.fm;
173             char[] chars = swctx.chars;
174             int wid = 0;
175             do {
176                 wid += fm.charsWidth(chars, 0, chars.length);
177             } while (--numReps >= 0);
178         }
179     }
180 
181     public static class CharsBounds extends TextMeasureTests {
CharsBounds()182         public CharsBounds() {
183             super(measuretestroot, "charsBounds", "Measuring Chars Bounds");
184         }
185 
runTest(Object ctx, int numReps)186         public void runTest(Object ctx, int numReps) {
187             SWContext swctx = (SWContext)ctx;
188             FontMetrics fm = swctx.fm;
189             char[] chars = swctx.chars;
190             int wid = 0;
191             Rectangle r = null;
192             do {
193                 r = null;
194                 int dx = fm.charsWidth(chars, 0, chars.length);
195                 int dy = fm.getAscent() + fm.getDescent() + fm.getLeading();
196                 int x = 0;
197                 int y = -fm.getAscent();
198                 r = new Rectangle(x, y, dx, dy);
199             } while (--numReps >= 0);
200         }
201     }
202 
203     public static class FontCanDisplay extends TextMeasureTests {
FontCanDisplay()204         public FontCanDisplay() {
205             super(measuretestroot, "fontcandisplay", "Font canDisplay(char)");
206         }
207 
runTest(Object ctx, int numReps)208         public void runTest(Object ctx, int numReps) {
209             Font font = ((TextContext)ctx).font;
210             boolean b = false;
211             do {
212                 for (int i = 0; i < 0x10000; i += 0x64) {
213                     b ^= font.canDisplay((char)i);
214                 }
215             } while (--numReps >= 0);
216         }
217     }
218 
219     public static class GVContext extends G2DContext {
220         GlyphVector gv;
221 
init(TestEnvironment env, Result results)222         public void init(TestEnvironment env, Result results) {
223             super.init(env, results);
224 
225             int flags = Font.LAYOUT_LEFT_TO_RIGHT;
226             if (Bidi.requiresBidi(chars, 0, chars.length)) { // assume rtl
227                 flags = Font.LAYOUT_RIGHT_TO_LEFT;
228             }
229             gv = font.layoutGlyphVector(frc, chars, 0, chars.length, flags);
230 
231             // gv options
232         }
233     }
234 
235     public abstract static class GVMeasureTest extends TextMeasureTests {
GVMeasureTest(Group parent, String nodeName, String description)236         protected GVMeasureTest(Group parent, String nodeName, String description) {
237             super(parent, nodeName, description);
238         }
239 
createContext()240         public Context createContext() {
241             return new GVContext();
242         }
243     }
244 
245     public static class GVWidth extends GVMeasureTest {
GVWidth()246         public GVWidth() {
247             super(measuretestroot, "gvWidth", "Measuring GV Width");
248         }
249 
runTest(Object ctx, int numReps)250         public void runTest(Object ctx, int numReps) {
251             GVContext gvctx = (GVContext)ctx;
252             GlyphVector gv = gvctx.gv;
253             double wid = 0;
254             do {
255                 wid += gv.getGlyphPosition(gv.getNumGlyphs()).getX();
256             } while (--numReps >= 0);
257         }
258     }
259 
260     public static class GVLogicalBounds extends GVMeasureTest {
GVLogicalBounds()261         public GVLogicalBounds() {
262             super(measuretestroot, "gvLogicalBounds", "Measuring GV Logical Bounds");
263         }
264 
runTest(Object ctx, int numReps)265         public void runTest(Object ctx, int numReps) {
266             GVContext gvctx = (GVContext)ctx;
267             GlyphVector gv = gvctx.gv;
268             Rectangle2D r;
269             do {
270                 r = gv.getLogicalBounds();
271             } while (--numReps >= 0);
272         }
273     }
274 
275     public static class GVVisualBounds extends GVMeasureTest {
GVVisualBounds()276         public GVVisualBounds() {
277             super(measuretestroot, "gvVisualBounds", "Measuring GV Visual Bounds");
278         }
279 
runTest(Object ctx, int numReps)280         public void runTest(Object ctx, int numReps) {
281             GVContext gvctx = (GVContext)ctx;
282             GlyphVector gv = gvctx.gv;
283             Rectangle2D r;
284             do {
285                 r = gv.getVisualBounds();
286             } while (--numReps >= 0);
287         }
288     }
289 
290     public static class GVPixelBounds extends GVMeasureTest {
GVPixelBounds()291         public GVPixelBounds() {
292             super(measuretestroot, "gvPixelBounds", "Measuring GV Pixel Bounds");
293         }
294 
runTest(Object ctx, int numReps)295         public void runTest(Object ctx, int numReps) {
296             GVContext gvctx = (GVContext)ctx;
297             GlyphVector gv = gvctx.gv;
298             Rectangle2D r;
299             do {
300                 r = gv.getPixelBounds(null, 0, 0); // !!! add opt to provide different frc?
301             } while (--numReps >= 0);
302         }
303     }
304 
305     public static class GVOutline extends GVMeasureTest {
GVOutline()306         public GVOutline() {
307             super(measuretestroot, "gvOutline", "Getting GV Outline");
308         }
309 
runTest(Object ctx, int numReps)310         public void runTest(Object ctx, int numReps) {
311             GVContext gvctx = (GVContext)ctx;
312             GlyphVector gv = gvctx.gv;
313             Shape s;
314             do {
315                 s = gv.getOutline();
316             } while (--numReps >= 0);
317         }
318     }
319 
320     public static class GVGlyphLogicalBounds extends GVMeasureTest {
GVGlyphLogicalBounds()321         public GVGlyphLogicalBounds() {
322             super(measuretestroot, "gvGlyphLogicalBounds", "Measuring GV Glyph Logical Bounds");
323         }
324 
runTest(Object ctx, int numReps)325         public void runTest(Object ctx, int numReps) {
326             GVContext gvctx = (GVContext)ctx;
327             GlyphVector gv = gvctx.gv;
328             Shape s;
329             do {
330                 for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
331                     s = gv.getGlyphLogicalBounds(i);
332                 }
333             } while (--numReps >= 0);
334         }
335     }
336 
337     public static class GVGlyphVisualBounds extends GVMeasureTest {
GVGlyphVisualBounds()338         public GVGlyphVisualBounds() {
339             super(measuretestroot, "gvGlyphVisualBounds", "Measuring GV Glyph Visual Bounds");
340         }
341 
runTest(Object ctx, int numReps)342         public void runTest(Object ctx, int numReps) {
343             GVContext gvctx = (GVContext)ctx;
344             GlyphVector gv = gvctx.gv;
345             Shape s;
346             do {
347                 for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
348                     s = gv.getGlyphVisualBounds(i);
349                 }
350             } while (--numReps >= 0);
351         }
352     }
353 
354 
355     public static class GVGlyphPixelBounds extends GVMeasureTest {
GVGlyphPixelBounds()356         public GVGlyphPixelBounds() {
357             super(measuretestroot, "gvGlyphPixelBounds", "Measuring GV Glyph Pixel Bounds");
358         }
359 
runTest(Object ctx, int numReps)360         public void runTest(Object ctx, int numReps) {
361             GVContext gvctx = (GVContext)ctx;
362             GlyphVector gv = gvctx.gv;
363             Rectangle2D r;
364             do {
365                 for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
366                     r = gv.getGlyphPixelBounds(i, null, 0, 0); // !!! add opt to provide different frc?
367                 }
368             } while (--numReps >= 0);
369         }
370     }
371 
372     public static class GVGlyphOutline extends GVMeasureTest {
GVGlyphOutline()373         public GVGlyphOutline() {
374             super(measuretestroot, "gvGlyphOutline", "Getting GV Glyph Outline");
375         }
376 
runTest(Object ctx, int numReps)377         public void runTest(Object ctx, int numReps) {
378             GVContext gvctx = (GVContext)ctx;
379             GlyphVector gv = gvctx.gv;
380             Shape s;
381             do {
382                 for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
383                     s = gv.getGlyphOutline(i);
384                 }
385             } while (--numReps >= 0);
386         }
387     }
388 
389     public static class GVGlyphTransform extends GVMeasureTest {
GVGlyphTransform()390         public GVGlyphTransform() {
391             super(measuretestroot, "gvGlyphTransform", "Getting GV Glyph Transform");
392         }
393 
runTest(Object ctx, int numReps)394         public void runTest(Object ctx, int numReps) {
395             GVContext gvctx = (GVContext)ctx;
396             GlyphVector gv = gvctx.gv;
397             AffineTransform tx;
398             do {
399                 for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
400                     tx = gv.getGlyphTransform(i);
401                 }
402             } while (--numReps >= 0);
403         }
404     }
405 
406     public static class GVGlyphMetrics extends GVMeasureTest {
GVGlyphMetrics()407         public GVGlyphMetrics() {
408             super(measuretestroot, "gvGlyphMetrics", "Getting GV Glyph Metrics");
409         }
410 
runTest(Object ctx, int numReps)411         public void runTest(Object ctx, int numReps) {
412             GVContext gvctx = (GVContext)ctx;
413             GlyphVector gv = gvctx.gv;
414             GlyphMetrics gm;
415             do {
416                 for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
417                     gm = gv.getGlyphMetrics(i);
418                 }
419             } while (--numReps >= 0);
420         }
421     }
422 
423     public static class TLContext extends G2DContext {
424         TextLayout tl;
425 
init(TestEnvironment env, Result results)426         public void init(TestEnvironment env, Result results) {
427             super.init(env, results);
428 
429             // need more tl options here
430             tl = new TextLayout(text, font, frc);
431         }
432     }
433 
434     public abstract static class TLMeasureTest extends TextMeasureTests {
TLMeasureTest(Group parent, String nodeName, String description)435         protected TLMeasureTest(Group parent, String nodeName, String description) {
436             super(parent, nodeName, description);
437         }
438 
createContext()439         public Context createContext() {
440             return new TLContext();
441         }
442     }
443 
444     public static class TLAdvance extends TLMeasureTest {
TLAdvance()445         public TLAdvance() {
446             super(measuretestroot, "tlAdvance", "Measuring TL advance");
447         }
448 
runTest(Object ctx, int numReps)449         public void runTest(Object ctx, int numReps) {
450             TLContext tlctx = (TLContext)ctx;
451             TextLayout tl = tlctx.tl;
452             double wid = 0;
453             do {
454                 wid += tl.getAdvance();
455             } while (--numReps >= 0);
456         }
457     }
458 
459     public static class TLAscent extends TLMeasureTest {
TLAscent()460         public TLAscent() {
461             super(measuretestroot, "tlAscent", "Measuring TL ascent");
462         }
463 
runTest(Object ctx, int numReps)464         public void runTest(Object ctx, int numReps) {
465             TLContext tlctx = (TLContext)ctx;
466             TextLayout tl = tlctx.tl;
467             float ht = 0;
468             do {
469                 ht += tl.getAscent();
470             } while (--numReps >= 0);
471         }
472     }
473 
474     public static class TLBounds extends TLMeasureTest {
TLBounds()475         public TLBounds() {
476             super(measuretestroot, "tlBounds", "Measuring TL advance");
477         }
478 
runTest(Object ctx, int numReps)479         public void runTest(Object ctx, int numReps) {
480             TLContext tlctx = (TLContext)ctx;
481             TextLayout tl = tlctx.tl;
482             Rectangle2D r;
483             do {
484                 r = tl.getBounds();
485             } while (--numReps >= 0);
486         }
487     }
488 
489     static class TLExContext extends TLContext {
490         TextHitInfo[] hits;
491         Rectangle2D lb;
492 
init(TestEnvironment env, Result results)493         public void init(TestEnvironment env, Result results) {
494             super.init(env, results);
495 
496             ArrayList list = new ArrayList(text.length() * 2 + 2);
497             TextHitInfo hit = TextHitInfo.trailing(-1);
498             do {
499                 list.add(hit);
500                 hit = tl.getNextRightHit(hit);
501             } while (hit != null);
502             hits = (TextHitInfo[])list.toArray(new TextHitInfo[list.size()]);
503 
504             lb = tl.getBounds();
505             lb.setRect(lb.getMinX() - 10, lb.getMinY(), lb.getWidth() + 20, lb.getHeight());
506         }
507     }
508 
509     public abstract static class TLExtendedMeasureTest extends TLMeasureTest {
TLExtendedMeasureTest(Group parent, String nodeName, String description)510         protected TLExtendedMeasureTest(Group parent, String nodeName, String description) {
511             super(parent, nodeName, description);
512         }
513 
createContext()514         public Context createContext() {
515             return new TLExContext();
516         }
517     }
518 
519     public static class TLGetCaretInfo extends TLExtendedMeasureTest {
TLGetCaretInfo()520         public TLGetCaretInfo() {
521             super(measuretestroot, "tlGetCaretInfo", "Measuring TL caret info");
522         }
523 
runTest(Object ctx, int numReps)524         public void runTest(Object ctx, int numReps) {
525             TLExContext tlctx = (TLExContext)ctx;
526             TextLayout tl = tlctx.tl;
527             TextHitInfo[] hits = tlctx.hits;
528             do {
529                 for (int i = 0; i < hits.length; ++i) {
530                     tl.getCaretInfo(hits[i]);
531                 }
532             } while (--numReps >= 0);
533         }
534     }
535 
536     public static class TLGetNextHit extends TLExtendedMeasureTest {
TLGetNextHit()537         public TLGetNextHit() {
538             super(measuretestroot, "tlGetNextHit", "Measuring TL getNextRight/LeftHit");
539         }
540 
runTest(Object ctx, int numReps)541         public void runTest(Object ctx, int numReps) {
542             TLExContext tlctx = (TLExContext)ctx;
543             TextLayout tl = tlctx.tl;
544             TextHitInfo[] hits = tlctx.hits;
545             TextHitInfo hit;
546             do {
547                 for (int i = 0; i < hits.length; ++i) {
548                     hit = tl.getNextLeftHit(hits[i]);
549                 }
550             } while (--numReps >= 0);
551         }
552     }
553 
554     public static class TLGetCaretShape extends TLExtendedMeasureTest {
TLGetCaretShape()555         public TLGetCaretShape() {
556             super(measuretestroot, "tlGetCaretShape", "Measuring TL getCaretShape");
557         }
558 
runTest(Object ctx, int numReps)559         public void runTest(Object ctx, int numReps) {
560             TLExContext tlctx = (TLExContext)ctx;
561             TextLayout tl = tlctx.tl;
562             TextHitInfo[] hits = tlctx.hits;
563             Shape s;
564             do {
565                 for (int i = 0; i < hits.length; ++i) {
566                     s = tl.getCaretShape(hits[i]);
567                 }
568             } while (--numReps >= 0);
569         }
570     }
571 
572     public static class TLGetLogicalHighlightShape extends TLExtendedMeasureTest {
TLGetLogicalHighlightShape()573         public TLGetLogicalHighlightShape() {
574             super(measuretestroot, "tlGetLogicalHighlightShape", "Measuring TL getLogicalHighlightShape");
575         }
576 
runTest(Object ctx, int numReps)577         public void runTest(Object ctx, int numReps) {
578             TLExContext tlctx = (TLExContext)ctx;
579             TextLayout tl = tlctx.tl;
580             int len = tlctx.text.length();
581             Rectangle2D lb = tlctx.lb;
582             Shape s;
583             if (len < 3) {
584                 do {
585                     s = tl.getLogicalHighlightShape(0, len, lb);
586                 } while (--numReps >= 0);
587             } else {
588                 do {
589                     for (int i = 3; i < len; ++i) {
590                         s = tl.getLogicalHighlightShape(i-3, i, lb);
591                     }
592                 } while (--numReps >= 0);
593             }
594         }
595     }
596 
597     public static class TLGetVisualHighlightShape extends TLExtendedMeasureTest {
TLGetVisualHighlightShape()598         public TLGetVisualHighlightShape() {
599             super(measuretestroot, "tlGetVisualHighlightShape", "Measuring TL getVisualHighlightShape");
600         }
601 
runTest(Object ctx, int numReps)602         public void runTest(Object ctx, int numReps) {
603             TLExContext tlctx = (TLExContext)ctx;
604             TextLayout tl = tlctx.tl;
605             TextHitInfo[] hits = tlctx.hits;
606             Rectangle2D lb = tlctx.lb;
607             Shape s;
608             if (hits.length < 3) {
609                 do {
610                     s = tl.getVisualHighlightShape(hits[0], hits[hits.length - 1], lb);
611                 } while (--numReps >= 0);
612             } else {
613                 do {
614                     for (int i = 3; i < hits.length; ++i) {
615                         s = tl.getVisualHighlightShape(hits[i-3], hits[i], lb);
616                     }
617                 } while (--numReps >= 0);
618             }
619         }
620     }
621 
622     public static class TLHitTest extends TLExtendedMeasureTest {
TLHitTest()623         public TLHitTest() {
624             super(measuretestroot, "tlHitTest", "Measuring TL hitTest");
625         }
626 
runTest(Object ctx, int numReps)627         public void runTest(Object ctx, int numReps) {
628             TLExContext tlctx = (TLExContext)ctx;
629             TextLayout tl = tlctx.tl;
630             int numhits = tlctx.hits.length;
631             Rectangle2D lb = tlctx.lb;
632             TextHitInfo hit;
633             for (int i = 0; i <= numhits; ++i) {
634                 float x = (float)(lb.getMinX() + lb.getWidth() * i / numhits);
635                 float y = (float)(lb.getMinY() + lb.getHeight() * i / numhits);
636                 hit = tl.hitTestChar(x, y, lb);
637             }
638         }
639     }
640 
641     public static class TLOutline extends TLMeasureTest {
TLOutline()642         public TLOutline() {
643             super(measuretestroot, "tlOutline", "Measuring TL outline");
644         }
645 
runTest(Object ctx, int numReps)646         public void runTest(Object ctx, int numReps) {
647             TLContext tlctx = (TLContext)ctx;
648             TextLayout tl = tlctx.tl;
649             Shape s;
650             do {
651                 s = tl.getOutline(null);
652             } while (--numReps >= 0);
653         }
654     }
655 }
656