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