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