blob: 267a8ef0dcce43ba4a41ffd80d972f759f36d25c [file] [log] [blame]
Dominic Mazzoni394d4142017-02-14 11:15:31 -08001/*
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 virtual ~SkHighContrast_Filter() { }
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