1 /* 2 * Copyright 2017 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 "SkHighContrastFilter.h" 9 #include "SkPM4f.h" 10 #include "SkArenaAlloc.h" 11 #include "SkRasterPipeline.h" 12 #include "SkReadBuffer.h" 13 #include "SkString.h" 14 #include "SkWriteBuffer.h" 15 #include "../jumper/SkJumper.h" 16 17 #if SK_SUPPORT_GPU 18 #include "GrContext.h" 19 #include "glsl/GrGLSLFragmentProcessor.h" 20 #include "glsl/GrGLSLFragmentShaderBuilder.h" 21 #endif 22 23 using InvertStyle = SkHighContrastConfig::InvertStyle; 24 25 class SkHighContrast_Filter : public SkColorFilter { 26 public: 27 SkHighContrast_Filter(const SkHighContrastConfig& config) { 28 fConfig = config; 29 // Clamp contrast to just inside -1 to 1 to avoid division by zero. 30 fConfig.fContrast = SkScalarPin(fConfig.fContrast, 31 -1.0f + FLT_EPSILON, 32 1.0f - FLT_EPSILON); 33 } 34 35 ~SkHighContrast_Filter() override {} 36 37 #if SK_SUPPORT_GPU 38 sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override; 39 #endif 40 41 void onAppendStages(SkRasterPipeline* p, 42 SkColorSpace* dst, 43 SkArenaAlloc* scratch, 44 bool shaderIsOpaque) const override; 45 46 SK_TO_STRING_OVERRIDE() 47 48 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkHighContrast_Filter) 49 50 protected: 51 void flatten(SkWriteBuffer&) const override; 52 53 private: 54 SkHighContrastConfig fConfig; 55 56 friend class SkHighContrastFilter; 57 58 typedef SkColorFilter INHERITED; 59 }; 60 61 void SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p, 62 SkColorSpace* dstCS, 63 SkArenaAlloc* alloc, 64 bool shaderIsOpaque) const { 65 if (!shaderIsOpaque) { 66 p->append(SkRasterPipeline::unpremul); 67 } 68 69 if (!dstCS) { 70 // In legacy draws this effect approximately linearizes by squaring. 71 // When non-legacy, we're already (better) linearized. 72 auto square = alloc->make<SkJumper_ParametricTransferFunction>(); 73 square->G = 2.0f; square->A = 1.0f; 74 square->B = square->C = square->D = square->E = square->F = 0; 75 76 p->append(SkRasterPipeline::parametric_r, square); 77 p->append(SkRasterPipeline::parametric_g, square); 78 p->append(SkRasterPipeline::parametric_b, square); 79 } 80 81 if (fConfig.fGrayscale) { 82 float r = SK_LUM_COEFF_R; 83 float g = SK_LUM_COEFF_G; 84 float b = SK_LUM_COEFF_B; 85 float* matrix = alloc->makeArray<float>(12); 86 matrix[0] = matrix[1] = matrix[2] = r; 87 matrix[3] = matrix[4] = matrix[5] = g; 88 matrix[6] = matrix[7] = matrix[8] = b; 89 p->append(SkRasterPipeline::matrix_3x4, matrix); 90 } 91 92 if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) { 93 float* matrix = alloc->makeArray<float>(12); 94 matrix[0] = matrix[4] = matrix[8] = -1; 95 matrix[9] = matrix[10] = matrix[11] = 1; 96 p->append(SkRasterPipeline::matrix_3x4, matrix); 97 } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) { 98 p->append(SkRasterPipeline::rgb_to_hsl); 99 float* matrix = alloc->makeArray<float>(12); 100 matrix[0] = matrix[4] = matrix[11] = 1; 101 matrix[8] = -1; 102 p->append(SkRasterPipeline::matrix_3x4, matrix); 103 p->append(SkRasterPipeline::hsl_to_rgb); 104 } 105 106 if (fConfig.fContrast != 0.0) { 107 float* matrix = alloc->makeArray<float>(12); 108 float c = fConfig.fContrast; 109 float m = (1 + c) / (1 - c); 110 float b = (-0.5f * m + 0.5f); 111 matrix[0] = matrix[4] = matrix[8] = m; 112 matrix[9] = matrix[10] = matrix[11] = b; 113 p->append(SkRasterPipeline::matrix_3x4, matrix); 114 } 115 116 p->append(SkRasterPipeline::clamp_0); 117 p->append(SkRasterPipeline::clamp_1); 118 119 if (!dstCS) { 120 // See the previous if(!dstCS) { ... } 121 auto sqrt = alloc->make<SkJumper_ParametricTransferFunction>(); 122 sqrt->G = 0.5f; sqrt->A = 1.0f; 123 sqrt->B = sqrt->C = sqrt->D = sqrt->E = sqrt->F = 0; 124 125 p->append(SkRasterPipeline::parametric_r, sqrt); 126 p->append(SkRasterPipeline::parametric_g, sqrt); 127 p->append(SkRasterPipeline::parametric_b, sqrt); 128 } 129 130 if (!shaderIsOpaque) { 131 p->append(SkRasterPipeline::premul); 132 } 133 } 134 135 void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const { 136 buffer.writeBool(fConfig.fGrayscale); 137 buffer.writeInt(static_cast<int>(fConfig.fInvertStyle)); 138 buffer.writeScalar(fConfig.fContrast); 139 } 140 141 sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) { 142 SkHighContrastConfig config; 143 config.fGrayscale = buffer.readBool(); 144 config.fInvertStyle = static_cast<InvertStyle>(buffer.readInt()); 145 config.fContrast = buffer.readScalar(); 146 return SkHighContrastFilter::Make(config); 147 } 148 149 sk_sp<SkColorFilter> SkHighContrastFilter::Make( 150 const SkHighContrastConfig& config) { 151 if (!config.isValid()) 152 return nullptr; 153 return sk_make_sp<SkHighContrast_Filter>(config); 154 } 155 156 #ifndef SK_IGNORE_TO_STRING 157 void SkHighContrast_Filter::toString(SkString* str) const { 158 str->append("SkHighContrastColorFilter "); 159 } 160 #endif 161 162 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter) 163 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter) 164 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 165 166 #if SK_SUPPORT_GPU 167 class HighContrastFilterEffect : public GrFragmentProcessor { 168 public: 169 static sk_sp<GrFragmentProcessor> Make(const SkHighContrastConfig& config) { 170 return sk_sp<GrFragmentProcessor>(new HighContrastFilterEffect(config)); 171 } 172 173 const char* name() const override { return "HighContrastFilter"; } 174 175 const SkHighContrastConfig& config() const { return fConfig; } 176 177 private: 178 HighContrastFilterEffect(const SkHighContrastConfig& config) 179 : INHERITED(kNone_OptimizationFlags) 180 , fConfig(config) { 181 this->initClassID<HighContrastFilterEffect>(); 182 } 183 184 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 185 186 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, 187 GrProcessorKeyBuilder* b) const override; 188 189 bool onIsEqual(const GrFragmentProcessor& other) const override { 190 const HighContrastFilterEffect& that = other.cast<HighContrastFilterEffect>(); 191 return fConfig.fGrayscale == that.fConfig.fGrayscale && 192 fConfig.fInvertStyle == that.fConfig.fInvertStyle && 193 fConfig.fContrast == that.fConfig.fContrast; 194 } 195 196 SkHighContrastConfig fConfig; 197 198 typedef GrFragmentProcessor INHERITED; 199 }; 200 201 class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor { 202 public: 203 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); 204 205 GLHighContrastFilterEffect(const SkHighContrastConfig& config); 206 207 protected: 208 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; 209 void emitCode(EmitArgs& args) override; 210 211 private: 212 UniformHandle fContrastUni; 213 SkHighContrastConfig fConfig; 214 215 typedef GrGLSLFragmentProcessor INHERITED; 216 }; 217 218 GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const { 219 return new GLHighContrastFilterEffect(fConfig); 220 } 221 222 void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 223 GrProcessorKeyBuilder* b) const { 224 GLHighContrastFilterEffect::GenKey(*this, caps, b); 225 } 226 227 void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, 228 const GrFragmentProcessor& proc) { 229 const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); 230 pdm.set1f(fContrastUni, hcfe.config().fContrast); 231 } 232 233 GLHighContrastFilterEffect::GLHighContrastFilterEffect(const SkHighContrastConfig& config) 234 : INHERITED() 235 , fConfig(config) { 236 } 237 238 void GLHighContrastFilterEffect::GenKey( 239 const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { 240 const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); 241 b->add32(static_cast<uint32_t>(hcfe.config().fGrayscale)); 242 b->add32(static_cast<uint32_t>(hcfe.config().fInvertStyle)); 243 } 244 245 void GLHighContrastFilterEffect::emitCode(EmitArgs& args) { 246 const char* contrast; 247 fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, 248 kFloat_GrSLType, kDefault_GrSLPrecision, 249 "contrast", &contrast); 250 251 if (nullptr == args.fInputColor) { 252 args.fInputColor = "vec4(1)"; 253 } 254 255 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 256 257 fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor); 258 259 // Unpremultiply. The max() is to guard against 0 / 0. 260 fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);"); 261 fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);"); 262 263 // Grayscale. 264 if (fConfig.fGrayscale) { 265 fragBuilder->codeAppendf("float luma = dot(color, vec4(%f, %f, %f, 0));", 266 SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B); 267 fragBuilder->codeAppendf("color = vec4(luma, luma, luma, 0);"); 268 } 269 270 if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) { 271 fragBuilder->codeAppendf("color = vec4(1, 1, 1, 1) - color;"); 272 } 273 274 if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) { 275 // Convert from RGB to HSL. 276 fragBuilder->codeAppendf("float fmax = max(color.r, max(color.g, color.b));"); 277 fragBuilder->codeAppendf("float fmin = min(color.r, min(color.g, color.b));"); 278 fragBuilder->codeAppendf("float l = (fmax + fmin) / 2;"); 279 280 fragBuilder->codeAppendf("float h;"); 281 fragBuilder->codeAppendf("float s;"); 282 283 fragBuilder->codeAppendf("if (fmax == fmin) {"); 284 fragBuilder->codeAppendf(" h = 0;"); 285 fragBuilder->codeAppendf(" s = 0;"); 286 fragBuilder->codeAppendf("} else {"); 287 fragBuilder->codeAppendf(" float d = fmax - fmin;"); 288 fragBuilder->codeAppendf(" s = l > 0.5 ?"); 289 fragBuilder->codeAppendf(" d / (2 - fmax - fmin) :"); 290 fragBuilder->codeAppendf(" d / (fmax + fmin);"); 291 fragBuilder->codeAppendf(" if (fmax == color.r) {"); 292 fragBuilder->codeAppendf(" h = (color.g - color.b) / d + "); 293 fragBuilder->codeAppendf(" (color.g < color.b ? 6 : 0);"); 294 fragBuilder->codeAppendf(" } else if (fmax == color.g) {"); 295 fragBuilder->codeAppendf(" h = (color.b - color.r) / d + 2;"); 296 fragBuilder->codeAppendf(" } else {"); 297 fragBuilder->codeAppendf(" h = (color.r - color.g) / d + 4;"); 298 fragBuilder->codeAppendf(" }"); 299 fragBuilder->codeAppendf("}"); 300 fragBuilder->codeAppendf("h /= 6;"); 301 fragBuilder->codeAppendf("l = 1.0 - l;"); 302 // Convert back from HSL to RGB. 303 SkString hue2rgbFuncName; 304 static const GrShaderVar gHue2rgbArgs[] = { 305 GrShaderVar("p", kFloat_GrSLType), 306 GrShaderVar("q", kFloat_GrSLType), 307 GrShaderVar("t", kFloat_GrSLType), 308 }; 309 fragBuilder->emitFunction(kFloat_GrSLType, 310 "hue2rgb", 311 SK_ARRAY_COUNT(gHue2rgbArgs), 312 gHue2rgbArgs, 313 "if (t < 0)" 314 " t += 1;" 315 "if (t > 1)" 316 " t -= 1;" 317 "if (t < 1/6.)" 318 " return p + (q - p) * 6 * t;" 319 "if (t < 1/2.)" 320 " return q;" 321 "if (t < 2/3.)" 322 " return p + (q - p) * (2/3. - t) * 6;" 323 "return p;", 324 &hue2rgbFuncName); 325 fragBuilder->codeAppendf("if (s == 0) {"); 326 fragBuilder->codeAppendf(" color = vec4(l, l, l, 0);"); 327 fragBuilder->codeAppendf("} else {"); 328 fragBuilder->codeAppendf(" float q = l < 0.5 ? l * (1 + s) : l + s - l * s;"); 329 fragBuilder->codeAppendf(" float p = 2 * l - q;"); 330 fragBuilder->codeAppendf(" color.r = %s(p, q, h + 1/3.);", hue2rgbFuncName.c_str()); 331 fragBuilder->codeAppendf(" color.g = %s(p, q, h);", hue2rgbFuncName.c_str()); 332 fragBuilder->codeAppendf(" color.b = %s(p, q, h - 1/3.);", hue2rgbFuncName.c_str()); 333 fragBuilder->codeAppendf("}"); 334 } 335 336 // Contrast. 337 fragBuilder->codeAppendf("if (%s != 0) {", contrast); 338 fragBuilder->codeAppendf(" float m = (1 + %s) / (1 - %s);", contrast, contrast); 339 fragBuilder->codeAppendf(" float off = (-0.5 * m + 0.5);"); 340 fragBuilder->codeAppendf(" color = m * color + off;"); 341 fragBuilder->codeAppendf("}"); 342 343 // Clamp. 344 fragBuilder->codeAppendf("color = clamp(color, 0, 1);"); 345 346 // Restore the original alpha and premultiply. 347 fragBuilder->codeAppendf("color.a = %s.a;", args.fInputColor); 348 fragBuilder->codeAppendf("color.rgb *= color.a;"); 349 350 // Copy to the output color. 351 fragBuilder->codeAppendf("%s = color;", args.fOutputColor); 352 } 353 354 sk_sp<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(GrContext*, SkColorSpace*) const { 355 return HighContrastFilterEffect::Make(fConfig); 356 } 357 #endif 358