Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2016 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 "SkColor.h"
      9 #include "SkColorMatrixFilter.h"
     10 #include "SkGradientShader.h"
     11 #include "SkImage.h"
     12 #include "SkPM4f.h"
     13 #include "SkShader.h"
     14 
     15 #include "Test.h"
     16 #include "SkRandom.h"
     17 
     18 const float kTolerance = 1.0f / (1 << 20);
     19 
     20 static bool nearly_equal(float a, float b, float tol = kTolerance) {
     21     SkASSERT(tol >= 0);
     22     return fabsf(a - b) <= tol;
     23 }
     24 
     25 static bool nearly_equal(const SkPM4f a, const SkPM4f& b, float tol = kTolerance) {
     26     for (int i = 0; i < 4; ++i) {
     27         if (!nearly_equal(a.fVec[i], b.fVec[i], tol)) {
     28             return false;
     29         }
     30     }
     31     return true;
     32 }
     33 
     34 DEF_TEST(SkColor4f_FromColor, reporter) {
     35     const struct {
     36         SkColor     fC;
     37         SkColor4f   fC4;
     38     } recs[] = {
     39         { SK_ColorBLACK, { 1, 0, 0, 0 } },
     40         { SK_ColorWHITE, { 1, 1, 1, 1 } },
     41         { SK_ColorRED,   { 1, 1, 0, 0 } },
     42         { SK_ColorGREEN, { 1, 0, 1, 0 } },
     43         { SK_ColorBLUE,  { 1, 0, 0, 1 } },
     44         { 0,             { 0, 0, 0, 0 } },
     45         { 0x55AAFF00,    { 1/3.0f, 2/3.0f, 1, 0 } },
     46     };
     47 
     48     for (const auto& r : recs) {
     49         SkColor4f c4 = SkColor4f::FromColor(r.fC);
     50         REPORTER_ASSERT(reporter, c4 == r.fC4);
     51     }
     52 }
     53 
     54 DEF_TEST(Color4f_premul, reporter) {
     55     SkRandom rand;
     56 
     57     for (int i = 0; i < 1000000; ++i) {
     58         // First just test opaque colors, so that the premul should be exact
     59         SkColor4f c4 {
     60             1, rand.nextUScalar1(), rand.nextUScalar1(), rand.nextUScalar1()
     61         };
     62         SkPM4f pm4 = c4.premul();
     63         REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
     64         REPORTER_ASSERT(reporter, pm4.fVec[SK_R_INDEX] == c4.fA * c4.fR);
     65         REPORTER_ASSERT(reporter, pm4.fVec[SK_G_INDEX] == c4.fA * c4.fG);
     66         REPORTER_ASSERT(reporter, pm4.fVec[SK_B_INDEX] == c4.fA * c4.fB);
     67 
     68         // We compare with a tolerance, in case our premul multiply is implemented at slightly
     69         // different precision than the test code.
     70         c4.fA = rand.nextUScalar1();
     71         pm4 = c4.premul();
     72         REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
     73         REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_R_INDEX], c4.fA * c4.fR));
     74         REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_G_INDEX], c4.fA * c4.fG));
     75         REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_B_INDEX], c4.fA * c4.fB));
     76     }
     77 }
     78 
     79 //////////////////////////////////////////////////////////////////////////////////////////////////
     80 
     81 static SkColorFilter* make_mode_cf() {
     82     return SkColorFilter::CreateModeFilter(0xFFBB8855, SkXfermode::kPlus_Mode);
     83 }
     84 
     85 static SkColorFilter* make_mx_cf() {
     86     const float mx[] = {
     87         0.5f, 0,    0, 0, 0.1f,
     88         0,    0.5f, 0, 0, 0.2f,
     89         0,    0,    1, 0, -0.1f,
     90         0,    0,    0, 1, 0,
     91     };
     92     return SkColorMatrixFilter::Create(mx);
     93 }
     94 
     95 static SkColorFilter* make_compose_cf() {
     96     SkAutoTUnref<SkColorFilter> cf0(make_mode_cf());
     97     SkAutoTUnref<SkColorFilter> cf1(make_mx_cf());
     98     return SkColorFilter::CreateComposeFilter(cf0, cf1);
     99 }
    100 
    101 static SkShader* make_color_sh() { return SkShader::CreateColorShader(0xFFBB8855); }
    102 
    103 static SkShader* make_image_sh() {
    104     const SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
    105     const SkPMColor pixels[] {
    106         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
    107         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
    108         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
    109         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
    110     };
    111     SkAutoTUnref<SkImage> image(SkImage::NewRasterCopy(info, pixels, sizeof(SkPMColor) * 2));
    112     return image->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
    113 }
    114 
    115 static SkShader* make_grad_sh() {
    116     const SkPoint pts[] {{ 0, 0 }, { 100, 100 }};
    117     const SkColor colors[] { SK_ColorRED, SK_ColorBLUE };
    118     return SkGradientShader::CreateLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
    119 }
    120 
    121 static SkShader* make_cf_sh() {
    122     SkAutoTUnref<SkColorFilter> filter(make_mx_cf());
    123     SkAutoTUnref<SkShader> shader(make_color_sh());
    124     return shader->newWithColorFilter(filter);
    125 }
    126 
    127 static void compare_spans(const SkPM4f span4f[], const SkPMColor span4b[], int count,
    128                           skiatest::Reporter* reporter, float tolerance = 1.0f/255) {
    129     for (int i = 0; i < count; ++i) {
    130         SkPM4f c0 = SkPM4f::FromPMColor(span4b[i]);
    131         SkPM4f c1 = span4f[i];
    132         REPORTER_ASSERT(reporter, nearly_equal(c0, c1, tolerance));
    133     }
    134 }
    135 
    136 DEF_TEST(Color4f_shader, reporter) {
    137     struct {
    138         SkShader* (*fFact)();
    139         bool      fSupports4f;
    140         float     fTolerance;
    141     } recs[] = {
    142         { make_color_sh, true,  1.0f/255   },
    143         // PMColor 4f gradients are interpolated in 255-multiplied values, so we need a
    144         // slightly relaxed tolerance to accommodate the cumulative precision deviation.
    145         { make_grad_sh,  true,  1.001f/255 },
    146         { make_image_sh, false, 1.0f/255   },
    147         { make_cf_sh,    true,  1.0f/255   },
    148     };
    149 
    150     SkPaint paint;
    151     for (const auto& rec : recs) {
    152         uint32_t storage[200];
    153         paint.setShader(rec.fFact())->unref();
    154         // Encourage 4f context selection. At some point we may need
    155         // to instantiate two separate contexts for optimal 4b/4f selection.
    156         const SkShader::ContextRec contextRec(paint, SkMatrix::I(), nullptr,
    157                                               SkShader::ContextRec::kPM4f_DstType);
    158         SkASSERT(paint.getShader()->contextSize(contextRec) <= sizeof(storage));
    159         SkShader::Context* ctx = paint.getShader()->createContext(contextRec, storage);
    160         if (rec.fSupports4f) {
    161             const int N = 100;
    162             SkPM4f buffer4f[N];
    163             ctx->shadeSpan4f(0, 0, buffer4f, N);
    164             SkPMColor buffer4b[N];
    165             ctx->shadeSpan(0, 0, buffer4b, N);
    166             compare_spans(buffer4f, buffer4b, N, reporter, rec.fTolerance);
    167         }
    168         ctx->~Context();
    169     }
    170 }
    171 
    172 DEF_TEST(Color4f_colorfilter, reporter) {
    173     struct {
    174         SkColorFilter* (*fFact)();
    175         bool           fSupports4f;
    176     } recs[] = {
    177         { make_mode_cf,     true },
    178         { make_mx_cf,       true },
    179         { make_compose_cf,  true },
    180     };
    181 
    182     // prepare the src
    183     const int N = 100;
    184     SkPMColor src4b[N];
    185     SkPM4f    src4f[N];
    186     SkRandom rand;
    187     for (int i = 0; i < N; ++i) {
    188         src4b[i] = SkPreMultiplyColor(rand.nextU());
    189         src4f[i] = SkPM4f::FromPMColor(src4b[i]);
    190     }
    191     // confirm that our srcs are (nearly) equal
    192     compare_spans(src4f, src4b, N, reporter);
    193 
    194     for (const auto& rec : recs) {
    195         SkAutoTUnref<SkColorFilter> filter(rec.fFact());
    196         SkPMColor dst4b[N];
    197         filter->filterSpan(src4b, N, dst4b);
    198         SkPM4f dst4f[N];
    199         filter->filterSpan4f(src4f, N, dst4f);
    200         compare_spans(dst4f, dst4b, N, reporter);
    201     }
    202 }
    203 
    204 ///////////////////////////////////////////////////////////////////////////////////////////////////
    205 
    206 typedef SkPM4f (*SkXfermodeProc4f)(const SkPM4f& src, const SkPM4f& dst);
    207 
    208 static bool compare_procs(SkXfermodeProc proc32, SkXfermodeProc4f proc4f) {
    209     const float kTolerance = 1.0f / 255;
    210 
    211     const SkColor colors[] = {
    212         0, 0xFF000000, 0xFFFFFFFF, 0x80FF0000
    213     };
    214 
    215     for (auto s32 : colors) {
    216         SkPMColor s_pm32 = SkPreMultiplyColor(s32);
    217         SkPM4f    s_pm4f = SkColor4f::FromColor(s32).premul();
    218         for (auto d32 : colors) {
    219             SkPMColor d_pm32 = SkPreMultiplyColor(d32);
    220             SkPM4f    d_pm4f = SkColor4f::FromColor(d32).premul();
    221 
    222             SkPMColor r32 = proc32(s_pm32, d_pm32);
    223             SkPM4f    r4f = proc4f(s_pm4f, d_pm4f);
    224 
    225             SkPM4f r32_4f = SkPM4f::FromPMColor(r32);
    226             if (!nearly_equal(r4f, r32_4f, kTolerance)) {
    227                 return false;
    228             }
    229         }
    230     }
    231     return true;
    232 }
    233 
    234 // Check that our Proc and Proc4f return (nearly) the same results
    235 //
    236 DEF_TEST(Color4f_xfermode_proc4f, reporter) {
    237     // TODO: extend xfermodes so that all cases can be tested.
    238     //
    239     for (int mode = SkXfermode::kClear_Mode; mode <= SkXfermode::kScreen_Mode; ++mode) {
    240         SkXfermodeProc   proc32 = SkXfermode::GetProc((SkXfermode::Mode)mode);
    241         SkXfermodeProc4f proc4f = SkXfermode::GetProc4f((SkXfermode::Mode)mode);
    242         REPORTER_ASSERT(reporter, compare_procs(proc32, proc4f));
    243     }
    244 }
    245