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/private/SkColorData.h"
13#include "include/private/SkNx.h"
14#include "src/core/SkColorFilter_Matrix.h"
15#include "src/core/SkColorSpacePriv.h"
16#include "src/core/SkRasterPipeline.h"
17#include "src/core/SkReadBuffer.h"
18#include "src/core/SkVM.h"
19#include "src/core/SkWriteBuffer.h"
20
21static uint16_t ComputeFlags(const float matrix[20]) {
22 const float* srcA = matrix + 15;
23
24 return SkScalarNearlyZero (srcA[0])
25 && SkScalarNearlyZero (srcA[1])
26 && SkScalarNearlyZero (srcA[2])
27 && SkScalarNearlyEqual(srcA[3], 1)
28 && SkScalarNearlyZero (srcA[4])
29 ? SkColorFilter::kAlphaUnchanged_Flag : 0;
30}
31
32SkColorFilter_Matrix::SkColorFilter_Matrix(const float array[20], Domain domain)
33 : fFlags(ComputeFlags(array))
34 , fDomain(domain) {
35 memcpy(fMatrix, array, 20 * sizeof(float));
36}
37
38uint32_t SkColorFilter_Matrix::getFlags() const {
39 return this->INHERITED::getFlags() | fFlags;
40}
41
42void SkColorFilter_Matrix::flatten(SkWriteBuffer& buffer) const {
43 SkASSERT(sizeof(fMatrix)/sizeof(float) == 20);
44 buffer.writeScalarArray(fMatrix, 20);
45
46 // RGBA flag
47 buffer.writeBool(fDomain == Domain::kRGBA);
48}
49
50sk_sp<SkFlattenable> SkColorFilter_Matrix::CreateProc(SkReadBuffer& buffer) {
51 float matrix[20];
52 if (!buffer.readScalarArray(matrix, 20)) {
53 return nullptr;
54 }
55
56 auto is_rgba = buffer.isVersionLT(SkPicturePriv::kMatrixColorFilterDomain_Version) ||
57 buffer.readBool();
58 return is_rgba ? SkColorFilters::Matrix(matrix)
59 : SkColorFilters::HSLAMatrix(matrix);
60}
61
62bool SkColorFilter_Matrix::onAsAColorMatrix(float matrix[20]) const {
63 if (matrix) {
64 memcpy(matrix, fMatrix, 20 * sizeof(float));
65 }
66 return true;
67}
68
69bool SkColorFilter_Matrix::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
70 const bool willStayOpaque = shaderIsOpaque && (fFlags & kAlphaUnchanged_Flag),
71 hsla = fDomain == Domain::kHSLA;
72
73 SkRasterPipeline* p = rec.fPipeline;
74 if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); }
75 if ( hsla) { p->append(SkRasterPipeline::rgb_to_hsl); }
76 if ( true) { p->append(SkRasterPipeline::matrix_4x5, fMatrix); }
77 if ( hsla) { p->append(SkRasterPipeline::hsl_to_rgb); }
78 if ( true) { p->append(SkRasterPipeline::clamp_0); }
79 if ( true) { p->append(SkRasterPipeline::clamp_1); }
80 if (!willStayOpaque) { p->append(SkRasterPipeline::premul); }
81 return true;
82}
83
84
85skvm::Color SkColorFilter_Matrix::onProgram(skvm::Builder* p, skvm::Color c,
86 SkColorSpace* /*dstCS*/,
87 skvm::Uniforms* uniforms, SkArenaAlloc*) const {
88 auto apply_matrix = [&](auto xyzw) {
89 auto dot = [&](int j) {
90 auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
91 // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
92 // While looking, also simplify f == ±1. Anything else becomes a uniform.
93 return f == 0.0f ? a
94 : f == +1.0f ? a + m
95 : f == -1.0f ? a - m
96 : m * p->uniformF(uniforms->pushF(f)) + a;
97 };
98
99 // Similarly, let skvm::Builder fold away the additive bias when zero.
100 const float b = fMatrix[4+j*5];
101 skvm::F32 bias = b == 0.0f ? p->splat(0.0f)
102 : p->uniformF(uniforms->pushF(b));
103
104 auto [x,y,z,w] = xyzw;
105 return custom_mad(fMatrix[0+j*5], x,
106 custom_mad(fMatrix[1+j*5], y,
107 custom_mad(fMatrix[2+j*5], z,
108 custom_mad(fMatrix[3+j*5], w, bias))));
109 };
110 return std::make_tuple(dot(0), dot(1), dot(2), dot(3));
111 };
112
113 c = unpremul(c);
114
115 if (fDomain == Domain::kHSLA) {
116 auto [h,s,l,a] = apply_matrix(p->to_hsla(c));
117 c = p->to_rgba({h,s,l,a});
118 } else {
119 auto [r,g,b,a] = apply_matrix(c);
120 c = {r,g,b,a};
121 }
122
123 return premul(c); // note: rasterpipeline version does clamp01 first
124}
125
126#if SK_SUPPORT_GPU
127#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
128#include "src/gpu/effects/generated/GrHSLToRGBFilterEffect.h"
129#include "src/gpu/effects/generated/GrRGBToHSLFilterEffect.h"
130std::unique_ptr<GrFragmentProcessor> SkColorFilter_Matrix::asFragmentProcessor(
131 GrRecordingContext*, const GrColorInfo&) const {
132 switch (fDomain) {
133 case Domain::kRGBA:
134 return GrColorMatrixFragmentProcessor::Make(fMatrix,
135 /* premulInput = */ true,
136 /* clampRGBOutput = */ true,
137 /* premulOutput = */ true);
138 case Domain::kHSLA: {
139 std::unique_ptr<GrFragmentProcessor> series[] = {
140 GrRGBToHSLFilterEffect::Make(),
141 GrColorMatrixFragmentProcessor::Make(fMatrix,
142 /* premulInput = */ false,
143 /* clampRGBOutput = */ false,
144 /* premulOutput = */ false),
145 GrHSLToRGBFilterEffect::Make(),
146 };
147 return GrFragmentProcessor::RunInSeries(series, SK_ARRAY_COUNT(series));
148 }
149 }
150
151 SkUNREACHABLE;
152}
153
154#endif
155
156///////////////////////////////////////////////////////////////////////////////
157
158static sk_sp<SkColorFilter> MakeMatrix(const float array[20],
159 SkColorFilter_Matrix::Domain domain) {
160 return sk_floats_are_finite(array, 20)
161 ? sk_make_sp<SkColorFilter_Matrix>(array, domain)
162 : nullptr;
163}
164
165sk_sp<SkColorFilter> SkColorFilters::Matrix(const float array[20]) {
166 return MakeMatrix(array, SkColorFilter_Matrix::Domain::kRGBA);
167}
168
169sk_sp<SkColorFilter> SkColorFilters::Matrix(const SkColorMatrix& cm) {
170 return MakeMatrix(cm.fMat.data(), SkColorFilter_Matrix::Domain::kRGBA);
171}
172
173sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const float array[20]) {
174 return MakeMatrix(array, SkColorFilter_Matrix::Domain::kHSLA);
175}
176
177void SkColorFilter_Matrix::RegisterFlattenables() {
178 SK_REGISTER_FLATTENABLE(SkColorFilter_Matrix);
179
180 // This subclass was removed 4/2019
181 SkFlattenable::Register("SkColorMatrixFilterRowMajor255",
182 [](SkReadBuffer& buffer) -> sk_sp<SkFlattenable> {
183 float matrix[20];
184 if (buffer.readScalarArray(matrix, 20)) {
185 matrix[ 4] *= (1.0f/255);
186 matrix[ 9] *= (1.0f/255);
187 matrix[14] *= (1.0f/255);
188 matrix[19] *= (1.0f/255);
189 return SkColorFilters::Matrix(matrix);
190 }
191 return nullptr;
192 });
193}
194