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