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 "SkColor.h"
      9 #include "SkColorFilter.h"
     10 #include "SkColorPriv.h"
     11 #include "SkLumaColorFilter.h"
     12 #include "SkReadBuffer.h"
     13 #include "SkWriteBuffer.h"
     14 #include "SkRandom.h"
     15 #include "SkXfermode.h"
     16 #include "Test.h"
     17 
     18 static SkColorFilter* reincarnate_colorfilter(SkFlattenable* obj) {
     19     SkWriteBuffer wb;
     20     wb.writeFlattenable(obj);
     21 
     22     size_t size = wb.bytesWritten();
     23     SkAutoSMalloc<1024> storage(size);
     24     // make a copy into storage
     25     wb.writeToMemory(storage.get());
     26 
     27     SkReadBuffer rb(storage.get(), size);
     28     return rb.readColorFilter();
     29 }
     30 
     31 ///////////////////////////////////////////////////////////////////////////////
     32 
     33 static SkColorFilter* make_filter() {
     34     // pick a filter that cannot compose with itself via newComposed()
     35     return SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kColorBurn_Mode);
     36 }
     37 
     38 static void test_composecolorfilter_limit(skiatest::Reporter* reporter) {
     39     // Test that CreateComposeFilter() has some finite limit (i.e. that the factory can return null)
     40     const int way_too_many = 100;
     41     SkAutoTUnref<SkColorFilter> parent(make_filter());
     42     for (int i = 2; i < way_too_many; ++i) {
     43         SkAutoTUnref<SkColorFilter> filter(make_filter());
     44         parent.reset(SkColorFilter::CreateComposeFilter(parent, filter));
     45         if (nullptr == parent) {
     46             REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once!
     47             return;
     48         }
     49     }
     50     REPORTER_ASSERT(reporter, false); // we never saw a nullptr :(
     51 }
     52 
     53 #define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
     54 
     55 DEF_TEST(ColorFilter, reporter) {
     56     SkRandom rand;
     57 
     58     for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
     59         SkColor color = rand.nextU();
     60 
     61         // ensure we always get a filter, by avoiding the possibility of a
     62         // special case that would return nullptr (if color's alpha is 0 or 0xFF)
     63         color = SkColorSetA(color, 0x7F);
     64 
     65         SkColorFilter* cf = SkColorFilter::CreateModeFilter(color,
     66                                                         (SkXfermode::Mode)mode);
     67 
     68         // allow for no filter if we're in Dst mode (its a no op)
     69         if (SkXfermode::kDst_Mode == mode && nullptr == cf) {
     70             continue;
     71         }
     72 
     73         SkAutoUnref aur(cf);
     74         REPORTER_ASSERT(reporter, cf);
     75 
     76         SkColor c = ~color;
     77         SkXfermode::Mode m = ILLEGAL_MODE;
     78 
     79         SkColor expectedColor = color;
     80         SkXfermode::Mode expectedMode = (SkXfermode::Mode)mode;
     81 
     82 //        SkDebugf("--- mc [%d %x] ", mode, color);
     83 
     84         REPORTER_ASSERT(reporter, cf->asColorMode(&c, &m));
     85         // handle special-case folding by the factory
     86         if (SkXfermode::kClear_Mode == mode) {
     87             if (c != expectedColor) {
     88                 expectedColor = 0;
     89             }
     90             if (m != expectedMode) {
     91                 expectedMode = SkXfermode::kSrc_Mode;
     92             }
     93         }
     94 
     95 //        SkDebugf("--- got [%d %x] expected [%d %x]\n", m, c, expectedMode, expectedColor);
     96 
     97         REPORTER_ASSERT(reporter, c == expectedColor);
     98         REPORTER_ASSERT(reporter, m == expectedMode);
     99 
    100         {
    101             SkColorFilter* cf2 = reincarnate_colorfilter(cf);
    102             SkAutoUnref aur2(cf2);
    103             REPORTER_ASSERT(reporter, cf2);
    104 
    105             SkColor c2 = ~color;
    106             SkXfermode::Mode m2 = ILLEGAL_MODE;
    107             REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, &m2));
    108             REPORTER_ASSERT(reporter, c2 == expectedColor);
    109             REPORTER_ASSERT(reporter, m2 == expectedMode);
    110         }
    111     }
    112 
    113     test_composecolorfilter_limit(reporter);
    114 }
    115 
    116 ///////////////////////////////////////////////////////////////////////////////
    117 
    118 DEF_TEST(LumaColorFilter, reporter) {
    119     SkPMColor in, out;
    120     SkAutoTUnref<SkColorFilter> lf(SkLumaColorFilter::Create());
    121 
    122     // Applying luma to white produces black with the same transparency.
    123     for (unsigned i = 0; i < 256; ++i) {
    124         in = SkPackARGB32(i, i, i, i);
    125         lf->filterSpan(&in, 1, &out);
    126         REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i);
    127         REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
    128         REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
    129         REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
    130     }
    131 
    132     // Applying luma to black yields transparent black (luminance(black) == 0)
    133     for (unsigned i = 0; i < 256; ++i) {
    134         in = SkPackARGB32(i, 0, 0, 0);
    135         lf->filterSpan(&in, 1, &out);
    136         REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT);
    137     }
    138 
    139     // For general colors, a luma filter generates black with an attenuated alpha channel.
    140     for (unsigned i = 1; i < 256; ++i) {
    141         in = SkPackARGB32(i, i, i / 2, i / 3);
    142         lf->filterSpan(&in, 1, &out);
    143         REPORTER_ASSERT(reporter, out != in);
    144         REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i);
    145         REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
    146         REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
    147         REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
    148     }
    149 }
    150 
    151 ///////////////////////////////////////////////////////////////////////////////
    152 
    153 #include "SkColorMatrixFilter.h"
    154 
    155 static void get_brightness_matrix(float amount, float matrix[20]) {
    156     // Spec implementation
    157     // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
    158     // <feFunc[R|G|B] type="linear" slope="[amount]">
    159     memset(matrix, 0, 20 * sizeof(SkScalar));
    160     matrix[0] = matrix[6] = matrix[12] = amount;
    161     matrix[18] = 1.f;
    162 }
    163 
    164 static void get_grayscale_matrix(float amount, float matrix[20]) {
    165     // Note, these values are computed to ensure MatrixNeedsClamping is false
    166     // for amount in [0..1]
    167     matrix[0] = 0.2126f + 0.7874f * amount;
    168     matrix[1] = 0.7152f - 0.7152f * amount;
    169     matrix[2] = 1.f - (matrix[0] + matrix[1]);
    170     matrix[3] = matrix[4] = 0.f;
    171 
    172     matrix[5] = 0.2126f - 0.2126f * amount;
    173     matrix[6] = 0.7152f + 0.2848f * amount;
    174     matrix[7] = 1.f - (matrix[5] + matrix[6]);
    175     matrix[8] = matrix[9] = 0.f;
    176 
    177     matrix[10] = 0.2126f - 0.2126f * amount;
    178     matrix[11] = 0.7152f - 0.7152f * amount;
    179     matrix[12] = 1.f - (matrix[10] + matrix[11]);
    180     matrix[13] = matrix[14] = 0.f;
    181 
    182     matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
    183     matrix[18] = 1.f;
    184 }
    185 
    186 static SkColorFilter* make_cf0() {
    187     SkScalar matrix[20];
    188     get_brightness_matrix(0.5f, matrix);
    189     return SkColorMatrixFilter::Create(matrix);
    190 }
    191 static SkColorFilter* make_cf1() {
    192     SkScalar matrix[20];
    193     get_grayscale_matrix(1, matrix);
    194     return SkColorMatrixFilter::Create(matrix);
    195 }
    196 static SkColorFilter* make_cf2() {
    197     SkColorMatrix m0, m1;
    198     get_brightness_matrix(0.5f, m0.fMat);
    199     get_grayscale_matrix(1, m1.fMat);
    200     m0.preConcat(m1);
    201     return SkColorMatrixFilter::Create(m0);
    202 }
    203 static SkColorFilter* make_cf3() {
    204     SkColorMatrix m0, m1;
    205     get_brightness_matrix(0.5f, m0.fMat);
    206     get_grayscale_matrix(1, m1.fMat);
    207     m0.postConcat(m1);
    208     return SkColorMatrixFilter::Create(m0);
    209 }
    210 typedef SkColorFilter* (*CFProc)();
    211 
    212 // Test that a colormatrix that "should" preserve opaquness actually does.
    213 DEF_TEST(ColorMatrixFilter, reporter) {
    214     const CFProc procs[] = {
    215         make_cf0, make_cf1, make_cf2, make_cf3,
    216     };
    217 
    218     for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
    219         SkAutoTUnref<SkColorFilter> cf(procs[i]());
    220 
    221         // generate all possible r,g,b triples
    222         for (int r = 0; r < 256; ++r) {
    223             for (int g = 0; g < 256; ++g) {
    224                 SkPMColor storage[256];
    225                 for (int b = 0; b < 256; ++b) {
    226                     storage[b] = SkPackARGB32(0xFF, r, g, b);
    227                 }
    228                 cf->filterSpan(storage, 256, storage);
    229                 for (int b = 0; b < 256; ++b) {
    230                     REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b]));
    231                 }
    232             }
    233         }
    234     }
    235 }
    236