Line data Source code
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 "SkColorMatrixFilterRowMajor255.h"
9 : #include "SkColorPriv.h"
10 : #include "SkNx.h"
11 : #include "SkPM4fPriv.h"
12 : #include "SkRasterPipeline.h"
13 : #include "SkReadBuffer.h"
14 : #include "SkRefCnt.h"
15 : #include "SkString.h"
16 : #include "SkUnPreMultiply.h"
17 : #include "SkWriteBuffer.h"
18 :
19 0 : static void transpose_and_scale01(float dst[20], const float src[20]) {
20 0 : const float* srcR = src + 0;
21 0 : const float* srcG = src + 5;
22 0 : const float* srcB = src + 10;
23 0 : const float* srcA = src + 15;
24 :
25 0 : for (int i = 0; i < 16; i += 4) {
26 0 : dst[i + 0] = *srcR++;
27 0 : dst[i + 1] = *srcG++;
28 0 : dst[i + 2] = *srcB++;
29 0 : dst[i + 3] = *srcA++;
30 : }
31 : // Might as well scale these translates down to [0,1] here instead of every filter call.
32 0 : dst[16] = *srcR * (1/255.0f);
33 0 : dst[17] = *srcG * (1/255.0f);
34 0 : dst[18] = *srcB * (1/255.0f);
35 0 : dst[19] = *srcA * (1/255.0f);
36 0 : }
37 :
38 0 : void SkColorMatrixFilterRowMajor255::initState() {
39 0 : transpose_and_scale01(fTranspose, fMatrix);
40 :
41 0 : const float* array = fMatrix;
42 :
43 : // check if we have to munge Alpha
44 0 : bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]);
45 0 : bool usesAlpha = (array[3] || array[8] || array[13]);
46 :
47 0 : if (changesAlpha || usesAlpha) {
48 0 : fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag;
49 : } else {
50 0 : fFlags = kAlphaUnchanged_Flag;
51 : }
52 0 : }
53 :
54 : ///////////////////////////////////////////////////////////////////////////////
55 :
56 0 : SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) {
57 0 : memcpy(fMatrix, array, 20 * sizeof(SkScalar));
58 0 : this->initState();
59 0 : }
60 :
61 0 : uint32_t SkColorMatrixFilterRowMajor255::getFlags() const {
62 0 : return this->INHERITED::getFlags() | fFlags;
63 : }
64 :
65 0 : static Sk4f scale_rgb(float scale) {
66 : static_assert(SkPM4f::A == 3, "Alpha is lane 3");
67 0 : return Sk4f(scale, scale, scale, 1);
68 : }
69 :
70 0 : static Sk4f premul(const Sk4f& x) {
71 0 : return x * scale_rgb(x[SkPM4f::A]);
72 : }
73 :
74 0 : static Sk4f unpremul(const Sk4f& x) {
75 0 : return x * scale_rgb(1 / x[SkPM4f::A]); // TODO: fast/approx invert?
76 : }
77 :
78 0 : static Sk4f clamp_0_1(const Sk4f& x) {
79 0 : return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0));
80 : }
81 :
82 0 : static SkPMColor round(const Sk4f& x) {
83 : SkPMColor c;
84 0 : SkNx_cast<uint8_t>(x * Sk4f(255) + Sk4f(0.5f)).store(&c);
85 0 : return c;
86 : }
87 :
88 : template <typename Adaptor, typename T>
89 0 : void filter_span(const float array[], const T src[], int count, T dst[]) {
90 0 : const Sk4f c0 = Sk4f::Load(array + 0);
91 0 : const Sk4f c1 = Sk4f::Load(array + 4);
92 0 : const Sk4f c2 = Sk4f::Load(array + 8);
93 0 : const Sk4f c3 = Sk4f::Load(array + 12);
94 0 : const Sk4f c4 = Sk4f::Load(array + 16);
95 :
96 : // todo: we could cache this in the constructor...
97 0 : T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4)));
98 :
99 0 : for (int i = 0; i < count; i++) {
100 0 : Sk4f srcf = Adaptor::To4f(src[i]);
101 0 : float srcA = srcf[SkPM4f::A];
102 :
103 0 : if (0 == srcA) {
104 0 : dst[i] = matrix_translate_pmcolor;
105 0 : continue;
106 : }
107 0 : if (1 != srcA) {
108 0 : srcf = unpremul(srcf);
109 : }
110 :
111 0 : Sk4f r4 = srcf[Adaptor::R];
112 0 : Sk4f g4 = srcf[Adaptor::G];
113 0 : Sk4f b4 = srcf[Adaptor::B];
114 0 : Sk4f a4 = srcf[Adaptor::A];
115 : // apply matrix
116 0 : Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4;
117 :
118 0 : dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4)));
119 : }
120 0 : }
121 :
122 : struct SkPMColorAdaptor {
123 : enum {
124 : R = SK_R_INDEX,
125 : G = SK_G_INDEX,
126 : B = SK_B_INDEX,
127 : A = SK_A_INDEX,
128 : };
129 0 : static SkPMColor From4f(const Sk4f& c4) {
130 0 : return round(swizzle_rb_if_bgra(c4));
131 : }
132 0 : static Sk4f To4f(SkPMColor c) {
133 0 : return Sk4f_fromL32(c);
134 : }
135 : };
136 0 : void SkColorMatrixFilterRowMajor255::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
137 0 : filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst);
138 0 : }
139 :
140 : struct SkPM4fAdaptor {
141 : enum {
142 : R = SkPM4f::R,
143 : G = SkPM4f::G,
144 : B = SkPM4f::B,
145 : A = SkPM4f::A,
146 : };
147 0 : static SkPM4f From4f(const Sk4f& c4) {
148 0 : return SkPM4f::From4f(c4);
149 : }
150 0 : static Sk4f To4f(const SkPM4f& c) {
151 0 : return c.to4f();
152 : }
153 : };
154 0 : void SkColorMatrixFilterRowMajor255::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
155 0 : filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst);
156 0 : }
157 :
158 : ///////////////////////////////////////////////////////////////////////////////
159 :
160 0 : void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const {
161 : SkASSERT(sizeof(fMatrix)/sizeof(SkScalar) == 20);
162 0 : buffer.writeScalarArray(fMatrix, 20);
163 0 : }
164 :
165 0 : sk_sp<SkFlattenable> SkColorMatrixFilterRowMajor255::CreateProc(SkReadBuffer& buffer) {
166 : SkScalar matrix[20];
167 0 : if (buffer.readScalarArray(matrix, 20)) {
168 0 : return sk_make_sp<SkColorMatrixFilterRowMajor255>(matrix);
169 : }
170 0 : return nullptr;
171 : }
172 :
173 0 : bool SkColorMatrixFilterRowMajor255::asColorMatrix(SkScalar matrix[20]) const {
174 0 : if (matrix) {
175 0 : memcpy(matrix, fMatrix, 20 * sizeof(SkScalar));
176 : }
177 0 : return true;
178 : }
179 :
180 : ///////////////////////////////////////////////////////////////////////////////
181 : // This code was duplicated from src/effects/SkColorMatrixc.cpp in order to be used in core.
182 : //////
183 :
184 : // To detect if we need to apply clamping after applying a matrix, we check if
185 : // any output component might go outside of [0, 255] for any combination of
186 : // input components in [0..255].
187 : // Each output component is an affine transformation of the input component, so
188 : // the minimum and maximum values are for any combination of minimum or maximum
189 : // values of input components (i.e. 0 or 255).
190 : // E.g. if R' = x*R + y*G + z*B + w*A + t
191 : // Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
192 : // minimum value will be for R=0 if x>0 or R=255 if x<0.
193 : // Same goes for all components.
194 0 : static bool component_needs_clamping(const SkScalar row[5]) {
195 0 : SkScalar maxValue = row[4] / 255;
196 0 : SkScalar minValue = row[4] / 255;
197 0 : for (int i = 0; i < 4; ++i) {
198 0 : if (row[i] > 0)
199 0 : maxValue += row[i];
200 : else
201 0 : minValue += row[i];
202 : }
203 0 : return (maxValue > 1) || (minValue < 0);
204 : }
205 :
206 0 : static bool needs_clamping(const SkScalar matrix[20]) {
207 0 : return component_needs_clamping(matrix)
208 0 : || component_needs_clamping(matrix+5)
209 0 : || component_needs_clamping(matrix+10)
210 0 : || component_needs_clamping(matrix+15);
211 : }
212 :
213 0 : static void set_concat(SkScalar result[20], const SkScalar outer[20], const SkScalar inner[20]) {
214 0 : int index = 0;
215 0 : for (int j = 0; j < 20; j += 5) {
216 0 : for (int i = 0; i < 4; i++) {
217 0 : result[index++] = outer[j + 0] * inner[i + 0] +
218 0 : outer[j + 1] * inner[i + 5] +
219 0 : outer[j + 2] * inner[i + 10] +
220 0 : outer[j + 3] * inner[i + 15];
221 : }
222 0 : result[index++] = outer[j + 0] * inner[4] +
223 0 : outer[j + 1] * inner[9] +
224 0 : outer[j + 2] * inner[14] +
225 0 : outer[j + 3] * inner[19] +
226 0 : outer[j + 4];
227 : }
228 0 : }
229 :
230 : ///////////////////////////////////////////////////////////////////////////////
231 : // End duplication
232 : //////
233 :
234 0 : bool SkColorMatrixFilterRowMajor255::onAppendStages(SkRasterPipeline* p,
235 : SkColorSpace* dst,
236 : SkArenaAlloc* scratch,
237 : bool shaderIsOpaque) const {
238 0 : bool willStayOpaque = shaderIsOpaque && (fFlags & kAlphaUnchanged_Flag);
239 0 : bool needsClamp0 = false,
240 0 : needsClamp1 = false;
241 0 : for (int i = 0; i < 4; i++) {
242 0 : SkScalar min = fTranspose[i+16],
243 0 : max = fTranspose[i+16];
244 0 : (fTranspose[i+ 0] < 0 ? min : max) += fTranspose[i+ 0];
245 0 : (fTranspose[i+ 4] < 0 ? min : max) += fTranspose[i+ 4];
246 0 : (fTranspose[i+ 8] < 0 ? min : max) += fTranspose[i+ 8];
247 0 : (fTranspose[i+12] < 0 ? min : max) += fTranspose[i+12];
248 0 : needsClamp0 = needsClamp0 || min < 0;
249 0 : needsClamp1 = needsClamp1 || max > 1;
250 : }
251 :
252 0 : if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); }
253 0 : if ( true) { p->append(SkRasterPipeline::matrix_4x5, fTranspose); }
254 0 : if (!willStayOpaque) { p->append(SkRasterPipeline::premul); }
255 0 : if ( needsClamp0) { p->append(SkRasterPipeline::clamp_0); }
256 0 : if ( needsClamp1) { p->append(SkRasterPipeline::clamp_a); }
257 0 : return true;
258 : }
259 :
260 : sk_sp<SkColorFilter>
261 0 : SkColorMatrixFilterRowMajor255::makeComposed(sk_sp<SkColorFilter> innerFilter) const {
262 : SkScalar innerMatrix[20];
263 0 : if (innerFilter->asColorMatrix(innerMatrix) && !needs_clamping(innerMatrix)) {
264 : SkScalar concat[20];
265 0 : set_concat(concat, fMatrix, innerMatrix);
266 0 : return sk_make_sp<SkColorMatrixFilterRowMajor255>(concat);
267 : }
268 0 : return nullptr;
269 : }
270 :
271 : #if SK_SUPPORT_GPU
272 : #include "GrFragmentProcessor.h"
273 : #include "glsl/GrGLSLFragmentProcessor.h"
274 : #include "glsl/GrGLSLFragmentShaderBuilder.h"
275 : #include "glsl/GrGLSLProgramDataManager.h"
276 : #include "glsl/GrGLSLUniformHandler.h"
277 :
278 0 : class ColorMatrixEffect : public GrFragmentProcessor {
279 : public:
280 0 : static sk_sp<GrFragmentProcessor> Make(const SkScalar matrix[20]) {
281 0 : return sk_sp<GrFragmentProcessor>(new ColorMatrixEffect(matrix));
282 : }
283 :
284 0 : const char* name() const override { return "Color Matrix"; }
285 :
286 : GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
287 :
288 0 : class GLSLProcessor : public GrGLSLFragmentProcessor {
289 : public:
290 : // this class always generates the same code.
291 0 : static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
292 :
293 0 : void emitCode(EmitArgs& args) override {
294 0 : GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
295 : fMatrixHandle = uniformHandler->addUniform(kFragment_GrShaderFlag,
296 : kMat44f_GrSLType, kDefault_GrSLPrecision,
297 0 : "ColorMatrix");
298 : fVectorHandle = uniformHandler->addUniform(kFragment_GrShaderFlag,
299 : kVec4f_GrSLType, kDefault_GrSLPrecision,
300 0 : "ColorMatrixVector");
301 :
302 0 : if (nullptr == args.fInputColor) {
303 : // could optimize this case, but we aren't for now.
304 0 : args.fInputColor = "vec4(1)";
305 : }
306 0 : GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
307 : // The max() is to guard against 0 / 0 during unpremul when the incoming color is
308 : // transparent black.
309 0 : fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n",
310 0 : args.fInputColor);
311 0 : fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
312 : args.fOutputColor,
313 0 : uniformHandler->getUniformCStr(fMatrixHandle),
314 : args.fInputColor,
315 0 : uniformHandler->getUniformCStr(fVectorHandle));
316 0 : fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n",
317 0 : args.fOutputColor, args.fOutputColor);
318 0 : fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
319 0 : }
320 :
321 : protected:
322 0 : void onSetData(const GrGLSLProgramDataManager& uniManager,
323 : const GrFragmentProcessor& proc) override {
324 0 : const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
325 0 : const float* m = cme.fMatrix;
326 : // The GL matrix is transposed from SkColorMatrix.
327 : float mt[] = {
328 0 : m[0], m[5], m[10], m[15],
329 0 : m[1], m[6], m[11], m[16],
330 0 : m[2], m[7], m[12], m[17],
331 0 : m[3], m[8], m[13], m[18],
332 0 : };
333 : static const float kScale = 1.0f / 255.0f;
334 : float vec[] = {
335 0 : m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
336 0 : };
337 0 : uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
338 0 : uniManager.set4fv(fVectorHandle, 1, vec);
339 0 : }
340 :
341 : private:
342 : GrGLSLProgramDataManager::UniformHandle fMatrixHandle;
343 : GrGLSLProgramDataManager::UniformHandle fVectorHandle;
344 :
345 : typedef GrGLSLFragmentProcessor INHERITED;
346 : };
347 : private:
348 : // We could implement the constant input->constant output optimization but haven't. Other
349 : // optimizations would be matrix-dependent.
350 0 : ColorMatrixEffect(const SkScalar matrix[20]) : INHERITED(kNone_OptimizationFlags) {
351 0 : memcpy(fMatrix, matrix, sizeof(SkScalar) * 20);
352 0 : this->initClassID<ColorMatrixEffect>();
353 0 : }
354 :
355 0 : GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
356 0 : return new GLSLProcessor;
357 : }
358 :
359 0 : virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
360 : GrProcessorKeyBuilder* b) const override {
361 0 : GLSLProcessor::GenKey(*this, caps, b);
362 0 : }
363 :
364 0 : bool onIsEqual(const GrFragmentProcessor& s) const override {
365 0 : const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>();
366 0 : return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix));
367 : }
368 :
369 : SkScalar fMatrix[20];
370 :
371 : typedef GrFragmentProcessor INHERITED;
372 : };
373 :
374 : GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect);
375 :
376 : #if GR_TEST_UTILS
377 0 : sk_sp<GrFragmentProcessor> ColorMatrixEffect::TestCreate(GrProcessorTestData* d) {
378 : SkScalar colorMatrix[20];
379 0 : for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) {
380 0 : colorMatrix[i] = d->fRandom->nextSScalar1();
381 : }
382 0 : return ColorMatrixEffect::Make(colorMatrix);
383 : }
384 : #endif
385 :
386 0 : sk_sp<GrFragmentProcessor> SkColorMatrixFilterRowMajor255::asFragmentProcessor(
387 : GrContext*, SkColorSpace*) const {
388 0 : return ColorMatrixEffect::Make(fMatrix);
389 : }
390 :
391 : #endif
392 :
393 : #ifndef SK_IGNORE_TO_STRING
394 0 : void SkColorMatrixFilterRowMajor255::toString(SkString* str) const {
395 0 : str->append("SkColorMatrixFilterRowMajor255: ");
396 :
397 0 : str->append("matrix: (");
398 0 : for (int i = 0; i < 20; ++i) {
399 0 : str->appendScalar(fMatrix[i]);
400 0 : if (i < 19) {
401 0 : str->append(", ");
402 : }
403 : }
404 0 : str->append(")");
405 0 : }
406 : #endif
407 :
408 : ///////////////////////////////////////////////////////////////////////////////
409 :
410 0 : sk_sp<SkColorFilter> SkColorFilter::MakeMatrixFilterRowMajor255(const SkScalar array[20]) {
411 0 : return sk_sp<SkColorFilter>(new SkColorMatrixFilterRowMajor255(array));
412 : }
413 :
414 : ///////////////////////////////////////////////////////////////////////////////
415 :
416 : sk_sp<SkColorFilter>
417 0 : SkColorMatrixFilterRowMajor255::MakeSingleChannelOutput(const SkScalar row[5]) {
418 0 : SkASSERT(row);
419 0 : auto cf = sk_make_sp<SkColorMatrixFilterRowMajor255>();
420 : static_assert(sizeof(SkScalar) * 5 * 4 == sizeof(cf->fMatrix), "sizes don't match");
421 0 : for (int i = 0; i < 4; ++i) {
422 0 : memcpy(cf->fMatrix + 5 * i, row, sizeof(SkScalar) * 5);
423 : }
424 0 : cf->initState();
425 0 : return cf;
426 : }
|