1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics.cts;
18 
19 
20 import android.graphics.ColorFilter;
21 import android.graphics.MaskFilter;
22 import android.graphics.Matrix;
23 import android.graphics.Paint;
24 import android.graphics.Paint.Align;
25 import android.graphics.Paint.Cap;
26 import android.graphics.Paint.Join;
27 import android.graphics.Paint.Style;
28 import android.graphics.Path;
29 import android.graphics.PathEffect;
30 import android.graphics.Rasterizer;
31 import android.graphics.Rect;
32 import android.graphics.Shader;
33 import android.graphics.Bitmap;
34 import android.graphics.BitmapShader;
35 import android.graphics.Typeface;
36 import android.graphics.Xfermode;
37 import android.os.Build;
38 import android.os.LocaleList;
39 import android.test.AndroidTestCase;
40 import android.text.SpannedString;
41 import android.util.Log;
42 
43 import java.util.Locale;
44 
45 public class PaintTest extends AndroidTestCase {
46 
47     private static final Typeface[] TYPEFACES = new Typeface[] {
48             Typeface.DEFAULT,
49             Typeface.DEFAULT_BOLD,
50             Typeface.MONOSPACE,
51             Typeface.SANS_SERIF,
52             Typeface.SERIF,
53     };
54 
testConstructor()55     public void testConstructor() {
56         new Paint();
57 
58         new Paint(1);
59 
60         Paint p = new Paint();
61         new Paint(p);
62     }
63 
testBreakText()64     public void testBreakText() {
65         String text = "HIJKLMN";
66         char[] textChars = text.toCharArray();
67         SpannedString textSpan = new SpannedString(text);
68 
69         Paint p = new Paint();
70 
71         // We need to turn off kerning in order to get accurate comparisons
72         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
73 
74         float[] widths = new float[text.length()];
75         assertEquals(text.length(), p.getTextWidths(text, widths));
76 
77         float totalWidth = 0.0f;
78         for (int i = 0; i < text.length(); i++) {
79             totalWidth += widths[i];
80         }
81 
82         float[] measured = new float[1];
83         for (int i = 0; i < text.length(); i++) {
84             assertBreakText(text, textChars, textSpan, i, i + 1, true, totalWidth, 1, widths[i]);
85         }
86 
87         // Measure empty string
88         assertBreakText(text, textChars, textSpan, 0, 0, true, totalWidth, 0, 0);
89 
90         // Measure substring from front: "HIJ"
91         assertBreakText(text, textChars, textSpan, 0, 3, true, totalWidth,
92                 3, widths[0] + widths[1] + widths[2]);
93 
94         // Reverse measure substring from front: "HIJ"
95         assertBreakText(text, textChars, textSpan, 0, 3, false, totalWidth,
96                 3, widths[0] + widths[1] + widths[2]);
97 
98         // Measure substring from back: "MN"
99         assertBreakText(text, textChars, textSpan, 5, 7, true, totalWidth,
100                 2, widths[5] + widths[6]);
101 
102         // Reverse measure substring from back: "MN"
103         assertBreakText(text, textChars, textSpan, 5, 7, false, totalWidth,
104                 2, widths[5] + widths[6]);
105 
106         // Measure substring in the middle: "JKL"
107         assertBreakText(text, textChars, textSpan, 2, 5, true, totalWidth,
108                 3, widths[2] + widths[3] + widths[4]);
109 
110         // Reverse measure substring in the middle: "JKL"
111         assertBreakText(text, textChars, textSpan, 2, 5, false, totalWidth,
112                 3, widths[2] + widths[3] + widths[4]);
113 
114         // Measure substring in the middle and restrict width to the first 2 characters.
115         assertBreakText(text, textChars, textSpan, 2, 5, true, widths[2] + widths[3],
116                 2, widths[2] + widths[3]);
117 
118         // Reverse measure substring in the middle and restrict width to the last 2 characters.
119         assertBreakText(text, textChars, textSpan, 2, 5, false, widths[3] + widths[4],
120                 2, widths[3] + widths[4]);
121 
122         // a single Emoji (U+1f601)
123         String emoji = "\ud83d\ude01";
124         char[] emojiChars = emoji.toCharArray();
125         SpannedString emojiSpan = new SpannedString(emoji);
126 
127         float[] emojiWidths = new float[emoji.length()];
128         assertEquals(emoji.length(), p.getTextWidths(emoji, emojiWidths));
129 
130         // Measure substring with a cluster
131         assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, 0,
132                 0, 0);
133 
134         // Measure substring with a cluster
135         assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, emojiWidths[0],
136                 2, emojiWidths[0]);
137 
138         // Reverse measure substring with a cluster
139         assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, 0,
140                 0, 0);
141 
142         // Measure substring with a cluster
143         assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, emojiWidths[0],
144                 2, emojiWidths[0]);
145     }
146 
assertBreakText(String text, char[] textChars, SpannedString textSpan, int start, int end, boolean measureForwards, float maxWidth, int expectedCount, float expectedWidth)147     private void assertBreakText(String text, char[] textChars, SpannedString textSpan,
148             int start, int end, boolean measureForwards, float maxWidth, int expectedCount,
149             float expectedWidth) {
150         Paint p = new Paint();
151 
152         // We need to turn off kerning in order to get accurate comparisons
153         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
154 
155         int count = end - start;
156         if (!measureForwards) {
157             count = -count;
158         }
159 
160         float[][] measured = new float[][] {
161             new float[1],
162             new float[1],
163             new float[1]
164         };
165         String textSlice = text.substring(start, end);
166         assertEquals(expectedCount, p.breakText(textSlice, measureForwards, maxWidth, measured[0]));
167         assertEquals(expectedCount, p.breakText(textChars, start, count, maxWidth, measured[1]));
168         assertEquals(expectedCount, p.breakText(textSpan, start, end, measureForwards, maxWidth,
169                 measured[2]));
170 
171         for (int i = 0; i < measured.length; i++) {
172             assertEquals("i: " + i, expectedWidth, measured[i][0]);
173         }
174     }
175 
testSet()176     public void testSet() {
177         Paint p  = new Paint();
178         Paint p2 = new Paint();
179         ColorFilter c = new ColorFilter();
180         MaskFilter m  = new MaskFilter();
181         PathEffect e  = new PathEffect();
182         Rasterizer r  = new Rasterizer();
183         Shader s      = new Shader();
184         Typeface t    = Typeface.DEFAULT;
185         Xfermode x = new Xfermode();
186 
187         p.setColorFilter(c);
188         p.setMaskFilter(m);
189         p.setPathEffect(e);
190         p.setRasterizer(r);
191         p.setShader(s);
192         p.setTypeface(t);
193         p.setXfermode(x);
194         p2.set(p);
195         assertEquals(c, p2.getColorFilter());
196         assertEquals(m, p2.getMaskFilter());
197         assertEquals(e, p2.getPathEffect());
198         assertEquals(r, p2.getRasterizer());
199         assertEquals(s, p2.getShader());
200         assertEquals(t, p2.getTypeface());
201         assertEquals(x, p2.getXfermode());
202 
203         p2.set(p2);
204         assertEquals(c, p2.getColorFilter());
205         assertEquals(m, p2.getMaskFilter());
206         assertEquals(e, p2.getPathEffect());
207         assertEquals(r, p2.getRasterizer());
208         assertEquals(s, p2.getShader());
209         assertEquals(t, p2.getTypeface());
210         assertEquals(x, p2.getXfermode());
211 
212         p.setColorFilter(null);
213         p.setMaskFilter(null);
214         p.setPathEffect(null);
215         p.setRasterizer(null);
216         p.setShader(null);
217         p.setTypeface(null);
218         p.setXfermode(null);
219         p2.set(p);
220         assertNull(p2.getColorFilter());
221         assertNull(p2.getMaskFilter());
222         assertNull(p2.getPathEffect());
223         assertNull(p2.getRasterizer());
224         assertNull(p2.getShader());
225         assertNull(p2.getTypeface());
226         assertNull(p2.getXfermode());
227 
228         p2.set(p2);
229         assertNull(p2.getColorFilter());
230         assertNull(p2.getMaskFilter());
231         assertNull(p2.getPathEffect());
232         assertNull(p2.getRasterizer());
233         assertNull(p2.getShader());
234         assertNull(p2.getTypeface());
235         assertNull(p2.getXfermode());
236     }
237 
testAccessStrokeCap()238     public void testAccessStrokeCap() {
239         Paint p = new Paint();
240 
241         p.setStrokeCap(Cap.BUTT);
242         assertEquals(Cap.BUTT, p.getStrokeCap());
243 
244         p.setStrokeCap(Cap.ROUND);
245         assertEquals(Cap.ROUND, p.getStrokeCap());
246 
247         p.setStrokeCap(Cap.SQUARE);
248         assertEquals(Cap.SQUARE, p.getStrokeCap());
249 
250         try {
251             p.setStrokeCap(null);
252             fail("Should throw an Exception");
253         } catch (RuntimeException e) {
254             //except here
255         }
256     }
257 
testAccessXfermode()258     public void testAccessXfermode() {
259         Paint p = new Paint();
260         Xfermode x = new Xfermode();
261 
262         assertEquals(x, p.setXfermode(x));
263         assertEquals(x, p.getXfermode());
264 
265         assertNull(p.setXfermode(null));
266         assertNull(p.getXfermode());
267     }
268 
testAccessShader()269     public void testAccessShader() {
270         Paint p = new Paint();
271         Shader s = new Shader();
272 
273         assertEquals(s, p.setShader(s));
274         assertEquals(s, p.getShader());
275 
276         assertNull(p.setShader(null));
277         assertNull(p.getShader());
278     }
279 
testShaderLocalMatrix()280     public void testShaderLocalMatrix() {
281         int width = 80;
282         int height = 120;
283         int[] color = new int[width * height];
284         Bitmap bitmap = Bitmap.createBitmap(color, width, height, Bitmap.Config.RGB_565);
285 
286         Paint p = new Paint();
287         Matrix m = new Matrix();
288         Shader s = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
289 
290         // set the shaders matrix to a non identity value and attach to paint
291         m.setScale(10, 0);
292         s.setLocalMatrix(m);
293         p.setShader(s);
294 
295         Matrix m2 = new Matrix();
296         assertTrue(p.getShader().getLocalMatrix(m2));
297         assertEquals(m, m2);
298 
299         // updated the matrix again and set it on the shader but NOT the paint
300         m.setScale(0, 10);
301         s.setLocalMatrix(m);
302 
303         // assert that the matrix on the paint's shader also changed
304         Matrix m3 = new Matrix();
305         assertTrue(p.getShader().getLocalMatrix(m3));
306         assertEquals(m, m3);
307     }
308 
testSetAntiAlias()309     public void testSetAntiAlias() {
310         Paint p = new Paint();
311 
312         p.setAntiAlias(true);
313         assertTrue(p.isAntiAlias());
314 
315         p.setAntiAlias(false);
316         assertFalse(p.isAntiAlias());
317 
318     }
319 
320 
testAccessTypeface()321     public void testAccessTypeface() {
322         Paint p = new Paint();
323 
324         assertEquals(Typeface.DEFAULT, p.setTypeface(Typeface.DEFAULT));
325         assertEquals(Typeface.DEFAULT, p.getTypeface());
326 
327         assertEquals(Typeface.DEFAULT_BOLD, p.setTypeface(Typeface.DEFAULT_BOLD));
328         assertEquals(Typeface.DEFAULT_BOLD, p.getTypeface());
329 
330         assertEquals(Typeface.MONOSPACE, p.setTypeface(Typeface.MONOSPACE));
331         assertEquals(Typeface.MONOSPACE, p.getTypeface());
332 
333         assertNull(p.setTypeface(null));
334         assertNull(p.getTypeface());
335     }
336 
testAccessPathEffect()337     public void testAccessPathEffect() {
338         Paint p = new Paint();
339         PathEffect e = new PathEffect();
340 
341         assertEquals(e, p.setPathEffect(e));
342         assertEquals(e, p.getPathEffect());
343 
344         assertNull(p.setPathEffect(null));
345         assertNull(p.getPathEffect());
346     }
347 
testSetFakeBoldText()348     public void testSetFakeBoldText() {
349         Paint p = new Paint();
350 
351         p.setFakeBoldText(true);
352         assertTrue(p.isFakeBoldText());
353 
354         p.setFakeBoldText(false);
355         assertFalse(p.isFakeBoldText());
356     }
357 
testAccessStrokeJoin()358     public void testAccessStrokeJoin() {
359         Paint p = new Paint();
360 
361         p.setStrokeJoin(Join.BEVEL);
362         assertEquals(Join.BEVEL, p.getStrokeJoin());
363 
364         p.setStrokeJoin(Join.MITER);
365         assertEquals(Join.MITER, p.getStrokeJoin());
366 
367         p.setStrokeJoin(Join.ROUND);
368         assertEquals(Join.ROUND, p.getStrokeJoin());
369 
370         try {
371             p.setStrokeJoin(null);
372             fail("Should throw an Exception");
373         } catch (RuntimeException e) {
374             //except here
375         }
376     }
377 
testAccessStyle()378     public void testAccessStyle() {
379         Paint p = new Paint();
380 
381         p.setStyle(Style.FILL);
382         assertEquals(Style.FILL, p.getStyle());
383 
384         p.setStyle(Style.FILL_AND_STROKE);
385         assertEquals(Style.FILL_AND_STROKE, p.getStyle());
386 
387         p.setStyle(Style.STROKE);
388         assertEquals(Style.STROKE, p.getStyle());
389 
390         try {
391             p.setStyle(null);
392             fail("Should throw an Exception");
393         } catch (RuntimeException e) {
394             //except here
395         }
396     }
397 
testGetFontSpacing()398     public void testGetFontSpacing() {
399         Paint p = new Paint();
400 
401         for (Typeface typeface : TYPEFACES) {
402             p.setTypeface(typeface);
403 
404             p.setTextSize(10);
405             float spacing10 = p.getFontSpacing();
406             assertTrue(spacing10 > 0);
407 
408             p.setTextSize(20);
409             float spacing20 = p.getFontSpacing();
410             assertTrue(spacing20 > spacing10);
411         }
412     }
413 
testSetSubpixelText()414     public void testSetSubpixelText() {
415         Paint p = new Paint();
416 
417         p.setSubpixelText(true);
418         assertTrue(p.isSubpixelText());
419 
420         p.setSubpixelText(false);
421         assertFalse(p.isSubpixelText());
422     }
423 
testAccessTextScaleX()424     public void testAccessTextScaleX() {
425         Paint p = new Paint();
426 
427         p.setTextScaleX(2.0f);
428         assertEquals(2.0f, p.getTextScaleX());
429 
430         p.setTextScaleX(1.0f);
431         assertEquals(1.0f, p.getTextScaleX());
432 
433         p.setTextScaleX(0.0f);
434         assertEquals(0.0f, p.getTextScaleX());
435 
436     }
437 
testAccessMaskFilter()438     public void testAccessMaskFilter() {
439         Paint p = new Paint();
440         MaskFilter m = new MaskFilter();
441 
442         assertEquals(m, p.setMaskFilter(m));
443         assertEquals(m, p.getMaskFilter());
444 
445         assertNull(p.setMaskFilter(null));
446         assertNull(p.getMaskFilter());
447     }
448 
testAccessColorFilter()449     public void testAccessColorFilter() {
450         Paint p = new Paint();
451         ColorFilter c = new ColorFilter();
452 
453         assertEquals(c, p.setColorFilter(c));
454         assertEquals(c, p.getColorFilter());
455 
456         assertNull(p.setColorFilter(null));
457         assertNull(p.getColorFilter());
458     }
459 
testAccessRasterizer()460     public void testAccessRasterizer() {
461         Paint p = new Paint();
462         Rasterizer r = new Rasterizer();
463 
464         assertEquals(r, p.setRasterizer(r));
465         assertEquals(r, p.getRasterizer());
466 
467         assertNull(p.setRasterizer(null));
468         assertNull(p.getRasterizer());
469     }
470 
testSetARGB()471     public void testSetARGB() {
472         Paint p = new Paint();
473 
474         p.setARGB(0, 0, 0, 0);
475         assertEquals(0, p.getColor());
476 
477         p.setARGB(3, 3, 3, 3);
478         assertEquals((3 << 24) | (3 << 16) | (3 << 8) | 3, p.getColor());
479 
480     }
481 
testAscent()482     public void testAscent() {
483         Paint p = new Paint();
484 
485         for (Typeface typeface : TYPEFACES) {
486             p.setTypeface(typeface);
487 
488             p.setTextSize(10);
489             float ascent10 = p.ascent();
490             assertTrue(ascent10 < 0);
491 
492             p.setTextSize(20);
493             float ascent20 = p.ascent();
494             assertTrue(ascent20 < ascent10);
495         }
496     }
497 
498     public void testAccessTextSkewX() {
499         Paint p = new Paint();
500 
501         p.setTextSkewX(1.0f);
502         assertEquals(1.0f, p.getTextSkewX());
503 
504         p.setTextSkewX(0.0f);
505         assertEquals(0.0f, p.getTextSkewX());
506 
507         p.setTextSkewX(-0.25f);
508         assertEquals(-0.25f, p.getTextSkewX());
509     }
510 
511     public void testAccessTextSize() {
512         Paint p = new Paint();
513 
514         p.setTextSize(1.0f);
515         assertEquals(1.0f, p.getTextSize());
516 
517         p.setTextSize(2.0f);
518         assertEquals(2.0f, p.getTextSize());
519 
520         // text size should be greater than 0, so set -1 has no effect
521         p.setTextSize(-1.0f);
522         assertEquals(2.0f, p.getTextSize());
523 
524         // text size should be greater than or equals to 0
525         p.setTextSize(0.0f);
526         assertEquals(0.0f, p.getTextSize());
527     }
528 
529     public void testGetTextWidths() throws Exception {
530         String text = "HIJKLMN";
531         char[] textChars = text.toCharArray();
532         SpannedString textSpan = new SpannedString(text);
533 
534         // Test measuring the widths of the entire text
535         assertGetTextWidths(text, textChars, textSpan, 0, 7);
536 
537         // Test measuring a substring of the text
538         assertGetTextWidths(text, textChars, textSpan, 1, 3);
539 
540         // Test measuring a substring of zero length.
541         assertGetTextWidths(text, textChars, textSpan, 3, 3);
542 
543         // Test measuring substrings from the front and back
544         assertGetTextWidths(text, textChars, textSpan, 0, 2);
545         assertGetTextWidths(text, textChars, textSpan, 4, 7);
546     }
547 
548     /** Tests all four overloads of getTextWidths are the same. */
549     private void assertGetTextWidths(String text, char[] textChars, SpannedString textSpan,
550             int start, int end) {
551         Paint p = new Paint();
552         int count = end - start;
553         float[][] widths = new float[][] {
554             new float[count],
555             new float[count],
556             new float[count],
557             new float[count]
558         };
559 
560         String textSlice = text.substring(start, end);
561         assertEquals(count, p.getTextWidths(textSlice, widths[0]));
562         assertEquals(count, p.getTextWidths(textChars, start, count, widths[1]));
563         assertEquals(count, p.getTextWidths(textSpan, start, end, widths[2]));
564         assertEquals(count, p.getTextWidths(text, start, end, widths[3]));
565 
566         // Check that the widths returned by the overloads are the same.
567         for (int i = 0; i < count; i++) {
568             assertEquals(widths[0][i], widths[1][i]);
569             assertEquals(widths[1][i], widths[2][i]);
570             assertEquals(widths[2][i], widths[3][i]);
571         }
572     }
573 
574     public void testSetStrikeThruText() {
575         Paint p = new Paint();
576 
577         p.setStrikeThruText(true);
578         assertTrue(p.isStrikeThruText());
579 
580         p.setStrikeThruText(false);
581         assertFalse(p.isStrikeThruText());
582     }
583 
584     public void testAccessTextAlign() {
585         Paint p = new Paint();
586 
587         p.setTextAlign(Align.CENTER);
588         assertEquals(Align.CENTER, p.getTextAlign());
589 
590         p.setTextAlign(Align.LEFT);
591         assertEquals(Align.LEFT, p.getTextAlign());
592 
593         p.setTextAlign(Align.RIGHT);
594         assertEquals(Align.RIGHT, p.getTextAlign());
595     }
596 
597     public void testAccessTextLocale() {
598         Paint p = new Paint();
599 
600         final Locale defaultLocale = Locale.getDefault();
601 
602         // Check default
603         assertEquals(defaultLocale, p.getTextLocale());
604 
605         // Check setter / getters
606         p.setTextLocale(Locale.US);
607         assertEquals(Locale.US, p.getTextLocale());
608         assertEquals(new LocaleList(Locale.US), p.getTextLocales());
609 
610         p.setTextLocale(Locale.CHINESE);
611         assertEquals(Locale.CHINESE, p.getTextLocale());
612         assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales());
613 
614         p.setTextLocale(Locale.JAPANESE);
615         assertEquals(Locale.JAPANESE, p.getTextLocale());
616         assertEquals(new LocaleList(Locale.JAPANESE), p.getTextLocales());
617 
618         p.setTextLocale(Locale.KOREAN);
619         assertEquals(Locale.KOREAN, p.getTextLocale());
620         assertEquals(new LocaleList(Locale.KOREAN), p.getTextLocales());
621 
622         // Check reverting back to default
623         p.setTextLocale(defaultLocale);
624         assertEquals(defaultLocale, p.getTextLocale());
625         assertEquals(new LocaleList(defaultLocale), p.getTextLocales());
626 
627         // Check that we cannot pass a null locale
628         try {
629             p.setTextLocale(null);
630             fail("Setting the text locale to null should throw");
631         } catch (Throwable e) {
632             assertEquals(IllegalArgumentException.class, e.getClass());
633         }
634     }
635 
636     public void testAccessTextLocales() {
637         Paint p = new Paint();
638 
639         final LocaleList defaultLocales = LocaleList.getDefault();
640 
641         // Check default
642         assertEquals(defaultLocales, p.getTextLocales());
643 
644         // Check setter / getters for a one-member locale list
645         p.setTextLocales(new LocaleList(Locale.CHINESE));
646         assertEquals(Locale.CHINESE, p.getTextLocale());
647         assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales());
648 
649         // Check setter / getters for a two-member locale list
650         p.setTextLocales(LocaleList.forLanguageTags("fr,de"));
651         assertEquals(Locale.forLanguageTag("fr"), p.getTextLocale());
652         assertEquals(LocaleList.forLanguageTags("fr,de"), p.getTextLocales());
653 
654         // Check reverting back to default
655         p.setTextLocales(defaultLocales);
656         assertEquals(defaultLocales, p.getTextLocales());
657 
658         // Check that we cannot pass a null locale list
659         try {
660             p.setTextLocales(null);
661             fail("Setting the text locale list to null should throw");
662         } catch (Throwable e) {
663             assertEquals(IllegalArgumentException.class, e.getClass());
664         }
665 
666         // Check that we cannot pass an empty locale list
667         try {
668             p.setTextLocales(new LocaleList());
669             fail("Setting the text locale list to an empty list should throw");
670         } catch (Throwable e) {
671             assertEquals(IllegalArgumentException.class, e.getClass());
672         }
673     }
674 
675     public void testGetFillPath() {
676         Paint p = new Paint();
677         Path path1 = new Path();
678         Path path2 = new Path();
679 
680         assertTrue(path1.isEmpty());
681         assertTrue(path2.isEmpty());
682         p.getFillPath(path1, path2);
683         assertTrue(path1.isEmpty());
684         assertTrue(path2.isEmpty());
685 
686         // No setter
687 
688     }
689 
690     public void testAccessAlpha() {
691         Paint p = new Paint();
692 
693         p.setAlpha(0);
694         assertEquals(0, p.getAlpha());
695 
696         p.setAlpha(255);
697         assertEquals(255, p.getAlpha());
698 
699         // set value should between 0 and 255, so 266 is rounded to 10
700         p.setAlpha(266);
701         assertEquals(10, p.getAlpha());
702 
703         // set value should between 0 and 255, so -20 is rounded to 236
704         p.setAlpha(-20);
705         assertEquals(236, p.getAlpha());
706     }
707 
708     public void testSetFilterBitmap() {
709         Paint p = new Paint();
710 
711         p.setFilterBitmap(true);
712         assertTrue(p.isFilterBitmap());
713 
714         p.setFilterBitmap(false);
715         assertFalse(p.isFilterBitmap());
716     }
717 
718     public void testAccessColor() {
719         Paint p = new Paint();
720 
721         p.setColor(1);
722         assertEquals(1, p.getColor());
723 
724         p.setColor(0);
725         assertEquals(0, p.getColor());
726 
727         p.setColor(255);
728         assertEquals(255, p.getColor());
729 
730         p.setColor(-1);
731         assertEquals(-1, p.getColor());
732 
733         p.setColor(256);
734         assertEquals(256, p.getColor());
735     }
736 
737     public void testSetShadowLayer() {
738         new Paint().setShadowLayer(10, 1, 1, 0);
739     }
740 
741     public void testGetFontMetrics1() {
742         Paint p = new Paint();
743         Paint.FontMetrics fm = new Paint.FontMetrics();
744 
745         for (Typeface typeface : TYPEFACES) {
746             p.setTypeface(typeface);
747 
748             p.setTextSize(10);
749             float spacing10 = p.getFontMetrics(fm);
750             assertEquals(p.ascent(), fm.ascent);
751             assertEquals(p.descent(), fm.descent);
752 
753             p.setTextSize(20);
754             float spacing20 = p.getFontMetrics(fm);
755             assertEquals(p.ascent(), fm.ascent);
756             assertEquals(p.descent(), fm.descent);
757         }
758     }
759 
760     public void testGetFontMetrics2() {
761         Paint p = new Paint();
762 
763         for (Typeface typeface : TYPEFACES) {
764             p.setTypeface(typeface);
765 
766             p.setTextSize(10);
767             Paint.FontMetrics fm = p.getFontMetrics();
768             assertEquals(p.ascent(), fm.ascent);
769             assertEquals(p.descent(), fm.descent);
770 
771             p.setTextSize(20);
772             fm = p.getFontMetrics();
773             assertEquals(p.ascent(), fm.ascent);
774             assertEquals(p.descent(), fm.descent);
775         }
776     }
777 
778     public void testAccessStrokeMiter() {
779         Paint p = new Paint();
780 
781         p.setStrokeMiter(0.0f);
782         assertEquals(0.0f, p.getStrokeMiter());
783 
784         p.setStrokeMiter(10.0f);
785         assertEquals(10.0f, p.getStrokeMiter());
786 
787         // set value should be greater or equal to 0, set to -10.0f has no effect
788         p.setStrokeMiter(-10.0f);
789         assertEquals(10.0f, p.getStrokeMiter());
790     }
791 
792     public void testClearShadowLayer() {
793         new Paint().clearShadowLayer();
794     }
795 
796     public void testSetUnderlineText() {
797         Paint p = new Paint();
798 
799         p.setUnderlineText(true);
800         assertTrue(p.isUnderlineText());
801 
802         p.setUnderlineText(false);
803         assertFalse(p.isUnderlineText());
804     }
805 
806     public void testSetDither() {
807         Paint p = new Paint();
808 
809         p.setDither(true);
810         assertTrue(p.isDither());
811 
812         p.setDither(false);
813         assertFalse(p.isDither());
814     }
815 
816     public void testDescent() {
817         Paint p = new Paint();
818 
819         for (Typeface typeface : TYPEFACES) {
820             p.setTypeface(typeface);
821 
822             p.setTextSize(10);
823             float descent10 = p.descent();
824             assertTrue(descent10 > 0);
825 
826             p.setTextSize(20);
827             float descent20 = p.descent();
828             assertTrue(descent20 > descent10);
829         }
830     }
831 
832     public void testAccessFlags() {
833         Paint p = new Paint();
834 
835         p.setFlags(Paint.ANTI_ALIAS_FLAG);
836         assertEquals(Paint.ANTI_ALIAS_FLAG, p.getFlags());
837 
838         p.setFlags(Paint.DEV_KERN_TEXT_FLAG);
839         assertEquals(Paint.DEV_KERN_TEXT_FLAG, p.getFlags());
840     }
841 
842     public void testAccessStrokeWidth() {
843         Paint p = new Paint();
844 
845         p.setStrokeWidth(0.0f);
846         assertEquals(0.0f, p.getStrokeWidth());
847 
848         p.setStrokeWidth(10.0f);
849         assertEquals(10.0f, p.getStrokeWidth());
850 
851         // set value must greater or equal to 0, set -10.0f has no effect
852         p.setStrokeWidth(-10.0f);
853         assertEquals(10.0f, p.getStrokeWidth());
854     }
855 
856     public void testSetFontFeatureSettings() {
857         Paint p = new Paint();
858         // Roboto font (system default) has "fi" ligature
859         String text = "fi";
860         float[] widths = new float[text.length()];
861         p.getTextWidths(text, widths);
862         assertTrue(widths[0] > 0.0f);
863         assertEquals(0.0f, widths[1]);
864 
865         // Disable ligature using OpenType feature
866         p.setFontFeatureSettings("'liga' off");
867         p.getTextWidths(text, widths);
868         assertTrue(widths[0] > 0.0f);
869         assertTrue(widths[1] > 0.0f);
870 
871         // Re-enable ligature
872         p.setFontFeatureSettings("'liga' on");
873         p.getTextWidths(text, widths);
874         assertTrue(widths[0] > 0.0f);
875         assertEquals(0.0f, widths[1]);
876     }
877 
testGetTextBounds()878     public void testGetTextBounds() {
879         Paint p = new Paint();
880         p.setTextSize(10);
881         String text1 = "hello";
882         Rect bounds1 = new Rect();
883         Rect bounds2 = new Rect();
884         p.getTextBounds(text1, 0, text1.length(), bounds1);
885         char[] textChars1 = text1.toCharArray();
886         p.getTextBounds(textChars1, 0, textChars1.length, bounds2);
887         // verify that string and char array methods produce consistent results
888         assertEquals(bounds1, bounds2);
889         String text2 = "hello world";
890 
891         // verify substring produces consistent results
892         p.getTextBounds(text2, 0, text1.length(), bounds2);
893         assertEquals(bounds1, bounds2);
894 
895         // longer string is expected to have same left edge but be wider
896         p.getTextBounds(text2, 0, text2.length(), bounds2);
897         assertEquals(bounds1.left, bounds2.left);
898         assertTrue(bounds2.right > bounds1.right);
899 
900         // bigger size implies bigger bounding rect
901         p.setTextSize(20);
902         p.getTextBounds(text1, 0, text1.length(), bounds2);
903         assertTrue(bounds2.right > bounds1.right);
904         assertTrue(bounds2.bottom - bounds2.top > bounds1.bottom - bounds1.top);
905     }
906 
testReset()907     public void testReset() {
908         Paint p  = new Paint();
909         ColorFilter c = new ColorFilter();
910         MaskFilter m  = new MaskFilter();
911         PathEffect e  = new PathEffect();
912         Rasterizer r  = new Rasterizer();
913         Shader s      = new Shader();
914         Typeface t    = Typeface.DEFAULT;
915         Xfermode x = new Xfermode();
916 
917         p.setColorFilter(c);
918         p.setMaskFilter(m);
919         p.setPathEffect(e);
920         p.setRasterizer(r);
921         p.setShader(s);
922         p.setTypeface(t);
923         p.setXfermode(x);
924         p.setFlags(Paint.ANTI_ALIAS_FLAG);
925         assertEquals(c, p.getColorFilter());
926         assertEquals(m, p.getMaskFilter());
927         assertEquals(e, p.getPathEffect());
928         assertEquals(r, p.getRasterizer());
929         assertEquals(s, p.getShader());
930         assertEquals(t, p.getTypeface());
931         assertEquals(x, p.getXfermode());
932         assertEquals(Paint.ANTI_ALIAS_FLAG, p.getFlags());
933 
934         p.reset();
935         assertEquals(Paint.DEV_KERN_TEXT_FLAG | Paint.EMBEDDED_BITMAP_TEXT_FLAG, p.getFlags());
936         assertEquals(null, p.getColorFilter());
937         assertEquals(null, p.getMaskFilter());
938         assertEquals(null, p.getPathEffect());
939         assertEquals(null, p.getRasterizer());
940         assertEquals(null, p.getShader());
941         assertEquals(null, p.getTypeface());
942         assertEquals(null, p.getXfermode());
943     }
944 
testSetLinearText()945     public void testSetLinearText() {
946         Paint p = new Paint();
947 
948         p.setLinearText(true);
949         assertTrue(p.isLinearText());
950 
951         p.setLinearText(false);
952         assertFalse(p.isLinearText());
953     }
954 
testGetFontMetricsInt1()955     public void testGetFontMetricsInt1() {
956         Paint p = new Paint();
957         Paint.FontMetricsInt fmi = new Paint.FontMetricsInt();
958 
959         for (Typeface typeface : TYPEFACES) {
960             p.setTypeface(typeface);
961 
962             p.setTextSize(10);
963             p.getFontMetricsInt(fmi);
964             assertEquals(Math.round(p.ascent()), fmi.ascent);
965             assertEquals(Math.round(p.descent()), fmi.descent);
966 
967             p.setTextSize(20);
968             p.getFontMetricsInt(fmi);
969             assertEquals(Math.round(p.ascent()), fmi.ascent);
970             assertEquals(Math.round(p.descent()), fmi.descent);
971         }
972     }
973 
testGetFontMetricsInt2()974     public void testGetFontMetricsInt2() {
975         Paint p = new Paint();
976         Paint.FontMetricsInt fmi;
977 
978         for (Typeface typeface : TYPEFACES) {
979             p.setTypeface(typeface);
980 
981             p.setTextSize(10);
982             fmi = p.getFontMetricsInt();
983             assertEquals(Math.round(p.ascent()), fmi.ascent);
984             assertEquals(Math.round(p.descent()), fmi.descent);
985 
986             p.setTextSize(20);
987             fmi = p.getFontMetricsInt();
988             assertEquals(Math.round(p.ascent()), fmi.ascent);
989             assertEquals(Math.round(p.descent()), fmi.descent);
990         }
991     }
992 
testMeasureText()993     public void testMeasureText() {
994         String text = "HIJKLMN";
995         char[] textChars = text.toCharArray();
996         SpannedString textSpan = new SpannedString(text);
997 
998         Paint p = new Paint();
999 
1000         // We need to turn off kerning in order to get accurate comparisons
1001         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
1002 
1003         float[] widths = new float[text.length()];
1004         for (int i = 0; i < widths.length; i++) {
1005             widths[i] = p.measureText(text, i, i + 1);
1006         }
1007 
1008         float totalWidth = 0;
1009         for (int i = 0; i < widths.length; i++) {
1010             totalWidth += widths[i];
1011         }
1012 
1013         // Test measuring the widths of the entire text
1014         assertMeasureText(text, textChars, textSpan, 0, 7, totalWidth);
1015 
1016         // Test measuring a substring of the text
1017         assertMeasureText(text, textChars, textSpan, 1, 3, widths[1] + widths[2]);
1018 
1019         // Test measuring a substring of zero length.
1020         assertMeasureText(text, textChars, textSpan, 3, 3, 0);
1021 
1022         // Test measuring substrings from the front and back
1023         assertMeasureText(text, textChars, textSpan, 0, 2, widths[0] + widths[1]);
1024         assertMeasureText(text, textChars, textSpan, 4, 7, widths[4] + widths[5] + widths[6]);
1025     }
1026 
testMeasureTextContext()1027     public void testMeasureTextContext() {
1028        Paint p = new Paint();
1029        // Arabic LAM, which is different width depending on context
1030        String shortString = "\u0644";
1031        String longString = "\u0644\u0644\u0644";
1032        char[] longChars = longString.toCharArray();
1033        SpannedString longSpanned = new SpannedString(longString);
1034        float width = p.measureText(shortString);
1035        // Verify that measurement of substring is consistent no matter what surrounds it.
1036        assertMeasureText(longString, longChars, longSpanned, 0, 1, width);
1037        assertMeasureText(longString, longChars, longSpanned, 1, 2, width);
1038        assertMeasureText(longString, longChars, longSpanned, 2, 3, width);
1039     }
1040 
testMeasureTextWithLongText()1041     public void testMeasureTextWithLongText() {
1042         // This test is not compatible with 4.0.3
1043         if ("4.0.3".equals(Build.VERSION.RELEASE)) {
1044             return;
1045         }
1046 
1047         final int MAX_COUNT = 65535;
1048         char[] longText = new char[MAX_COUNT];
1049         for (int n = 0; n < MAX_COUNT; n++) {
1050             longText[n] = 'm';
1051         }
1052 
1053         Paint p = new Paint();
1054         float width = p.measureText(longText, 0, 1);
1055         assertEquals(true, width > 0);
1056     }
1057 
1058     /** Tests that all four overloads of measureText are the same and match some value. */
assertMeasureText(String text, char[] textChars, SpannedString textSpan, int start, int end, float expectedWidth)1059     private void assertMeasureText(String text, char[] textChars, SpannedString textSpan,
1060             int start, int end, float expectedWidth) {
1061         Paint p = new Paint();
1062 
1063         // We need to turn off kerning in order to get accurate comparisons
1064         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
1065 
1066         int count = end - start;
1067         float[] widths = new float[] {-1, -1, -1, -1};
1068 
1069         String textSlice = text.substring(start, end);
1070         widths[0] = p.measureText(textSlice);
1071         widths[1] = p.measureText(textChars, start, count);
1072         widths[2] = p.measureText(textSpan, start, end);
1073         widths[3] = p.measureText(text, start, end);
1074 
1075         // Check that the widths returned by the overloads are the same.
1076         assertEquals(widths[0], widths[1]);
1077         assertEquals(widths[1], widths[2]);
1078         assertEquals(widths[2], widths[3]);
1079         assertEquals(widths[3], expectedWidth);
1080     }
1081 
testGetTextPath1()1082     public void testGetTextPath1() {
1083         Paint p = new Paint();
1084         char[] chars = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
1085         Path path = new Path();
1086 
1087         assertTrue(path.isEmpty());
1088         p.getTextPath(chars, 0, 7, 0, 0, path);
1089         assertFalse(path.isEmpty());
1090 
1091         try {
1092             p.getTextPath(chars, -2, 7, 0, 0, path);
1093             fail("Should throw an exception here");
1094         } catch (RuntimeException e) {
1095         }
1096 
1097         try {
1098             p.getTextPath(chars, 0, -3, 0, 0, path);
1099             fail("Should throw an exception here");
1100         } catch (RuntimeException e) {
1101         }
1102 
1103         try {
1104             p.getTextPath(chars, 3, 7, 0, 0, path);
1105             fail("Should throw an exception here");
1106         } catch (RuntimeException e) {
1107         }
1108 
1109     }
1110 
testGetTextPath2()1111     public void testGetTextPath2() {
1112         Paint p = new Paint();
1113         String string = "HIJKLMN";
1114         Path path = new Path();
1115 
1116         assertTrue(path.isEmpty());
1117         p.getTextPath(string, 0, 7, 0, 0, path);
1118         assertFalse(path.isEmpty());
1119 
1120         try {
1121             p.getTextPath(string, -2, 7, 0, 0, path);
1122             fail("Should throw an exception here");
1123         } catch (RuntimeException e) {
1124         }
1125 
1126         try {
1127             p.getTextPath(string, 0, -3, 0, 0, path);
1128             fail("Should throw an exception here");
1129         } catch (RuntimeException e) {
1130         }
1131 
1132         try {
1133             p.getTextPath(string, 7, 3, 0, 0, path);
1134             fail("Should throw an exception here");
1135         } catch (RuntimeException e) {
1136         }
1137 
1138         try {
1139             p.getTextPath(string, 3, 9, 0, 0, path);
1140             fail("Should throw an exception here");
1141         } catch (RuntimeException e) {
1142         }
1143     }
1144 
testHasGlyph()1145     public void testHasGlyph() {
1146         Paint p = new Paint();
1147 
1148         // This method tests both the logic of hasGlyph and the sanity of fonts present
1149         // on the device.
1150         assertTrue(p.hasGlyph("A"));
1151         assertFalse(p.hasGlyph("\uFFFE"));  // U+FFFE is guaranteed to be a noncharacter
1152 
1153         // Roboto 2 (the default typeface) does have an "fi" glyph and is mandated by CDD
1154         assertTrue(p.hasGlyph("fi"));
1155         assertFalse(p.hasGlyph("ab"));  // but it does not contain an "ab" glyph
1156         assertTrue(p.hasGlyph("\u02E5\u02E9"));  // IPA tone mark ligature
1157 
1158         // variation selectors
1159         assertFalse(p.hasGlyph("a\uFE0F"));
1160         assertFalse(p.hasGlyph("a\uDB40\uDDEF"));  // UTF-16 encoding of U+E01EF
1161         assertFalse(p.hasGlyph("\u2229\uFE0F"));  // base character is in mathematical symbol font
1162         // Note: U+FE0F is variation selection, unofficially reserved for emoji
1163 
1164         // regional indicator symbols
1165         assertTrue(p.hasGlyph("\uD83C\uDDEF\uD83C\uDDF5"));   // "JP" U+1F1EF U+1F1F5
1166         assertFalse(p.hasGlyph("\uD83C\uDDFF\uD83C\uDDFF"));  // "ZZ" U+1F1FF U+1F1FF
1167 
1168         // Mongolian, which is an optional font, but if present, should support FVS
1169         if (p.hasGlyph("\u182D")) {
1170             assertTrue(p.hasGlyph("\u182D\u180B"));
1171         }
1172 
1173         // TODO: when we support variation selectors, add positive tests
1174 
1175         // Unicode 7.0, 8.0, and 9.0 emoji should be supported.
1176         assertTrue(p.hasGlyph("\uD83D\uDD75"));  // SLEUTH OR SPY is introduced in Unicode 7.0
1177         assertTrue(p.hasGlyph("\uD83C\uDF2E"));  // TACO is introduced in Unicode 8.0
1178         assertTrue(p.hasGlyph("\uD83E\uDD33"));  // SELFIE is introduced in Unicode 9.0
1179     }
1180 
testGetRunAdvance()1181     public void testGetRunAdvance() {
1182         Paint p = new Paint();
1183         {
1184             // LTR
1185             String string = "abcdef";
1186             {
1187                 final float width = p.getRunAdvance(string, 0, string.length(), 0,
1188                         string.length(), false, 0);
1189                 assertEquals(0.0f, width);
1190             }
1191             {
1192                 for (int i = 0; i < string.length(); i++) {
1193                     final float width = p.getRunAdvance(string, i, i + 1, 0, string.length(),
1194                             false, i);
1195                     assertEquals(0.0f, width);
1196                 }
1197             }
1198             {
1199                 final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0,
1200                         string.length(), false, string.length() / 2);
1201                 final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0,
1202                         string.length(), false, string.length());
1203                 assertTrue(widthToMid > 0.0f);
1204                 assertTrue(widthToTail > widthToMid);
1205             }
1206             {
1207                 final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0,
1208                         string.length(), false, string.length());
1209                 final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0,
1210                         string.length(), false, string.length());
1211                 assertTrue(widthFromHead > widthFromSecond);
1212             }
1213             {
1214                 float width = 0.0f;
1215                 for (int i = 0; i < string.length(); i++) {
1216                     width += p.getRunAdvance(string, i, i + 1, 0, string.length(), false, i + 1);
1217                 }
1218                 final float totalWidth = p.getRunAdvance(string, 0, string.length(), 0,
1219                         string.length(), false, string.length());
1220                 assertEquals(totalWidth, width, 1.0f);
1221             }
1222         }
1223         {
1224             // RTL
1225             String string = "\u0644\u063A\u0629 \u0639\u0631\u0628\u064A\u0629"; // Arabic
1226             {
1227                 final float width = p.getRunAdvance(string, 0, string.length(), 0,
1228                         string.length(), true, 0);
1229                 assertEquals(0.0f, width);
1230             }
1231             {
1232                 for (int i = 0; i < string.length(); i++) {
1233                     final float width = p.getRunAdvance(string, i, i + 1, 0, string.length(),
1234                             true, i);
1235                     assertEquals(0.0f, width);
1236                 }
1237             }
1238             {
1239                 final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0,
1240                         string.length(), true, string.length() / 2);
1241                 final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0,
1242                         string.length(), true, string.length());
1243                 assertTrue(widthToMid > 0.0f);
1244                 assertTrue(widthToTail > widthToMid);
1245             }
1246             {
1247                 final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0,
1248                         string.length(), true, string.length());
1249                 final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0,
1250                         string.length(), true, string.length());
1251                 assertTrue(widthFromHead > widthFromSecond);
1252             }
1253         }
1254     }
1255 
testGetRunAdvance_invalidArguments()1256     public void testGetRunAdvance_invalidArguments() {
1257         Paint p = new Paint();
1258         try {
1259             p.getRunAdvance((CharSequence)null, 0, 0, 0, 0, false, 0);
1260             fail("Should throw an IllegalArgumentException.");
1261         } catch (IllegalArgumentException e) {
1262         } catch (Exception e) {
1263             fail("Should throw an IllegalArgumentException.");
1264         }
1265 
1266         try {
1267             p.getRunAdvance((char[])null, 0, 0, 0, 0, false, 0);
1268             fail("Should throw an IllegalArgumentException.");
1269         } catch (IllegalArgumentException e) {
1270         } catch (Exception e) {
1271             fail("Should throw an IllegalArgumentException.");
1272         }
1273 
1274         final String string = "abcde";
1275 
1276         try {
1277             // text length < context end
1278             p.getRunAdvance(string, 0, string.length(), 0, string.length() + 1, false,
1279                     string.length());
1280             fail("Should throw an IndexOutOfBoundsException.");
1281         } catch (IndexOutOfBoundsException e) {
1282         } catch (Exception e) {
1283             fail("Should throw an IndexOutOfBoundsException.");
1284         }
1285         try {
1286             // context end < end
1287             p.getRunAdvance(string, 0, string.length(), 0, string.length() - 1, false, 0);
1288             fail("Should throw an IndexOutOfBoundsException.");
1289         } catch (IndexOutOfBoundsException e) {
1290         } catch (Exception e) {
1291             fail("Should throw an IndexOutOfBoundsException.");
1292         }
1293         try {
1294             // end < offset
1295             p.getRunAdvance(string, 0, string.length() - 1, 0, string.length() - 1, false,
1296                     string.length());
1297             fail("Should throw an IndexOutOfBoundsException.");
1298         } catch (IndexOutOfBoundsException e) {
1299         } catch (Exception e) {
1300             fail("Should throw an IndexOutOfBoundsException.");
1301         }
1302         try {
1303             // offset < start
1304             p.getRunAdvance(string, 1, string.length(), 1, string.length(), false, 0);
1305             fail("Should throw an IndexOutOfBoundsException.");
1306         } catch (IndexOutOfBoundsException e) {
1307         } catch (Exception e) {
1308             fail("Should throw an IndexOutOfBoundsException.");
1309         }
1310         try {
1311             // start < context start
1312             p.getRunAdvance(string, 0, string.length(), 1, string.length(), false, 1);
1313             fail("Should throw an IndexOutOfBoundsException.");
1314         } catch (IndexOutOfBoundsException e) {
1315         } catch (Exception e) {
1316             fail("Should throw an IndexOutOfBoundsException.");
1317         }
1318         try {
1319             // context start < 0
1320             p.getRunAdvance(string, 0, string.length(), -1, string.length(), false, 0);
1321             fail("Should throw an IndexOutOfBoundsException.");
1322         } catch (IndexOutOfBoundsException e) {
1323         } catch (Exception e) {
1324             fail("Should throw an IndexOutOfBoundsException.");
1325         }
1326     }
1327 
testGetRunAdvance_nonzeroIndex()1328     public void testGetRunAdvance_nonzeroIndex() {
1329         Paint p = new Paint();
1330         final String text = "Android powers hundreds of millions of mobile " +
1331                 "devices in more than 190 countries around the world. It's" +
1332                 "the largest installed base of any mobile platform and" +
1333                 "growing fast—every day another million users power up their" +
1334                 "Android devices for the first time and start looking for" +
1335                 "apps, games, and other digital content.";
1336         // Test offset index does not affect width.
1337         final float widthAndroidFirst = p.getRunAdvance(
1338                 text, 0, 7, 0, text.length(), false, 7);
1339         final float widthAndroidSecond = p.getRunAdvance(
1340                 text, 215, 222, 0, text.length(), false, 222);
1341         assertTrue(Math.abs(widthAndroidFirst - widthAndroidSecond) < 1);
1342     }
1343 
1344     public void testGetRunAdvance_glyphDependingContext() {
1345         Paint p = new Paint();
1346         // Test the context change the character shape.
1347         // First character should be isolated form because the context ends at index 1.
1348         final float isolatedFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 1, true, 1);
1349         // First character should be initial form because the context ends at index 2.
1350         final float initialFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 2, true, 1);
1351         assertTrue(isolatedFormWidth > initialFormWidth);
1352     }
1353 
testGetRunAdvance_arabic()1354     public void testGetRunAdvance_arabic() {
1355         Paint p = new Paint();
1356         // Test total width is equals to sum of each character's width.
1357         // "What is Unicode?" in Arabic.
1358         final String text =
1359                 "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
1360                 "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
1361                 "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
1362                 "\u062F\u061F";
1363         final float totalWidth = p.getRunAdvance(
1364                 text, 0, text.length(), 0, text.length(), true, text.length());
1365         float sumOfCharactersWidth = 0;
1366         for (int i = 0; i < text.length(); i++) {
1367             sumOfCharactersWidth += p.getRunAdvance(
1368                     text, i, i + 1, 0, text.length(), true, i + 1);
1369         }
1370         assertTrue(Math.abs(totalWidth - sumOfCharactersWidth) < 1);
1371     }
1372 
1373     public void testGetOffsetForAdvance() {
1374         Paint p = new Paint();
1375         {
1376             // LTR
1377             String string = "abcdef";
1378             {
1379                 for (int offset = 0; offset <= string.length(); ++offset) {
1380                     final float widthToOffset = p.getRunAdvance(string, 0,
1381                             string.length(), 0, string.length(), false, offset);
1382                     final int restoredOffset = p.getOffsetForAdvance(string, 0,
1383                             string.length(), 0, string.length(), false, widthToOffset);
1384                     assertEquals(offset, restoredOffset);
1385                 }
1386             }
1387             {
1388                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1389                         string.length(), false, -10.0f);
1390                 assertEquals(0, offset);
1391             }
1392             {
1393                 final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0,
1394                         string.length(), true, string.length());
1395                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1396                         string.length(), true, widthToEnd + 10.0f);
1397                 assertEquals(string.length(), offset);
1398             }
1399         }
1400         {
1401             // RTL
1402             String string = "\u0639\u0631\u0628\u0649"; // Arabic
1403             {
1404                 for (int offset = 0; offset <= string.length(); ++offset) {
1405                     final float widthToOffset = p.getRunAdvance(string, 0,
1406                             string.length(), 0, string.length(), true, offset);
1407                     final int restoredOffset = p.getOffsetForAdvance(string, 0,
1408                             string.length(), 0, string.length(), true, widthToOffset);
1409                     assertEquals(offset, restoredOffset);
1410                 }
1411             }
1412             {
1413                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1414                         string.length(), true, -10.0f);
1415                 assertEquals(0, offset);
1416             }
1417             {
1418                 final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0,
1419                         string.length(), true, string.length());
1420                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1421                         string.length(), true, widthToEnd + 10.0f);
1422                 assertEquals(string.length(), offset);
1423             }
1424         }
1425     }
1426 
1427     public void testGetOffsetForAdvance_invalidArguments() {
1428         Paint p = new Paint();
1429         try {
1430             p.getOffsetForAdvance((CharSequence)null, 0, 0, 0, 0, false, 0.0f);
1431             fail("Should throw an IllegalArgumentException.");
1432         } catch (IllegalArgumentException e) {
1433         } catch (Exception e) {
1434             fail("Should throw an IllegalArgumentException.");
1435         }
1436         try {
1437             p.getOffsetForAdvance((char[])null, 0, 0, 0, 0, false, 0.0f);
1438             fail("Should throw an IllegalArgumentException.");
1439         } catch (IllegalArgumentException e) {
1440         } catch (Exception e) {
1441             fail("Should throw an IllegalArgumentException.");
1442         }
1443 
1444         final String string = "abcde";
1445 
1446         try {
1447             // context start < 0
1448             p.getOffsetForAdvance(string, -1, string.length(), 0, string.length(), false, 0.0f);
1449             fail("Should throw an IndexOutOfBoundsException.");
1450         } catch (IndexOutOfBoundsException e) {
1451         } catch (Exception e) {
1452             fail("Should throw an IndexOutOfBoundsException.");
1453         }
1454 
1455         try {
1456             // start < context start
1457             p.getOffsetForAdvance(string, 0, string.length(), 1, string.length(), false, 0.0f);
1458             fail("Should throw an IndexOutOfBoundsException.");
1459         } catch (IndexOutOfBoundsException e) {
1460         } catch (Exception e) {
1461             fail("Should throw an IndexOutOfBoundsException.");
1462         }
1463 
1464         try {
1465             // end < start
1466             p.getOffsetForAdvance(string, 1, 0, 0, 0, false, 0);
1467             fail("Should throw an IndexOutOfBoundsException.");
1468         } catch (IndexOutOfBoundsException e) {
1469         } catch (Exception e) {
1470             fail("Should throw an IndexOutOfBoundsException.");
1471         }
1472 
1473         try {
1474             // context end < end
1475             p.getOffsetForAdvance(string, 0, string.length(), 0, string.length() - 1, false, 0.0f);
1476             fail("Should throw an IndexOutOfBoundsException.");
1477         } catch (IndexOutOfBoundsException e) {
1478         } catch (Exception e) {
1479             fail("Should throw an IndexOutOfBoundsException.");
1480         }
1481 
1482         try {
1483             // text length < context end
1484             p.getOffsetForAdvance(string, 0, string.length(), 0, string.length() + 1, false, 0.0f);
1485             fail("Should throw an IndexOutOfBoundsException.");
1486         } catch (IndexOutOfBoundsException e) {
1487         } catch (Exception e) {
1488             fail("Should throw an IndexOutOfBoundsException.");
1489         }
1490     }
1491 
1492     public void testGetOffsetForAdvance_grahpemeCluster() {
1493         Paint p = new Paint();
1494         {
1495             String string = "\uD83C\uDF37"; // U+1F337: TULIP
1496             {
1497                 final float widthToOffset = p.getRunAdvance(string, 0,
1498                         string.length(), 0, string.length(), false, 1);
1499                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1500                         string.length(), false, widthToOffset);
1501                 assertFalse(1 == offset);
1502                 assertTrue(0 == offset || string.length() == offset);
1503             }
1504         }
1505         {
1506             String string = "\uD83C\uDDFA\uD83C\uDDF8"; // US flag
1507             {
1508                 final float widthToOffset = p.getRunAdvance(string, 0,
1509                         string.length(), 0, string.length(), false, 2);
1510                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1511                         string.length(), false, widthToOffset);
1512                 assertFalse(2 == offset);
1513                 assertTrue(0 == offset || string.length() == offset);
1514             }
1515             {
1516                 final float widthToOffset = p.getRunAdvance(string, 0, 2, 0, 2, false, 2);
1517                 final int offset = p.getOffsetForAdvance(string, 0, 2,
1518                         0, 2, false, widthToOffset);
1519                 assertEquals(2, offset);
1520             }
1521         }
1522         {
1523             // HANGUL CHOSEONG KIYEOK, HANGUL JUNGSEONG A, HANDUL JONGSEONG KIYEOK
1524             String string = "\u1100\u1161\u11A8";
1525             {
1526                 for (int offset = 0; offset <= string.length(); ++offset) {
1527                     final float widthToOffset = p.getRunAdvance(string, 0,
1528                             string.length(), 0, string.length(), false, offset);
1529                     final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(),
1530                             0, string.length(), false, widthToOffset);
1531                     assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance);
1532                 }
1533                 for (int offset = 0; offset <= string.length(); ++offset) {
1534                     final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset,
1535                             false, offset);
1536                     final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(),
1537                             0, string.length(), false, widthToOffset);
1538                     assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance);
1539                 }
1540                 for (int offset = 0; offset <= string.length(); ++offset) {
1541                     final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset,
1542                             false, offset);
1543                     final int offsetForAdvance = p.getOffsetForAdvance(string, 0, offset, 0,
1544                             offset, false, widthToOffset);
1545                     assertEquals(offset, offsetForAdvance);
1546                 }
1547             }
1548         }
1549     }
1550 }
1551