2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkColorMatrixFilter.h"
9 #include "SkColorMatrix.h"
10 #include "SkColorPriv.h"
12 #include "SkReadBuffer.h"
13 #include "SkWriteBuffer.h"
14 #include "SkUnPreMultiply.h"
17 #define SK_PMORDER_INDEX_A (SK_A32_SHIFT / 8)
18 #define SK_PMORDER_INDEX_R (SK_R32_SHIFT / 8)
19 #define SK_PMORDER_INDEX_G (SK_G32_SHIFT / 8)
20 #define SK_PMORDER_INDEX_B (SK_B32_SHIFT / 8)
22 static void transpose_to_pmorder(float dst[20], const float src[20]) {
23 const float* srcR = src + 0;
24 const float* srcG = src + 5;
25 const float* srcB = src + 10;
26 const float* srcA = src + 15;
28 for (int i = 0; i < 20; i += 4) {
29 dst[i + SK_PMORDER_INDEX_A] = *srcA++;
30 dst[i + SK_PMORDER_INDEX_R] = *srcR++;
31 dst[i + SK_PMORDER_INDEX_G] = *srcG++;
32 dst[i + SK_PMORDER_INDEX_B] = *srcB++;
36 static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
37 unsigned b, unsigned a) {
38 return array[0] * r + array[1] * g + array[2] * b + array[3] * a + array[4];
41 static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
43 return array[0] * r + array[1] * g + array[2] * b + array[4];
46 static void General(const SkColorMatrixFilter::State& state,
47 unsigned r, unsigned g, unsigned b, unsigned a,
48 int32_t* SK_RESTRICT result) {
49 const int32_t* SK_RESTRICT array = state.fArray;
50 const int shift = state.fShift;
52 result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
53 result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
54 result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
55 result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
58 static void General16(const SkColorMatrixFilter::State& state,
59 unsigned r, unsigned g, unsigned b, unsigned a,
60 int32_t* SK_RESTRICT result) {
61 const int32_t* SK_RESTRICT array = state.fArray;
63 result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
64 result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
65 result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
66 result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
69 static void AffineAdd(const SkColorMatrixFilter::State& state,
70 unsigned r, unsigned g, unsigned b, unsigned a,
71 int32_t* SK_RESTRICT result) {
72 const int32_t* SK_RESTRICT array = state.fArray;
73 const int shift = state.fShift;
75 result[0] = rowmul3(&array[0], r, g, b) >> shift;
76 result[1] = rowmul3(&array[5], r, g, b) >> shift;
77 result[2] = rowmul3(&array[10], r, g, b) >> shift;
81 static void AffineAdd16(const SkColorMatrixFilter::State& state,
82 unsigned r, unsigned g, unsigned b, unsigned a,
83 int32_t* SK_RESTRICT result) {
84 const int32_t* SK_RESTRICT array = state.fArray;
86 result[0] = rowmul3(&array[0], r, g, b) >> 16;
87 result[1] = rowmul3(&array[5], r, g, b) >> 16;
88 result[2] = rowmul3(&array[10], r, g, b) >> 16;
92 static void ScaleAdd(const SkColorMatrixFilter::State& state,
93 unsigned r, unsigned g, unsigned b, unsigned a,
94 int32_t* SK_RESTRICT result) {
95 const int32_t* SK_RESTRICT array = state.fArray;
96 const int shift = state.fShift;
98 // cast to (int) to keep the expression signed for the shift
99 result[0] = (array[SkColorMatrix::kR_Scale] * (int)r + array[4]) >> shift;
100 result[1] = (array[SkColorMatrix::kG_Scale] * (int)g + array[9]) >> shift;
101 result[2] = (array[SkColorMatrix::kB_Scale] * (int)b + array[14]) >> shift;
105 static void ScaleAdd16(const SkColorMatrixFilter::State& state,
106 unsigned r, unsigned g, unsigned b, unsigned a,
107 int32_t* SK_RESTRICT result) {
108 const int32_t* SK_RESTRICT array = state.fArray;
110 // cast to (int) to keep the expression signed for the shift
111 result[0] = (array[SkColorMatrix::kR_Scale] * (int)r + array[4]) >> 16;
112 result[1] = (array[SkColorMatrix::kG_Scale] * (int)g + array[9]) >> 16;
113 result[2] = (array[SkColorMatrix::kB_Scale] * (int)b + array[14]) >> 16;
117 static void Add(const SkColorMatrixFilter::State& state,
118 unsigned r, unsigned g, unsigned b, unsigned a,
119 int32_t* SK_RESTRICT result) {
120 const int32_t* SK_RESTRICT array = state.fArray;
121 const int shift = state.fShift;
123 result[0] = r + (array[SkColorMatrix::kR_Trans] >> shift);
124 result[1] = g + (array[SkColorMatrix::kG_Trans] >> shift);
125 result[2] = b + (array[SkColorMatrix::kB_Trans] >> shift);
129 static void Add16(const SkColorMatrixFilter::State& state,
130 unsigned r, unsigned g, unsigned b, unsigned a,
131 int32_t* SK_RESTRICT result) {
132 const int32_t* SK_RESTRICT array = state.fArray;
134 result[0] = r + (array[SkColorMatrix::kR_Trans] >> 16);
135 result[1] = g + (array[SkColorMatrix::kG_Trans] >> 16);
136 result[2] = b + (array[SkColorMatrix::kB_Trans] >> 16);
140 // src is [20] but some compilers won't accept __restrict__ on anything
141 // but an raw pointer or reference
142 void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
143 transpose_to_pmorder(fTranspose, src);
145 int32_t* array = fState.fArray;
147 for (int i = 0; i < 20; i++) {
148 SkFixed value = SkScalarToFixed(src[i]);
150 value = SkAbs32(value);
151 max = SkMax32(max, value);
154 /* All of fArray[] values must fit in 23 bits, to safely allow me to
155 multiply them by 8bit unsigned values, and get a signed answer without
156 overflow. This means clz needs to be 9 or bigger
158 int bits = SkCLZ(max);
159 int32_t one = SK_Fixed1;
161 fState.fShift = 16; // we are starting out as fixed 16.16
164 fState.fShift -= bits;
165 for (int i = 0; i < 20; i++) {
171 // check if we have to munge Alpha
172 int32_t changesAlpha = (array[15] | array[16] | array[17] |
173 (array[18] - one) | array[19]);
174 int32_t usesAlpha = (array[3] | array[8] | array[13]);
175 bool shiftIs16 = (16 == fState.fShift);
177 if (changesAlpha | usesAlpha) {
178 fProc = shiftIs16 ? General16 : General;
179 fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
181 fFlags = SkColorFilter::kAlphaUnchanged_Flag;
183 int32_t needsScale = (array[SkColorMatrix::kR_Scale] - one) |
184 (array[SkColorMatrix::kG_Scale] - one) |
185 (array[SkColorMatrix::kB_Scale] - one);
187 int32_t needs3x3 = array[1] | array[2] | // red off-axis
188 array[5] | array[7] | // green off-axis
189 array[10] | array[11]; // blue off-axis
192 fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
193 } else if (needsScale) {
194 fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
195 } else if (array[SkColorMatrix::kR_Trans] |
196 array[SkColorMatrix::kG_Trans] |
197 array[SkColorMatrix::kB_Trans]) {
198 fProc = shiftIs16 ? Add16 : Add;
200 fProc = nullptr; // identity
204 /* preround our add values so we get a rounded shift. We do this after we
205 analyze the array, so we don't miss the case where the caller has zeros
206 which could make us accidentally take the General or Add case.
209 int32_t add = 1 << (fState.fShift - 1);
217 ///////////////////////////////////////////////////////////////////////////////
219 static int32_t pin(int32_t value, int32_t max) {
229 SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
230 this->initState(cm.fMat);
233 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
234 memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
235 this->initState(array);
238 uint32_t SkColorMatrixFilter::getFlags() const {
239 return this->INHERITED::getFlags() | fFlags;
242 static Sk4f scale_rgb(float scale) {
243 static_assert(SK_A32_SHIFT == 24, "Alpha is lane 3");
244 return Sk4f(scale, scale, scale, 1);
247 static Sk4f premul(const Sk4f& x) {
248 return x * scale_rgb(x.kth<SK_A32_SHIFT/8>());
251 static Sk4f unpremul(const Sk4f& x) {
252 return x * scale_rgb(1 / x.kth<SK_A32_SHIFT/8>()); // TODO: fast/approx invert?
255 static Sk4f clamp_0_1(const Sk4f& x) {
256 return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0));
259 static SkPMColor round(const Sk4f& x) {
261 (x * Sk4f(255) + Sk4f(0.5f)).toBytes((uint8_t*)&c);
265 void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
267 if (nullptr == proc) {
269 memcpy(dst, src, count * sizeof(SkPMColor));
274 #ifdef SK_SUPPORT_LEGACY_INT_COLORMATRIX
275 const bool use_floats = false;
277 const bool use_floats = true;
281 // c0-c3 are already in [0,1].
282 const Sk4f c0 = Sk4f::Load(fTranspose + 0);
283 const Sk4f c1 = Sk4f::Load(fTranspose + 4);
284 const Sk4f c2 = Sk4f::Load(fTranspose + 8);
285 const Sk4f c3 = Sk4f::Load(fTranspose + 12);
286 // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1].
287 const Sk4f c4 = Sk4f::Load(fTranspose + 16)*Sk4f(1.0f/255);
289 // todo: we could cache this in the constructor...
290 SkPMColor matrix_translate_pmcolor = round(premul(clamp_0_1(c4)));
292 for (int i = 0; i < count; i++) {
293 const SkPMColor src_c = src[i];
295 dst[i] = matrix_translate_pmcolor;
299 Sk4f srcf = Sk4f::FromBytes((const uint8_t*)&src_c) * Sk4f(1.0f/255);
301 if (0xFF != SkGetPackedA32(src_c)) {
302 srcf = unpremul(srcf);
305 Sk4f r4 = SkNx_dup<SK_R32_SHIFT/8>(srcf);
306 Sk4f g4 = SkNx_dup<SK_G32_SHIFT/8>(srcf);
307 Sk4f b4 = SkNx_dup<SK_B32_SHIFT/8>(srcf);
308 Sk4f a4 = SkNx_dup<SK_A32_SHIFT/8>(srcf);
311 Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4;
313 // clamp, re-premul, and write
314 dst[i] = round(premul(clamp_0_1(dst4)));
317 const State& state = fState;
319 const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
321 for (int i = 0; i < count; i++) {
322 SkPMColor c = src[i];
324 unsigned r = SkGetPackedR32(c);
325 unsigned g = SkGetPackedG32(c);
326 unsigned b = SkGetPackedB32(c);
327 unsigned a = SkGetPackedA32(c);
329 // need our components to be un-premultiplied
331 SkUnPreMultiply::Scale scale = table[a];
332 r = SkUnPreMultiply::ApplyScale(scale, r);
333 g = SkUnPreMultiply::ApplyScale(scale, g);
334 b = SkUnPreMultiply::ApplyScale(scale, b);
341 proc(state, r, g, b, a, result);
343 r = pin(result[0], SK_R32_MASK);
344 g = pin(result[1], SK_G32_MASK);
345 b = pin(result[2], SK_B32_MASK);
346 a = pin(result[3], SK_A32_MASK);
347 // re-prepremultiply if needed
348 dst[i] = SkPremultiplyARGBInline(a, r, g, b);
353 ///////////////////////////////////////////////////////////////////////////////
355 void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const {
356 SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
357 buffer.writeScalarArray(fMatrix.fMat, 20);
360 SkFlattenable* SkColorMatrixFilter::CreateProc(SkReadBuffer& buffer) {
361 SkColorMatrix matrix;
362 if (buffer.readScalarArray(matrix.fMat, 20)) {
363 return Create(matrix);
368 bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
370 memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
375 SkColorFilter* SkColorMatrixFilter::newComposed(const SkColorFilter* innerFilter) const {
376 SkScalar innerMatrix[20];
377 if (innerFilter->asColorMatrix(innerMatrix) && !SkColorMatrix::NeedsClamping(innerMatrix)) {
379 SkColorMatrix::SetConcat(concat, fMatrix.fMat, innerMatrix);
380 return SkColorMatrixFilter::Create(concat);
386 #include "GrFragmentProcessor.h"
387 #include "GrInvariantOutput.h"
388 #include "glsl/GrGLSLFragmentProcessor.h"
389 #include "glsl/GrGLSLFragmentShaderBuilder.h"
390 #include "glsl/GrGLSLProgramDataManager.h"
391 #include "glsl/GrGLSLUniformHandler.h"
393 class ColorMatrixEffect : public GrFragmentProcessor {
395 static const GrFragmentProcessor* Create(const SkColorMatrix& matrix) {
396 return new ColorMatrixEffect(matrix);
399 const char* name() const override { return "Color Matrix"; }
401 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
403 class GLSLProcessor : public GrGLSLFragmentProcessor {
405 // this class always generates the same code.
406 static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {}
408 GLSLProcessor(const GrProcessor&) {}
410 virtual void emitCode(EmitArgs& args) override {
411 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
412 fMatrixHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility,
413 kMat44f_GrSLType, kDefault_GrSLPrecision,
415 fVectorHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility,
416 kVec4f_GrSLType, kDefault_GrSLPrecision,
417 "ColorMatrixVector");
419 if (nullptr == args.fInputColor) {
420 // could optimize this case, but we aren't for now.
421 args.fInputColor = "vec4(1)";
423 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
424 // The max() is to guard against 0 / 0 during unpremul when the incoming color is
425 // transparent black.
426 fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n",
428 fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
430 uniformHandler->getUniformCStr(fMatrixHandle),
432 uniformHandler->getUniformCStr(fVectorHandle));
433 fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n",
434 args.fOutputColor, args.fOutputColor);
435 fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
439 virtual void onSetData(const GrGLSLProgramDataManager& uniManager,
440 const GrProcessor& proc) override {
441 const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
442 const float* m = cme.fMatrix.fMat;
443 // The GL matrix is transposed from SkColorMatrix.
445 m[0], m[5], m[10], m[15],
446 m[1], m[6], m[11], m[16],
447 m[2], m[7], m[12], m[17],
448 m[3], m[8], m[13], m[18],
450 static const float kScale = 1.0f / 255.0f;
452 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
454 uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
455 uniManager.set4fv(fVectorHandle, 1, vec);
459 GrGLSLProgramDataManager::UniformHandle fMatrixHandle;
460 GrGLSLProgramDataManager::UniformHandle fVectorHandle;
462 typedef GrGLSLFragmentProcessor INHERITED;
466 ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {
467 this->initClassID<ColorMatrixEffect>();
470 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
471 return new GLSLProcessor(*this);
474 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
475 GrProcessorKeyBuilder* b) const override {
476 GLSLProcessor::GenKey(*this, caps, b);
479 bool onIsEqual(const GrFragmentProcessor& s) const override {
480 const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>();
481 return cme.fMatrix == fMatrix;
484 void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
485 // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
486 // type flags it might be worth checking the other components.
488 // The matrix is defined such the 4th row determines the output alpha. The first four
489 // columns of that row multiply the input r, g, b, and a, respectively, and the last column
490 // is the "translation".
491 static const uint32_t kRGBAFlags[] = {
492 kR_GrColorComponentFlag,
493 kG_GrColorComponentFlag,
494 kB_GrColorComponentFlag,
495 kA_GrColorComponentFlag
497 static const int kShifts[] = {
498 GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
501 kAlphaRowStartIdx = 15,
502 kAlphaRowTranslateIdx = 19,
505 SkScalar outputA = 0;
506 for (int i = 0; i < 4; ++i) {
507 // If any relevant component of the color to be passed through the matrix is non-const
508 // then we can't know the final result.
509 if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
510 if (!(inout->validFlags() & kRGBAFlags[i])) {
511 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
514 uint32_t component = (inout->color() >> kShifts[i]) & 0xFF;
515 outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
519 outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
520 // We pin the color to [0,1]. This would happen to the *final* color output from the frag
521 // shader but currently the effect does not pin its own output. So in the case of over/
522 // underflow this may deviate from the actual result. Maybe the effect should pin its
523 // result if the matrix could over/underflow for any component?
524 inout->setToOther(kA_GrColorComponentFlag,
525 static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A,
526 GrInvariantOutput::kWill_ReadInput);
529 SkColorMatrix fMatrix;
531 typedef GrFragmentProcessor INHERITED;
534 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect);
536 const GrFragmentProcessor* ColorMatrixEffect::TestCreate(GrProcessorTestData* d) {
537 SkColorMatrix colorMatrix;
538 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
539 colorMatrix.fMat[i] = d->fRandom->nextSScalar1();
541 return ColorMatrixEffect::Create(colorMatrix);
544 const GrFragmentProcessor* SkColorMatrixFilter::asFragmentProcessor(GrContext*) const {
545 return ColorMatrixEffect::Create(fMatrix);
550 #ifndef SK_IGNORE_TO_STRING
551 void SkColorMatrixFilter::toString(SkString* str) const {
552 str->append("SkColorMatrixFilter: ");
554 str->append("matrix: (");
555 for (int i = 0; i < 20; ++i) {
556 str->appendScalar(fMatrix.fMat[i]);