Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "Test.h"
      9 #include "SkPath.h"
     10 #include "SkPaint.h"
     11 #include "SkLayerDrawLooper.h"
     12 #include "SkBlurMaskFilter.h"
     13 #include "SkRandom.h"
     14 #include "SkTypeface.h"
     15 #include "SkUtils.h"
     16 
     17 static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) {
     18     char* u8 = (char*)dst;
     19     for (int i = 0; i < count; ++i) {
     20         int n = SkUTF8_FromUnichar(src[i], u8);
     21         u8 += n;
     22     }
     23     return u8 - (char*)dst;
     24 }
     25 
     26 static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) {
     27     uint16_t* u16 = (uint16_t*)dst;
     28     for (int i = 0; i < count; ++i) {
     29         int n = SkUTF16_FromUnichar(src[i], u16);
     30         u16 += n;
     31     }
     32     return (char*)u16 - (char*)dst;
     33 }
     34 
     35 static size_t uni_to_utf32(const SkUnichar src[], void* dst, int count) {
     36     SkUnichar* u32 = (SkUnichar*)dst;
     37     if (src != u32) {
     38         memcpy(u32, src, count * sizeof(SkUnichar));
     39     }
     40     return count * sizeof(SkUnichar);
     41 }
     42 
     43 static SkTypeface::Encoding paint2encoding(const SkPaint& paint) {
     44     SkPaint::TextEncoding enc = paint.getTextEncoding();
     45     SkASSERT(SkPaint::kGlyphID_TextEncoding != enc);
     46     return (SkTypeface::Encoding)enc;
     47 }
     48 
     49 static int find_first_zero(const uint16_t glyphs[], int count) {
     50     for (int i = 0; i < count; ++i) {
     51         if (0 == glyphs[i]) {
     52             return i;
     53         }
     54     }
     55     return count;
     56 }
     57 
     58 static void test_cmap(skiatest::Reporter* reporter) {
     59     static const int NGLYPHS = 64;
     60 
     61     SkUnichar src[NGLYPHS];
     62     SkUnichar dst[NGLYPHS]; // used for utf8, utf16, utf32 storage
     63 
     64     static const struct {
     65         size_t (*fSeedTextProc)(const SkUnichar[], void* dst, int count);
     66         SkPaint::TextEncoding   fEncoding;
     67     } gRec[] = {
     68         { uni_to_utf8,  SkPaint::kUTF8_TextEncoding },
     69         { uni_to_utf16, SkPaint::kUTF16_TextEncoding },
     70         { uni_to_utf32, SkPaint::kUTF32_TextEncoding },
     71     };
     72 
     73     SkRandom rand;
     74     SkPaint paint;
     75     paint.setTypeface(SkTypeface::RefDefault())->unref();
     76     SkTypeface* face = paint.getTypeface();
     77 
     78     for (int i = 0; i < 1000; ++i) {
     79         // generate some random text
     80         for (int j = 0; j < NGLYPHS; ++j) {
     81             src[j] = ' ' + j;
     82         }
     83         // inject some random chars, to sometimes abort early
     84         src[rand.nextU() & 63] = rand.nextU() & 0xFFF;
     85 
     86         for (size_t k = 0; k < SK_ARRAY_COUNT(gRec); ++k) {
     87             paint.setTextEncoding(gRec[k].fEncoding);
     88 
     89             size_t len = gRec[k].fSeedTextProc(src, dst, NGLYPHS);
     90 
     91             uint16_t    glyphs0[NGLYPHS], glyphs1[NGLYPHS];
     92 
     93             bool contains = paint.containsText(dst, len);
     94             int nglyphs = paint.textToGlyphs(dst, len, glyphs0);
     95             int first = face->charsToGlyphs(dst, paint2encoding(paint), glyphs1, NGLYPHS);
     96             int index = find_first_zero(glyphs1, NGLYPHS);
     97 
     98             REPORTER_ASSERT(reporter, NGLYPHS == nglyphs);
     99             REPORTER_ASSERT(reporter, index == first);
    100             REPORTER_ASSERT(reporter,
    101                         !memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t)));
    102             if (contains) {
    103                 REPORTER_ASSERT(reporter, NGLYPHS == first);
    104             } else {
    105                 REPORTER_ASSERT(reporter, NGLYPHS > first);
    106             }
    107         }
    108     }
    109 }
    110 
    111 // temparary api for bicubic, just be sure we can set/clear it
    112 static void test_filterlevel(skiatest::Reporter* reporter) {
    113     SkPaint p0, p1;
    114 
    115     REPORTER_ASSERT(reporter,
    116                     SkPaint::kNone_FilterLevel == p0.getFilterLevel());
    117 
    118     static const SkPaint::FilterLevel gLevels[] = {
    119         SkPaint::kNone_FilterLevel,
    120         SkPaint::kLow_FilterLevel,
    121         SkPaint::kMedium_FilterLevel,
    122         SkPaint::kHigh_FilterLevel
    123     };
    124     for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
    125         p0.setFilterLevel(gLevels[i]);
    126         REPORTER_ASSERT(reporter, gLevels[i] == p0.getFilterLevel());
    127         p1 = p0;
    128         REPORTER_ASSERT(reporter, gLevels[i] == p1.getFilterLevel());
    129 
    130         p0.reset();
    131         REPORTER_ASSERT(reporter,
    132                         SkPaint::kNone_FilterLevel == p0.getFilterLevel());
    133     }
    134 }
    135 
    136 static void test_copy(skiatest::Reporter* reporter) {
    137     SkPaint paint;
    138     // set a few member variables
    139     paint.setStyle(SkPaint::kStrokeAndFill_Style);
    140     paint.setTextAlign(SkPaint::kLeft_Align);
    141     paint.setStrokeWidth(SkIntToScalar(2));
    142     // set a few pointers
    143     SkLayerDrawLooper* looper = new SkLayerDrawLooper();
    144     paint.setLooper(looper)->unref();
    145     SkMaskFilter* mask = SkBlurMaskFilter::Create(1, SkBlurMaskFilter::kNormal_BlurStyle);
    146     paint.setMaskFilter(mask)->unref();
    147 
    148     // copy the paint using the copy constructor and check they are the same
    149     SkPaint copiedPaint = paint;
    150     REPORTER_ASSERT(reporter, paint == copiedPaint);
    151 
    152 #ifdef SK_BUILD_FOR_ANDROID
    153     // the copy constructor should preserve the Generation ID
    154     uint32_t paintGenID = paint.getGenerationID();
    155     uint32_t copiedPaintGenID = copiedPaint.getGenerationID();
    156     REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID);
    157     REPORTER_ASSERT(reporter, !memcmp(&paint, &copiedPaint, sizeof(paint)));
    158 #endif
    159 
    160     // copy the paint using the equal operator and check they are the same
    161     copiedPaint = paint;
    162     REPORTER_ASSERT(reporter, paint == copiedPaint);
    163 
    164 #ifdef SK_BUILD_FOR_ANDROID
    165     // the equals operator should increment the Generation ID
    166     REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID);
    167     REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
    168     copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value
    169     REPORTER_ASSERT(reporter, memcmp(&paint, &copiedPaint, sizeof(paint)));
    170 #endif
    171 
    172     // clean the paint and check they are back to their initial states
    173     SkPaint cleanPaint;
    174     paint.reset();
    175     copiedPaint.reset();
    176     REPORTER_ASSERT(reporter, cleanPaint == paint);
    177     REPORTER_ASSERT(reporter, cleanPaint == copiedPaint);
    178 
    179 #ifdef SK_BUILD_FOR_ANDROID
    180     // the reset function should increment the Generation ID
    181     REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID);
    182     REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
    183     REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &paint, sizeof(cleanPaint)));
    184     REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &copiedPaint, sizeof(cleanPaint)));
    185 #endif
    186 }
    187 
    188 // found and fixed for webkit: mishandling when we hit recursion limit on
    189 // mostly degenerate cubic flatness test
    190 static void regression_cubic(skiatest::Reporter* reporter) {
    191     SkPath path, stroke;
    192     SkPaint paint;
    193 
    194     path.moveTo(SkFloatToScalar(460.2881309415525f),
    195                 SkFloatToScalar(303.250847066498f));
    196     path.cubicTo(SkFloatToScalar(463.36378422175284f),
    197                  SkFloatToScalar(302.1169735073363f),
    198                  SkFloatToScalar(456.32239330810046f),
    199                  SkFloatToScalar(304.720354932878f),
    200                  SkFloatToScalar(453.15255460013304f),
    201                  SkFloatToScalar(305.788586869862f));
    202 
    203     SkRect fillR, strokeR;
    204     fillR = path.getBounds();
    205 
    206     paint.setStyle(SkPaint::kStroke_Style);
    207     paint.setStrokeWidth(SkIntToScalar(2));
    208     paint.getFillPath(path, &stroke);
    209     strokeR = stroke.getBounds();
    210 
    211     SkRect maxR = fillR;
    212     SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter());
    213     SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ?
    214                             SkScalarMul(paint.getStrokeWidth(), miter) :
    215                             paint.getStrokeWidth();
    216     maxR.inset(-inset, -inset);
    217 
    218     // test that our stroke didn't explode
    219     REPORTER_ASSERT(reporter, maxR.contains(strokeR));
    220 }
    221 
    222 // found and fixed for android: not initializing rect for string's of length 0
    223 static void regression_measureText(skiatest::Reporter* reporter) {
    224 
    225     SkPaint paint;
    226     paint.setTextSize(SkFloatToScalar(12.0f));
    227 
    228     SkRect r;
    229     r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN);
    230 
    231     // test that the rect was reset
    232     paint.measureText("", 0, &r, SkFloatToScalar(1.0f));
    233     REPORTER_ASSERT(reporter, r.isEmpty());
    234 }
    235 
    236 static void TestPaint(skiatest::Reporter* reporter) {
    237     // TODO add general paint tests
    238     test_copy(reporter);
    239 
    240     // regression tests
    241     regression_cubic(reporter);
    242     regression_measureText(reporter);
    243 
    244     test_filterlevel(reporter);
    245 
    246     // need to implement charsToGlyphs on other backends (e.g. linux, win)
    247     // before we can run this tests everywhere
    248     if (false) {
    249        test_cmap(reporter);
    250     }
    251 }
    252 
    253 #include "TestClassDef.h"
    254 DEFINE_TESTCLASS("Paint", TestPaintClass, TestPaint)
    255