Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/core/SkColorFilter_Matrix.cpp
Line
Count
Source (jump to first uncovered line)
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 "include/core/SkRefCnt.h"
9
#include "include/core/SkString.h"
10
#include "include/core/SkUnPreMultiply.h"
11
#include "include/effects/SkColorMatrix.h"
12
#include "include/effects/SkRuntimeEffect.h"
13
#include "include/private/SkColorData.h"
14
#include "include/private/SkNx.h"
15
#include "src/core/SkColorFilter_Matrix.h"
16
#include "src/core/SkColorSpacePriv.h"
17
#include "src/core/SkRasterPipeline.h"
18
#include "src/core/SkReadBuffer.h"
19
#include "src/core/SkRuntimeEffectPriv.h"
20
#include "src/core/SkVM.h"
21
#include "src/core/SkWriteBuffer.h"
22
23
31.0k
static bool is_alpha_unchanged(const float matrix[20]) {
24
31.0k
    const float* srcA = matrix + 15;
25
26
31.0k
    return SkScalarNearlyZero (srcA[0])
27
30.8k
        && SkScalarNearlyZero (srcA[1])
28
30.5k
        && SkScalarNearlyZero (srcA[2])
29
30.1k
        && SkScalarNearlyEqual(srcA[3], 1)
30
27.7k
        && SkScalarNearlyZero (srcA[4]);
31
31.0k
}
32
33
SkColorFilter_Matrix::SkColorFilter_Matrix(const float array[20], Domain domain)
34
    : fAlphaIsUnchanged(is_alpha_unchanged(array))
35
31.0k
    , fDomain(domain) {
36
31.0k
    memcpy(fMatrix, array, 20 * sizeof(float));
37
31.0k
}
38
39
0
void SkColorFilter_Matrix::flatten(SkWriteBuffer& buffer) const {
40
0
    SkASSERT(sizeof(fMatrix)/sizeof(float) == 20);
41
0
    buffer.writeScalarArray(fMatrix, 20);
42
43
    // RGBA flag
44
0
    buffer.writeBool(fDomain == Domain::kRGBA);
45
0
}
Unexecuted instantiation: SkColorFilter_Matrix::flatten(SkWriteBuffer&) const
Unexecuted instantiation: SkColorFilter_Matrix::flatten(SkWriteBuffer&) const
46
47
0
sk_sp<SkFlattenable> SkColorFilter_Matrix::CreateProc(SkReadBuffer& buffer) {
48
0
    float matrix[20];
49
0
    if (!buffer.readScalarArray(matrix, 20)) {
50
0
        return nullptr;
51
0
    }
52
53
0
    auto   is_rgba = buffer.readBool();
54
0
    return is_rgba ? SkColorFilters::Matrix(matrix)
55
0
                   : SkColorFilters::HSLAMatrix(matrix);
56
0
}
57
58
0
bool SkColorFilter_Matrix::onAsAColorMatrix(float matrix[20]) const {
59
0
    if (matrix) {
60
0
        memcpy(matrix, fMatrix, 20 * sizeof(float));
61
0
    }
62
0
    return true;
63
0
}
64
65
6.75k
bool SkColorFilter_Matrix::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
66
6.75k
    const bool willStayOpaque = shaderIsOpaque && fAlphaIsUnchanged,
67
6.75k
                         hsla = fDomain == Domain::kHSLA;
68
69
6.75k
    SkRasterPipeline* p = rec.fPipeline;
70
6.75k
    if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); }
71
6.75k
    if (           hsla) { p->append(SkRasterPipeline::rgb_to_hsl); }
72
6.75k
    if (           true) { p->append(SkRasterPipeline::matrix_4x5, fMatrix); }
73
6.75k
    if (           hsla) { p->append(SkRasterPipeline::hsl_to_rgb); }
74
6.75k
    if (           true) { p->append(SkRasterPipeline::clamp_0); }
75
6.75k
    if (           true) { p->append(SkRasterPipeline::clamp_1); }
76
6.75k
    if (!willStayOpaque) { p->append(SkRasterPipeline::premul); }
77
6.75k
    return true;
78
6.75k
}
79
80
81
skvm::Color SkColorFilter_Matrix::onProgram(skvm::Builder* p, skvm::Color c,
82
                                            const SkColorInfo& /*dst*/,
83
1.19k
                                            skvm::Uniforms* uniforms, SkArenaAlloc*) const {
84
1.19k
    auto apply_matrix = [&](auto xyzw) {
85
4.76k
        auto dot = [&](int j) {
86
19.0k
            auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
87
                // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
88
                // While looking, also simplify f == ±1.  Anything else becomes a uniform.
89
13.0k
                return f ==  0.0f ? a
90
5.99k
                     : f == +1.0f ? a + m
91
4.73k
                     : f == -1.0f ? a - m
92
4.73k
                     : m * p->uniformF(uniforms->pushF(f)) + a;
93
19.0k
            };
Unexecuted instantiation: SkColorFilter_Matrix.cpp:_ZZZZNK20SkColorFilter_Matrix9onProgramEPN4skvm7BuilderENS0_5ColorERK11SkColorInfoPNS0_8UniformsEP12SkArenaAllocENK3$_0clINS0_4HSLAEEEDaT_ENKUliE_clEiENKUlfNS0_3F32ESG_E_clEfSG_SG_
SkColorFilter_Matrix.cpp:_ZZZZNK20SkColorFilter_Matrix9onProgramEPN4skvm7BuilderENS0_5ColorERK11SkColorInfoPNS0_8UniformsEP12SkArenaAllocENK3$_0clIS3_EEDaT_ENKUliE_clEiENKUlfNS0_3F32ESF_E_clEfSF_SF_
Line
Count
Source
86
19.0k
            auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
87
                // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
88
                // While looking, also simplify f == ±1.  Anything else becomes a uniform.
89
13.0k
                return f ==  0.0f ? a
90
5.99k
                     : f == +1.0f ? a + m
91
4.73k
                     : f == -1.0f ? a - m
92
4.73k
                     : m * p->uniformF(uniforms->pushF(f)) + a;
93
19.0k
            };
94
95
            // Similarly, let skvm::Builder fold away the additive bias when zero.
96
4.76k
            const float b = fMatrix[4+j*5];
97
2.48k
            skvm::F32 bias = b == 0.0f ? p->splat(0.0f)
98
2.27k
                                       : p->uniformF(uniforms->pushF(b));
99
100
4.76k
            auto [x,y,z,w] = xyzw;
101
4.76k
            return custom_mad(fMatrix[0+j*5], x,
102
4.76k
                   custom_mad(fMatrix[1+j*5], y,
103
4.76k
                   custom_mad(fMatrix[2+j*5], z,
104
4.76k
                   custom_mad(fMatrix[3+j*5], w, bias))));
105
4.76k
        };
Unexecuted instantiation: SkColorFilter_Matrix.cpp:_ZZZNK20SkColorFilter_Matrix9onProgramEPN4skvm7BuilderENS0_5ColorERK11SkColorInfoPNS0_8UniformsEP12SkArenaAllocENK3$_0clINS0_4HSLAEEEDaT_ENKUliE_clEi
SkColorFilter_Matrix.cpp:_ZZZNK20SkColorFilter_Matrix9onProgramEPN4skvm7BuilderENS0_5ColorERK11SkColorInfoPNS0_8UniformsEP12SkArenaAllocENK3$_0clIS3_EEDaT_ENKUliE_clEi
Line
Count
Source
85
4.76k
        auto dot = [&](int j) {
86
4.76k
            auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
87
                // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
88
                // While looking, also simplify f == ±1.  Anything else becomes a uniform.
89
4.76k
                return f ==  0.0f ? a
90
4.76k
                     : f == +1.0f ? a + m
91
4.76k
                     : f == -1.0f ? a - m
92
4.76k
                     : m * p->uniformF(uniforms->pushF(f)) + a;
93
4.76k
            };
94
95
            // Similarly, let skvm::Builder fold away the additive bias when zero.
96
4.76k
            const float b = fMatrix[4+j*5];
97
2.48k
            skvm::F32 bias = b == 0.0f ? p->splat(0.0f)
98
2.27k
                                       : p->uniformF(uniforms->pushF(b));
99
100
4.76k
            auto [x,y,z,w] = xyzw;
101
4.76k
            return custom_mad(fMatrix[0+j*5], x,
102
4.76k
                   custom_mad(fMatrix[1+j*5], y,
103
4.76k
                   custom_mad(fMatrix[2+j*5], z,
104
4.76k
                   custom_mad(fMatrix[3+j*5], w, bias))));
105
4.76k
        };
106
1.19k
        return std::make_tuple(dot(0), dot(1), dot(2), dot(3));
107
1.19k
    };
Unexecuted instantiation: SkColorFilter_Matrix.cpp:_ZZNK20SkColorFilter_Matrix9onProgramEPN4skvm7BuilderENS0_5ColorERK11SkColorInfoPNS0_8UniformsEP12SkArenaAllocENK3$_0clINS0_4HSLAEEEDaT_
SkColorFilter_Matrix.cpp:_ZZNK20SkColorFilter_Matrix9onProgramEPN4skvm7BuilderENS0_5ColorERK11SkColorInfoPNS0_8UniformsEP12SkArenaAllocENK3$_0clIS3_EEDaT_
Line
Count
Source
84
1.19k
    auto apply_matrix = [&](auto xyzw) {
85
1.19k
        auto dot = [&](int j) {
86
1.19k
            auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
87
                // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
88
                // While looking, also simplify f == ±1.  Anything else becomes a uniform.
89
1.19k
                return f ==  0.0f ? a
90
1.19k
                     : f == +1.0f ? a + m
91
1.19k
                     : f == -1.0f ? a - m
92
1.19k
                     : m * p->uniformF(uniforms->pushF(f)) + a;
93
1.19k
            };
94
95
            // Similarly, let skvm::Builder fold away the additive bias when zero.
96
1.19k
            const float b = fMatrix[4+j*5];
97
1.19k
            skvm::F32 bias = b == 0.0f ? p->splat(0.0f)
98
1.19k
                                       : p->uniformF(uniforms->pushF(b));
99
100
1.19k
            auto [x,y,z,w] = xyzw;
101
1.19k
            return custom_mad(fMatrix[0+j*5], x,
102
1.19k
                   custom_mad(fMatrix[1+j*5], y,
103
1.19k
                   custom_mad(fMatrix[2+j*5], z,
104
1.19k
                   custom_mad(fMatrix[3+j*5], w, bias))));
105
1.19k
        };
106
1.19k
        return std::make_tuple(dot(0), dot(1), dot(2), dot(3));
107
1.19k
    };
108
109
1.19k
    c = unpremul(c);
110
111
1.19k
    if (fDomain == Domain::kHSLA) {
112
0
        auto [h,s,l,a] = apply_matrix(p->to_hsla(c));
113
0
        c = p->to_rgba({h,s,l,a});
114
1.19k
    } else {
115
1.19k
        auto [r,g,b,a] = apply_matrix(c);
116
1.19k
        c = {r,g,b,a};
117
1.19k
    }
118
119
1.19k
    return premul(clamp01(c));
120
1.19k
}
121
122
#if SK_SUPPORT_GPU
123
#include "src/gpu/effects/GrSkSLFP.h"
124
125
// Convert RGBA -> HSLA (including unpremul).
126
//
127
// Based on work by Sam Hocevar, Emil Persson, and Ian Taylor [1][2][3].  High-level ideas:
128
//
129
//   - minimize the number of branches by sorting and computing the hue phase in parallel (vec4s)
130
//
131
//   - trade the third sorting branch for a potentially faster std::min and leaving 2nd/3rd
132
//     channels unsorted (based on the observation that swapping both the channels and the bias sign
133
//     has no effect under abs)
134
//
135
//   - use epsilon offsets for denominators, to avoid explicit zero-checks
136
//
137
// An additional trick we employ is deferring premul->unpremul conversion until the very end: the
138
// alpha factor gets naturally simplified for H and S, and only L requires a dedicated unpremul
139
// division (so we trade three divs for one).
140
//
141
// [1] http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
142
// [2] http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
143
// [3] http://www.chilliant.com/rgb2hsv.html
144
0
static std::unique_ptr<GrFragmentProcessor> rgb_to_hsl(std::unique_ptr<GrFragmentProcessor> child) {
145
0
    static auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, R"(
146
0
        half4 main(half4 c) {
147
0
            half4 p = (c.g < c.b) ? half4(c.bg, -1,  2/3.0)
148
0
                                  : half4(c.gb,  0, -1/3.0);
149
0
            half4 q = (c.r < p.x) ? half4(p.x, c.r, p.yw)
150
0
                                  : half4(c.r, p.x, p.yz);
151
0
152
0
            // q.x  -> max channel value
153
0
            // q.yz -> 2nd/3rd channel values (unsorted)
154
0
            // q.w  -> bias value dependent on max channel selection
155
0
156
0
            half eps = 0.0001;
157
0
            half pmV = q.x;
158
0
            half pmC = pmV - min(q.y, q.z);
159
0
            half pmL = pmV - pmC * 0.5;
160
0
            half   H = abs(q.w + (q.y - q.z) / (pmC * 6 + eps));
161
0
            half   S = pmC / (c.a + eps - abs(pmL * 2 - c.a));
162
0
            half   L = pmL / (c.a + eps);
163
0
164
0
            return half4(H, S, L, c.a);
165
0
        }
166
0
    )");
167
0
    SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
168
0
    return GrSkSLFP::Make(
169
0
            effect, "RgbToHsl", std::move(child), GrSkSLFP::OptFlags::kPreservesOpaqueInput);
170
0
}
Unexecuted instantiation: SkColorFilter_Matrix.cpp:rgb_to_hsl(std::__1::unique_ptr<GrFragmentProcessor, std::__1::default_delete<GrFragmentProcessor> >)
Unexecuted instantiation: SkColorFilter_Matrix.cpp:rgb_to_hsl(std::__1::unique_ptr<GrFragmentProcessor, std::__1::default_delete<GrFragmentProcessor> >)
171
172
// Convert HSLA -> RGBA (including clamp and premul).
173
//
174
// Based on work by Sam Hocevar, Emil Persson, and Ian Taylor [1][2][3].
175
//
176
// [1] http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
177
// [2] http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
178
// [3] http://www.chilliant.com/rgb2hsv.html
179
0
static std::unique_ptr<GrFragmentProcessor> hsl_to_rgb(std::unique_ptr<GrFragmentProcessor> child) {
180
0
    static auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, R"(
181
0
        half4 main(half4 color) {
182
0
            half3   hsl = color.rgb;
183
0
184
0
            half      C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
185
0
            half3     p = hsl.xxx + half3(0, 2/3.0, 1/3.0);
186
0
            half3     q = saturate(abs(fract(p) * 6 - 3) - 1);
187
0
            half3   rgb = (q - 0.5) * C + hsl.z;
188
0
189
0
            color = saturate(half4(rgb, color.a));
190
0
            color.rgb *= color.a;
191
0
            return color;
192
0
        }
193
0
    )");
194
0
    SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
195
0
    return GrSkSLFP::Make(
196
0
            effect, "HslToRgb", std::move(child), GrSkSLFP::OptFlags::kPreservesOpaqueInput);
197
0
}
Unexecuted instantiation: SkColorFilter_Matrix.cpp:hsl_to_rgb(std::__1::unique_ptr<GrFragmentProcessor, std::__1::default_delete<GrFragmentProcessor> >)
Unexecuted instantiation: SkColorFilter_Matrix.cpp:hsl_to_rgb(std::__1::unique_ptr<GrFragmentProcessor, std::__1::default_delete<GrFragmentProcessor> >)
198
199
GrFPResult SkColorFilter_Matrix::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> fp,
200
                                                     GrRecordingContext*,
201
1.10k
                                                     const GrColorInfo&) const {
202
1.10k
    switch (fDomain) {
203
1.10k
        case Domain::kRGBA:
204
1.10k
            fp = GrFragmentProcessor::ColorMatrix(std::move(fp), fMatrix,
205
1.10k
                                                  /* unpremulInput = */  true,
206
1.10k
                                                  /* clampRGBOutput = */ true,
207
1.10k
                                                  /* premulOutput = */   true);
208
1.10k
            break;
209
210
0
        case Domain::kHSLA:
211
0
            fp = rgb_to_hsl(std::move(fp));
212
0
            fp = GrFragmentProcessor::ColorMatrix(std::move(fp), fMatrix,
213
0
                                                  /* unpremulInput = */  false,
214
0
                                                  /* clampRGBOutput = */ false,
215
0
                                                  /* premulOutput = */   false);
216
0
            fp = hsl_to_rgb(std::move(fp));
217
0
            break;
218
1.10k
    }
219
220
1.10k
    return GrFPSuccess(std::move(fp));
221
1.10k
}
222
223
#endif
224
225
///////////////////////////////////////////////////////////////////////////////
226
227
static sk_sp<SkColorFilter> MakeMatrix(const float array[20],
228
31.2k
                                       SkColorFilter_Matrix::Domain domain) {
229
31.2k
    if (!sk_floats_are_finite(array, 20)) {
230
188
        return nullptr;
231
188
    }
232
31.0k
    return sk_make_sp<SkColorFilter_Matrix>(array, domain);
233
31.0k
}
234
235
9.65k
sk_sp<SkColorFilter> SkColorFilters::Matrix(const float array[20]) {
236
9.65k
    return MakeMatrix(array, SkColorFilter_Matrix::Domain::kRGBA);
237
9.65k
}
238
239
19.9k
sk_sp<SkColorFilter> SkColorFilters::Matrix(const SkColorMatrix& cm) {
240
19.9k
    return MakeMatrix(cm.fMat.data(), SkColorFilter_Matrix::Domain::kRGBA);
241
19.9k
}
242
243
163
sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const float array[20]) {
244
163
    return MakeMatrix(array, SkColorFilter_Matrix::Domain::kHSLA);
245
163
}
246
247
1.46k
sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const SkColorMatrix& cm) {
248
1.46k
    return MakeMatrix(cm.fMat.data(), SkColorFilter_Matrix::Domain::kHSLA);
249
1.46k
}
250
251
3
void SkColorFilter_Matrix::RegisterFlattenables() {
252
3
    SK_REGISTER_FLATTENABLE(SkColorFilter_Matrix);
253
3
}