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 "SkColorData.h" 10 #include "SkNx.h" 11 #include "SkRasterPipeline.h" 12 #include "SkReadBuffer.h" 13 #include "SkRefCnt.h" 14 #include "SkString.h" 15 #include "SkUnPreMultiply.h" 16 #include "SkWriteBuffer.h" 17 18 static void transpose_and_scale01(float dst[20], const float src[20]) { 19 const float* srcR = src + 0; 20 const float* srcG = src + 5; 21 const float* srcB = src + 10; 22 const float* srcA = src + 15; 23 24 for (int i = 0; i < 16; i += 4) { 25 dst[i + 0] = *srcR++; 26 dst[i + 1] = *srcG++; 27 dst[i + 2] = *srcB++; 28 dst[i + 3] = *srcA++; 29 } 30 // Might as well scale these translates down to [0,1] here instead of every filter call. 31 dst[16] = *srcR * (1/255.0f); 32 dst[17] = *srcG * (1/255.0f); 33 dst[18] = *srcB * (1/255.0f); 34 dst[19] = *srcA * (1/255.0f); 35 } 36 37 void SkColorMatrixFilterRowMajor255::initState() { 38 transpose_and_scale01(fTranspose, fMatrix); 39 40 const float* array = fMatrix; 41 42 // check if we have to munge Alpha 43 bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]); 44 bool usesAlpha = (array[3] || array[8] || array[13]); 45 46 if (changesAlpha || usesAlpha) { 47 fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag; 48 } else { 49 fFlags = kAlphaUnchanged_Flag; 50 } 51 } 52 53 /////////////////////////////////////////////////////////////////////////////// 54 55 SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) { 56 memcpy(fMatrix, array, 20 * sizeof(SkScalar)); 57 this->initState(); 58 } 59 60 uint32_t SkColorMatrixFilterRowMajor255::getFlags() const { 61 return this->INHERITED::getFlags() | fFlags; 62 } 63 64 /////////////////////////////////////////////////////////////////////////////// 65 66 void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const { 67 SkASSERT(sizeof(fMatrix)/sizeof(SkScalar) == 20); 68 buffer.writeScalarArray(fMatrix, 20); 69 } 70 71 sk_sp<SkFlattenable> SkColorMatrixFilterRowMajor255::CreateProc(SkReadBuffer& buffer) { 72 SkScalar matrix[20]; 73 if (buffer.readScalarArray(matrix, 20)) { 74 return sk_make_sp<SkColorMatrixFilterRowMajor255>(matrix); 75 } 76 return nullptr; 77 } 78 79 bool SkColorMatrixFilterRowMajor255::asColorMatrix(SkScalar matrix[20]) const { 80 if (matrix) { 81 memcpy(matrix, fMatrix, 20 * sizeof(SkScalar)); 82 } 83 return true; 84 } 85 86 /////////////////////////////////////////////////////////////////////////////// 87 // This code was duplicated from src/effects/SkColorMatrix.cpp in order to be used in core. 88 ////// 89 90 // To detect if we need to apply clamping after applying a matrix, we check if 91 // any output component might go outside of [0, 255] for any combination of 92 // input components in [0..255]. 93 // Each output component is an affine transformation of the input component, so 94 // the minimum and maximum values are for any combination of minimum or maximum 95 // values of input components (i.e. 0 or 255). 96 // E.g. if R' = x*R + y*G + z*B + w*A + t 97 // Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the 98 // minimum value will be for R=0 if x>0 or R=255 if x<0. 99 // Same goes for all components. 100 static bool component_needs_clamping(const SkScalar row[5]) { 101 SkScalar maxValue = row[4] / 255; 102 SkScalar minValue = row[4] / 255; 103 for (int i = 0; i < 4; ++i) { 104 if (row[i] > 0) 105 maxValue += row[i]; 106 else 107 minValue += row[i]; 108 } 109 return (maxValue > 1) || (minValue < 0); 110 } 111 112 static bool needs_clamping(const SkScalar matrix[20]) { 113 return component_needs_clamping(matrix) 114 || component_needs_clamping(matrix+5) 115 || component_needs_clamping(matrix+10) 116 || component_needs_clamping(matrix+15); 117 } 118 119 static void set_concat(SkScalar result[20], const SkScalar outer[20], const SkScalar inner[20]) { 120 int index = 0; 121 for (int j = 0; j < 20; j += 5) { 122 for (int i = 0; i < 4; i++) { 123 result[index++] = outer[j + 0] * inner[i + 0] + 124 outer[j + 1] * inner[i + 5] + 125 outer[j + 2] * inner[i + 10] + 126 outer[j + 3] * inner[i + 15]; 127 } 128 result[index++] = outer[j + 0] * inner[4] + 129 outer[j + 1] * inner[9] + 130 outer[j + 2] * inner[14] + 131 outer[j + 3] * inner[19] + 132 outer[j + 4]; 133 } 134 } 135 136 /////////////////////////////////////////////////////////////////////////////// 137 // End duplication 138 ////// 139 140 void SkColorMatrixFilterRowMajor255::onAppendStages(SkRasterPipeline* p, 141 SkColorSpace* dst, 142 SkArenaAlloc* scratch, 143 bool shaderIsOpaque) const { 144 bool willStayOpaque = shaderIsOpaque && (fFlags & kAlphaUnchanged_Flag); 145 bool needsClamp0 = false, 146 needsClamp1 = false; 147 for (int i = 0; i < 4; i++) { 148 SkScalar min = fTranspose[i+16], 149 max = fTranspose[i+16]; 150 (fTranspose[i+ 0] < 0 ? min : max) += fTranspose[i+ 0]; 151 (fTranspose[i+ 4] < 0 ? min : max) += fTranspose[i+ 4]; 152 (fTranspose[i+ 8] < 0 ? min : max) += fTranspose[i+ 8]; 153 (fTranspose[i+12] < 0 ? min : max) += fTranspose[i+12]; 154 needsClamp0 = needsClamp0 || min < 0; 155 needsClamp1 = needsClamp1 || max > 1; 156 } 157 158 if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); } 159 if ( true) { p->append(SkRasterPipeline::matrix_4x5, fTranspose); } 160 if ( needsClamp0) { p->append(SkRasterPipeline::clamp_0); } 161 if ( needsClamp1) { p->append(SkRasterPipeline::clamp_1); } 162 if (!willStayOpaque) { p->append(SkRasterPipeline::premul); } 163 } 164 165 sk_sp<SkColorFilter> 166 SkColorMatrixFilterRowMajor255::onMakeComposed(sk_sp<SkColorFilter> innerFilter) const { 167 SkScalar innerMatrix[20]; 168 if (innerFilter->asColorMatrix(innerMatrix) && !needs_clamping(innerMatrix)) { 169 SkScalar concat[20]; 170 set_concat(concat, fMatrix, innerMatrix); 171 return sk_make_sp<SkColorMatrixFilterRowMajor255>(concat); 172 } 173 return nullptr; 174 } 175 176 #if SK_SUPPORT_GPU 177 #include "GrFragmentProcessor.h" 178 #include "glsl/GrGLSLFragmentProcessor.h" 179 #include "glsl/GrGLSLFragmentShaderBuilder.h" 180 #include "glsl/GrGLSLProgramDataManager.h" 181 #include "glsl/GrGLSLUniformHandler.h" 182 183 class ColorMatrixEffect : public GrFragmentProcessor { 184 public: 185 static std::unique_ptr<GrFragmentProcessor> Make(const SkScalar matrix[20]) { 186 return std::unique_ptr<GrFragmentProcessor>(new ColorMatrixEffect(matrix)); 187 } 188 189 const char* name() const override { return "Color Matrix"; } 190 191 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 192 193 std::unique_ptr<GrFragmentProcessor> clone() const override { return Make(fMatrix); } 194 195 private: 196 class GLSLProcessor : public GrGLSLFragmentProcessor { 197 public: 198 // this class always generates the same code. 199 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {} 200 201 void emitCode(EmitArgs& args) override { 202 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 203 fMatrixHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4x4_GrSLType, 204 "ColorMatrix"); 205 fVectorHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, 206 "ColorMatrixVector"); 207 208 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; 209 // The max() is to guard against 0 / 0 during unpremul when the incoming color is 210 // transparent black. 211 fragBuilder->codeAppendf("\thalf nonZeroAlpha = max(%s.a, 0.00001);\n", 212 args.fInputColor); 213 fragBuilder->codeAppendf("\t%s = %s * half4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + " 214 "%s;\n", 215 args.fOutputColor, 216 uniformHandler->getUniformCStr(fMatrixHandle), 217 args.fInputColor, 218 uniformHandler->getUniformCStr(fVectorHandle)); 219 fragBuilder->codeAppendf("\t%s = saturate(%s);\n", 220 args.fOutputColor, args.fOutputColor); 221 fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor); 222 } 223 224 protected: 225 void onSetData(const GrGLSLProgramDataManager& uniManager, 226 const GrFragmentProcessor& proc) override { 227 const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>(); 228 const float* m = cme.fMatrix; 229 // The GL matrix is transposed from SkColorMatrix. 230 float mt[] = { 231 m[0], m[5], m[10], m[15], 232 m[1], m[6], m[11], m[16], 233 m[2], m[7], m[12], m[17], 234 m[3], m[8], m[13], m[18], 235 }; 236 static const float kScale = 1.0f / 255.0f; 237 float vec[] = { 238 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale, 239 }; 240 uniManager.setMatrix4fv(fMatrixHandle, 1, mt); 241 uniManager.set4fv(fVectorHandle, 1, vec); 242 } 243 244 private: 245 GrGLSLProgramDataManager::UniformHandle fMatrixHandle; 246 GrGLSLProgramDataManager::UniformHandle fVectorHandle; 247 248 typedef GrGLSLFragmentProcessor INHERITED; 249 }; 250 251 // We could implement the constant input->constant output optimization but haven't. Other 252 // optimizations would be matrix-dependent. 253 ColorMatrixEffect(const SkScalar matrix[20]) 254 : INHERITED(kColorMatrixEffect_ClassID, kNone_OptimizationFlags) { 255 memcpy(fMatrix, matrix, sizeof(SkScalar) * 20); 256 } 257 258 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { 259 return new GLSLProcessor; 260 } 261 262 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, 263 GrProcessorKeyBuilder* b) const override { 264 GLSLProcessor::GenKey(*this, caps, b); 265 } 266 267 bool onIsEqual(const GrFragmentProcessor& s) const override { 268 const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>(); 269 return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix)); 270 } 271 272 SkScalar fMatrix[20]; 273 274 typedef GrFragmentProcessor INHERITED; 275 }; 276 277 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect); 278 279 #if GR_TEST_UTILS 280 std::unique_ptr<GrFragmentProcessor> ColorMatrixEffect::TestCreate(GrProcessorTestData* d) { 281 SkScalar colorMatrix[20]; 282 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) { 283 colorMatrix[i] = d->fRandom->nextSScalar1(); 284 } 285 return ColorMatrixEffect::Make(colorMatrix); 286 } 287 288 #endif 289 290 std::unique_ptr<GrFragmentProcessor> SkColorMatrixFilterRowMajor255::asFragmentProcessor( 291 GrRecordingContext*, const GrColorSpaceInfo&) const { 292 return ColorMatrixEffect::Make(fMatrix); 293 } 294 295 #endif 296 297 /////////////////////////////////////////////////////////////////////////////// 298 299 sk_sp<SkColorFilter> SkColorFilter::MakeMatrixFilterRowMajor255(const SkScalar array[20]) { 300 if (!SkScalarsAreFinite(array, 20)) { 301 return nullptr; 302 } 303 304 return sk_sp<SkColorFilter>(new SkColorMatrixFilterRowMajor255(array)); 305 } 306 307 /////////////////////////////////////////////////////////////////////////////// 308 309 sk_sp<SkColorFilter> 310 SkColorMatrixFilterRowMajor255::MakeSingleChannelOutput(const SkScalar row[5]) { 311 if (!SkScalarsAreFinite(row, 5)) { 312 return nullptr; 313 } 314 315 SkASSERT(row); 316 auto cf = sk_make_sp<SkColorMatrixFilterRowMajor255>(); 317 static_assert(sizeof(SkScalar) * 5 * 4 == sizeof(cf->fMatrix), "sizes don't match"); 318 for (int i = 0; i < 4; ++i) { 319 memcpy(cf->fMatrix + 5 * i, row, sizeof(SkScalar) * 5); 320 } 321 cf->initState(); 322 return std::move(cf); 323 } 324