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