From 8610002ff81fb5d81d1b7c312b5d0a8b05b41e13 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Mon, 1 Feb 2016 12:09:07 -0800 Subject: [PATCH] Move SkColorMatrixFilter implementation to core. GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1648933002 Review URL: https://codereview.chromium.org/1648933002 --- gyp/core.gypi | 2 + gyp/effects.gypi | 2 +- include/core/SkColorFilter.h | 5 + include/effects/SkColorMatrixFilter.h | 32 +- src/core/SkColorMatrixFilterRowMajor255.cpp | 422 +++++++++++++++++++++++++++ src/core/SkColorMatrixFilterRowMajor255.h | 44 +++ src/effects/SkColorMatrixFilter.cpp | 360 ----------------------- src/ports/SkGlobalInitialization_default.cpp | 4 +- tests/ImageFilterTest.cpp | 1 + 9 files changed, 479 insertions(+), 393 deletions(-) create mode 100644 src/core/SkColorMatrixFilterRowMajor255.cpp create mode 100644 src/core/SkColorMatrixFilterRowMajor255.h diff --git a/gyp/core.gypi b/gyp/core.gypi index b80f43a..80f0eac 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -71,6 +71,8 @@ '<(skia_src_path)/core/SkColor.cpp', '<(skia_src_path)/core/SkColorFilter.cpp', '<(skia_src_path)/core/SkColorFilterShader.cpp', + '<(skia_src_path)/core/SkColorMatrixFilterRowMajor255.cpp', + '<(skia_src_path)/core/SkColorMatrixFilterRowMajor255.h', '<(skia_src_path)/core/SkColorShader.h', '<(skia_src_path)/core/SkColorTable.cpp', '<(skia_src_path)/core/SkComposeShader.cpp', diff --git a/gyp/effects.gypi b/gyp/effects.gypi index ea750b9..bbda38a 100644 --- a/gyp/effects.gypi +++ b/gyp/effects.gypi @@ -89,9 +89,9 @@ '<(skia_include_path)/effects/SkBlurImageFilter.h', '<(skia_include_path)/effects/SkBlurMaskFilter.h', '<(skia_include_path)/effects/SkColorCubeFilter.h', + '<(skia_include_path)/effects/SkColorFilterImageFilter.h', '<(skia_include_path)/effects/SkColorMatrix.h', '<(skia_include_path)/effects/SkColorMatrixFilter.h', - '<(skia_include_path)/effects/SkColorFilterImageFilter.h', '<(skia_include_path)/effects/SkCornerPathEffect.h', '<(skia_include_path)/effects/SkDashPathEffect.h', '<(skia_include_path)/effects/SkDiscretePathEffect.h', diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h index aece22e..14192c5 100644 --- a/include/core/SkColorFilter.h +++ b/include/core/SkColorFilter.h @@ -122,6 +122,11 @@ public: */ static SkColorFilter* CreateComposeFilter(SkColorFilter* outer, SkColorFilter* inner); + /** Construct a color filter that transforms a color by a 4x5 matrix. The matrix is in row- + * major order and the translation column is specified in unnormalized, 0...255, space. + */ + static SkColorFilter* CreateMatrixFilterRowMajor255(const SkScalar array[20]); + /** * A subclass may implement this factory function to work with the GPU backend. It returns * a GrFragmentProcessor that implemets the color filter in GPU shader code. diff --git a/include/effects/SkColorMatrixFilter.h b/include/effects/SkColorMatrixFilter.h index 3edf791..d71d2ed 100644 --- a/include/effects/SkColorMatrixFilter.h +++ b/include/effects/SkColorMatrixFilter.h @@ -14,10 +14,10 @@ class SK_API SkColorMatrixFilter : public SkColorFilter { public: static SkColorFilter* Create(const SkColorMatrix& cm) { - return new SkColorMatrixFilter(cm); + return SkColorFilter::CreateMatrixFilterRowMajor255(cm.fMat); } static SkColorFilter* Create(const SkScalar array[20]) { - return new SkColorMatrixFilter(array); + return SkColorFilter::CreateMatrixFilterRowMajor255(array); } /** @@ -27,34 +27,6 @@ public: * are ignored. */ static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add); - - void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; - void filterSpan4f(const SkPM4f src[], int count, SkPM4f[]) const override; - uint32_t getFlags() const override; - bool asColorMatrix(SkScalar matrix[20]) const override; - SkColorFilter* newComposed(const SkColorFilter*) const override; - -#if SK_SUPPORT_GPU - const GrFragmentProcessor* asFragmentProcessor(GrContext*) const override; -#endif - - SK_TO_STRING_OVERRIDE() - - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter) - -protected: - explicit SkColorMatrixFilter(const SkColorMatrix&); - explicit SkColorMatrixFilter(const SkScalar array[20]); - void flatten(SkWriteBuffer&) const override; - -private: - SkColorMatrix fMatrix; - float fTranspose[SkColorMatrix::kCount]; // for Sk4s - uint32_t fFlags; - - void initState(const SkScalar array[20]); - - typedef SkColorFilter INHERITED; }; #endif diff --git a/src/core/SkColorMatrixFilterRowMajor255.cpp b/src/core/SkColorMatrixFilterRowMajor255.cpp new file mode 100644 index 0000000..6fdfa0b --- /dev/null +++ b/src/core/SkColorMatrixFilterRowMajor255.cpp @@ -0,0 +1,422 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorMatrixFilterRowMajor255.h" +#include "SkColorPriv.h" +#include "SkNx.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "SkUnPreMultiply.h" +#include "SkString.h" + +#define SK_PMORDER_INDEX_A (SK_A32_SHIFT / 8) +#define SK_PMORDER_INDEX_R (SK_R32_SHIFT / 8) +#define SK_PMORDER_INDEX_G (SK_G32_SHIFT / 8) +#define SK_PMORDER_INDEX_B (SK_B32_SHIFT / 8) + +static void transpose_to_pmorder(float dst[20], const float src[20]) { + const float* srcR = src + 0; + const float* srcG = src + 5; + const float* srcB = src + 10; + const float* srcA = src + 15; + + for (int i = 0; i < 20; i += 4) { + dst[i + SK_PMORDER_INDEX_A] = *srcA++; + dst[i + SK_PMORDER_INDEX_R] = *srcR++; + dst[i + SK_PMORDER_INDEX_G] = *srcG++; + dst[i + SK_PMORDER_INDEX_B] = *srcB++; + } +} + +// src is [20] but some compilers won't accept __restrict__ on anything +// but an raw pointer or reference +void SkColorMatrixFilterRowMajor255::initState(const SkScalar* SK_RESTRICT src) { + transpose_to_pmorder(fTranspose, src); + + const float* array = fMatrix; + + // check if we have to munge Alpha + bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]); + bool usesAlpha = (array[3] || array[8] || array[13]); + + if (changesAlpha || usesAlpha) { + fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag; + } else { + fFlags = kAlphaUnchanged_Flag; + } + fFlags |= kSupports4f_Flag; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) { + memcpy(fMatrix, array, 20 * sizeof(SkScalar)); + this->initState(array); +} + +uint32_t SkColorMatrixFilterRowMajor255::getFlags() const { + return this->INHERITED::getFlags() | fFlags; +} + +static Sk4f scale_rgb(float scale) { + static_assert(SkPM4f::A == 3, "Alpha is lane 3"); + return Sk4f(scale, scale, scale, 1); +} + +static Sk4f premul(const Sk4f& x) { + return x * scale_rgb(x.kth()); +} + +static Sk4f unpremul(const Sk4f& x) { + return x * scale_rgb(1 / x.kth()); // TODO: fast/approx invert? +} + +static Sk4f clamp_0_1(const Sk4f& x) { + return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0)); +} + +static SkPMColor round(const Sk4f& x) { + SkPMColor c; + SkNx_cast(x * Sk4f(255) + Sk4f(0.5f)).store(&c); + return c; +} + +template +void filter_span(const float array[], const T src[], int count, T dst[]) { + // c0-c3 are already in [0,1]. + const Sk4f c0 = Sk4f::Load(array + 0); + const Sk4f c1 = Sk4f::Load(array + 4); + const Sk4f c2 = Sk4f::Load(array + 8); + const Sk4f c3 = Sk4f::Load(array + 12); + // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. + const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255); + + // todo: we could cache this in the constructor... + T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4))); + + for (int i = 0; i < count; i++) { + Sk4f srcf = Adaptor::To4f(src[i]); + float srcA = srcf.kth(); + + if (0 == srcA) { + dst[i] = matrix_translate_pmcolor; + continue; + } + if (1 != srcA) { + srcf = unpremul(srcf); + } + + Sk4f r4 = SkNx_dup(srcf); + Sk4f g4 = SkNx_dup(srcf); + Sk4f b4 = SkNx_dup(srcf); + Sk4f a4 = SkNx_dup(srcf); + + // apply matrix + Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; + + dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4))); + } +} + +struct SkPMColorAdaptor { + static SkPMColor From4f(const Sk4f& c4) { + return round(c4); + } + static Sk4f To4f(SkPMColor c) { + return SkNx_cast(Sk4b::Load(&c)) * Sk4f(1.0f/255); + } +}; +void SkColorMatrixFilterRowMajor255::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { + filter_span(fTranspose, src, count, dst); +} + +struct SkPM4fAdaptor { + static SkPM4f From4f(const Sk4f& c4) { + SkPM4f c; + c4.store(&c); + return c; + } + static Sk4f To4f(const SkPM4f& c) { + return Sk4f::Load(&c); + } +}; +void SkColorMatrixFilterRowMajor255::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const { + filter_span(fTranspose, src, count, dst); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const { + SkASSERT(sizeof(fMatrix)/sizeof(SkScalar) == 20); + buffer.writeScalarArray(fMatrix, 20); +} + +SkFlattenable* SkColorMatrixFilterRowMajor255::CreateProc(SkReadBuffer& buffer) { + SkScalar matrix[20]; + if (buffer.readScalarArray(matrix, 20)) { + return new SkColorMatrixFilterRowMajor255(matrix); + } + return nullptr; +} + +bool SkColorMatrixFilterRowMajor255::asColorMatrix(SkScalar matrix[20]) const { + if (matrix) { + memcpy(matrix, fMatrix, 20 * sizeof(SkScalar)); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// This code was duplicated from src/effects/SkColorMatrixc.cpp in order to be used in core. +////// + +// To detect if we need to apply clamping after applying a matrix, we check if +// any output component might go outside of [0, 255] for any combination of +// input components in [0..255]. +// Each output component is an affine transformation of the input component, so +// the minimum and maximum values are for any combination of minimum or maximum +// values of input components (i.e. 0 or 255). +// E.g. if R' = x*R + y*G + z*B + w*A + t +// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the +// minimum value will be for R=0 if x>0 or R=255 if x<0. +// Same goes for all components. +static bool component_needs_clamping(const SkScalar row[5]) { + SkScalar maxValue = row[4] / 255; + SkScalar minValue = row[4] / 255; + for (int i = 0; i < 4; ++i) { + if (row[i] > 0) + maxValue += row[i]; + else + minValue += row[i]; + } + return (maxValue > 1) || (minValue < 0); +} + +static bool needs_clamping(const SkScalar matrix[20]) { + return component_needs_clamping(matrix) + || component_needs_clamping(matrix+5) + || component_needs_clamping(matrix+10) + || component_needs_clamping(matrix+15); +} + +static void set_concat(SkScalar result[20], const SkScalar outer[20], const SkScalar inner[20]) { + int index = 0; + for (int j = 0; j < 20; j += 5) { + for (int i = 0; i < 4; i++) { + result[index++] = outer[j + 0] * inner[i + 0] + + outer[j + 1] * inner[i + 5] + + outer[j + 2] * inner[i + 10] + + outer[j + 3] * inner[i + 15]; + } + result[index++] = outer[j + 0] * inner[4] + + outer[j + 1] * inner[9] + + outer[j + 2] * inner[14] + + outer[j + 3] * inner[19] + + outer[j + 4]; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// End duplication +////// + +SkColorFilter* SkColorMatrixFilterRowMajor255::newComposed(const SkColorFilter* innerFilter) const { + SkScalar innerMatrix[20]; + if (innerFilter->asColorMatrix(innerMatrix) && !needs_clamping(innerMatrix)) { + SkScalar concat[20]; + set_concat(concat, fMatrix, innerMatrix); + return new SkColorMatrixFilterRowMajor255(concat); + } + return nullptr; +} + +#if SK_SUPPORT_GPU +#include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" + +class ColorMatrixEffect : public GrFragmentProcessor { +public: + static const GrFragmentProcessor* Create(const SkScalar matrix[20]) { + return new ColorMatrixEffect(matrix); + } + + const char* name() const override { return "Color Matrix"; } + + GR_DECLARE_FRAGMENT_PROCESSOR_TEST; + + class GLSLProcessor : public GrGLSLFragmentProcessor { + public: + // this class always generates the same code. + static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {} + + GLSLProcessor(const GrProcessor&) {} + + virtual void emitCode(EmitArgs& args) override { + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fMatrixHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kMat44f_GrSLType, kDefault_GrSLPrecision, + "ColorMatrix"); + fVectorHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "ColorMatrixVector"); + + if (nullptr == args.fInputColor) { + // could optimize this case, but we aren't for now. + args.fInputColor = "vec4(1)"; + } + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + // The max() is to guard against 0 / 0 during unpremul when the incoming color is + // transparent black. + fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", + args.fInputColor); + fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n", + args.fOutputColor, + uniformHandler->getUniformCStr(fMatrixHandle), + args.fInputColor, + uniformHandler->getUniformCStr(fVectorHandle)); + fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", + args.fOutputColor, args.fOutputColor); + fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor); + } + + protected: + virtual void onSetData(const GrGLSLProgramDataManager& uniManager, + const GrProcessor& proc) override { + const ColorMatrixEffect& cme = proc.cast(); + const float* m = cme.fMatrix; + // The GL matrix is transposed from SkColorMatrix. + float mt[] = { + m[0], m[5], m[10], m[15], + m[1], m[6], m[11], m[16], + m[2], m[7], m[12], m[17], + m[3], m[8], m[13], m[18], + }; + static const float kScale = 1.0f / 255.0f; + float vec[] = { + m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale, + }; + uniManager.setMatrix4fv(fMatrixHandle, 1, mt); + uniManager.set4fv(fVectorHandle, 1, vec); + } + + private: + GrGLSLProgramDataManager::UniformHandle fMatrixHandle; + GrGLSLProgramDataManager::UniformHandle fVectorHandle; + + typedef GrGLSLFragmentProcessor INHERITED; + }; + +private: + ColorMatrixEffect(const SkScalar matrix[20]) { + memcpy(fMatrix, matrix, sizeof(SkScalar) * 20); + this->initClassID(); + } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + return new GLSLProcessor(*this); + } + + virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps, + GrProcessorKeyBuilder* b) const override { + GLSLProcessor::GenKey(*this, caps, b); + } + + bool onIsEqual(const GrFragmentProcessor& s) const override { + const ColorMatrixEffect& cme = s.cast(); + return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix)); + } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had + // type flags it might be worth checking the other components. + + // The matrix is defined such the 4th row determines the output alpha. The first four + // columns of that row multiply the input r, g, b, and a, respectively, and the last column + // is the "translation". + static const uint32_t kRGBAFlags[] = { + kR_GrColorComponentFlag, + kG_GrColorComponentFlag, + kB_GrColorComponentFlag, + kA_GrColorComponentFlag + }; + static const int kShifts[] = { + GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A, + }; + enum { + kAlphaRowStartIdx = 15, + kAlphaRowTranslateIdx = 19, + }; + + SkScalar outputA = 0; + for (int i = 0; i < 4; ++i) { + // If any relevant component of the color to be passed through the matrix is non-const + // then we can't know the final result. + if (0 != fMatrix[kAlphaRowStartIdx + i]) { + if (!(inout->validFlags() & kRGBAFlags[i])) { + inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); + return; + } else { + uint32_t component = (inout->color() >> kShifts[i]) & 0xFF; + outputA += fMatrix[kAlphaRowStartIdx + i] * component; + } + } + } + outputA += fMatrix[kAlphaRowTranslateIdx]; + // We pin the color to [0,1]. This would happen to the *final* color output from the frag + // shader but currently the effect does not pin its own output. So in the case of over/ + // underflow this may deviate from the actual result. Maybe the effect should pin its + // result if the matrix could over/underflow for any component? + inout->setToOther(kA_GrColorComponentFlag, + static_cast(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A, + GrInvariantOutput::kWill_ReadInput); + } + + SkScalar fMatrix[20]; + + typedef GrFragmentProcessor INHERITED; +}; + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect); + +const GrFragmentProcessor* ColorMatrixEffect::TestCreate(GrProcessorTestData* d) { + SkScalar colorMatrix[20]; + for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) { + colorMatrix[i] = d->fRandom->nextSScalar1(); + } + return ColorMatrixEffect::Create(colorMatrix); +} + +const GrFragmentProcessor* SkColorMatrixFilterRowMajor255::asFragmentProcessor(GrContext*) const { + return ColorMatrixEffect::Create(fMatrix); +} + +#endif + +#ifndef SK_IGNORE_TO_STRING +void SkColorMatrixFilterRowMajor255::toString(SkString* str) const { + str->append("SkColorMatrixFilterRowMajor255: "); + + str->append("matrix: ("); + for (int i = 0; i < 20; ++i) { + str->appendScalar(fMatrix[i]); + if (i < 19) { + str->append(", "); + } + } + str->append(")"); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +SkColorFilter* SkColorFilter::CreateMatrixFilterRowMajor255(const SkScalar array[20]) { + return new SkColorMatrixFilterRowMajor255(array); +} diff --git a/src/core/SkColorMatrixFilterRowMajor255.h b/src/core/SkColorMatrixFilterRowMajor255.h new file mode 100644 index 0000000..453dd41 --- /dev/null +++ b/src/core/SkColorMatrixFilterRowMajor255.h @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorMatrixFilter_DEFINED +#define SkColorMatrixFilter_DEFINED + +#include "SkColorFilter.h" + +class SK_API SkColorMatrixFilterRowMajor255 : public SkColorFilter { +public: + explicit SkColorMatrixFilterRowMajor255(const SkScalar array[20]); + + void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; + void filterSpan4f(const SkPM4f src[], int count, SkPM4f[]) const override; + uint32_t getFlags() const override; + bool asColorMatrix(SkScalar matrix[20]) const override; + SkColorFilter* newComposed(const SkColorFilter*) const override; + +#if SK_SUPPORT_GPU + const GrFragmentProcessor* asFragmentProcessor(GrContext*) const override; +#endif + + SK_TO_STRING_OVERRIDE() + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter) + +protected: + void flatten(SkWriteBuffer&) const override; + +private: + SkScalar fMatrix[20]; + float fTranspose[20]; // for Sk4s + uint32_t fFlags; + + void initState(const SkScalar array[20]); + + typedef SkColorFilter INHERITED; +}; + +#endif diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp index a0878a5..e122b6a 100644 --- a/src/effects/SkColorMatrixFilter.cpp +++ b/src/effects/SkColorMatrixFilter.cpp @@ -6,366 +6,6 @@ */ #include "SkColorMatrixFilter.h" -#include "SkColorMatrix.h" -#include "SkColorPriv.h" -#include "SkNx.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -#include "SkUnPreMultiply.h" -#include "SkString.h" - -#define SK_PMORDER_INDEX_A (SK_A32_SHIFT / 8) -#define SK_PMORDER_INDEX_R (SK_R32_SHIFT / 8) -#define SK_PMORDER_INDEX_G (SK_G32_SHIFT / 8) -#define SK_PMORDER_INDEX_B (SK_B32_SHIFT / 8) - -static void transpose_to_pmorder(float dst[20], const float src[20]) { - const float* srcR = src + 0; - const float* srcG = src + 5; - const float* srcB = src + 10; - const float* srcA = src + 15; - - for (int i = 0; i < 20; i += 4) { - dst[i + SK_PMORDER_INDEX_A] = *srcA++; - dst[i + SK_PMORDER_INDEX_R] = *srcR++; - dst[i + SK_PMORDER_INDEX_G] = *srcG++; - dst[i + SK_PMORDER_INDEX_B] = *srcB++; - } -} - -// src is [20] but some compilers won't accept __restrict__ on anything -// but an raw pointer or reference -void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) { - transpose_to_pmorder(fTranspose, src); - - const float* array = fMatrix.fMat; - - // check if we have to munge Alpha - bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]); - bool usesAlpha = (array[3] || array[8] || array[13]); - - if (changesAlpha || usesAlpha) { - fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag; - } else { - fFlags = kAlphaUnchanged_Flag; - } - fFlags |= kSupports4f_Flag; -} - -/////////////////////////////////////////////////////////////////////////////// - -SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) { - this->initState(cm.fMat); -} - -SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) { - memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar)); - this->initState(array); -} - -uint32_t SkColorMatrixFilter::getFlags() const { - return this->INHERITED::getFlags() | fFlags; -} - -static Sk4f scale_rgb(float scale) { - static_assert(SkPM4f::A == 3, "Alpha is lane 3"); - return Sk4f(scale, scale, scale, 1); -} - -static Sk4f premul(const Sk4f& x) { - return x * scale_rgb(x.kth()); -} - -static Sk4f unpremul(const Sk4f& x) { - return x * scale_rgb(1 / x.kth()); // TODO: fast/approx invert? -} - -static Sk4f clamp_0_1(const Sk4f& x) { - return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0)); -} - -static SkPMColor round(const Sk4f& x) { - SkPMColor c; - SkNx_cast(x * Sk4f(255) + Sk4f(0.5f)).store(&c); - return c; -} - -template -void filter_span(const float array[], const T src[], int count, T dst[]) { - // c0-c3 are already in [0,1]. - const Sk4f c0 = Sk4f::Load(array + 0); - const Sk4f c1 = Sk4f::Load(array + 4); - const Sk4f c2 = Sk4f::Load(array + 8); - const Sk4f c3 = Sk4f::Load(array + 12); - // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. - const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255); - - // todo: we could cache this in the constructor... - T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4))); - - for (int i = 0; i < count; i++) { - Sk4f srcf = Adaptor::To4f(src[i]); - float srcA = srcf.kth(); - - if (0 == srcA) { - dst[i] = matrix_translate_pmcolor; - continue; - } - if (1 != srcA) { - srcf = unpremul(srcf); - } - - Sk4f r4 = SkNx_dup(srcf); - Sk4f g4 = SkNx_dup(srcf); - Sk4f b4 = SkNx_dup(srcf); - Sk4f a4 = SkNx_dup(srcf); - - // apply matrix - Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; - - dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4))); - } -} - -struct SkPMColorAdaptor { - static SkPMColor From4f(const Sk4f& c4) { - return round(c4); - } - static Sk4f To4f(SkPMColor c) { - return SkNx_cast(Sk4b::Load(&c)) * Sk4f(1.0f/255); - } -}; -void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { - filter_span(fTranspose, src, count, dst); -} - -struct SkPM4fAdaptor { - static SkPM4f From4f(const Sk4f& c4) { - SkPM4f c; - c4.store(&c); - return c; - } - static Sk4f To4f(const SkPM4f& c) { - return Sk4f::Load(&c); - } -}; -void SkColorMatrixFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const { - filter_span(fTranspose, src, count, dst); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const { - SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20); - buffer.writeScalarArray(fMatrix.fMat, 20); -} - -SkFlattenable* SkColorMatrixFilter::CreateProc(SkReadBuffer& buffer) { - SkColorMatrix matrix; - if (buffer.readScalarArray(matrix.fMat, 20)) { - return Create(matrix); - } - return nullptr; -} - -bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const { - if (matrix) { - memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar)); - } - return true; -} - -SkColorFilter* SkColorMatrixFilter::newComposed(const SkColorFilter* innerFilter) const { - SkScalar innerMatrix[20]; - if (innerFilter->asColorMatrix(innerMatrix) && !SkColorMatrix::NeedsClamping(innerMatrix)) { - SkScalar concat[20]; - SkColorMatrix::SetConcat(concat, fMatrix.fMat, innerMatrix); - return SkColorMatrixFilter::Create(concat); - } - return nullptr; -} - -#if SK_SUPPORT_GPU -#include "GrFragmentProcessor.h" -#include "GrInvariantOutput.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" - -class ColorMatrixEffect : public GrFragmentProcessor { -public: - static const GrFragmentProcessor* Create(const SkColorMatrix& matrix) { - return new ColorMatrixEffect(matrix); - } - - const char* name() const override { return "Color Matrix"; } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - class GLSLProcessor : public GrGLSLFragmentProcessor { - public: - // this class always generates the same code. - static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {} - - GLSLProcessor(const GrProcessor&) {} - - virtual void emitCode(EmitArgs& args) override { - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - fMatrixHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, - kMat44f_GrSLType, kDefault_GrSLPrecision, - "ColorMatrix"); - fVectorHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "ColorMatrixVector"); - - if (nullptr == args.fInputColor) { - // could optimize this case, but we aren't for now. - args.fInputColor = "vec4(1)"; - } - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - // The max() is to guard against 0 / 0 during unpremul when the incoming color is - // transparent black. - fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", - args.fInputColor); - fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n", - args.fOutputColor, - uniformHandler->getUniformCStr(fMatrixHandle), - args.fInputColor, - uniformHandler->getUniformCStr(fVectorHandle)); - fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", - args.fOutputColor, args.fOutputColor); - fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor); - } - - protected: - virtual void onSetData(const GrGLSLProgramDataManager& uniManager, - const GrProcessor& proc) override { - const ColorMatrixEffect& cme = proc.cast(); - const float* m = cme.fMatrix.fMat; - // The GL matrix is transposed from SkColorMatrix. - float mt[] = { - m[0], m[5], m[10], m[15], - m[1], m[6], m[11], m[16], - m[2], m[7], m[12], m[17], - m[3], m[8], m[13], m[18], - }; - static const float kScale = 1.0f / 255.0f; - float vec[] = { - m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale, - }; - uniManager.setMatrix4fv(fMatrixHandle, 1, mt); - uniManager.set4fv(fVectorHandle, 1, vec); - } - - private: - GrGLSLProgramDataManager::UniformHandle fMatrixHandle; - GrGLSLProgramDataManager::UniformHandle fVectorHandle; - - typedef GrGLSLFragmentProcessor INHERITED; - }; - -private: - ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) { - this->initClassID(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { - return new GLSLProcessor(*this); - } - - virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps, - GrProcessorKeyBuilder* b) const override { - GLSLProcessor::GenKey(*this, caps, b); - } - - bool onIsEqual(const GrFragmentProcessor& s) const override { - const ColorMatrixEffect& cme = s.cast(); - return cme.fMatrix == fMatrix; - } - - void onComputeInvariantOutput(GrInvariantOutput* inout) const override { - // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had - // type flags it might be worth checking the other components. - - // The matrix is defined such the 4th row determines the output alpha. The first four - // columns of that row multiply the input r, g, b, and a, respectively, and the last column - // is the "translation". - static const uint32_t kRGBAFlags[] = { - kR_GrColorComponentFlag, - kG_GrColorComponentFlag, - kB_GrColorComponentFlag, - kA_GrColorComponentFlag - }; - static const int kShifts[] = { - GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A, - }; - enum { - kAlphaRowStartIdx = 15, - kAlphaRowTranslateIdx = 19, - }; - - SkScalar outputA = 0; - for (int i = 0; i < 4; ++i) { - // If any relevant component of the color to be passed through the matrix is non-const - // then we can't know the final result. - if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) { - if (!(inout->validFlags() & kRGBAFlags[i])) { - inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); - return; - } else { - uint32_t component = (inout->color() >> kShifts[i]) & 0xFF; - outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component; - } - } - } - outputA += fMatrix.fMat[kAlphaRowTranslateIdx]; - // We pin the color to [0,1]. This would happen to the *final* color output from the frag - // shader but currently the effect does not pin its own output. So in the case of over/ - // underflow this may deviate from the actual result. Maybe the effect should pin its - // result if the matrix could over/underflow for any component? - inout->setToOther(kA_GrColorComponentFlag, - static_cast(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A, - GrInvariantOutput::kWill_ReadInput); - } - - SkColorMatrix fMatrix; - - typedef GrFragmentProcessor INHERITED; -}; - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect); - -const GrFragmentProcessor* ColorMatrixEffect::TestCreate(GrProcessorTestData* d) { - SkColorMatrix colorMatrix; - for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) { - colorMatrix.fMat[i] = d->fRandom->nextSScalar1(); - } - return ColorMatrixEffect::Create(colorMatrix); -} - -const GrFragmentProcessor* SkColorMatrixFilter::asFragmentProcessor(GrContext*) const { - return ColorMatrixEffect::Create(fMatrix); -} - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkColorMatrixFilter::toString(SkString* str) const { - str->append("SkColorMatrixFilter: "); - - str->append("matrix: ("); - for (int i = 0; i < 20; ++i) { - str->appendScalar(fMatrix.fMat[i]); - if (i < 19) { - str->append(", "); - } - } - str->append(")"); -} -#endif - -/////////////////////////////////////////////////////////////////////////////// static SkScalar byte_to_scale(U8CPU byte) { if (0xFF == byte) { diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 4c06817..a2b91b4 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -17,7 +17,7 @@ #include "SkBlurMaskFilter.h" #include "SkColorCubeFilter.h" #include "SkColorFilterImageFilter.h" -#include "SkColorMatrixFilter.h" +#include "SkColorMatrixFilterRowMajor255.h" #include "SkComposeImageFilter.h" #include "SkCornerPathEffect.h" #include "SkDashPathEffect.h" @@ -70,7 +70,7 @@ void SkFlattenable::PrivateInitializer::InitEffects() { // ColorFilter SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorCubeFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilterRowMajor255) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLumaColorFilter) SkAlphaThresholdFilter::InitializeFlattenables(); SkArithmeticMode::InitializeFlattenables(); diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index c9ef559..68a95fc 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -1,3 +1,4 @@ + /* * Copyright 2013 Google Inc. * -- 2.7.4