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 
22 using InvertStyle = SkHighContrastConfig::InvertStyle;
23 
24 namespace {
25 
Hue2RGB(SkScalar p,SkScalar q,SkScalar t)26 SkScalar 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 
SkScalarToUint8Clamp(SkScalar f)48 uint8_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 
IncreaseContrast(SkScalar f,SkScalar contrast)57 SkScalar 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 
ApplyHighContrastFilter(const SkHighContrastConfig & config,SkPMColor pmColor)63 static 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 
151 class SkHighContrast_Filter : public SkColorFilter {
152 public:
SkHighContrast_Filter(const SkHighContrastConfig & config)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 
~SkHighContrast_Filter()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 
178 protected:
179     void flatten(SkWriteBuffer&) const override;
180 
181 private:
182     SkHighContrastConfig fConfig;
183 
184     friend class SkHighContrastFilter;
185 
186     typedef SkColorFilter INHERITED;
187 };
188 
filterSpan(const SkPMColor src[],int count,SkPMColor dst[]) const189 void 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 
onAppendStages(SkRasterPipeline * p,SkColorSpace * dst,SkArenaAlloc * scratch,bool shaderIsOpaque) const195 bool 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 
flatten(SkWriteBuffer & buffer) const248 void 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 
CreateProc(SkReadBuffer & buffer)254 sk_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 
Make(const SkHighContrastConfig & config)262 sk_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
toString(SkString * str) const270 void SkHighContrast_Filter::toString(SkString* str) const {
271     str->append("SkHighContrastColorFilter ");
272 }
273 #endif
274 
275 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter)
276     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter)
277 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
278 
279 #if SK_SUPPORT_GPU
280 class HighContrastFilterEffect : public GrFragmentProcessor {
281 public:
Make(const SkHighContrastConfig & config)282     static sk_sp<GrFragmentProcessor> Make(const SkHighContrastConfig& config) {
283         return sk_sp<GrFragmentProcessor>(new HighContrastFilterEffect(config));
284     }
285 
name() const286     const char* name() const override { return "HighContrastFilter"; }
287 
config() const288     const SkHighContrastConfig& config() const { return fConfig; }
289 
290 private:
HighContrastFilterEffect(const SkHighContrastConfig & config)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 
onIsEqual(const GrFragmentProcessor & other) const302     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 
314 class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor {
315 public:
316     static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
317 
318     GLHighContrastFilterEffect(const SkHighContrastConfig& config);
319 
320 protected:
321     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
322     void emitCode(EmitArgs& args) override;
323 
324 private:
325     UniformHandle fContrastUni;
326     SkHighContrastConfig fConfig;
327 
328     typedef GrGLSLFragmentProcessor INHERITED;
329 };
330 
onCreateGLSLInstance() const331 GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const {
332     return new GLHighContrastFilterEffect(fConfig);
333 }
334 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const335 void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
336                                                      GrProcessorKeyBuilder* b) const {
337     GLHighContrastFilterEffect::GenKey(*this, caps, b);
338 }
339 
onSetData(const GrGLSLProgramDataManager & pdm,const GrProcessor & proc)340 void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) {
341     const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
342     pdm.set1f(fContrastUni, hcfe.config().fContrast);
343 }
344 
GLHighContrastFilterEffect(const SkHighContrastConfig & config)345 GLHighContrastFilterEffect::GLHighContrastFilterEffect(const SkHighContrastConfig& config)
346     : INHERITED()
347     , fConfig(config) {
348 }
349 
GenKey(const GrProcessor & proc,const GrShaderCaps &,GrProcessorKeyBuilder * b)350 void 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 
emitCode(EmitArgs & args)357 void 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 
asFragmentProcessor(GrContext *,SkColorSpace *) const466 sk_sp<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(GrContext*, SkColorSpace*) const {
467     return HighContrastFilterEffect::Make(fConfig);
468 }
469 #endif
470