Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2015 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;
     18 
     19 import android.graphics.Paint;
     20 import android.test.InstrumentationTestCase;
     21 import android.test.suitebuilder.annotation.SmallTest;
     22 
     23 import java.util.Arrays;
     24 import java.util.HashSet;
     25 
     26 /**
     27  * PaintTest tests {@link Paint}.
     28  */
     29 public class PaintTest extends InstrumentationTestCase {
     30     private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
     31 
     32     static void assertEquals(String message, float[] expected, float[] actual) {
     33         if (expected.length != actual.length) {
     34             fail(message + " expected array length:<" + expected.length + "> but was:<"
     35                     + actual.length + ">");
     36         }
     37         for (int i = 0; i < expected.length; ++i) {
     38             if (expected[i] != actual[i]) {
     39                 fail(message + " expected array element[" +i + "]:<" + expected[i] + ">but was:<"
     40                         + actual[i] + ">");
     41             }
     42         }
     43     }
     44 
     45     static class HintingTestCase {
     46         public final String mText;
     47         public final float mTextSize;
     48         public final float[] mWidthWithoutHinting;
     49         public final float[] mWidthWithHinting;
     50 
     51         public HintingTestCase(String text, float textSize, float[] widthWithoutHinting,
     52                                float[] widthWithHinting) {
     53             mText = text;
     54             mTextSize = textSize;
     55             mWidthWithoutHinting = widthWithoutHinting;
     56             mWidthWithHinting = widthWithHinting;
     57         }
     58     }
     59 
     60     // Following test cases are only valid for HintedAdvanceWidthTest-Regular.ttf in assets/fonts.
     61     HintingTestCase[] HINTING_TESTCASES = {
     62         new HintingTestCase("H", 11f, new float[] { 7f }, new float[] { 13f }),
     63         new HintingTestCase("O", 11f, new float[] { 7f }, new float[] { 13f }),
     64 
     65         new HintingTestCase("H", 13f, new float[] { 8f }, new float[] { 14f }),
     66         new HintingTestCase("O", 13f, new float[] { 9f }, new float[] { 15f }),
     67 
     68         new HintingTestCase("HO", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
     69         new HintingTestCase("OH", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
     70 
     71         new HintingTestCase("HO", 13f, new float[] { 8f, 9f }, new float[] { 14f, 15f }),
     72         new HintingTestCase("OH", 13f, new float[] { 9f, 8f }, new float[] { 15f, 14f }),
     73     };
     74 
     75     @SmallTest
     76     public void testHintingWidth() {
     77         final Typeface fontTypeface = Typeface.createFromAsset(
     78                 getInstrumentation().getContext().getAssets(), FONT_PATH);
     79         Paint paint = new Paint();
     80         paint.setTypeface(fontTypeface);
     81 
     82         for (int i = 0; i < HINTING_TESTCASES.length; ++i) {
     83             HintingTestCase testCase = HINTING_TESTCASES[i];
     84 
     85             paint.setTextSize(testCase.mTextSize);
     86 
     87             float[] widths = new float[testCase.mText.length()];
     88 
     89             paint.setHinting(Paint.HINTING_OFF);
     90             paint.getTextWidths(String.valueOf(testCase.mText), widths);
     91             assertEquals("Text width of '" + testCase.mText + "' without hinting is not expected.",
     92                     testCase.mWidthWithoutHinting, widths);
     93 
     94             paint.setHinting(Paint.HINTING_ON);
     95             paint.getTextWidths(String.valueOf(testCase.mText), widths);
     96             assertEquals("Text width of '" + testCase.mText + "' with hinting is not expected.",
     97                     testCase.mWidthWithHinting, widths);
     98         }
     99     }
    100 
    101     private static class HasGlyphTestCase {
    102         public final int mBaseCodepoint;
    103         public final HashSet<Integer> mVariationSelectors;
    104 
    105         public HasGlyphTestCase(int baseCodepoint, Integer[] variationSelectors) {
    106             mBaseCodepoint = baseCodepoint;
    107             mVariationSelectors = new HashSet<>(Arrays.asList(variationSelectors));
    108         }
    109     }
    110 
    111     private static String codePointsToString(int[] codepoints) {
    112         StringBuilder sb = new StringBuilder();
    113         for (int codepoint : codepoints) {
    114             sb.append(Character.toChars(codepoint));
    115         }
    116         return sb.toString();
    117     }
    118 
    119     public void testHasGlyph_variationSelectors() {
    120         final Typeface fontTypeface = Typeface.createFromAsset(
    121                 getInstrumentation().getContext().getAssets(), "fonts/hasGlyphTestFont.ttf");
    122         Paint p = new Paint();
    123         p.setTypeface(fontTypeface);
    124 
    125         // Usually latin letters U+0061..U+0064 and Mahjong Tiles U+1F000..U+1F003 don't have
    126         // variation selectors.  This test may fail if system pre-installed fonts have a variation
    127         // selector support for U+0061..U+0064 and U+1F000..U+1F003.
    128         HasGlyphTestCase[] HAS_GLYPH_TEST_CASES = {
    129             new HasGlyphTestCase(0x0061, new Integer[] {0xFE00, 0xE0100, 0xE0101, 0xE0102}),
    130             new HasGlyphTestCase(0x0062, new Integer[] {0xFE01, 0xE0101, 0xE0102, 0xE0103}),
    131             new HasGlyphTestCase(0x0063, new Integer[] {}),
    132             new HasGlyphTestCase(0x0064, new Integer[] {0xFE02, 0xE0102, 0xE0103}),
    133 
    134             new HasGlyphTestCase(0x1F000, new Integer[] {0xFE00, 0xE0100, 0xE0101, 0xE0102}),
    135             new HasGlyphTestCase(0x1F001, new Integer[] {0xFE01, 0xE0101, 0xE0102, 0xE0103}),
    136             new HasGlyphTestCase(0x1F002, new Integer[] {}),
    137             new HasGlyphTestCase(0x1F003, new Integer[] {0xFE02, 0xE0102, 0xE0103}),
    138         };
    139 
    140         for (HasGlyphTestCase testCase : HAS_GLYPH_TEST_CASES) {
    141             for (int vs = 0xFE00; vs <= 0xE01EF; ++vs) {
    142                 // Move to variation selector supplements after variation selectors.
    143                 if (vs == 0xFF00) {
    144                     vs = 0xE0100;
    145                 }
    146                 final String signature =
    147                         "hasGlyph(U+" + Integer.toHexString(testCase.mBaseCodepoint) +
    148                         " U+" + Integer.toHexString(vs) + ")";
    149                 final String testString =
    150                         codePointsToString(new int[] {testCase.mBaseCodepoint, vs});
    151                 if (testCase.mVariationSelectors.contains(vs)) {
    152                     assertTrue(signature + " is expected to be true", p.hasGlyph(testString));
    153                 } else {
    154                     assertFalse(signature + " is expected to be false", p.hasGlyph(testString));
    155                 }
    156             }
    157         }
    158     }
    159 
    160     public void testGetTextRunAdvances() {
    161         {
    162             // LTR
    163             String text = "abcdef";
    164             assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), false, true);
    165             assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), false, false);
    166         }
    167         {
    168             // RTL
    169             final String text =
    170                     "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
    171                             "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
    172                             "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
    173                             "\u062F\u061F";
    174             assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), true, true);
    175             assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), true, false);
    176         }
    177     }
    178 
    179     private void assertGetTextRunAdvances(String str, int start, int end,
    180             int contextStart, int contextEnd, boolean isRtl, boolean compareWithOtherMethods) {
    181         Paint p = new Paint();
    182 
    183         final int count = end - start;
    184         final float[][] advanceArrays = new float[4][count];
    185 
    186         final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd,
    187                 isRtl, advanceArrays[0], 0);
    188 
    189         char chars[] = str.toCharArray();
    190         final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart,
    191                 contextEnd - contextStart, isRtl, advanceArrays[1], 0);
    192         assertEquals(advance, advance_c, 1.0f);
    193 
    194         for (int c = 1; c < count; ++c) {
    195             final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c,
    196                     contextStart, contextEnd, isRtl, advanceArrays[2], 0);
    197             final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end,
    198                     contextStart, contextEnd, isRtl, advanceArrays[2], c);
    199             assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f);
    200 
    201 
    202             final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c,
    203                     contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0);
    204             final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c,
    205                     count - c, contextStart, contextEnd - contextStart, isRtl,
    206                     advanceArrays[3], c);
    207             assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f);
    208             assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f);
    209             assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f);
    210 
    211             for (int i = 1; i < advanceArrays.length; i++) {
    212                 for (int j = 0; j < count; j++) {
    213                     assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f);
    214                 }
    215             }
    216 
    217             // Compare results with measureText, getRunAdvance, and getTextWidths.
    218             if (compareWithOtherMethods && start == contextStart && end == contextEnd) {
    219                 assertEquals(advance, p.measureText(str, start, end), 1.0f);
    220                 assertEquals(advance, p.getRunAdvance(
    221                         str, start, end, contextStart, contextEnd, isRtl, end), 1.0f);
    222 
    223                 final float[] widths = new float[count];
    224                 p.getTextWidths(str, start, end, widths);
    225                 for (int i = 0; i < count; i++) {
    226                     assertEquals(advanceArrays[0][i], widths[i], 1.0f);
    227                 }
    228             }
    229         }
    230     }
    231 
    232     public void testGetTextRunAdvances_invalid() {
    233         Paint p = new Paint();
    234         String text = "test";
    235 
    236         try {
    237             p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0);
    238             fail("Should throw an IllegalArgumentException.");
    239         } catch (IllegalArgumentException e) {
    240         }
    241 
    242         try {
    243             p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0);
    244             fail("Should throw an IllegalArgumentException.");
    245         } catch (IllegalArgumentException e) {
    246         }
    247 
    248         try {
    249             p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0);
    250             fail("Should throw an IllegalArgumentException.");
    251         } catch (IllegalArgumentException e) {
    252         }
    253 
    254         try {
    255             p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
    256                     new float[text.length() - 1], 0);
    257             fail("Should throw an IndexOutOfBoundsException.");
    258         } catch (IndexOutOfBoundsException e) {
    259         }
    260 
    261         try {
    262             p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
    263                     new float[text.length()], 1);
    264             fail("Should throw an IndexOutOfBoundsException.");
    265         } catch (IndexOutOfBoundsException e) {
    266         }
    267 
    268         // 0 > contextStart
    269         try {
    270             p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0);
    271             fail("Should throw an IndexOutOfBoundsException.");
    272         } catch (IndexOutOfBoundsException e) {
    273         }
    274 
    275         // contextStart > start
    276         try {
    277             p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0);
    278             fail("Should throw an IndexOutOfBoundsException.");
    279         } catch (IndexOutOfBoundsException e) {
    280         }
    281 
    282         // start > end
    283         try {
    284             p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0);
    285             fail("Should throw an IndexOutOfBoundsException.");
    286         } catch (IndexOutOfBoundsException e) {
    287         }
    288 
    289         // end > contextEnd
    290         try {
    291             p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0);
    292             fail("Should throw an IndexOutOfBoundsException.");
    293         } catch (IndexOutOfBoundsException e) {
    294         }
    295 
    296         // contextEnd > text.length
    297         try {
    298             p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0);
    299             fail("Should throw an IndexOutOfBoundsException.");
    300         } catch (IndexOutOfBoundsException e) {
    301         }
    302     }
    303 
    304     public void testMeasureTextBidi() {
    305         Paint p = new Paint();
    306         {
    307             String bidiText = "abc \u0644\u063A\u0629 def";
    308             p.setBidiFlags(Paint.BIDI_LTR);
    309             float width = p.measureText(bidiText, 0, 4);
    310             p.setBidiFlags(Paint.BIDI_RTL);
    311             width += p.measureText(bidiText, 4, 7);
    312             p.setBidiFlags(Paint.BIDI_LTR);
    313             width += p.measureText(bidiText, 7, bidiText.length());
    314             assertEquals(width, p.measureText(bidiText), 1.0f);
    315         }
    316         {
    317             String bidiText = "abc \u0644\u063A\u0629 def";
    318             p.setBidiFlags(Paint.BIDI_DEFAULT_LTR);
    319             float width = p.measureText(bidiText, 0, 4);
    320             width += p.measureText(bidiText, 4, 7);
    321             width += p.measureText(bidiText, 7, bidiText.length());
    322             assertEquals(width, p.measureText(bidiText), 1.0f);
    323         }
    324         {
    325             String bidiText = "abc \u0644\u063A\u0629 def";
    326             p.setBidiFlags(Paint.BIDI_FORCE_LTR);
    327             float width = p.measureText(bidiText, 0, 4);
    328             width += p.measureText(bidiText, 4, 7);
    329             width += p.measureText(bidiText, 7, bidiText.length());
    330             assertEquals(width, p.measureText(bidiText), 1.0f);
    331         }
    332         {
    333             String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
    334             p.setBidiFlags(Paint.BIDI_RTL);
    335             float width = p.measureText(bidiText, 0, 4);
    336             p.setBidiFlags(Paint.BIDI_LTR);
    337             width += p.measureText(bidiText, 4, 7);
    338             p.setBidiFlags(Paint.BIDI_RTL);
    339             width += p.measureText(bidiText, 7, bidiText.length());
    340             assertEquals(width, p.measureText(bidiText), 1.0f);
    341         }
    342         {
    343             String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
    344             p.setBidiFlags(Paint.BIDI_DEFAULT_RTL);
    345             float width = p.measureText(bidiText, 0, 4);
    346             width += p.measureText(bidiText, 4, 7);
    347             width += p.measureText(bidiText, 7, bidiText.length());
    348             assertEquals(width, p.measureText(bidiText), 1.0f);
    349         }
    350         {
    351             String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
    352             p.setBidiFlags(Paint.BIDI_FORCE_RTL);
    353             float width = p.measureText(bidiText, 0, 4);
    354             width += p.measureText(bidiText, 4, 7);
    355             width += p.measureText(bidiText, 7, bidiText.length());
    356             assertEquals(width, p.measureText(bidiText), 1.0f);
    357         }
    358     }
    359 
    360     public void testSetGetWordSpacing() {
    361         Paint p = new Paint();
    362         assertEquals(0.0f, p.getWordSpacing());  // The default value should be 0.
    363         p.setWordSpacing(1.0f);
    364         assertEquals(1.0f, p.getWordSpacing());
    365         p.setWordSpacing(-2.0f);
    366         assertEquals(-2.0f, p.getWordSpacing());
    367     }
    368 }
    369