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 10#include "SkArenaAlloc.h" 11#include "SkRasterPipeline.h" 12#include "SkReadBuffer.h" 13#include "SkString.h" 14#include "SkWriteBuffer.h" 15 16#if SK_SUPPORT_GPU 17#include "GrContext.h" 18#include "glsl/GrGLSLFragmentProcessor.h" 19#include "glsl/GrGLSLFragmentShaderBuilder.h" 20#endif 21 22using InvertStyle = SkHighContrastConfig::InvertStyle; 23 24namespace { 25 26SkScalar Hue2RGB(SkScalar p, SkScalar q, SkScalar t) { 27 if (t < 0) { 28 t += 1; 29 } else if (t > 1) { 30 t -= 1; 31 } 32 33 if (t < 1/6.f) { 34 return p + (q - p) * 6 * t; 35 } 36 37 if (t < 1/2.f) { 38 return q; 39 } 40 41 if (t < 2/3.f) { 42 return p + (q - p) * (2/3.f - t) * 6; 43 } 44 45 return p; 46} 47 48uint8_t SkScalarToUint8Clamp(SkScalar f) { 49 if (f <= 0) { 50 return 0; 51 } else if (f >= 1) { 52 return 255; 53 } 54 return static_cast<unsigned char>(255 * f); 55} 56 57SkScalar IncreaseContrast(SkScalar f, SkScalar contrast) { 58 SkScalar m = (1 + contrast) / (1 - contrast); 59 SkScalar b = (-0.5f * m + 0.5f); 60 return m * f + b; 61} 62 63static SkPMColor ApplyHighContrastFilter(const SkHighContrastConfig& config, 64 SkPMColor pmColor) { 65 SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); 66 SkScalar rf = SkColorGetR(color) / 255.f; 67 SkScalar gf = SkColorGetG(color) / 255.f; 68 SkScalar bf = SkColorGetB(color) / 255.f; 69 70 // Apply a gamma of 2.0 so that the rest of the calculations 71 // happen roughly in linear space. 72 rf *= rf; 73 gf *= gf; 74 bf *= bf; 75 76 // Convert to grayscale using luminance coefficients. 77 if (config.fGrayscale) { 78 SkScalar lum = 79 rf * SK_LUM_COEFF_R + gf * SK_LUM_COEFF_G + bf * SK_LUM_COEFF_B; 80 rf = lum; 81 gf = lum; 82 bf = lum; 83 } 84 85 // Now invert. 86 if (config.fInvertStyle == InvertStyle::kInvertBrightness) { 87 rf = 1 - rf; 88 gf = 1 - gf; 89 bf = 1 - bf; 90 } else if (config.fInvertStyle == InvertStyle::kInvertLightness) { 91 // Convert to HSL 92 SkScalar max = SkTMax(SkTMax(rf, gf), bf); 93 SkScalar min = SkTMin(SkTMin(rf, gf), bf); 94 SkScalar l = (max + min) / 2; 95 SkScalar h, s; 96 97 if (max == min) { 98 h = 0; 99 s = 0; 100 } else { 101 SkScalar d = max - min; 102 s = l > 0.5f ? d / (2 - max - min) : d / (max + min); 103 if (max == rf) { 104 h = (gf - bf) / d + (gf < bf ? 6 : 0); 105 } else if (max == gf) { 106 h = (bf - rf) / d + 2; 107 } else { 108 h = (rf - gf) / d + 4; 109 } 110 h /= 6; 111 } 112 113 // Invert lightness. 114 l = 1 - l; 115 116 // Now convert back to RGB. 117 if (s == 0) { 118 // Grayscale 119 rf = l; 120 gf = l; 121 bf = l; 122 } else { 123 SkScalar q = l < 0.5f ? l * (1 + s) : l + s - l * s; 124 SkScalar p = 2 * l - q; 125 rf = Hue2RGB(p, q, h + 1/3.f); 126 gf = Hue2RGB(p, q, h); 127 bf = Hue2RGB(p, q, h - 1/3.f); 128 } 129 } 130 131 // Increase contrast. 132 if (config.fContrast != 0.0f) { 133 rf = IncreaseContrast(rf, config.fContrast); 134 gf = IncreaseContrast(gf, config.fContrast); 135 bf = IncreaseContrast(bf, config.fContrast); 136 } 137 138 // Convert back from linear to a color space with a gamma of ~2.0. 139 rf = SkScalarSqrt(rf); 140 gf = SkScalarSqrt(gf); 141 bf = SkScalarSqrt(bf); 142 143 return SkPremultiplyARGBInline(SkColorGetA(color), 144 SkScalarToUint8Clamp(rf), 145 SkScalarToUint8Clamp(gf), 146 SkScalarToUint8Clamp(bf)); 147} 148 149} // namespace 150 151class SkHighContrast_Filter : public SkColorFilter { 152public: 153 SkHighContrast_Filter(const SkHighContrastConfig& config) { 154 fConfig = config; 155 // Clamp contrast to just inside -1 to 1 to avoid division by zero. 156 fConfig.fContrast = SkScalarPin(fConfig.fContrast, 157 -1.0f + FLT_EPSILON, 158 1.0f - FLT_EPSILON); 159 } 160 161 ~SkHighContrast_Filter() override {} 162 163#if SK_SUPPORT_GPU 164 sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override; 165 #endif 166 167 void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const 168 override; 169 bool onAppendStages(SkRasterPipeline* p, 170 SkColorSpace* dst, 171 SkArenaAlloc* scratch, 172 bool shaderIsOpaque) const override; 173 174 SK_TO_STRING_OVERRIDE() 175 176 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkHighContrast_Filter) 177 178protected: 179 void flatten(SkWriteBuffer&) const override; 180 181private: 182 SkHighContrastConfig fConfig; 183 184 friend class SkHighContrastFilter; 185 186 typedef SkColorFilter INHERITED; 187}; 188 189void SkHighContrast_Filter::filterSpan(const SkPMColor src[], int count, 190 SkPMColor dst[]) const { 191 for (int i = 0; i < count; ++i) 192 dst[i] = ApplyHighContrastFilter(fConfig, src[i]); 193} 194 195bool SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p, 196 SkColorSpace* dst, 197 SkArenaAlloc* scratch, 198 bool shaderIsOpaque) const { 199 if (!shaderIsOpaque) { 200 p->append(SkRasterPipeline::unpremul); 201 } 202 203 if (fConfig.fGrayscale) { 204 float r = SK_LUM_COEFF_R; 205 float g = SK_LUM_COEFF_G; 206 float b = SK_LUM_COEFF_B; 207 float* matrix = scratch->makeArray<float>(12); 208 matrix[0] = matrix[1] = matrix[2] = r; 209 matrix[3] = matrix[4] = matrix[5] = g; 210 matrix[6] = matrix[7] = matrix[8] = b; 211 p->append(SkRasterPipeline::matrix_3x4, matrix); 212 } 213 214 if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) { 215 float* matrix = scratch->makeArray<float>(12); 216 matrix[0] = matrix[4] = matrix[8] = -1; 217 matrix[9] = matrix[10] = matrix[11] = 1; 218 p->append(SkRasterPipeline::matrix_3x4, matrix); 219 } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) { 220 p->append(SkRasterPipeline::rgb_to_hsl); 221 float* matrix = scratch->makeArray<float>(12); 222 matrix[0] = matrix[4] = matrix[11] = 1; 223 matrix[8] = -1; 224 p->append(SkRasterPipeline::matrix_3x4, matrix); 225 p->append(SkRasterPipeline::hsl_to_rgb); 226 } 227 228 if (fConfig.fContrast != 0.0) { 229 float* matrix = scratch->makeArray<float>(12); 230 float c = fConfig.fContrast; 231 float m = (1 + c) / (1 - c); 232 float b = (-0.5f * m + 0.5f); 233 matrix[0] = matrix[4] = matrix[8] = m; 234 matrix[9] = matrix[10] = matrix[11] = b; 235 p->append(SkRasterPipeline::matrix_3x4, matrix); 236 } 237 238 p->append(SkRasterPipeline::clamp_0); 239 p->append(SkRasterPipeline::clamp_1); 240 241 if (!shaderIsOpaque) { 242 p->append(SkRasterPipeline::premul); 243 } 244 245 return true; 246} 247 248void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const { 249 buffer.writeBool(fConfig.fGrayscale); 250 buffer.writeInt(static_cast<int>(fConfig.fInvertStyle)); 251 buffer.writeScalar(fConfig.fContrast); 252} 253 254sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) { 255 SkHighContrastConfig config; 256 config.fGrayscale = buffer.readBool(); 257 config.fInvertStyle = static_cast<InvertStyle>(buffer.readInt()); 258 config.fContrast = buffer.readScalar(); 259 return SkHighContrastFilter::Make(config); 260} 261 262sk_sp<SkColorFilter> SkHighContrastFilter::Make( 263 const SkHighContrastConfig& config) { 264 if (!config.isValid()) 265 return nullptr; 266 return sk_make_sp<SkHighContrast_Filter>(config); 267} 268 269#ifndef SK_IGNORE_TO_STRING 270void SkHighContrast_Filter::toString(SkString* str) const { 271 str->append("SkHighContrastColorFilter "); 272} 273#endif 274 275SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter) 276 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter) 277SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 278 279#if SK_SUPPORT_GPU 280class HighContrastFilterEffect : public GrFragmentProcessor { 281public: 282 static sk_sp<GrFragmentProcessor> Make(const SkHighContrastConfig& config) { 283 return sk_sp<GrFragmentProcessor>(new HighContrastFilterEffect(config)); 284 } 285 286 const char* name() const override { return "HighContrastFilter"; } 287 288 const SkHighContrastConfig& config() const { return fConfig; } 289 290private: 291 HighContrastFilterEffect(const SkHighContrastConfig& config) 292 : INHERITED(kNone_OptimizationFlags) 293 , fConfig(config) { 294 this->initClassID<HighContrastFilterEffect>(); 295 } 296 297 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 298 299 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, 300 GrProcessorKeyBuilder* b) const override; 301 302 bool onIsEqual(const GrFragmentProcessor& other) const override { 303 const HighContrastFilterEffect& that = other.cast<HighContrastFilterEffect>(); 304 return fConfig.fGrayscale == that.fConfig.fGrayscale && 305 fConfig.fInvertStyle == that.fConfig.fInvertStyle && 306 fConfig.fContrast == that.fConfig.fContrast; 307 } 308 309 SkHighContrastConfig fConfig; 310 311 typedef GrFragmentProcessor INHERITED; 312}; 313 314class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor { 315public: 316 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); 317 318 GLHighContrastFilterEffect(const SkHighContrastConfig& config); 319 320protected: 321 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; 322 void emitCode(EmitArgs& args) override; 323 324private: 325 UniformHandle fContrastUni; 326 SkHighContrastConfig fConfig; 327 328 typedef GrGLSLFragmentProcessor INHERITED; 329}; 330 331GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const { 332 return new GLHighContrastFilterEffect(fConfig); 333} 334 335void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 336 GrProcessorKeyBuilder* b) const { 337 GLHighContrastFilterEffect::GenKey(*this, caps, b); 338} 339 340void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) { 341 const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); 342 pdm.set1f(fContrastUni, hcfe.config().fContrast); 343} 344 345GLHighContrastFilterEffect::GLHighContrastFilterEffect(const SkHighContrastConfig& config) 346 : INHERITED() 347 , fConfig(config) { 348} 349 350void GLHighContrastFilterEffect::GenKey( 351 const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { 352 const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); 353 b->add32(static_cast<uint32_t>(hcfe.config().fGrayscale)); 354 b->add32(static_cast<uint32_t>(hcfe.config().fInvertStyle)); 355} 356 357void GLHighContrastFilterEffect::emitCode(EmitArgs& args) { 358 const char* contrast; 359 fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, 360 kFloat_GrSLType, kDefault_GrSLPrecision, 361 "contrast", &contrast); 362 363 if (nullptr == args.fInputColor) { 364 args.fInputColor = "vec4(1)"; 365 } 366 367 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 368 369 fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor); 370 371 // Unpremultiply. The max() is to guard against 0 / 0. 372 fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);"); 373 fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);"); 374 375 // Grayscale. 376 if (fConfig.fGrayscale) { 377 fragBuilder->codeAppendf("float luma = dot(color, vec4(%f, %f, %f, 0));", 378 SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B); 379 fragBuilder->codeAppendf("color = vec4(luma, luma, luma, 0);"); 380 } 381 382 if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) { 383 fragBuilder->codeAppendf("color = vec4(1, 1, 1, 1) - color;"); 384 } 385 386 if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) { 387 // Convert from RGB to HSL. 388 fragBuilder->codeAppendf("float fmax = max(color.r, max(color.g, color.b));"); 389 fragBuilder->codeAppendf("float fmin = min(color.r, min(color.g, color.b));"); 390 fragBuilder->codeAppendf("float l = (fmax + fmin) / 2;"); 391 392 fragBuilder->codeAppendf("float h;"); 393 fragBuilder->codeAppendf("float s;"); 394 395 fragBuilder->codeAppendf("if (fmax == fmin) {"); 396 fragBuilder->codeAppendf(" h = 0;"); 397 fragBuilder->codeAppendf(" s = 0;"); 398 fragBuilder->codeAppendf("} else {"); 399 fragBuilder->codeAppendf(" float d = fmax - fmin;"); 400 fragBuilder->codeAppendf(" s = l > 0.5 ?"); 401 fragBuilder->codeAppendf(" d / (2 - fmax - fmin) :"); 402 fragBuilder->codeAppendf(" d / (fmax + fmin);"); 403 fragBuilder->codeAppendf(" if (fmax == color.r) {"); 404 fragBuilder->codeAppendf(" h = (color.g - color.b) / d + "); 405 fragBuilder->codeAppendf(" (color.g < color.b ? 6 : 0);"); 406 fragBuilder->codeAppendf(" } else if (fmax == color.g) {"); 407 fragBuilder->codeAppendf(" h = (color.b - color.r) / d + 2;"); 408 fragBuilder->codeAppendf(" } else {"); 409 fragBuilder->codeAppendf(" h = (color.r - color.g) / d + 4;"); 410 fragBuilder->codeAppendf(" }"); 411 fragBuilder->codeAppendf("}"); 412 fragBuilder->codeAppendf("h /= 6;"); 413 fragBuilder->codeAppendf("l = 1.0 - l;"); 414 // Convert back from HSL to RGB. 415 SkString hue2rgbFuncName; 416 static const GrShaderVar gHue2rgbArgs[] = { 417 GrShaderVar("p", kFloat_GrSLType), 418 GrShaderVar("q", kFloat_GrSLType), 419 GrShaderVar("t", kFloat_GrSLType), 420 }; 421 fragBuilder->emitFunction(kFloat_GrSLType, 422 "hue2rgb", 423 SK_ARRAY_COUNT(gHue2rgbArgs), 424 gHue2rgbArgs, 425 "if (t < 0)" 426 " t += 1;" 427 "if (t > 1)" 428 " t -= 1;" 429 "if (t < 1/6.)" 430 " return p + (q - p) * 6 * t;" 431 "if (t < 1/2.)" 432 " return q;" 433 "if (t < 2/3.)" 434 " return p + (q - p) * (2/3. - t) * 6;" 435 "return p;", 436 &hue2rgbFuncName); 437 fragBuilder->codeAppendf("if (s == 0) {"); 438 fragBuilder->codeAppendf(" color = vec4(l, l, l, 0);"); 439 fragBuilder->codeAppendf("} else {"); 440 fragBuilder->codeAppendf(" float q = l < 0.5 ? l * (1 + s) : l + s - l * s;"); 441 fragBuilder->codeAppendf(" float p = 2 * l - q;"); 442 fragBuilder->codeAppendf(" color.r = %s(p, q, h + 1/3.);", hue2rgbFuncName.c_str()); 443 fragBuilder->codeAppendf(" color.g = %s(p, q, h);", hue2rgbFuncName.c_str()); 444 fragBuilder->codeAppendf(" color.b = %s(p, q, h - 1/3.);", hue2rgbFuncName.c_str()); 445 fragBuilder->codeAppendf("}"); 446 } 447 448 // Contrast. 449 fragBuilder->codeAppendf("if (%s != 0) {", contrast); 450 fragBuilder->codeAppendf(" float m = (1 + %s) / (1 - %s);", contrast, contrast); 451 fragBuilder->codeAppendf(" float off = (-0.5 * m + 0.5);"); 452 fragBuilder->codeAppendf(" color = m * color + off;"); 453 fragBuilder->codeAppendf("}"); 454 455 // Clamp. 456 fragBuilder->codeAppendf("color = clamp(color, 0, 1);"); 457 458 // Restore the original alpha and premultiply. 459 fragBuilder->codeAppendf("color.a = %s.a;", args.fInputColor); 460 fragBuilder->codeAppendf("color.rgb *= color.a;"); 461 462 // Copy to the output color. 463 fragBuilder->codeAppendf("%s = color;", args.fOutputColor); 464} 465 466sk_sp<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(GrContext*, SkColorSpace*) const { 467 return HighContrastFilterEffect::Make(fConfig); 468} 469#endif 470