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 import static android.graphics.Paint.CURSOR_AFTER; 20 import static android.graphics.Paint.CURSOR_AT; 21 import static android.graphics.Paint.CURSOR_AT_OR_AFTER; 22 import static android.graphics.Paint.CURSOR_AT_OR_BEFORE; 23 import static android.graphics.Paint.CURSOR_BEFORE; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertNull; 28 import static org.junit.Assert.assertTrue; 29 30 import android.content.Context; 31 import android.graphics.Bitmap; 32 import android.graphics.BitmapShader; 33 import android.graphics.BlendMode; 34 import android.graphics.Color; 35 import android.graphics.ColorFilter; 36 import android.graphics.ColorSpace; 37 import android.graphics.MaskFilter; 38 import android.graphics.Matrix; 39 import android.graphics.Paint; 40 import android.graphics.Paint.Align; 41 import android.graphics.Paint.Cap; 42 import android.graphics.Paint.Join; 43 import android.graphics.Paint.Style; 44 import android.graphics.Path; 45 import android.graphics.PathEffect; 46 import android.graphics.PorterDuff; 47 import android.graphics.PorterDuffXfermode; 48 import android.graphics.Rect; 49 import android.graphics.Shader; 50 import android.graphics.Typeface; 51 import android.graphics.Xfermode; 52 import android.os.LocaleList; 53 import android.text.SpannedString; 54 55 import androidx.test.InstrumentationRegistry; 56 import androidx.test.filters.SmallTest; 57 import androidx.test.runner.AndroidJUnit4; 58 59 import com.android.compatibility.common.util.CddTest; 60 import com.android.compatibility.common.util.ColorUtils; 61 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 65 import java.util.Locale; 66 import java.util.function.BiConsumer; 67 import java.util.function.Function; 68 import java.util.function.Supplier; 69 70 @SmallTest 71 @RunWith(AndroidJUnit4.class) 72 public class PaintTest { 73 private static final Typeface[] TYPEFACES = new Typeface[] { 74 Typeface.DEFAULT, 75 Typeface.DEFAULT_BOLD, 76 Typeface.MONOSPACE, 77 Typeface.SANS_SERIF, 78 Typeface.SERIF, 79 }; 80 81 @Test 82 public void testConstructor() { 83 new Paint(); 84 85 new Paint(1); 86 87 Paint p = new Paint(); 88 new Paint(p); 89 } 90 91 @Test 92 public void testDefaultColor() { 93 Supplier<Paint> set = () -> { 94 Paint result = new Paint(); 95 result.setColor(Color.BLUE); 96 assertEquals(Color.BLUE, result.getColor()); 97 result.setShadowLayer(10.0f, 1.0f, 1.0f, Color.RED); 98 assertEquals(Color.RED, result.getShadowLayerColor()); 99 100 Paint def = new Paint(); 101 result.set(def); 102 return result; 103 }; 104 Supplier<Paint> reset = () -> { 105 Paint result = new Paint(); 106 result.setColor(Color.GREEN); 107 assertEquals(Color.GREEN, result.getColor()); 108 result.setShadowLayer(10.0f, 1.0f, 1.0f, Color.WHITE); 109 assertEquals(Color.WHITE, result.getShadowLayerColor()); 110 111 result.reset(); 112 return result; 113 }; 114 for (Paint p : new Paint[]{ new Paint(), 115 new Paint(1), 116 new Paint(new Paint()), 117 set.get(), 118 reset.get()}) { 119 assertEquals(Color.BLACK, p.getColor()); 120 assertEquals(Color.TRANSPARENT, p.getShadowLayerColor()); 121 122 assertEquals(Color.BLACK, Color.toArgb(p.getColorLong())); 123 assertEquals(Color.TRANSPARENT, Color.toArgb(p.getShadowLayerColorLong())); 124 } 125 } 126 127 @Test 128 public void testBreakText() { 129 String text = "HIJKLMN"; 130 char[] textChars = text.toCharArray(); 131 SpannedString textSpan = new SpannedString(text); 132 133 Paint p = new Paint(); 134 135 // We need to turn off kerning in order to get accurate comparisons 136 p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG); 137 138 float[] widths = new float[text.length()]; 139 assertEquals(text.length(), p.getTextWidths(text, widths)); 140 141 float totalWidth = 0.0f; 142 for (int i = 0; i < text.length(); i++) { 143 totalWidth += widths[i]; 144 } 145 146 for (int i = 0; i < text.length(); i++) { 147 verifyBreakText(text, textChars, textSpan, i, i + 1, true, totalWidth, 1, widths[i]); 148 } 149 150 // Measure empty string 151 verifyBreakText(text, textChars, textSpan, 0, 0, true, totalWidth, 0, 0); 152 153 // Measure substring from front: "HIJ" 154 verifyBreakText(text, textChars, textSpan, 0, 3, true, totalWidth, 155 3, widths[0] + widths[1] + widths[2]); 156 157 // Reverse measure substring from front: "HIJ" 158 verifyBreakText(text, textChars, textSpan, 0, 3, false, totalWidth, 159 3, widths[0] + widths[1] + widths[2]); 160 161 // Measure substring from back: "MN" 162 verifyBreakText(text, textChars, textSpan, 5, 7, true, totalWidth, 163 2, widths[5] + widths[6]); 164 165 // Reverse measure substring from back: "MN" 166 verifyBreakText(text, textChars, textSpan, 5, 7, false, totalWidth, 167 2, widths[5] + widths[6]); 168 169 // Measure substring in the middle: "JKL" 170 verifyBreakText(text, textChars, textSpan, 2, 5, true, totalWidth, 171 3, widths[2] + widths[3] + widths[4]); 172 173 // Reverse measure substring in the middle: "JKL" 174 verifyBreakText(text, textChars, textSpan, 2, 5, false, totalWidth, 175 3, widths[2] + widths[3] + widths[4]); 176 177 // Measure substring in the middle and restrict width to the first 2 characters. 178 verifyBreakText(text, textChars, textSpan, 2, 5, true, widths[2] + widths[3], 179 2, widths[2] + widths[3]); 180 181 // Reverse measure substring in the middle and restrict width to the last 2 characters. 182 verifyBreakText(text, textChars, textSpan, 2, 5, false, widths[3] + widths[4], 183 2, widths[3] + widths[4]); 184 185 // a single Emoji (U+1f601) 186 String emoji = "\ud83d\ude01"; 187 char[] emojiChars = emoji.toCharArray(); 188 SpannedString emojiSpan = new SpannedString(emoji); 189 190 float[] emojiWidths = new float[emoji.length()]; 191 assertEquals(emoji.length(), p.getTextWidths(emoji, emojiWidths)); 192 193 // Measure substring with a cluster 194 verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, 0, 195 0, 0); 196 197 // Measure substring with a cluster 198 verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, emojiWidths[0], 199 2, emojiWidths[0]); 200 201 // Reverse measure substring with a cluster 202 verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, 0, 203 0, 0); 204 205 // Measure substring with a cluster 206 verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, emojiWidths[0], 207 2, emojiWidths[0]); 208 } 209 210 private void verifyBreakText(String text, char[] textChars, SpannedString textSpan, 211 int start, int end, boolean measureForwards, float maxWidth, int expectedCount, 212 float expectedWidth) { 213 Paint p = new Paint(); 214 215 // We need to turn off kerning in order to get accurate comparisons 216 p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG); 217 218 int count = end - start; 219 if (!measureForwards) { 220 count = -count; 221 } 222 223 float[][] measured = new float[][] { 224 new float[1], 225 new float[1], 226 new float[1] 227 }; 228 String textSlice = text.substring(start, end); 229 assertEquals(expectedCount, p.breakText(textSlice, measureForwards, maxWidth, measured[0])); 230 assertEquals(expectedCount, p.breakText(textChars, start, count, maxWidth, measured[1])); 231 assertEquals(expectedCount, p.breakText(textSpan, start, end, measureForwards, maxWidth, 232 measured[2])); 233 234 for (int i = 0; i < measured.length; i++) { 235 assertEquals("i: " + i, expectedWidth, measured[i][0], 0.0f); 236 } 237 } 238 239 @Test 240 public void testSet() { 241 Paint p = new Paint(); 242 Paint p2 = new Paint(); 243 ColorFilter c = new ColorFilter(); 244 MaskFilter m = new MaskFilter(); 245 PathEffect e = new PathEffect(); 246 Shader s = new Shader(); 247 Typeface t = Typeface.DEFAULT; 248 Xfermode x = new Xfermode(); 249 250 p.setColorFilter(c); 251 p.setMaskFilter(m); 252 p.setPathEffect(e); 253 p.setShader(s); 254 p.setTypeface(t); 255 p.setXfermode(x); 256 p2.set(p); 257 assertEquals(c, p2.getColorFilter()); 258 assertEquals(m, p2.getMaskFilter()); 259 assertEquals(e, p2.getPathEffect()); 260 assertEquals(s, p2.getShader()); 261 assertEquals(t, p2.getTypeface()); 262 assertEquals(x, p2.getXfermode()); 263 264 p2.set(p2); 265 assertEquals(c, p2.getColorFilter()); 266 assertEquals(m, p2.getMaskFilter()); 267 assertEquals(e, p2.getPathEffect()); 268 assertEquals(s, p2.getShader()); 269 assertEquals(t, p2.getTypeface()); 270 assertEquals(x, p2.getXfermode()); 271 272 p.setColorFilter(null); 273 p.setMaskFilter(null); 274 p.setPathEffect(null); 275 p.setShader(null); 276 p.setTypeface(null); 277 p.setXfermode(null); 278 p2.set(p); 279 assertNull(p2.getColorFilter()); 280 assertNull(p2.getMaskFilter()); 281 assertNull(p2.getPathEffect()); 282 assertNull(p2.getShader()); 283 assertNull(p2.getTypeface()); 284 assertNull(p2.getXfermode()); 285 286 p2.set(p2); 287 assertNull(p2.getColorFilter()); 288 assertNull(p2.getMaskFilter()); 289 assertNull(p2.getPathEffect()); 290 assertNull(p2.getShader()); 291 assertNull(p2.getTypeface()); 292 assertNull(p2.getXfermode()); 293 } 294 295 @Test 296 public void testAccessStrokeCap() { 297 Paint p = new Paint(); 298 299 p.setStrokeCap(Cap.BUTT); 300 assertEquals(Cap.BUTT, p.getStrokeCap()); 301 302 p.setStrokeCap(Cap.ROUND); 303 assertEquals(Cap.ROUND, p.getStrokeCap()); 304 305 p.setStrokeCap(Cap.SQUARE); 306 assertEquals(Cap.SQUARE, p.getStrokeCap()); 307 } 308 309 @Test(expected=RuntimeException.class) 310 public void testSetStrokeCapNull() { 311 Paint p = new Paint(); 312 313 p.setStrokeCap(null); 314 } 315 316 @Test 317 public void testAccessXfermode() { 318 Paint p = new Paint(); 319 Xfermode x = new Xfermode(); 320 321 assertEquals(x, p.setXfermode(x)); 322 assertEquals(x, p.getXfermode()); 323 324 assertNull(p.setXfermode(null)); 325 assertNull(p.getXfermode()); 326 } 327 328 @Test 329 public void testAccessShader() { 330 Paint p = new Paint(); 331 Shader s = new Shader(); 332 333 assertEquals(s, p.setShader(s)); 334 assertEquals(s, p.getShader()); 335 336 assertNull(p.setShader(null)); 337 assertNull(p.getShader()); 338 } 339 340 @Test 341 public void testShaderLocalMatrix() { 342 int width = 80; 343 int height = 120; 344 int[] color = new int[width * height]; 345 Bitmap bitmap = Bitmap.createBitmap(color, width, height, Bitmap.Config.RGB_565); 346 347 Paint p = new Paint(); 348 Matrix m = new Matrix(); 349 Shader s = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); 350 351 // set the shaders matrix to a non identity value and attach to paint 352 m.setScale(10, 0); 353 s.setLocalMatrix(m); 354 p.setShader(s); 355 356 Matrix m2 = new Matrix(); 357 assertTrue(p.getShader().getLocalMatrix(m2)); 358 assertEquals(m, m2); 359 360 // updated the matrix again and set it on the shader but NOT the paint 361 m.setScale(0, 10); 362 s.setLocalMatrix(m); 363 364 // assert that the matrix on the paint's shader also changed 365 Matrix m3 = new Matrix(); 366 assertTrue(p.getShader().getLocalMatrix(m3)); 367 assertEquals(m, m3); 368 } 369 370 @Test 371 public void testSetAntiAlias() { 372 Paint p = new Paint(); 373 374 p.setAntiAlias(true); 375 assertTrue(p.isAntiAlias()); 376 377 p.setAntiAlias(false); 378 assertFalse(p.isAntiAlias()); 379 } 380 381 @Test 382 public void testAccessTypeface() { 383 Paint p = new Paint(); 384 385 assertEquals(Typeface.DEFAULT, p.setTypeface(Typeface.DEFAULT)); 386 assertEquals(Typeface.DEFAULT, p.getTypeface()); 387 388 assertEquals(Typeface.DEFAULT_BOLD, p.setTypeface(Typeface.DEFAULT_BOLD)); 389 assertEquals(Typeface.DEFAULT_BOLD, p.getTypeface()); 390 391 assertEquals(Typeface.MONOSPACE, p.setTypeface(Typeface.MONOSPACE)); 392 assertEquals(Typeface.MONOSPACE, p.getTypeface()); 393 394 assertNull(p.setTypeface(null)); 395 assertNull(p.getTypeface()); 396 } 397 398 @Test 399 public void testAccessPathEffect() { 400 Paint p = new Paint(); 401 PathEffect e = new PathEffect(); 402 403 assertEquals(e, p.setPathEffect(e)); 404 assertEquals(e, p.getPathEffect()); 405 406 assertNull(p.setPathEffect(null)); 407 assertNull(p.getPathEffect()); 408 } 409 410 @Test 411 public void testSetFakeBoldText() { 412 Paint p = new Paint(); 413 414 p.setFakeBoldText(true); 415 assertTrue(p.isFakeBoldText()); 416 417 p.setFakeBoldText(false); 418 assertFalse(p.isFakeBoldText()); 419 } 420 421 @Test 422 public void testAccessStrokeJoin() { 423 Paint p = new Paint(); 424 425 p.setStrokeJoin(Join.BEVEL); 426 assertEquals(Join.BEVEL, p.getStrokeJoin()); 427 428 p.setStrokeJoin(Join.MITER); 429 assertEquals(Join.MITER, p.getStrokeJoin()); 430 431 p.setStrokeJoin(Join.ROUND); 432 assertEquals(Join.ROUND, p.getStrokeJoin()); 433 } 434 435 @Test(expected=RuntimeException.class) 436 public void testSetStrokeJoinNull() { 437 Paint p = new Paint(); 438 439 p.setStrokeJoin(null); 440 } 441 442 @Test 443 public void testAccessStyle() { 444 Paint p = new Paint(); 445 446 p.setStyle(Style.FILL); 447 assertEquals(Style.FILL, p.getStyle()); 448 449 p.setStyle(Style.FILL_AND_STROKE); 450 assertEquals(Style.FILL_AND_STROKE, p.getStyle()); 451 452 p.setStyle(Style.STROKE); 453 assertEquals(Style.STROKE, p.getStyle()); 454 } 455 456 @Test(expected=RuntimeException.class) 457 public void testSetStyleNull() { 458 Paint p = new Paint(); 459 460 p.setStyle(null); 461 } 462 463 @Test 464 public void testGetFontSpacing() { 465 Paint p = new Paint(); 466 467 for (Typeface typeface : TYPEFACES) { 468 p.setTypeface(typeface); 469 470 p.setTextSize(10); 471 float spacing10 = p.getFontSpacing(); 472 assertTrue(spacing10 > 0); 473 474 p.setTextSize(20); 475 float spacing20 = p.getFontSpacing(); 476 assertTrue(spacing20 > spacing10); 477 } 478 } 479 480 @Test 481 public void testSetSubpixelText() { 482 Paint p = new Paint(); 483 484 p.setSubpixelText(true); 485 assertTrue(p.isSubpixelText()); 486 487 p.setSubpixelText(false); 488 assertFalse(p.isSubpixelText()); 489 } 490 491 @Test 492 public void testAccessTextScaleX() { 493 Paint p = new Paint(); 494 495 p.setTextScaleX(2.0f); 496 assertEquals(2.0f, p.getTextScaleX(), 0.0f); 497 498 p.setTextScaleX(1.0f); 499 assertEquals(1.0f, p.getTextScaleX(), 0.0f); 500 501 p.setTextScaleX(0.0f); 502 assertEquals(0.0f, p.getTextScaleX(), 0.0f); 503 504 } 505 506 @Test 507 public void testAccessMaskFilter() { 508 Paint p = new Paint(); 509 MaskFilter m = new MaskFilter(); 510 511 assertEquals(m, p.setMaskFilter(m)); 512 assertEquals(m, p.getMaskFilter()); 513 514 assertNull(p.setMaskFilter(null)); 515 assertNull(p.getMaskFilter()); 516 } 517 518 @Test 519 public void testAccessColorFilter() { 520 Paint p = new Paint(); 521 ColorFilter c = new ColorFilter(); 522 523 assertEquals(c, p.setColorFilter(c)); 524 assertEquals(c, p.getColorFilter()); 525 526 assertNull(p.setColorFilter(null)); 527 assertNull(p.getColorFilter()); 528 } 529 530 @Test 531 public void testSetARGB() { 532 Paint p = new Paint(); 533 534 p.setARGB(0, 0, 0, 0); 535 assertEquals(0, p.getColor()); 536 537 p.setARGB(3, 3, 3, 3); 538 assertEquals((3 << 24) | (3 << 16) | (3 << 8) | 3, p.getColor()); 539 } 540 541 @Test 542 public void testAscent() { 543 Paint p = new Paint(); 544 545 for (Typeface typeface : TYPEFACES) { 546 p.setTypeface(typeface); 547 548 p.setTextSize(10); 549 float ascent10 = p.ascent(); 550 assertTrue(ascent10 < 0); 551 552 p.setTextSize(20); 553 float ascent20 = p.ascent(); 554 assertTrue(ascent20 < ascent10); 555 } 556 } 557 558 @Test 559 public void testAccessTextSkewX() { 560 Paint p = new Paint(); 561 562 p.setTextSkewX(1.0f); 563 assertEquals(1.0f, p.getTextSkewX(), 0.0f); 564 565 p.setTextSkewX(0.0f); 566 assertEquals(0.0f, p.getTextSkewX(), 0.0f); 567 568 p.setTextSkewX(-0.25f); 569 assertEquals(-0.25f, p.getTextSkewX(), 0.0f); 570 } 571 572 @Test 573 public void testAccessTextSize() { 574 Paint p = new Paint(); 575 576 p.setTextSize(1.0f); 577 assertEquals(1.0f, p.getTextSize(), 0.0f); 578 579 p.setTextSize(2.0f); 580 assertEquals(2.0f, p.getTextSize(), 0.0f); 581 582 // text size should be greater than 0, so set -1 has no effect 583 p.setTextSize(-1.0f); 584 assertEquals(2.0f, p.getTextSize(), 0.0f); 585 586 // text size should be greater than or equals to 0 587 p.setTextSize(0.0f); 588 assertEquals(0.0f, p.getTextSize(), 0.0f); 589 } 590 591 @Test 592 public void testGetTextWidths() throws Exception { 593 String text = "HIJKLMN"; 594 char[] textChars = text.toCharArray(); 595 SpannedString textSpan = new SpannedString(text); 596 597 // Test measuring the widths of the entire text 598 verifyGetTextWidths(text, textChars, textSpan, 0, 7); 599 600 // Test measuring a substring of the text 601 verifyGetTextWidths(text, textChars, textSpan, 1, 3); 602 603 // Test measuring a substring of zero length. 604 verifyGetTextWidths(text, textChars, textSpan, 3, 3); 605 606 // Test measuring substrings from the front and back 607 verifyGetTextWidths(text, textChars, textSpan, 0, 2); 608 verifyGetTextWidths(text, textChars, textSpan, 4, 7); 609 } 610 611 /** Tests all four overloads of getTextWidths are the same. */ 612 private void verifyGetTextWidths(String text, char[] textChars, SpannedString textSpan, 613 int start, int end) { 614 Paint p = new Paint(); 615 int count = end - start; 616 float[][] widths = new float[][] { 617 new float[count], 618 new float[count], 619 new float[count], 620 new float[count] 621 }; 622 623 String textSlice = text.substring(start, end); 624 assertEquals(count, p.getTextWidths(textSlice, widths[0])); 625 assertEquals(count, p.getTextWidths(textChars, start, count, widths[1])); 626 assertEquals(count, p.getTextWidths(textSpan, start, end, widths[2])); 627 assertEquals(count, p.getTextWidths(text, start, end, widths[3])); 628 629 // Check that the widths returned by the overloads are the same. 630 for (int i = 0; i < count; i++) { 631 assertEquals(widths[0][i], widths[1][i], 0.0f); 632 assertEquals(widths[1][i], widths[2][i], 0.0f); 633 assertEquals(widths[2][i], widths[3][i], 0.0f); 634 } 635 } 636 637 @Test 638 public void testSetStrikeThruText() { 639 Paint p = new Paint(); 640 641 p.setStrikeThruText(true); 642 assertTrue(p.isStrikeThruText()); 643 644 p.setStrikeThruText(false); 645 assertFalse(p.isStrikeThruText()); 646 } 647 648 @Test 649 public void testAccessTextAlign() { 650 Paint p = new Paint(); 651 652 p.setTextAlign(Align.CENTER); 653 assertEquals(Align.CENTER, p.getTextAlign()); 654 655 p.setTextAlign(Align.LEFT); 656 assertEquals(Align.LEFT, p.getTextAlign()); 657 658 p.setTextAlign(Align.RIGHT); 659 assertEquals(Align.RIGHT, p.getTextAlign()); 660 } 661 662 @Test 663 public void testAccessTextLocale() { 664 Paint p = new Paint(); 665 666 final Locale defaultLocale = Locale.getDefault(); 667 668 // Check default 669 assertEquals(defaultLocale, p.getTextLocale()); 670 671 // Check setter / getters 672 p.setTextLocale(Locale.US); 673 assertEquals(Locale.US, p.getTextLocale()); 674 assertEquals(new LocaleList(Locale.US), p.getTextLocales()); 675 676 p.setTextLocale(Locale.CHINESE); 677 assertEquals(Locale.CHINESE, p.getTextLocale()); 678 assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales()); 679 680 p.setTextLocale(Locale.JAPANESE); 681 assertEquals(Locale.JAPANESE, p.getTextLocale()); 682 assertEquals(new LocaleList(Locale.JAPANESE), p.getTextLocales()); 683 684 p.setTextLocale(Locale.KOREAN); 685 assertEquals(Locale.KOREAN, p.getTextLocale()); 686 assertEquals(new LocaleList(Locale.KOREAN), p.getTextLocales()); 687 688 // Check reverting back to default 689 p.setTextLocale(defaultLocale); 690 assertEquals(defaultLocale, p.getTextLocale()); 691 assertEquals(new LocaleList(defaultLocale), p.getTextLocales()); 692 } 693 694 @Test(expected=IllegalArgumentException.class) 695 public void testSetTextLocaleNull() { 696 Paint p = new Paint(); 697 698 p.setTextLocale(null); 699 } 700 701 @Test 702 public void testAccessTextLocales() { 703 Paint p = new Paint(); 704 705 final LocaleList defaultLocales = LocaleList.getDefault(); 706 707 // Check default 708 assertEquals(defaultLocales, p.getTextLocales()); 709 710 // Check setter / getters for a one-member locale list 711 p.setTextLocales(new LocaleList(Locale.CHINESE)); 712 assertEquals(Locale.CHINESE, p.getTextLocale()); 713 assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales()); 714 715 // Check setter / getters for a two-member locale list 716 p.setTextLocales(LocaleList.forLanguageTags("fr,de")); 717 assertEquals(Locale.forLanguageTag("fr"), p.getTextLocale()); 718 assertEquals(LocaleList.forLanguageTags("fr,de"), p.getTextLocales()); 719 720 // Check reverting back to default 721 p.setTextLocales(defaultLocales); 722 assertEquals(defaultLocales, p.getTextLocales()); 723 } 724 725 @Test(expected=IllegalArgumentException.class) 726 public void testAccessTextLocalesNull() { 727 Paint p = new Paint(); 728 729 // Check that we cannot pass a null locale list 730 p.setTextLocales(null); 731 } 732 733 @Test(expected=IllegalArgumentException.class) 734 public void testAccessTextLocalesEmpty() { 735 Paint p = new Paint(); 736 737 // Check that we cannot pass an empty locale list 738 p.setTextLocales(new LocaleList()); 739 } 740 741 @Test 742 public void testGetFillPath() { 743 Paint p = new Paint(); 744 Path path1 = new Path(); 745 Path path2 = new Path(); 746 747 assertTrue(path1.isEmpty()); 748 assertTrue(path2.isEmpty()); 749 p.getFillPath(path1, path2); 750 assertTrue(path1.isEmpty()); 751 assertTrue(path2.isEmpty()); 752 753 // No setter 754 } 755 756 @Test 757 public void testAccessAlpha() { 758 Paint p = new Paint(); 759 760 p.setAlpha(0); 761 assertEquals(0, p.getAlpha()); 762 763 p.setAlpha(255); 764 assertEquals(255, p.getAlpha()); 765 766 // set value should between 0 and 255, ensure return value is always in range 767 p.setAlpha(266); 768 assertTrue(0 <= p.getAlpha() && p.getAlpha() <= 255); 769 770 // set value should between 0 and 255, ensure return value is always in range 771 p.setAlpha(-20); 772 assertTrue(0 <= p.getAlpha() && p.getAlpha() <= 255); 773 } 774 775 @Test 776 public void testSetAlpha() { 777 for (ColorSpace.Named e : new ColorSpace.Named[] { 778 ColorSpace.Named.SRGB, 779 ColorSpace.Named.LINEAR_EXTENDED_SRGB, 780 ColorSpace.Named.DISPLAY_P3}) { 781 ColorSpace cs = ColorSpace.get(e); 782 783 // Arbitrary colors 784 final float red = .2f; 785 final float green = .7f; 786 final float blue = .9f; 787 final long desiredColor = Color.pack(red, green, blue, 1.0f, cs); 788 789 Paint p = new Paint(); 790 p.setColor(desiredColor); 791 final long origColor = p.getColorLong(); 792 assertEquals(desiredColor, origColor); 793 794 final float origRed = Color.red(origColor); 795 final float origGreen = Color.green(origColor); 796 final float origBlue = Color.blue(origColor); 797 798 // There is a slight difference in the packed color. 799 assertEquals(red, Color.red(origColor), 0.002f); 800 assertEquals(green, Color.green(origColor), 0.002f); 801 assertEquals(blue, Color.blue(origColor), 0.002f); 802 803 for (int alpha = 0; alpha <= 255; ++alpha) { 804 p.setAlpha(alpha); 805 assertEquals(alpha, p.getAlpha()); 806 807 final long color = p.getColorLong(); 808 assertEquals(origRed, Color.red(color), 0.0f); 809 assertEquals(origGreen, Color.green(color), 0.0f); 810 assertEquals(origBlue, Color.blue(color), 0.0f); 811 } 812 } 813 } 814 815 private void testIsFilterBitmap(Paint orig) { 816 Paint p = new Paint(orig); 817 assertEquals(orig.isFilterBitmap(), p.isFilterBitmap()); 818 819 p = new Paint(); 820 p.set(orig); 821 assertEquals(orig.isFilterBitmap(), p.isFilterBitmap()); 822 } 823 824 @Test 825 public void testSetFilterBitmap() { 826 Paint p = new Paint(); 827 assertTrue(p.isFilterBitmap()); 828 testIsFilterBitmap(p); 829 830 p.setFilterBitmap(true); 831 assertTrue(p.isFilterBitmap()); 832 833 p.setFilterBitmap(false); 834 assertFalse(p.isFilterBitmap()); 835 testIsFilterBitmap(p); 836 837 p.reset(); 838 assertTrue(p.isFilterBitmap()); 839 840 p.setFilterBitmap(false); 841 assertFalse(p.isFilterBitmap()); 842 843 p.setFlags(Paint.FILTER_BITMAP_FLAG); 844 assertTrue(p.isFilterBitmap()); 845 846 p.setFlags(~Paint.FILTER_BITMAP_FLAG); 847 assertFalse(p.isFilterBitmap()); 848 } 849 850 @Test 851 public void testAccessColor() { 852 Paint p = new Paint(); 853 854 p.setColor(1); 855 assertEquals(1, p.getColor()); 856 857 p.setColor(0); 858 assertEquals(0, p.getColor()); 859 860 p.setColor(255); 861 assertEquals(255, p.getColor()); 862 863 p.setColor(-1); 864 assertEquals(-1, p.getColor()); 865 866 p.setColor(256); 867 assertEquals(256, p.getColor()); 868 } 869 870 @Test 871 public void testSetGetShadowLayer() { 872 Paint paint = new Paint(); 873 paint.setShadowLayer(10, 1, 1, 0); 874 assertEquals(10, paint.getShadowLayerRadius(), 0.0f); 875 assertEquals(1, paint.getShadowLayerDx(), 0.0f); 876 assertEquals(1, paint.getShadowLayerDy(), 0.0f); 877 assertEquals(0, paint.getShadowLayerColor()); 878 } 879 880 @Test 881 public void testGetFontMetrics1() { 882 Paint p = new Paint(); 883 Paint.FontMetrics fm = new Paint.FontMetrics(); 884 885 for (Typeface typeface : TYPEFACES) { 886 p.setTypeface(typeface); 887 888 p.setTextSize(10); 889 p.getFontMetrics(fm); 890 assertEquals(p.ascent(), fm.ascent, 0.0f); 891 assertEquals(p.descent(), fm.descent, 0.0f); 892 893 p.setTextSize(20); 894 p.getFontMetrics(fm); 895 assertEquals(p.ascent(), fm.ascent, 0.0f); 896 assertEquals(p.descent(), fm.descent, 0.0f); 897 } 898 } 899 900 @Test 901 public void testGetFontMetrics2() { 902 Paint p = new Paint(); 903 904 for (Typeface typeface : TYPEFACES) { 905 p.setTypeface(typeface); 906 907 p.setTextSize(10); 908 Paint.FontMetrics fm = p.getFontMetrics(); 909 assertEquals(p.ascent(), fm.ascent, 0.0f); 910 assertEquals(p.descent(), fm.descent, 0.0f); 911 912 p.setTextSize(20); 913 fm = p.getFontMetrics(); 914 assertEquals(p.ascent(), fm.ascent, 0.0f); 915 assertEquals(p.descent(), fm.descent, 0.0f); 916 } 917 } 918 919 @Test 920 public void testAccessStrokeMiter() { 921 Paint p = new Paint(); 922 923 p.setStrokeMiter(0.0f); 924 assertEquals(0.0f, p.getStrokeMiter(), 0.0f); 925 926 p.setStrokeMiter(10.0f); 927 assertEquals(10.0f, p.getStrokeMiter(), 0.0f); 928 929 // set value should be greater or equal to 0, set to -10.0f has no effect 930 p.setStrokeMiter(-10.0f); 931 assertEquals(10.0f, p.getStrokeMiter(), 0.0f); 932 } 933 934 @Test 935 public void testClearShadowLayer() { 936 new Paint().clearShadowLayer(); 937 } 938 939 @Test 940 public void testSetUnderlineText() { 941 Paint p = new Paint(); 942 943 p.setUnderlineText(true); 944 assertTrue(p.isUnderlineText()); 945 946 p.setUnderlineText(false); 947 assertFalse(p.isUnderlineText()); 948 } 949 950 @Test 951 public void testSetDither() { 952 Paint p = new Paint(); 953 954 p.setDither(true); 955 assertTrue(p.isDither()); 956 957 p.setDither(false); 958 assertFalse(p.isDither()); 959 } 960 961 @Test 962 public void testDescent() { 963 Paint p = new Paint(); 964 965 for (Typeface typeface : TYPEFACES) { 966 p.setTypeface(typeface); 967 968 p.setTextSize(10); 969 float descent10 = p.descent(); 970 assertTrue(descent10 > 0); 971 972 p.setTextSize(20); 973 float descent20 = p.descent(); 974 assertTrue(descent20 > descent10); 975 } 976 } 977 978 @Test 979 public void testAccessFlags() { 980 Paint p = new Paint(); 981 982 p.setFlags(Paint.ANTI_ALIAS_FLAG); 983 assertEquals(Paint.ANTI_ALIAS_FLAG, p.getFlags()); 984 985 p.setFlags(Paint.DEV_KERN_TEXT_FLAG); 986 assertEquals(Paint.DEV_KERN_TEXT_FLAG, p.getFlags()); 987 } 988 989 @Test 990 public void testAccessStrokeWidth() { 991 Paint p = new Paint(); 992 993 p.setStrokeWidth(0.0f); 994 assertEquals(0.0f, p.getStrokeWidth(), 0.0f); 995 996 p.setStrokeWidth(10.0f); 997 assertEquals(10.0f, p.getStrokeWidth(), 0.0f); 998 999 // set value must greater or equal to 0, set -10.0f has no effect 1000 p.setStrokeWidth(-10.0f); 1001 assertEquals(10.0f, p.getStrokeWidth(), 0.0f); 1002 } 1003 1004 @Test 1005 public void testSetFontFeatureSettings() { 1006 Paint p = new Paint(); 1007 // Roboto font (system default) has "fi" ligature 1008 String text = "fi"; 1009 float[] widths = new float[text.length()]; 1010 p.getTextWidths(text, widths); 1011 assertTrue(widths[0] > 0.0f); 1012 assertEquals(0.0f, widths[1], 0.0f); 1013 1014 // Disable ligature using OpenType feature 1015 p.setFontFeatureSettings("'liga' off"); 1016 p.getTextWidths(text, widths); 1017 assertTrue(widths[0] > 0.0f); 1018 assertTrue(widths[1] > 0.0f); 1019 1020 // Re-enable ligature 1021 p.setFontFeatureSettings("'liga' on"); 1022 p.getTextWidths(text, widths); 1023 assertTrue(widths[0] > 0.0f); 1024 assertEquals(0.0f, widths[1], 0.0f); 1025 } 1026 1027 @Test 1028 public void testSetFontVariationSettings_defaultTypeface() { 1029 new Paint().setFontVariationSettings("'wght' 400"); 1030 } 1031 1032 @Test 1033 public void testSetGetFontVariationSettings() { 1034 final Paint defaultPaint = new Paint(); 1035 1036 Paint p = new Paint(); 1037 Context context = InstrumentationRegistry.getTargetContext(); 1038 Typeface typeface = Typeface.createFromAsset(context.getAssets(), 1039 "fonts/var_fonts/multiaxis.ttf"); 1040 p.setTypeface(typeface); 1041 1042 // multiaxis.ttf supports "wght", "PRIV", "PR12" axes. 1043 1044 // The default variation settings should be null. 1045 assertNull(p.getFontVariationSettings()); 1046 1047 final String[] nonEffectiveSettings = { 1048 "'slnt' 30", // unsupported tag 1049 "'BBBB' 1.0", // unsupported tag 1050 "'A ' 1.0", // unsupported tag 1051 "'PR0 ' 1.3", // unsupported tag 1052 "'WGHT' 0.7", // unsupported tag (case sensitive) 1053 "'BBBB' 1.0, 'CCCC' 2.0", // none of them are supported. 1054 }; 1055 1056 for (String notEffectiveSetting : nonEffectiveSettings) { 1057 if (!defaultPaint.setFontVariationSettings(notEffectiveSetting)) { 1058 // Test only when the system font don't support the above axes. OEMs may add 1059 // their fonts and these font may support above axes. 1060 assertFalse("Must return false for " + notEffectiveSetting, 1061 p.setFontVariationSettings(notEffectiveSetting)); 1062 assertNull("Must not change settings for " + notEffectiveSetting, 1063 p.getFontVariationSettings()); 1064 } 1065 } 1066 1067 String retainSettings = "'wght' 400"; 1068 assertTrue(p.setFontVariationSettings(retainSettings)); 1069 for (String notEffectiveSetting : nonEffectiveSettings) { 1070 assertFalse(p.setFontVariationSettings(notEffectiveSetting)); 1071 assertEquals("Must not change settings for " + notEffectiveSetting, 1072 retainSettings, p.getFontVariationSettings()); 1073 } 1074 1075 // At least one axis is supported, the settings should be applied. 1076 final String[] effectiveSettings = { 1077 "'wght' 300", // supported tag 1078 "'wght' 300, 'PRIV' 0.5", // both are supported 1079 "'PRIV' 1.0, 'BBBB' 0.4", // 'BBBB' is unsupported 1080 }; 1081 1082 for (String effectiveSetting : effectiveSettings) { 1083 assertTrue("Must return true for " + effectiveSetting, 1084 p.setFontVariationSettings(effectiveSetting)); 1085 assertEquals(effectiveSetting, p.getFontVariationSettings()); 1086 } 1087 1088 p.setFontVariationSettings(""); 1089 assertNull(p.getFontVariationSettings()); 1090 } 1091 1092 @Test 1093 public void testGetTextBounds() { 1094 Paint p = new Paint(); 1095 p.setTextSize(10); 1096 String text1 = "hello"; 1097 Rect bounds1 = new Rect(); 1098 Rect bounds2 = new Rect(); 1099 Rect bounds3 = new Rect(); 1100 p.getTextBounds(text1, 0, text1.length(), bounds1); 1101 char[] textChars1 = text1.toCharArray(); 1102 p.getTextBounds(textChars1, 0, textChars1.length, bounds2); 1103 CharSequence charSequence1 = new StringBuilder(text1); 1104 p.getTextBounds(charSequence1, 0, textChars1.length, bounds3); 1105 // verify that string and char array methods produce consistent results 1106 assertEquals(bounds1, bounds2); 1107 assertEquals(bounds2, bounds3); 1108 String text2 = "hello world"; 1109 1110 // verify substring produces consistent results 1111 p.getTextBounds(text2, 0, text1.length(), bounds2); 1112 assertEquals(bounds1, bounds2); 1113 1114 // longer string is expected to have same left edge but be wider 1115 p.getTextBounds(text2, 0, text2.length(), bounds2); 1116 assertEquals(bounds1.left, bounds2.left); 1117 assertTrue(bounds2.right > bounds1.right); 1118 1119 // bigger size implies bigger bounding rect 1120 p.setTextSize(20); 1121 p.getTextBounds(text1, 0, text1.length(), bounds2); 1122 assertTrue(bounds2.right > bounds1.right); 1123 assertTrue(bounds2.bottom - bounds2.top > bounds1.bottom - bounds1.top); 1124 } 1125 1126 @Test 1127 public void testReset() { 1128 Paint p = new Paint(); 1129 ColorFilter c = new ColorFilter(); 1130 MaskFilter m = new MaskFilter(); 1131 PathEffect e = new PathEffect(); 1132 Shader s = new Shader(); 1133 Typeface t = Typeface.DEFAULT; 1134 Xfermode x = new Xfermode(); 1135 1136 p.setColorFilter(c); 1137 p.setMaskFilter(m); 1138 p.setPathEffect(e); 1139 p.setShader(s); 1140 p.setTypeface(t); 1141 p.setXfermode(x); 1142 p.setFlags(Paint.ANTI_ALIAS_FLAG); 1143 assertEquals(c, p.getColorFilter()); 1144 assertEquals(m, p.getMaskFilter()); 1145 assertEquals(e, p.getPathEffect()); 1146 assertEquals(s, p.getShader()); 1147 assertEquals(t, p.getTypeface()); 1148 assertEquals(x, p.getXfermode()); 1149 assertEquals(Paint.ANTI_ALIAS_FLAG, p.getFlags()); 1150 1151 p.reset(); 1152 assertEquals(Paint.FILTER_BITMAP_FLAG | Paint.DEV_KERN_TEXT_FLAG 1153 | Paint.EMBEDDED_BITMAP_TEXT_FLAG, p.getFlags()); 1154 assertEquals(null, p.getColorFilter()); 1155 assertEquals(null, p.getMaskFilter()); 1156 assertEquals(null, p.getPathEffect()); 1157 assertEquals(null, p.getShader()); 1158 assertEquals(null, p.getTypeface()); 1159 assertEquals(null, p.getXfermode()); 1160 } 1161 1162 @Test 1163 public void testSetLinearText() { 1164 Paint p = new Paint(); 1165 1166 p.setLinearText(true); 1167 assertTrue(p.isLinearText()); 1168 1169 p.setLinearText(false); 1170 assertFalse(p.isLinearText()); 1171 } 1172 1173 @Test 1174 public void testGetFontMetricsInt1() { 1175 Paint p = new Paint(); 1176 Paint.FontMetricsInt fmi = new Paint.FontMetricsInt(); 1177 1178 for (Typeface typeface : TYPEFACES) { 1179 p.setTypeface(typeface); 1180 1181 p.setTextSize(10); 1182 p.getFontMetricsInt(fmi); 1183 assertEquals(Math.round(p.ascent()), fmi.ascent); 1184 assertEquals(Math.round(p.descent()), fmi.descent); 1185 1186 p.setTextSize(20); 1187 p.getFontMetricsInt(fmi); 1188 assertEquals(Math.round(p.ascent()), fmi.ascent); 1189 assertEquals(Math.round(p.descent()), fmi.descent); 1190 } 1191 } 1192 1193 @Test 1194 public void testGetFontMetricsInt2() { 1195 Paint p = new Paint(); 1196 Paint.FontMetricsInt fmi; 1197 1198 for (Typeface typeface : TYPEFACES) { 1199 p.setTypeface(typeface); 1200 1201 p.setTextSize(10); 1202 fmi = p.getFontMetricsInt(); 1203 assertEquals(Math.round(p.ascent()), fmi.ascent); 1204 assertEquals(Math.round(p.descent()), fmi.descent); 1205 1206 p.setTextSize(20); 1207 fmi = p.getFontMetricsInt(); 1208 assertEquals(Math.round(p.ascent()), fmi.ascent); 1209 assertEquals(Math.round(p.descent()), fmi.descent); 1210 } 1211 } 1212 1213 @Test 1214 public void testMeasureText() { 1215 String text = "HIJKLMN"; 1216 char[] textChars = text.toCharArray(); 1217 SpannedString textSpan = new SpannedString(text); 1218 1219 Paint p = new Paint(); 1220 1221 // We need to turn off kerning in order to get accurate comparisons 1222 p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG); 1223 1224 float[] widths = new float[text.length()]; 1225 for (int i = 0; i < widths.length; i++) { 1226 widths[i] = p.measureText(text, i, i + 1); 1227 } 1228 1229 float totalWidth = 0; 1230 for (int i = 0; i < widths.length; i++) { 1231 totalWidth += widths[i]; 1232 } 1233 1234 // Test measuring the widths of the entire text 1235 verifyMeasureText(text, textChars, textSpan, 0, 7, totalWidth); 1236 1237 // Test measuring a substring of the text 1238 verifyMeasureText(text, textChars, textSpan, 1, 3, widths[1] + widths[2]); 1239 1240 // Test measuring a substring of zero length. 1241 verifyMeasureText(text, textChars, textSpan, 3, 3, 0); 1242 1243 // Test measuring substrings from the front and back 1244 verifyMeasureText(text, textChars, textSpan, 0, 2, widths[0] + widths[1]); 1245 verifyMeasureText(text, textChars, textSpan, 4, 7, widths[4] + widths[5] + widths[6]); 1246 } 1247 1248 @Test 1249 public void testMeasureTextContext() { 1250 Paint p = new Paint(); 1251 // Arabic LAM, which is different width depending on context 1252 String shortString = "\u0644"; 1253 String longString = "\u0644\u0644\u0644"; 1254 char[] longChars = longString.toCharArray(); 1255 SpannedString longSpanned = new SpannedString(longString); 1256 float width = p.measureText(shortString); 1257 // Verify that measurement of substring is consistent no matter what surrounds it. 1258 verifyMeasureText(longString, longChars, longSpanned, 0, 1, width); 1259 verifyMeasureText(longString, longChars, longSpanned, 1, 2, width); 1260 verifyMeasureText(longString, longChars, longSpanned, 2, 3, width); 1261 } 1262 1263 @Test 1264 public void testMeasureTextWithLongText() { 1265 final int MAX_COUNT = 65535; 1266 char[] longText = new char[MAX_COUNT]; 1267 for (int n = 0; n < MAX_COUNT; n++) { 1268 longText[n] = 'm'; 1269 } 1270 1271 Paint p = new Paint(); 1272 float width = p.measureText(longText, 0, 1); 1273 assertEquals(true, width > 0); 1274 } 1275 1276 /** Tests that all four overloads of measureText are the same and match some value. */ 1277 private void verifyMeasureText(String text, char[] textChars, SpannedString textSpan, 1278 int start, int end, float expectedWidth) { 1279 Paint p = new Paint(); 1280 1281 // We need to turn off kerning in order to get accurate comparisons 1282 p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG); 1283 1284 int count = end - start; 1285 float[] widths = new float[] {-1, -1, -1, -1}; 1286 1287 String textSlice = text.substring(start, end); 1288 widths[0] = p.measureText(textSlice); 1289 widths[1] = p.measureText(textChars, start, count); 1290 widths[2] = p.measureText(textSpan, start, end); 1291 widths[3] = p.measureText(text, start, end); 1292 1293 // Check that the widths returned by the overloads are the same. 1294 assertEquals(widths[0], widths[1], 0.0f); 1295 assertEquals(widths[1], widths[2], 0.0f); 1296 assertEquals(widths[2], widths[3], 0.0f); 1297 assertEquals(widths[3], expectedWidth, 0.0f); 1298 } 1299 1300 @Test 1301 public void testGetTextPathCharArray() { 1302 Path path = new Path(); 1303 1304 assertTrue(path.isEmpty()); 1305 new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, 0, 7, 0, 0, path); 1306 assertFalse(path.isEmpty()); 1307 } 1308 1309 @Test(expected=RuntimeException.class) 1310 public void testGetTextPathCharArrayNegativeIndex() { 1311 new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, -2, 7, 0, 0, 1312 new Path()); 1313 } 1314 1315 @Test(expected=RuntimeException.class) 1316 public void testGetTextPathCharArrayNegativeCount() { 1317 new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, 0, -3, 0, 0, 1318 new Path()); 1319 } 1320 1321 @Test(expected=RuntimeException.class) 1322 public void testGetTextPathCharArrayCountTooHigh() { 1323 new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, 3, 7, 0, 0, 1324 new Path()); 1325 } 1326 1327 @Test 1328 public void testGetTextPathString() { 1329 Path path = new Path(); 1330 1331 assertTrue(path.isEmpty()); 1332 new Paint().getTextPath("HIJKLMN", 0, 7, 0, 0, path); 1333 assertFalse(path.isEmpty()); 1334 } 1335 1336 @Test(expected=RuntimeException.class) 1337 public void testGetTextPathStringNegativeIndex() { 1338 new Paint().getTextPath("HIJKLMN", -2, 7, 0, 0, new Path()); 1339 } 1340 1341 @Test(expected=RuntimeException.class) 1342 public void testGetTextPathStringNegativeCount() { 1343 new Paint().getTextPath("HIJKLMN", 0, -3, 0, 0, new Path()); 1344 } 1345 1346 @Test(expected=RuntimeException.class) 1347 public void testGetTextPathStringStartTooHigh() { 1348 new Paint().getTextPath("HIJKLMN", 7, 3, 0, 0, new Path()); 1349 } 1350 1351 @Test(expected=RuntimeException.class) 1352 public void testGetTextPathStringCountTooHigh() { 1353 new Paint().getTextPath("HIJKLMN", 3, 9, 0, 0, new Path()); 1354 } 1355 1356 @CddTest(requirement="3.8.13/C-1-2") 1357 @Test 1358 public void testHasGlyph() { 1359 Paint p = new Paint(); 1360 1361 // This method tests both the logic of hasGlyph and the sanity of fonts present 1362 // on the device. 1363 assertTrue(p.hasGlyph("A")); 1364 assertFalse(p.hasGlyph("\uFFFE")); // U+FFFE is guaranteed to be a noncharacter 1365 1366 // Roboto 2 (the default typeface) does have an "fi" glyph and is mandated by CDD 1367 assertTrue(p.hasGlyph("fi")); 1368 assertFalse(p.hasGlyph("ab")); // but it does not contain an "ab" glyph 1369 assertTrue(p.hasGlyph("\u02E5\u02E9")); // IPA tone mark ligature 1370 1371 // variation selectors 1372 assertFalse(p.hasGlyph("a\uFE0F")); 1373 assertFalse(p.hasGlyph("a\uDB40\uDDEF")); // UTF-16 encoding of U+E01EF 1374 assertFalse(p.hasGlyph("\u2229\uFE0F")); // base character is in mathematical symbol font 1375 // Note: U+FE0F is variation selection, unofficially reserved for emoji 1376 1377 // regional indicator symbols 1378 assertTrue(p.hasGlyph("\uD83C\uDDEF\uD83C\uDDF5")); // "JP" U+1F1EF U+1F1F5 1379 assertFalse(p.hasGlyph("\uD83C\uDDFF\uD83C\uDDFF")); // "ZZ" U+1F1FF U+1F1FF 1380 1381 // Mongolian, which is an optional font, but if present, should support FVS 1382 if (p.hasGlyph("\u182D")) { 1383 assertTrue(p.hasGlyph("\u182D\u180B")); 1384 } 1385 1386 // Emoji with variation selector support for both text and emoji presentation 1387 assertTrue(p.hasGlyph("\u231A\uFE0E")); // WATCH + VS15 1388 assertTrue(p.hasGlyph("\u231A\uFE0F")); // WATCH + VS16 1389 1390 // Unicode 7.0, 8.0, and 9.0 emoji should be supported. 1391 assertTrue(p.hasGlyph("\uD83D\uDD75")); // SLEUTH OR SPY is introduced in Unicode 7.0 1392 assertTrue(p.hasGlyph("\uD83C\uDF2E")); // TACO is introduced in Unicode 8.0 1393 assertTrue(p.hasGlyph("\uD83E\uDD33")); // SELFIE is introduced in Unicode 9.0 1394 1395 // We don't require gender-neutral emoji, but if present, results must be consistent 1396 // whether VS is present or not. 1397 assertTrue(p.hasGlyph("\uD83D\uDC69\u200D\u2695") == // WOMAN, ZWJ, STAFF OF AESCULAPIUS 1398 p.hasGlyph("\uD83D\uDC69\u200D\u2695\uFE0F")); // above + VS16 1399 } 1400 1401 @Test 1402 public void testGetRunAdvance() { 1403 Paint p = new Paint(); 1404 { 1405 // LTR 1406 String string = "abcdef"; 1407 { 1408 final float width = p.getRunAdvance(string, 0, string.length(), 0, 1409 string.length(), false, 0); 1410 assertEquals(0.0f, width, 0.0f); 1411 } 1412 { 1413 for (int i = 0; i < string.length(); i++) { 1414 final float width = p.getRunAdvance(string, i, i + 1, 0, string.length(), 1415 false, i); 1416 assertEquals(0.0f, width, 0.0f); 1417 } 1418 } 1419 { 1420 final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0, 1421 string.length(), false, string.length() / 2); 1422 final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0, 1423 string.length(), false, string.length()); 1424 assertTrue(widthToMid > 0.0f); 1425 assertTrue(widthToTail > widthToMid); 1426 } 1427 { 1428 final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0, 1429 string.length(), false, string.length()); 1430 final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0, 1431 string.length(), false, string.length()); 1432 assertTrue(widthFromHead > widthFromSecond); 1433 } 1434 { 1435 float width = 0.0f; 1436 for (int i = 0; i < string.length(); i++) { 1437 width += p.getRunAdvance(string, i, i + 1, 0, string.length(), false, i + 1); 1438 } 1439 final float totalWidth = p.getRunAdvance(string, 0, string.length(), 0, 1440 string.length(), false, string.length()); 1441 assertEquals(totalWidth, width, 1.0f); 1442 } 1443 } 1444 { 1445 // RTL 1446 String string = "\u0644\u063A\u0629 \u0639\u0631\u0628\u064A\u0629"; // Arabic 1447 { 1448 final float width = p.getRunAdvance(string, 0, string.length(), 0, 1449 string.length(), true, 0); 1450 assertEquals(0.0f, width, 0.0f); 1451 } 1452 { 1453 for (int i = 0; i < string.length(); i++) { 1454 final float width = p.getRunAdvance(string, i, i + 1, 0, string.length(), 1455 true, i); 1456 assertEquals(0.0f, width, 0.0f); 1457 } 1458 } 1459 { 1460 final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0, 1461 string.length(), true, string.length() / 2); 1462 final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0, 1463 string.length(), true, string.length()); 1464 assertTrue(widthToMid > 0.0f); 1465 assertTrue(widthToTail > widthToMid); 1466 } 1467 { 1468 final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0, 1469 string.length(), true, string.length()); 1470 final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0, 1471 string.length(), true, string.length()); 1472 assertTrue(widthFromHead > widthFromSecond); 1473 } 1474 } 1475 } 1476 1477 @Test(expected=IllegalArgumentException.class) 1478 public void testGetRunAdvanceNullCharSequence() { 1479 new Paint().getRunAdvance((CharSequence) null, 0, 0, 0, 0, false, 0); 1480 } 1481 1482 @Test(expected=IllegalArgumentException.class) 1483 public void testGetRunAdvanceNullCharArray() { 1484 new Paint().getRunAdvance((char[]) null, 0, 0, 0, 0, false, 0); 1485 } 1486 1487 @Test(expected=IndexOutOfBoundsException.class) 1488 public void testGetRunAdvanceTextLengthLessThenContextEnd() { 1489 final String string = "abcde"; 1490 1491 // text length < context end 1492 new Paint().getRunAdvance(string, 0, string.length(), 0, string.length() + 1, false, 1493 string.length()); 1494 } 1495 1496 @Test(expected=IndexOutOfBoundsException.class) 1497 public void testGetRunAdvanceContextEndLessThanEnd() { 1498 final String string = "abcde"; 1499 1500 // context end < end 1501 new Paint().getRunAdvance(string, 0, string.length(), 0, string.length() - 1, false, 0); 1502 } 1503 1504 @Test(expected=IndexOutOfBoundsException.class) 1505 public void testGetRunAdvanceEndLessThanOffset() { 1506 final String string = "abcde"; 1507 1508 // end < offset 1509 new Paint().getRunAdvance(string, 0, string.length() - 1, 0, string.length() - 1, false, 1510 string.length()); 1511 } 1512 1513 @Test(expected=IndexOutOfBoundsException.class) 1514 public void testGetRunAdvanceOffsetLessThanStart() { 1515 final String string = "abcde"; 1516 1517 // offset < start 1518 new Paint().getRunAdvance(string, 1, string.length(), 1, string.length(), false, 0); 1519 } 1520 1521 @Test(expected=IndexOutOfBoundsException.class) 1522 public void testGetRunAdvanceStartLessThanContextStart() { 1523 final String string = "abcde"; 1524 1525 // start < context start 1526 new Paint().getRunAdvance(string, 0, string.length(), 1, string.length(), false, 1); 1527 } 1528 1529 @Test(expected=IndexOutOfBoundsException.class) 1530 public void testGetRunAdvanceContextStartNegative() { 1531 final String string = "abcde"; 1532 1533 // context start < 0 1534 new Paint().getRunAdvance(string, 0, string.length(), -1, string.length(), false, 0); 1535 } 1536 1537 @Test 1538 public void testGetRunAdvance_nonzeroIndex() { 1539 Paint p = new Paint(); 1540 final String text = "Android powers hundreds of millions of mobile " + 1541 "devices in more than 190 countries around the world. It's" + 1542 "the largest installed base of any mobile platform and" + 1543 "growing fastevery day another million users power up their" + 1544 "Android devices for the first time and start looking for" + 1545 "apps, games, and other digital content."; 1546 // Test offset index does not affect width. 1547 final float widthAndroidFirst = p.getRunAdvance( 1548 text, 0, 7, 0, text.length(), false, 7); 1549 final float widthAndroidSecond = p.getRunAdvance( 1550 text, 215, 222, 0, text.length(), false, 222); 1551 assertTrue(Math.abs(widthAndroidFirst - widthAndroidSecond) < 1); 1552 } 1553 1554 @Test 1555 public void testGetRunAdvance_glyphDependingContext() { 1556 Paint p = new Paint(); 1557 // Test the context change the character shape. 1558 // First character should be isolated form because the context ends at index 1. 1559 final float isolatedFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 1, true, 1); 1560 // First character should be initial form because the context ends at index 2. 1561 final float initialFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 2, true, 1); 1562 assertTrue(isolatedFormWidth > initialFormWidth); 1563 } 1564 1565 @Test 1566 public void testGetRunAdvance_arabic() { 1567 Paint p = new Paint(); 1568 // Test total width is equals to sum of each character's width. 1569 // "What is Unicode?" in Arabic. 1570 final String text = 1571 "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" + 1572 "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" + 1573 "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" + 1574 "\u062F\u061F"; 1575 final float totalWidth = p.getRunAdvance( 1576 text, 0, text.length(), 0, text.length(), true, text.length()); 1577 float sumOfCharactersWidth = 0; 1578 for (int i = 0; i < text.length(); i++) { 1579 sumOfCharactersWidth += p.getRunAdvance( 1580 text, i, i + 1, 0, text.length(), true, i + 1); 1581 } 1582 assertTrue(Math.abs(totalWidth - sumOfCharactersWidth) < 1); 1583 } 1584 1585 @Test 1586 public void testGetOffsetForAdvance() { 1587 Paint p = new Paint(); 1588 { 1589 // LTR 1590 String string = "abcdef"; 1591 { 1592 for (int offset = 0; offset <= string.length(); ++offset) { 1593 final float widthToOffset = p.getRunAdvance(string, 0, 1594 string.length(), 0, string.length(), false, offset); 1595 final int restoredOffset = p.getOffsetForAdvance(string, 0, 1596 string.length(), 0, string.length(), false, widthToOffset); 1597 assertEquals(offset, restoredOffset); 1598 } 1599 } 1600 { 1601 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0, 1602 string.length(), false, -10.0f); 1603 assertEquals(0, offset); 1604 } 1605 { 1606 final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0, 1607 string.length(), true, string.length()); 1608 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0, 1609 string.length(), true, widthToEnd + 10.0f); 1610 assertEquals(string.length(), offset); 1611 } 1612 } 1613 { 1614 // RTL 1615 String string = "\u0639\u0631\u0628\u0649"; // Arabic 1616 { 1617 for (int offset = 0; offset <= string.length(); ++offset) { 1618 final float widthToOffset = p.getRunAdvance(string, 0, 1619 string.length(), 0, string.length(), true, offset); 1620 final int restoredOffset = p.getOffsetForAdvance(string, 0, 1621 string.length(), 0, string.length(), true, widthToOffset); 1622 assertEquals(offset, restoredOffset); 1623 } 1624 } 1625 { 1626 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0, 1627 string.length(), true, -10.0f); 1628 assertEquals(0, offset); 1629 } 1630 { 1631 final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0, 1632 string.length(), true, string.length()); 1633 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0, 1634 string.length(), true, widthToEnd + 10.0f); 1635 assertEquals(string.length(), offset); 1636 } 1637 } 1638 } 1639 1640 @Test(expected=IllegalArgumentException.class) 1641 public void testGetOffsetForAdvanceNullCharSequence() { 1642 new Paint().getOffsetForAdvance((CharSequence) null, 0, 0, 0, 0, false, 0.0f); 1643 } 1644 1645 @Test(expected=IllegalArgumentException.class) 1646 public void testGetOffsetForAdvanceNullCharArray() { 1647 new Paint().getOffsetForAdvance((char[]) null, 0, 0, 0, 0, false, 0.0f); 1648 } 1649 1650 @Test(expected=IndexOutOfBoundsException.class) 1651 public void testGetOffsetForAdvanceContextStartNegative() { 1652 final String string = "abcde"; 1653 1654 // context start < 0 1655 new Paint().getOffsetForAdvance(string, -1, string.length(), 0, string.length(), false, 1656 0.0f); 1657 } 1658 1659 @Test(expected=IndexOutOfBoundsException.class) 1660 public void testGetOffsetForAdvanceStartLessThanContextStart() { 1661 final String string = "abcde"; 1662 1663 // start < context start 1664 new Paint().getOffsetForAdvance(string, 0, string.length(), 1, string.length(), false, 1665 0.0f); 1666 } 1667 1668 @Test(expected=IndexOutOfBoundsException.class) 1669 public void testGetOffsetForAdvanceEndLessThanStart() { 1670 final String string = "abcde"; 1671 1672 // end < start 1673 new Paint().getOffsetForAdvance(string, 1, 0, 0, 0, false, 0); 1674 } 1675 1676 @Test(expected=IndexOutOfBoundsException.class) 1677 public void testGetOffsetForAdvanceContextEndLessThanEnd() { 1678 final String string = "abcde"; 1679 1680 // context end < end 1681 new Paint().getOffsetForAdvance(string, 0, string.length(), 0, string.length() - 1, false, 1682 0.0f); 1683 } 1684 1685 @Test(expected=IndexOutOfBoundsException.class) 1686 public void testGetOffsetForAdvanceTextLengthLessThanContextEnd() { 1687 final String string = "abcde"; 1688 1689 // text length < context end 1690 new Paint().getOffsetForAdvance(string, 0, string.length(), 0, string.length() + 1, false, 1691 0.0f); 1692 } 1693 1694 @Test 1695 public void testGetOffsetForAdvance_graphemeCluster() { 1696 Paint p = new Paint(); 1697 { 1698 String string = "\uD83C\uDF37"; // U+1F337: TULIP 1699 { 1700 final float widthToOffset = p.getRunAdvance(string, 0, 1701 string.length(), 0, string.length(), false, 1); 1702 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0, 1703 string.length(), false, widthToOffset); 1704 assertFalse(1 == offset); 1705 assertTrue(0 == offset || string.length() == offset); 1706 } 1707 } 1708 { 1709 String string = "\uD83C\uDDFA\uD83C\uDDF8"; // US flag 1710 { 1711 final float widthToOffset = p.getRunAdvance(string, 0, 1712 string.length(), 0, string.length(), false, 2); 1713 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0, 1714 string.length(), false, widthToOffset); 1715 assertFalse(2 == offset); 1716 assertTrue(0 == offset || string.length() == offset); 1717 } 1718 { 1719 final float widthToOffset = p.getRunAdvance(string, 0, 2, 0, 2, false, 2); 1720 final int offset = p.getOffsetForAdvance(string, 0, 2, 1721 0, 2, false, widthToOffset); 1722 assertEquals(2, offset); 1723 } 1724 } 1725 { 1726 // HANGUL CHOSEONG KIYEOK, HANGUL JUNGSEONG A, HANDUL JONGSEONG KIYEOK 1727 String string = "\u1100\u1161\u11A8"; 1728 { 1729 for (int offset = 0; offset <= string.length(); ++offset) { 1730 final float widthToOffset = p.getRunAdvance(string, 0, 1731 string.length(), 0, string.length(), false, offset); 1732 final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(), 1733 0, string.length(), false, widthToOffset); 1734 assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance); 1735 } 1736 for (int offset = 0; offset <= string.length(); ++offset) { 1737 final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset, 1738 false, offset); 1739 final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(), 1740 0, string.length(), false, widthToOffset); 1741 assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance); 1742 } 1743 for (int offset = 0; offset <= string.length(); ++offset) { 1744 final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset, 1745 false, offset); 1746 final int offsetForAdvance = p.getOffsetForAdvance(string, 0, offset, 0, 1747 offset, false, widthToOffset); 1748 assertEquals(offset, offsetForAdvance); 1749 } 1750 } 1751 } 1752 } 1753 1754 @Test 1755 public void testElegantText() { 1756 final Paint p = new Paint(); 1757 p.setTextSize(10); 1758 assertFalse(p.isElegantTextHeight()); 1759 final float nonElegantTop = p.getFontMetrics().top; 1760 final float nonElegantBottom = p.getFontMetrics().bottom; 1761 1762 p.setElegantTextHeight(true); 1763 assertTrue(p.isElegantTextHeight()); 1764 final float elegantTop = p.getFontMetrics().top; 1765 final float elegantBottom = p.getFontMetrics().bottom; 1766 1767 assertTrue(elegantTop < nonElegantTop); 1768 assertTrue(elegantBottom > nonElegantBottom); 1769 1770 p.setElegantTextHeight(false); 1771 assertFalse(p.isElegantTextHeight()); 1772 } 1773 1774 @Test 1775 public void testEqualsForTextMeasurment() { 1776 Paint p1 = new Paint(); 1777 Paint p2 = new Paint(); 1778 1779 assertTrue(p1.equalsForTextMeasurement(p2)); 1780 } 1781 1782 @Test 1783 public void testEqualsForTextMeasurment_textSize() { 1784 Paint p1 = new Paint(); 1785 Paint p2 = new Paint(); 1786 1787 p1.setTextSize(p2.getTextSize() * 2.0f); 1788 assertFalse(p1.equalsForTextMeasurement(p2)); 1789 p1.setTextSize(p2.getTextSize()); 1790 assertTrue(p1.equalsForTextMeasurement(p2)); 1791 } 1792 1793 @Test 1794 public void testEqualsForTextMeasurment_textSkew() { 1795 Paint p1 = new Paint(); 1796 Paint p2 = new Paint(); 1797 1798 p1.setTextSkewX(p2.getTextSkewX() + 2.0f); 1799 assertFalse(p1.equalsForTextMeasurement(p2)); 1800 p1.setTextSkewX(p2.getTextSkewX()); 1801 assertTrue(p1.equalsForTextMeasurement(p2)); 1802 } 1803 1804 @Test 1805 public void testEqualsForTextMeasurment_textScale() { 1806 Paint p1 = new Paint(); 1807 Paint p2 = new Paint(); 1808 1809 p1.setTextScaleX(p2.getTextScaleX() * 2.0f); 1810 assertFalse(p1.equalsForTextMeasurement(p2)); 1811 p1.setTextScaleX(p2.getTextScaleX()); 1812 assertTrue(p1.equalsForTextMeasurement(p2)); 1813 } 1814 1815 @Test 1816 public void testEqualsForTextMeasurment_letterSpacing() { 1817 Paint p1 = new Paint(); 1818 Paint p2 = new Paint(); 1819 1820 p1.setLetterSpacing(p2.getLetterSpacing() + 2.0f); 1821 assertFalse(p1.equalsForTextMeasurement(p2)); 1822 p1.setLetterSpacing(p2.getLetterSpacing()); 1823 assertTrue(p1.equalsForTextMeasurement(p2)); 1824 } 1825 1826 @Test 1827 public void testEqualsForTextMeasurment_localeList() { 1828 Paint p1 = new Paint(); 1829 Paint p2 = new Paint(); 1830 1831 LocaleList enUS = LocaleList.forLanguageTags("en-US"); 1832 LocaleList jaJP = LocaleList.forLanguageTags("ja-JP"); 1833 LocaleList differentLocale = p2.getTextLocales().equals(enUS) ? jaJP : enUS; 1834 p1.setTextLocales(differentLocale); 1835 assertFalse(p1.equalsForTextMeasurement(p2)); 1836 p1.setTextLocales(p2.getTextLocales()); 1837 assertTrue(p1.equalsForTextMeasurement(p2)); 1838 } 1839 1840 @Test 1841 public void testEqualsForTextMeasurment_typeface() { 1842 Paint p1 = new Paint(); 1843 Paint p2 = new Paint(); 1844 1845 p1.setTypeface(Typeface.DEFAULT); 1846 p2.setTypeface(Typeface.SERIF); 1847 assertFalse(p1.equalsForTextMeasurement(p2)); 1848 p1.setTypeface(p2.getTypeface()); 1849 assertTrue(p1.equalsForTextMeasurement(p2)); 1850 } 1851 1852 @Test 1853 public void testWordSpacing() { 1854 Paint p = new Paint(); 1855 assertEquals(0.0f, p.getWordSpacing(), 0.0f); // The default value is 0. 1856 p.setWordSpacing(10.0f); 1857 assertEquals(10.0f, p.getWordSpacing(), 0.0f); 1858 p.setWordSpacing(20.0f); 1859 assertEquals(20.0f, p.getWordSpacing(), 0.0f); 1860 } 1861 1862 @Test 1863 public void testStrikeThruPosition_notCrashes() { 1864 // We can't expect any values of strike-through position in CTS. 1865 // Just make sure calling that method doesn't crash the app. 1866 new Paint().getStrikeThruPosition(); 1867 } 1868 1869 @Test 1870 public void testStrikeThruThickness_notCrashes() { 1871 // We can't expect any values of strike-through thickness in CTS. 1872 // Just make sure calling that method doesn't crash the app. 1873 new Paint().getStrikeThruThickness(); 1874 } 1875 1876 @Test 1877 public void testUnderlinePosition_notCrashes() { 1878 // We can't expect any values of underline position in CTS. 1879 // Just make sure calling that method doesn't crash the app. 1880 new Paint().getUnderlinePosition(); 1881 } 1882 1883 @Test 1884 public void testUnderlineThickness_notCrashes() { 1885 // We can't expect any values of underline thickness in CTS. 1886 // Just make sure calling that method doesn't crash the app. 1887 new Paint().getUnderlineThickness(); 1888 } 1889 1890 @Test 1891 public void testSetGetHyphenEdit() { 1892 Paint paint = new Paint(); 1893 1894 // By default, no hyphen edit is specified. 1895 assertEquals(Paint.START_HYPHEN_EDIT_NO_EDIT, paint.getStartHyphenEdit()); 1896 assertEquals(Paint.END_HYPHEN_EDIT_NO_EDIT, paint.getEndHyphenEdit()); 1897 1898 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN); 1899 assertEquals(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN, paint.getStartHyphenEdit()); 1900 assertEquals(Paint.END_HYPHEN_EDIT_NO_EDIT, paint.getEndHyphenEdit()); 1901 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1902 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); 1903 1904 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); 1905 assertEquals(Paint.START_HYPHEN_EDIT_NO_EDIT, paint.getStartHyphenEdit()); 1906 assertEquals(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN, paint.getEndHyphenEdit()); 1907 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1908 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); 1909 1910 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN); 1911 assertEquals(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN, paint.getStartHyphenEdit()); 1912 assertEquals(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN, paint.getEndHyphenEdit()); 1913 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1914 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); 1915 } 1916 1917 @Test 1918 public void testHyphenEdit() { 1919 final Paint paint = new Paint(); 1920 final Context context = InstrumentationRegistry.getTargetContext(); 1921 // The hyphenation.ttf font supports following characters 1922 // - U+0061..U+007A (a..z): The glyph has 1em width. 1923 // - U+2010 (HYPHEN): The glyph has 2em width. 1924 // - U+058A (ARMENIAN HYPHEN): The glyph has 3em width. 1925 // - U+05BE (MAQAF): The glyph has 4em width. 1926 // - U+1400 (UCAS HYPHEN): The glyph has 5em width. 1927 paint.setTypeface(Typeface.createFromAsset(context.getAssets(), 1928 "fonts/layout/hyphenation.ttf")); 1929 paint.setTextSize(10.0f); // Make 1em = 10px 1930 1931 assertEquals(30.0f, paint.measureText("abc", 0, 3), 0.0f); 1932 1933 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1934 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); 1935 assertEquals(50.0f, paint.measureText("abc", 0, 3), 0.0f); // "abc-" in visual. 1936 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1937 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1938 1939 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN); 1940 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); 1941 assertEquals(70.0f, paint.measureText("abc", 0, 3), 0.0f); // "-abc-" in visual. 1942 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1943 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1944 1945 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1946 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN); 1947 assertEquals(60.0f, paint.measureText("abc", 0, 3), 0.0f); // "abcU+058A" in visual. 1948 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1949 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1950 1951 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1952 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_MAQAF); 1953 assertEquals(70.0f, paint.measureText("abc", 0, 3), 0.0f); // "abcU+05BE" in visual. 1954 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1955 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1956 1957 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1958 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN); 1959 assertEquals(80.0f, paint.measureText("abc", 0, 3), 0.0f); // "abcU+1400" in visual. 1960 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1961 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1962 1963 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1964 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN); 1965 // "abcU+200D-" in visual. Note that ZWJ is zero width. 1966 assertEquals(50.0f, paint.measureText("abc", 0, 3), 0.0f); 1967 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1968 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1969 1970 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1971 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN); 1972 assertEquals(40.0f, paint.measureText("abc", 0, 3), 0.0f); // "ab-" in visual. 1973 paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_NO_EDIT); 1974 paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_NO_EDIT); 1975 } 1976 1977 @Test 1978 public void testGetTextRunAdvances() { 1979 final Paint paint = new Paint(); 1980 final Context context = InstrumentationRegistry.getTargetContext(); 1981 paint.setTypeface(Typeface.createFromAsset(context.getAssets(), 1982 "fonts/layout/textrunadvances.ttf")); 1983 // The textrunadvances.ttf font supports following characters 1984 // - U+0061 (a): The glyph has 3em width. 1985 // - U+0062..U+0065 (b..e): The glyph has 1em width. 1986 // - U+1F600 (GRINNING FACE): The glyph has 3em width. 1987 paint.setTextSize(10.0f); // Make 1em = 10px 1988 1989 final char[] chars = { 'a', 'b', 'a', 'b' }; 1990 final float[] buffer = new float[32]; 1991 1992 assertEquals(80.0f, 1993 paint.getTextRunAdvances(chars, 0, 4, 0, 4, false /* isRtl */, buffer, 0), 0.0f); 1994 assertEquals(30.0f, buffer[0], 0.0f); 1995 assertEquals(10.0f, buffer[1], 0.0f); 1996 assertEquals(30.0f, buffer[2], 0.0f); 1997 assertEquals(10.0f, buffer[3], 0.0f); 1998 1999 // Output offset test 2000 assertEquals(40.0f, 2001 paint.getTextRunAdvances(chars, 1, 2, 1, 2, false /* isRtl */, buffer, 5), 0.0f); 2002 assertEquals(10.0f, buffer[5], 0.0f); 2003 assertEquals(30.0f, buffer[6], 0.0f); 2004 2005 // Surrogate pairs 2006 final char[] chars2 = Character.toChars(0x1F600); 2007 assertEquals(30.0f, 2008 paint.getTextRunAdvances(chars2, 0, 2, 0, 2, false /* isRtl */, buffer, 0), 0.0f); 2009 assertEquals(30.0f, buffer[0], 0.0f); 2010 assertEquals(0.0f, buffer[1], 0.0f); 2011 } 2012 2013 private int getTextRunCursor(String text, int offset, int cursorOpt) { 2014 final int contextStart = 0; 2015 final int contextEnd = text.length(); 2016 final int contextCount = text.length(); 2017 Paint p = new Paint(); 2018 int result = p.getTextRunCursor(new StringBuilder(text), // as a CharSequence 2019 contextStart, contextEnd, false /* isRtl */, offset, cursorOpt); 2020 assertEquals(result, p.getTextRunCursor(text.toCharArray(), 2021 contextStart, contextCount, false /* isRtl */, offset, cursorOpt)); 2022 assertEquals(result, p.getTextRunCursor(new StringBuilder(text), // as a CharSequence 2023 contextStart, contextCount, true /* isRtl */, offset, cursorOpt)); 2024 assertEquals(result, p.getTextRunCursor(text.toCharArray(), 2025 contextStart, contextCount, true, offset, cursorOpt)); 2026 return result; 2027 } 2028 2029 @Test 2030 public void testGetRunCursor_CURSOR_AFTER() { 2031 assertEquals(1, getTextRunCursor("abc", 0, CURSOR_AFTER)); 2032 assertEquals(2, getTextRunCursor("abc", 1, CURSOR_AFTER)); 2033 assertEquals(3, getTextRunCursor("abc", 2, CURSOR_AFTER)); 2034 assertEquals(3, getTextRunCursor("abc", 3, CURSOR_AFTER)); 2035 2036 // Surrogate pairs 2037 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 0, CURSOR_AFTER)); 2038 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 1, CURSOR_AFTER)); 2039 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 2, CURSOR_AFTER)); 2040 assertEquals(4, getTextRunCursor("a\uD83D\uDE00c", 3, CURSOR_AFTER)); 2041 assertEquals(4, getTextRunCursor("a\uD83D\uDE00c", 4, CURSOR_AFTER)); 2042 2043 // Combining marks 2044 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 0, CURSOR_AFTER)); 2045 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 1, CURSOR_AFTER)); 2046 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 2, CURSOR_AFTER)); 2047 assertEquals(4, getTextRunCursor("a\u0061\u0302c", 3, CURSOR_AFTER)); 2048 assertEquals(4, getTextRunCursor("a\u0061\u0302c", 4, CURSOR_AFTER)); 2049 } 2050 2051 @Test 2052 public void testGetRunCursor_CURSOR_AT() { 2053 assertEquals(0, getTextRunCursor("abc", 0, CURSOR_AT)); 2054 assertEquals(1, getTextRunCursor("abc", 1, CURSOR_AT)); 2055 assertEquals(2, getTextRunCursor("abc", 2, CURSOR_AT)); 2056 assertEquals(3, getTextRunCursor("abc", 3, CURSOR_AT)); 2057 2058 // Surrogate pairs 2059 assertEquals(0, getTextRunCursor("a\uD83D\uDE00c", 0, CURSOR_AT)); 2060 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 1, CURSOR_AT)); 2061 assertEquals(-1, getTextRunCursor("a\uD83D\uDE00c", 2, CURSOR_AT)); 2062 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 3, CURSOR_AT)); 2063 assertEquals(4, getTextRunCursor("a\uD83D\uDE00c", 4, CURSOR_AT)); 2064 2065 // Combining marks 2066 assertEquals(0, getTextRunCursor("a\u0061\u0302c", 0, CURSOR_AT)); 2067 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 1, CURSOR_AT)); 2068 assertEquals(-1, getTextRunCursor("a\u0061\u0302c", 2, CURSOR_AT)); 2069 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 3, CURSOR_AT)); 2070 assertEquals(4, getTextRunCursor("a\u0061\u0302c", 4, CURSOR_AT)); 2071 } 2072 2073 @Test 2074 public void testGetRunCursor_CURSOR_AT_OR_AFTER() { 2075 assertEquals(0, getTextRunCursor("abc", 0, CURSOR_AT_OR_AFTER)); 2076 assertEquals(1, getTextRunCursor("abc", 1, CURSOR_AT_OR_AFTER)); 2077 assertEquals(2, getTextRunCursor("abc", 2, CURSOR_AT_OR_AFTER)); 2078 assertEquals(3, getTextRunCursor("abc", 3, CURSOR_AT_OR_AFTER)); 2079 2080 // Surrogate pairs 2081 assertEquals(0, getTextRunCursor("a\uD83D\uDE00c", 0, CURSOR_AT_OR_AFTER)); 2082 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 1, CURSOR_AT_OR_AFTER)); 2083 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 2, CURSOR_AT_OR_AFTER)); 2084 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 3, CURSOR_AT_OR_AFTER)); 2085 assertEquals(4, getTextRunCursor("a\uD83D\uDE00c", 4, CURSOR_AT_OR_AFTER)); 2086 2087 // Combining marks 2088 assertEquals(0, getTextRunCursor("a\u0061\u0302c", 0, CURSOR_AT_OR_AFTER)); 2089 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 1, CURSOR_AT_OR_AFTER)); 2090 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 2, CURSOR_AT_OR_AFTER)); 2091 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 3, CURSOR_AT_OR_AFTER)); 2092 assertEquals(4, getTextRunCursor("a\u0061\u0302c", 4, CURSOR_AT_OR_AFTER)); 2093 } 2094 2095 @Test 2096 public void testGetRunCursor_CURSOR_AT_OR_BEFORE() { 2097 assertEquals(0, getTextRunCursor("abc", 0, CURSOR_AT_OR_BEFORE)); 2098 assertEquals(1, getTextRunCursor("abc", 1, CURSOR_AT_OR_BEFORE)); 2099 assertEquals(2, getTextRunCursor("abc", 2, CURSOR_AT_OR_BEFORE)); 2100 assertEquals(3, getTextRunCursor("abc", 3, CURSOR_AT_OR_BEFORE)); 2101 2102 // Surrogate pairs 2103 assertEquals(0, getTextRunCursor("a\uD83D\uDE00c", 0, CURSOR_AT_OR_BEFORE)); 2104 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 1, CURSOR_AT_OR_BEFORE)); 2105 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 2, CURSOR_AT_OR_BEFORE)); 2106 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 3, CURSOR_AT_OR_BEFORE)); 2107 assertEquals(4, getTextRunCursor("a\uD83D\uDE00c", 4, CURSOR_AT_OR_BEFORE)); 2108 2109 // Combining marks 2110 assertEquals(0, getTextRunCursor("a\u0061\u0302c", 0, CURSOR_AT_OR_BEFORE)); 2111 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 1, CURSOR_AT_OR_BEFORE)); 2112 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 2, CURSOR_AT_OR_BEFORE)); 2113 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 3, CURSOR_AT_OR_BEFORE)); 2114 assertEquals(4, getTextRunCursor("a\u0061\u0302c", 4, CURSOR_AT_OR_BEFORE)); 2115 } 2116 2117 @Test 2118 public void testGetRunCursor_CURSOR_BEFORE() { 2119 assertEquals(0, getTextRunCursor("abc", 0, CURSOR_BEFORE)); 2120 assertEquals(0, getTextRunCursor("abc", 1, CURSOR_BEFORE)); 2121 assertEquals(1, getTextRunCursor("abc", 2, CURSOR_BEFORE)); 2122 assertEquals(2, getTextRunCursor("abc", 3, CURSOR_BEFORE)); 2123 2124 // Surrogate pairs 2125 assertEquals(0, getTextRunCursor("a\uD83D\uDE00c", 0, CURSOR_BEFORE)); 2126 assertEquals(0, getTextRunCursor("a\uD83D\uDE00c", 1, CURSOR_BEFORE)); 2127 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 2, CURSOR_BEFORE)); 2128 assertEquals(1, getTextRunCursor("a\uD83D\uDE00c", 3, CURSOR_BEFORE)); 2129 assertEquals(3, getTextRunCursor("a\uD83D\uDE00c", 4, CURSOR_BEFORE)); 2130 2131 // Combining marks 2132 assertEquals(0, getTextRunCursor("a\u0061\u0302c", 0, CURSOR_BEFORE)); 2133 assertEquals(0, getTextRunCursor("a\u0061\u0302c", 1, CURSOR_BEFORE)); 2134 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 2, CURSOR_BEFORE)); 2135 assertEquals(1, getTextRunCursor("a\u0061\u0302c", 3, CURSOR_BEFORE)); 2136 assertEquals(3, getTextRunCursor("a\u0061\u0302c", 4, CURSOR_BEFORE)); 2137 } 2138 2139 @Test 2140 public void testGetBlendModeFromPorterDuffMode() { 2141 Paint p = new Paint(); 2142 PorterDuff.Mode[] porterDuffModes = PorterDuff.Mode.values(); 2143 for (PorterDuff.Mode mode : porterDuffModes) { 2144 p.setXfermode(new PorterDuffXfermode(mode)); 2145 assertEquals(getBlendModeFromPorterDuffMode(mode), p.getBlendMode()); 2146 } 2147 2148 } 2149 2150 private BlendMode getBlendModeFromPorterDuffMode(PorterDuff.Mode mode) { 2151 switch (mode) { 2152 case CLEAR: return BlendMode.CLEAR; 2153 case SRC: return BlendMode.SRC; 2154 case DST: return BlendMode.DST; 2155 case SRC_OVER: return BlendMode.SRC_OVER; 2156 case DST_OVER: return BlendMode.DST_OVER; 2157 case SRC_IN: return BlendMode.SRC_IN; 2158 case DST_IN: return BlendMode.DST_IN; 2159 case SRC_OUT: return BlendMode.SRC_OUT; 2160 case DST_OUT: return BlendMode.DST_OUT; 2161 case SRC_ATOP: return BlendMode.SRC_ATOP; 2162 case DST_ATOP: return BlendMode.DST_ATOP; 2163 case XOR: return BlendMode.XOR; 2164 case DARKEN: return BlendMode.DARKEN; 2165 case LIGHTEN: return BlendMode.LIGHTEN; 2166 // The odd one out, see b/73224934. PorterDuff.Mode.MULTIPLY was improperly mapped 2167 // to Skia's modulate 2168 case MULTIPLY: return BlendMode.MODULATE; 2169 case SCREEN: return BlendMode.SCREEN; 2170 case ADD: return BlendMode.PLUS; 2171 case OVERLAY: return BlendMode.OVERLAY; 2172 default: throw new IllegalArgumentException("Unknown PorterDuffmode: " + mode); 2173 } 2174 } 2175 2176 private void testColorLongs(String methodName, BiConsumer<Paint, Long> setColor, 2177 Function<Paint, Integer> getColor, 2178 Function<Paint, Long> getColorLong) { 2179 // Pack SRGB colors into ColorLongs 2180 for (int color : new int[]{ Color.RED, Color.BLUE, Color.GREEN, Color.BLACK, 2181 Color.WHITE, Color.TRANSPARENT }) { 2182 final Paint p = new Paint(); 2183 final long longColor = Color.pack(color); 2184 setColor.accept(p, longColor); 2185 2186 assertEquals(color, getColor.apply(p).intValue()); 2187 assertEquals(longColor, getColorLong.apply(p).longValue()); 2188 } 2189 2190 // Arbitrary colors in various ColorSpaces 2191 for (int srgbColor : new int[]{ Color.argb(1.0f, .5f, .5f, .5f), 2192 Color.argb(1.0f, .3f, .6f, .9f), 2193 Color.argb(0.5f, .2f, .8f, .7f) }) { 2194 for (ColorSpace.Named e : ColorSpace.Named.values()) { 2195 ColorSpace cs = ColorSpace.get(e); 2196 if (cs.getModel() != ColorSpace.Model.RGB) { 2197 continue; 2198 } 2199 if (((ColorSpace.Rgb) cs).getTransferParameters() == null) { 2200 continue; 2201 } 2202 2203 final long longColor = Color.convert(srgbColor, cs); 2204 Paint p = new Paint(); 2205 setColor.accept(p, longColor); 2206 assertEquals(longColor, getColorLong.apply(p).longValue()); 2207 2208 // These tolerances were chosen by trial and error. It is expected that 2209 // some conversions do not round-trip perfectly. 2210 int tolerance = 0; 2211 if (cs.equals(ColorSpace.get(ColorSpace.Named.SMPTE_C))) { 2212 tolerance = 2; 2213 } 2214 int color = getColor.apply(p); 2215 ColorUtils.verifyColor("Paint#" + methodName + " mismatch for " + cs, srgbColor, 2216 color, tolerance); 2217 } 2218 } 2219 } 2220 2221 @Test 2222 public void testSetColorLong() { 2223 testColorLongs("setColor", (p, c) -> p.setColor(c), (p) -> p.getColor(), 2224 (p) -> p.getColorLong()); 2225 } 2226 2227 @Test(expected = IllegalArgumentException.class) 2228 public void testSetColorXYZ() { 2229 Paint p = new Paint(); 2230 p.setColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ))); 2231 } 2232 2233 @Test(expected = IllegalArgumentException.class) 2234 public void testSetColorLAB() { 2235 Paint p = new Paint(); 2236 p.setColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB))); 2237 } 2238 2239 @Test(expected = IllegalArgumentException.class) 2240 public void testSetColorUnknown() { 2241 Paint p = new Paint(); 2242 p.setColor(-1L); 2243 } 2244 2245 @Test 2246 public void testSetShadowLayerLong() { 2247 testColorLongs("setShadowLayer", (p, c) -> p.setShadowLayer(10.0f, 1.0f, 1.0f, c), 2248 (p) -> p.getShadowLayerColor(), (p) -> p.getShadowLayerColorLong()); 2249 } 2250 2251 @Test(expected = IllegalArgumentException.class) 2252 public void testSetShadowLayerXYZ() { 2253 Paint p = new Paint(); 2254 p.setShadowLayer(10.0f, 1.0f, 1.0f, 2255 Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ))); 2256 } 2257 2258 @Test(expected = IllegalArgumentException.class) 2259 public void testSetShadowLayerLAB() { 2260 Paint p = new Paint(); 2261 p.setShadowLayer(10.0f, 1.0f, 1.0f, 2262 Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB))); 2263 } 2264 2265 @Test(expected = IllegalArgumentException.class) 2266 public void testSetShadowLayerUnknown() { 2267 Paint p = new Paint(); 2268 p.setShadowLayer(10.0f, 1.0f, 1.0f, -1L); 2269 } 2270 } 2271