| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkColorMatrixFilter.h" | 8 #include "SkColorMatrixFilter.h" |
| 9 #include "SkColorMatrix.h" | |
| 10 #include "SkColorPriv.h" | |
| 11 #include "SkNx.h" | |
| 12 #include "SkReadBuffer.h" | |
| 13 #include "SkWriteBuffer.h" | |
| 14 #include "SkUnPreMultiply.h" | |
| 15 #include "SkString.h" | |
| 16 | |
| 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) | |
| 21 | |
| 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; | |
| 27 | |
| 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++; | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 // src is [20] but some compilers won't accept __restrict__ on anything | |
| 37 // but an raw pointer or reference | |
| 38 void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) { | |
| 39 transpose_to_pmorder(fTranspose, src); | |
| 40 | |
| 41 const float* array = fMatrix.fMat; | |
| 42 | |
| 43 // check if we have to munge Alpha | |
| 44 bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1)
|| array[19]); | |
| 45 bool usesAlpha = (array[3] || array[8] || array[13]); | |
| 46 | |
| 47 if (changesAlpha || usesAlpha) { | |
| 48 fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag; | |
| 49 } else { | |
| 50 fFlags = kAlphaUnchanged_Flag; | |
| 51 } | |
| 52 fFlags |= kSupports4f_Flag; | |
| 53 } | |
| 54 | |
| 55 /////////////////////////////////////////////////////////////////////////////// | |
| 56 | |
| 57 SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm)
{ | |
| 58 this->initState(cm.fMat); | |
| 59 } | |
| 60 | |
| 61 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) { | |
| 62 memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar)); | |
| 63 this->initState(array); | |
| 64 } | |
| 65 | |
| 66 uint32_t SkColorMatrixFilter::getFlags() const { | |
| 67 return this->INHERITED::getFlags() | fFlags; | |
| 68 } | |
| 69 | |
| 70 static Sk4f scale_rgb(float scale) { | |
| 71 static_assert(SkPM4f::A == 3, "Alpha is lane 3"); | |
| 72 return Sk4f(scale, scale, scale, 1); | |
| 73 } | |
| 74 | |
| 75 static Sk4f premul(const Sk4f& x) { | |
| 76 return x * scale_rgb(x.kth<SkPM4f::A>()); | |
| 77 } | |
| 78 | |
| 79 static Sk4f unpremul(const Sk4f& x) { | |
| 80 return x * scale_rgb(1 / x.kth<SkPM4f::A>()); // TODO: fast/approx invert? | |
| 81 } | |
| 82 | |
| 83 static Sk4f clamp_0_1(const Sk4f& x) { | |
| 84 return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0)); | |
| 85 } | |
| 86 | |
| 87 static SkPMColor round(const Sk4f& x) { | |
| 88 SkPMColor c; | |
| 89 SkNx_cast<uint8_t>(x * Sk4f(255) + Sk4f(0.5f)).store(&c); | |
| 90 return c; | |
| 91 } | |
| 92 | |
| 93 template <typename Adaptor, typename T> | |
| 94 void filter_span(const float array[], const T src[], int count, T dst[]) { | |
| 95 // c0-c3 are already in [0,1]. | |
| 96 const Sk4f c0 = Sk4f::Load(array + 0); | |
| 97 const Sk4f c1 = Sk4f::Load(array + 4); | |
| 98 const Sk4f c2 = Sk4f::Load(array + 8); | |
| 99 const Sk4f c3 = Sk4f::Load(array + 12); | |
| 100 // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. | |
| 101 const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255); | |
| 102 | |
| 103 // todo: we could cache this in the constructor... | |
| 104 T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4))); | |
| 105 | |
| 106 for (int i = 0; i < count; i++) { | |
| 107 Sk4f srcf = Adaptor::To4f(src[i]); | |
| 108 float srcA = srcf.kth<SkPM4f::A>(); | |
| 109 | |
| 110 if (0 == srcA) { | |
| 111 dst[i] = matrix_translate_pmcolor; | |
| 112 continue; | |
| 113 } | |
| 114 if (1 != srcA) { | |
| 115 srcf = unpremul(srcf); | |
| 116 } | |
| 117 | |
| 118 Sk4f r4 = SkNx_dup<SK_R32_SHIFT/8>(srcf); | |
| 119 Sk4f g4 = SkNx_dup<SK_G32_SHIFT/8>(srcf); | |
| 120 Sk4f b4 = SkNx_dup<SK_B32_SHIFT/8>(srcf); | |
| 121 Sk4f a4 = SkNx_dup<SK_A32_SHIFT/8>(srcf); | |
| 122 | |
| 123 // apply matrix | |
| 124 Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; | |
| 125 | |
| 126 dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4))); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 struct SkPMColorAdaptor { | |
| 131 static SkPMColor From4f(const Sk4f& c4) { | |
| 132 return round(c4); | |
| 133 } | |
| 134 static Sk4f To4f(SkPMColor c) { | |
| 135 return SkNx_cast<float>(Sk4b::Load(&c)) * Sk4f(1.0f/255); | |
| 136 } | |
| 137 }; | |
| 138 void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor
dst[]) const { | |
| 139 filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst); | |
| 140 } | |
| 141 | |
| 142 struct SkPM4fAdaptor { | |
| 143 static SkPM4f From4f(const Sk4f& c4) { | |
| 144 SkPM4f c; | |
| 145 c4.store(&c); | |
| 146 return c; | |
| 147 } | |
| 148 static Sk4f To4f(const SkPM4f& c) { | |
| 149 return Sk4f::Load(&c); | |
| 150 } | |
| 151 }; | |
| 152 void SkColorMatrixFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst
[]) const { | |
| 153 filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst); | |
| 154 } | |
| 155 | |
| 156 /////////////////////////////////////////////////////////////////////////////// | |
| 157 | |
| 158 void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const { | |
| 159 SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20); | |
| 160 buffer.writeScalarArray(fMatrix.fMat, 20); | |
| 161 } | |
| 162 | |
| 163 SkFlattenable* SkColorMatrixFilter::CreateProc(SkReadBuffer& buffer) { | |
| 164 SkColorMatrix matrix; | |
| 165 if (buffer.readScalarArray(matrix.fMat, 20)) { | |
| 166 return Create(matrix); | |
| 167 } | |
| 168 return nullptr; | |
| 169 } | |
| 170 | |
| 171 bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const { | |
| 172 if (matrix) { | |
| 173 memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar)); | |
| 174 } | |
| 175 return true; | |
| 176 } | |
| 177 | |
| 178 SkColorFilter* SkColorMatrixFilter::newComposed(const SkColorFilter* innerFilter
) const { | |
| 179 SkScalar innerMatrix[20]; | |
| 180 if (innerFilter->asColorMatrix(innerMatrix) && !SkColorMatrix::NeedsClamping
(innerMatrix)) { | |
| 181 SkScalar concat[20]; | |
| 182 SkColorMatrix::SetConcat(concat, fMatrix.fMat, innerMatrix); | |
| 183 return SkColorMatrixFilter::Create(concat); | |
| 184 } | |
| 185 return nullptr; | |
| 186 } | |
| 187 | |
| 188 #if SK_SUPPORT_GPU | |
| 189 #include "GrFragmentProcessor.h" | |
| 190 #include "GrInvariantOutput.h" | |
| 191 #include "glsl/GrGLSLFragmentProcessor.h" | |
| 192 #include "glsl/GrGLSLFragmentShaderBuilder.h" | |
| 193 #include "glsl/GrGLSLProgramDataManager.h" | |
| 194 #include "glsl/GrGLSLUniformHandler.h" | |
| 195 | |
| 196 class ColorMatrixEffect : public GrFragmentProcessor { | |
| 197 public: | |
| 198 static const GrFragmentProcessor* Create(const SkColorMatrix& matrix) { | |
| 199 return new ColorMatrixEffect(matrix); | |
| 200 } | |
| 201 | |
| 202 const char* name() const override { return "Color Matrix"; } | |
| 203 | |
| 204 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; | |
| 205 | |
| 206 class GLSLProcessor : public GrGLSLFragmentProcessor { | |
| 207 public: | |
| 208 // this class always generates the same code. | |
| 209 static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKey
Builder* b) {} | |
| 210 | |
| 211 GLSLProcessor(const GrProcessor&) {} | |
| 212 | |
| 213 virtual void emitCode(EmitArgs& args) override { | |
| 214 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; | |
| 215 fMatrixHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFr
agment_Visibility, | |
| 216 kMat44f_GrSLType, kDefaul
t_GrSLPrecision, | |
| 217 "ColorMatrix"); | |
| 218 fVectorHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFr
agment_Visibility, | |
| 219 kVec4f_GrSLType, kDefault
_GrSLPrecision, | |
| 220 "ColorMatrixVector"); | |
| 221 | |
| 222 if (nullptr == args.fInputColor) { | |
| 223 // could optimize this case, but we aren't for now. | |
| 224 args.fInputColor = "vec4(1)"; | |
| 225 } | |
| 226 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; | |
| 227 // The max() is to guard against 0 / 0 during unpremul when the inco
ming color is | |
| 228 // transparent black. | |
| 229 fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);
\n", | |
| 230 args.fInputColor); | |
| 231 fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, no
nZeroAlpha) + %s;\n", | |
| 232 args.fOutputColor, | |
| 233 uniformHandler->getUniformCStr(fMatrixHandl
e), | |
| 234 args.fInputColor, | |
| 235 uniformHandler->getUniformCStr(fVectorHandl
e)); | |
| 236 fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", | |
| 237 args.fOutputColor, args.fOutputColor); | |
| 238 fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, a
rgs.fOutputColor); | |
| 239 } | |
| 240 | |
| 241 protected: | |
| 242 virtual void onSetData(const GrGLSLProgramDataManager& uniManager, | |
| 243 const GrProcessor& proc) override { | |
| 244 const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>(); | |
| 245 const float* m = cme.fMatrix.fMat; | |
| 246 // The GL matrix is transposed from SkColorMatrix. | |
| 247 float mt[] = { | |
| 248 m[0], m[5], m[10], m[15], | |
| 249 m[1], m[6], m[11], m[16], | |
| 250 m[2], m[7], m[12], m[17], | |
| 251 m[3], m[8], m[13], m[18], | |
| 252 }; | |
| 253 static const float kScale = 1.0f / 255.0f; | |
| 254 float vec[] = { | |
| 255 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale, | |
| 256 }; | |
| 257 uniManager.setMatrix4fv(fMatrixHandle, 1, mt); | |
| 258 uniManager.set4fv(fVectorHandle, 1, vec); | |
| 259 } | |
| 260 | |
| 261 private: | |
| 262 GrGLSLProgramDataManager::UniformHandle fMatrixHandle; | |
| 263 GrGLSLProgramDataManager::UniformHandle fVectorHandle; | |
| 264 | |
| 265 typedef GrGLSLFragmentProcessor INHERITED; | |
| 266 }; | |
| 267 | |
| 268 private: | |
| 269 ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) { | |
| 270 this->initClassID<ColorMatrixEffect>(); | |
| 271 } | |
| 272 | |
| 273 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { | |
| 274 return new GLSLProcessor(*this); | |
| 275 } | |
| 276 | |
| 277 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps, | |
| 278 GrProcessorKeyBuilder* b) const override
{ | |
| 279 GLSLProcessor::GenKey(*this, caps, b); | |
| 280 } | |
| 281 | |
| 282 bool onIsEqual(const GrFragmentProcessor& s) const override { | |
| 283 const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>(); | |
| 284 return cme.fMatrix == fMatrix; | |
| 285 } | |
| 286 | |
| 287 void onComputeInvariantOutput(GrInvariantOutput* inout) const override { | |
| 288 // We only bother to check whether the alpha channel will be constant. I
f SkColorMatrix had | |
| 289 // type flags it might be worth checking the other components. | |
| 290 | |
| 291 // The matrix is defined such the 4th row determines the output alpha. T
he first four | |
| 292 // columns of that row multiply the input r, g, b, and a, respectively,
and the last column | |
| 293 // is the "translation". | |
| 294 static const uint32_t kRGBAFlags[] = { | |
| 295 kR_GrColorComponentFlag, | |
| 296 kG_GrColorComponentFlag, | |
| 297 kB_GrColorComponentFlag, | |
| 298 kA_GrColorComponentFlag | |
| 299 }; | |
| 300 static const int kShifts[] = { | |
| 301 GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A, | |
| 302 }; | |
| 303 enum { | |
| 304 kAlphaRowStartIdx = 15, | |
| 305 kAlphaRowTranslateIdx = 19, | |
| 306 }; | |
| 307 | |
| 308 SkScalar outputA = 0; | |
| 309 for (int i = 0; i < 4; ++i) { | |
| 310 // If any relevant component of the color to be passed through the m
atrix is non-const | |
| 311 // then we can't know the final result. | |
| 312 if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) { | |
| 313 if (!(inout->validFlags() & kRGBAFlags[i])) { | |
| 314 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); | |
| 315 return; | |
| 316 } else { | |
| 317 uint32_t component = (inout->color() >> kShifts[i]) & 0xFF; | |
| 318 outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component; | |
| 319 } | |
| 320 } | |
| 321 } | |
| 322 outputA += fMatrix.fMat[kAlphaRowTranslateIdx]; | |
| 323 // We pin the color to [0,1]. This would happen to the *final* color out
put from the frag | |
| 324 // shader but currently the effect does not pin its own output. So in th
e case of over/ | |
| 325 // underflow this may deviate from the actual result. Maybe the effect s
hould pin its | |
| 326 // result if the matrix could over/underflow for any component? | |
| 327 inout->setToOther(kA_GrColorComponentFlag, | |
| 328 static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) <<
GrColor_SHIFT_A, | |
| 329 GrInvariantOutput::kWill_ReadInput); | |
| 330 } | |
| 331 | |
| 332 SkColorMatrix fMatrix; | |
| 333 | |
| 334 typedef GrFragmentProcessor INHERITED; | |
| 335 }; | |
| 336 | |
| 337 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect); | |
| 338 | |
| 339 const GrFragmentProcessor* ColorMatrixEffect::TestCreate(GrProcessorTestData* d)
{ | |
| 340 SkColorMatrix colorMatrix; | |
| 341 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) { | |
| 342 colorMatrix.fMat[i] = d->fRandom->nextSScalar1(); | |
| 343 } | |
| 344 return ColorMatrixEffect::Create(colorMatrix); | |
| 345 } | |
| 346 | |
| 347 const GrFragmentProcessor* SkColorMatrixFilter::asFragmentProcessor(GrContext*)
const { | |
| 348 return ColorMatrixEffect::Create(fMatrix); | |
| 349 } | |
| 350 | |
| 351 #endif | |
| 352 | |
| 353 #ifndef SK_IGNORE_TO_STRING | |
| 354 void SkColorMatrixFilter::toString(SkString* str) const { | |
| 355 str->append("SkColorMatrixFilter: "); | |
| 356 | |
| 357 str->append("matrix: ("); | |
| 358 for (int i = 0; i < 20; ++i) { | |
| 359 str->appendScalar(fMatrix.fMat[i]); | |
| 360 if (i < 19) { | |
| 361 str->append(", "); | |
| 362 } | |
| 363 } | |
| 364 str->append(")"); | |
| 365 } | |
| 366 #endif | |
| 367 | |
| 368 /////////////////////////////////////////////////////////////////////////////// | |
| 369 | 9 |
| 370 static SkScalar byte_to_scale(U8CPU byte) { | 10 static SkScalar byte_to_scale(U8CPU byte) { |
| 371 if (0xFF == byte) { | 11 if (0xFF == byte) { |
| 372 // want to get this exact | 12 // want to get this exact |
| 373 return 1; | 13 return 1; |
| 374 } else { | 14 } else { |
| 375 return byte * 0.00392156862745f; | 15 return byte * 0.00392156862745f; |
| 376 } | 16 } |
| 377 } | 17 } |
| 378 | 18 |
| 379 SkColorFilter* SkColorMatrixFilter::CreateLightingFilter(SkColor mul, SkColor ad
d) { | 19 SkColorFilter* SkColorMatrixFilter::CreateLightingFilter(SkColor mul, SkColor ad
d) { |
| 380 SkColorMatrix matrix; | 20 SkColorMatrix matrix; |
| 381 matrix.setScale(byte_to_scale(SkColorGetR(mul)), | 21 matrix.setScale(byte_to_scale(SkColorGetR(mul)), |
| 382 byte_to_scale(SkColorGetG(mul)), | 22 byte_to_scale(SkColorGetG(mul)), |
| 383 byte_to_scale(SkColorGetB(mul)), | 23 byte_to_scale(SkColorGetB(mul)), |
| 384 1); | 24 1); |
| 385 matrix.postTranslate(SkIntToScalar(SkColorGetR(add)), | 25 matrix.postTranslate(SkIntToScalar(SkColorGetR(add)), |
| 386 SkIntToScalar(SkColorGetG(add)), | 26 SkIntToScalar(SkColorGetG(add)), |
| 387 SkIntToScalar(SkColorGetB(add)), | 27 SkIntToScalar(SkColorGetB(add)), |
| 388 0); | 28 0); |
| 389 return SkColorMatrixFilter::Create(matrix); | 29 return SkColorMatrixFilter::Create(matrix); |
| 390 } | 30 } |
| OLD | NEW |