blob: d6299f90c482458f05459a5d03ad109f7c09bbb2 [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"
Mike Reedb9641bd2017-05-04 10:57:40 -04009#include "SkPM4f.h"
Dominic Mazzoni394d4142017-02-14 11:15:31 -080010#include "SkArenaAlloc.h"
11#include "SkRasterPipeline.h"
12#include "SkReadBuffer.h"
13#include "SkString.h"
14#include "SkWriteBuffer.h"
Mike Klein8b96b372017-05-12 12:24:04 -040015#include "../jumper/SkJumper.h"
Dominic Mazzoni394d4142017-02-14 11:15:31 -080016
17#if SK_SUPPORT_GPU
Brian Osman8c68c652017-11-06 15:25:10 -050018#include "GrColorSpaceInfo.h"
Dominic Mazzoni394d4142017-02-14 11:15:31 -080019#include "GrContext.h"
20#include "glsl/GrGLSLFragmentProcessor.h"
21#include "glsl/GrGLSLFragmentShaderBuilder.h"
22#endif
23
24using InvertStyle = SkHighContrastConfig::InvertStyle;
25
Dominic Mazzoni394d4142017-02-14 11:15:31 -080026class SkHighContrast_Filter : public SkColorFilter {
27public:
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
Brian Salomond3b65972017-03-22 12:05:03 -040036 ~SkHighContrast_Filter() override {}
Dominic Mazzoni394d4142017-02-14 11:15:31 -080037
38#if SK_SUPPORT_GPU
Brian Salomon4cbb6e62017-10-25 15:12:19 -040039 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
40 GrContext*, const GrColorSpaceInfo&) const override;
Dominic Mazzoni394d4142017-02-14 11:15:31 -080041 #endif
42
Mike Kleinfdf31032017-05-09 14:57:58 -040043 void onAppendStages(SkRasterPipeline* p,
Dominic Mazzoni394d4142017-02-14 11:15:31 -080044 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
52protected:
53 void flatten(SkWriteBuffer&) const override;
54
55private:
56 SkHighContrastConfig fConfig;
57
58 friend class SkHighContrastFilter;
59
60 typedef SkColorFilter INHERITED;
61};
62
Mike Kleinfdf31032017-05-09 14:57:58 -040063void SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p,
Mike Klein8b96b372017-05-12 12:24:04 -040064 SkColorSpace* dstCS,
65 SkArenaAlloc* alloc,
Dominic Mazzoni394d4142017-02-14 11:15:31 -080066 bool shaderIsOpaque) const {
67 if (!shaderIsOpaque) {
68 p->append(SkRasterPipeline::unpremul);
69 }
70
Mike Klein8b96b372017-05-12 12:24:04 -040071 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
Dominic Mazzoni394d4142017-02-14 11:15:31 -080083 if (fConfig.fGrayscale) {
84 float r = SK_LUM_COEFF_R;
85 float g = SK_LUM_COEFF_G;
86 float b = SK_LUM_COEFF_B;
Mike Klein8b96b372017-05-12 12:24:04 -040087 float* matrix = alloc->makeArray<float>(12);
Dominic Mazzoni394d4142017-02-14 11:15:31 -080088 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) {
Mike Klein8b96b372017-05-12 12:24:04 -040095 float* matrix = alloc->makeArray<float>(12);
Dominic Mazzoni394d4142017-02-14 11:15:31 -080096 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);
Mike Klein8b96b372017-05-12 12:24:04 -0400101 float* matrix = alloc->makeArray<float>(12);
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800102 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) {
Mike Klein8b96b372017-05-12 12:24:04 -0400109 float* matrix = alloc->makeArray<float>(12);
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800110 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
Mike Klein8b96b372017-05-12 12:24:04 -0400121 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
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800132 if (!shaderIsOpaque) {
133 p->append(SkRasterPipeline::premul);
134 }
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800135}
136
137void 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
143sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) {
144 SkHighContrastConfig config;
145 config.fGrayscale = buffer.readBool();
Mike Reedde5c5022018-01-26 14:59:12 -0500146 config.fInvertStyle = buffer.read32LE(InvertStyle::kLast);
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800147 config.fContrast = buffer.readScalar();
Robert Phillipsa83d0132018-01-24 14:46:38 -0500148
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800149 return SkHighContrastFilter::Make(config);
150}
151
152sk_sp<SkColorFilter> SkHighContrastFilter::Make(
153 const SkHighContrastConfig& config) {
Mike Reedde5c5022018-01-26 14:59:12 -0500154 if (!config.isValid()) {
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800155 return nullptr;
Mike Reedde5c5022018-01-26 14:59:12 -0500156 }
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800157 return sk_make_sp<SkHighContrast_Filter>(config);
158}
159
160#ifndef SK_IGNORE_TO_STRING
161void SkHighContrast_Filter::toString(SkString* str) const {
162 str->append("SkHighContrastColorFilter ");
163}
164#endif
165
166SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter)
167 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter)
168SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
169
170#if SK_SUPPORT_GPU
171class HighContrastFilterEffect : public GrFragmentProcessor {
172public:
Brian Osman8c68c652017-11-06 15:25:10 -0500173 static std::unique_ptr<GrFragmentProcessor> Make(const SkHighContrastConfig& config,
174 bool linearize) {
175 return std::unique_ptr<GrFragmentProcessor>(new HighContrastFilterEffect(config,
176 linearize));
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800177 }
178
179 const char* name() const override { return "HighContrastFilter"; }
180
181 const SkHighContrastConfig& config() const { return fConfig; }
Brian Osman8c68c652017-11-06 15:25:10 -0500182 bool linearize() const { return fLinearize; }
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800183
Brian Osman8c68c652017-11-06 15:25:10 -0500184 std::unique_ptr<GrFragmentProcessor> clone() const override {
185 return Make(fConfig, fLinearize);
186 }
Brian Salomon164f6ec2017-07-25 15:32:17 -0400187
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800188private:
Brian Osman8c68c652017-11-06 15:25:10 -0500189 HighContrastFilterEffect(const SkHighContrastConfig& config, bool linearize)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400190 : INHERITED(kHighContrastFilterEffect_ClassID, kNone_OptimizationFlags)
Brian Osman8c68c652017-11-06 15:25:10 -0500191 , fConfig(config)
192 , fLinearize(linearize) {
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800193 }
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 &&
Brian Osman8c68c652017-11-06 15:25:10 -0500204 fConfig.fContrast == that.fConfig.fContrast &&
205 fLinearize == that.fLinearize;
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800206 }
207
208 SkHighContrastConfig fConfig;
Brian Osman8c68c652017-11-06 15:25:10 -0500209 bool fLinearize;
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800210
211 typedef GrFragmentProcessor INHERITED;
212};
213
214class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor {
215public:
216 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
217
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800218protected:
Brian Salomonab015ef2017-04-04 10:15:51 -0400219 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800220 void emitCode(EmitArgs& args) override;
221
222private:
223 UniformHandle fContrastUni;
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800224
225 typedef GrGLSLFragmentProcessor INHERITED;
226};
227
228GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const {
Brian Osman8c68c652017-11-06 15:25:10 -0500229 return new GLHighContrastFilterEffect();
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800230}
231
232void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
233 GrProcessorKeyBuilder* b) const {
234 GLHighContrastFilterEffect::GenKey(*this, caps, b);
235}
236
Brian Salomonab015ef2017-04-04 10:15:51 -0400237void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm,
238 const GrFragmentProcessor& proc) {
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800239 const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
240 pdm.set1f(fContrastUni, hcfe.config().fContrast);
241}
242
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800243void 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));
Brian Osman8c68c652017-11-06 15:25:10 -0500248 b->add32(hcfe.linearize() ? 1 : 0);
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800249}
250
251void GLHighContrastFilterEffect::emitCode(EmitArgs& args) {
Brian Osman8c68c652017-11-06 15:25:10 -0500252 const HighContrastFilterEffect& hcfe = args.fFp.cast<HighContrastFilterEffect>();
253 const SkHighContrastConfig& config = hcfe.config();
254
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800255 const char* contrast;
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400256 fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800257 "contrast", &contrast);
258
259 if (nullptr == args.fInputColor) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400260 args.fInputColor = "half4(1)";
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800261 }
262
263 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
264
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400265 fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor);
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800266
267 // Unpremultiply. The max() is to guard against 0 / 0.
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400268 fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.00001);");
269 fragBuilder->codeAppendf("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800270
Brian Osman8c68c652017-11-06 15:25:10 -0500271 if (hcfe.linearize()) {
272 fragBuilder->codeAppend("color.rgb = color.rgb * color.rgb;");
273 }
274
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800275 // Grayscale.
Brian Osman8c68c652017-11-06 15:25:10 -0500276 if (config.fGrayscale) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400277 fragBuilder->codeAppendf("half luma = dot(color, half4(%f, %f, %f, 0));",
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800278 SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400279 fragBuilder->codeAppendf("color = half4(luma, luma, luma, 0);");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800280 }
281
Brian Osman8c68c652017-11-06 15:25:10 -0500282 if (config.fInvertStyle == InvertStyle::kInvertBrightness) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400283 fragBuilder->codeAppendf("color = half4(1, 1, 1, 1) - color;");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800284 }
285
Brian Osman8c68c652017-11-06 15:25:10 -0500286 if (config.fInvertStyle == InvertStyle::kInvertLightness) {
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800287 // Convert from RGB to HSL.
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400288 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;");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800291
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400292 fragBuilder->codeAppendf("half h;");
293 fragBuilder->codeAppendf("half s;");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800294
295 fragBuilder->codeAppendf("if (fmax == fmin) {");
296 fragBuilder->codeAppendf(" h = 0;");
297 fragBuilder->codeAppendf(" s = 0;");
298 fragBuilder->codeAppendf("} else {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400299 fragBuilder->codeAppendf(" half d = fmax - fmin;");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800300 fragBuilder->codeAppendf(" s = l > 0.5 ?");
301 fragBuilder->codeAppendf(" d / (2 - fmax - fmin) :");
302 fragBuilder->codeAppendf(" d / (fmax + fmin);");
Brian Osman43a0aa52017-11-07 15:39:55 -0500303 // 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) {");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800307 fragBuilder->codeAppendf(" h = (color.g - color.b) / d + ");
308 fragBuilder->codeAppendf(" (color.g < color.b ? 6 : 0);");
Brian Osman43a0aa52017-11-07 15:39:55 -0500309 fragBuilder->codeAppendf(" } else if (color.g >= color.b) {");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800310 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[] = {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400320 GrShaderVar("p", kHalf_GrSLType),
321 GrShaderVar("q", kHalf_GrSLType),
322 GrShaderVar("t", kHalf_GrSLType),
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800323 };
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400324 fragBuilder->emitFunction(kHalf_GrSLType,
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800325 "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) {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400341 fragBuilder->codeAppendf(" color = half4(l, l, l, 0);");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800342 fragBuilder->codeAppendf("} else {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400343 fragBuilder->codeAppendf(" half q = l < 0.5 ? l * (1 + s) : l + s - l * s;");
344 fragBuilder->codeAppendf(" half p = 2 * l - q;");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800345 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);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400353 fragBuilder->codeAppendf(" half m = (1 + %s) / (1 - %s);", contrast, contrast);
354 fragBuilder->codeAppendf(" half off = (-0.5 * m + 0.5);");
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800355 fragBuilder->codeAppendf(" color = m * color + off;");
356 fragBuilder->codeAppendf("}");
357
358 // Clamp.
359 fragBuilder->codeAppendf("color = clamp(color, 0, 1);");
360
Brian Osman8c68c652017-11-06 15:25:10 -0500361 if (hcfe.linearize()) {
362 fragBuilder->codeAppend("color.rgb = sqrt(color.rgb);");
363 }
364
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800365 // 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
Brian Salomonaff329b2017-08-11 09:40:37 -0400373std::unique_ptr<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(
Brian Osman8c68c652017-11-06 15:25:10 -0500374 GrContext*, const GrColorSpaceInfo& csi) const {
375 bool linearize = !csi.isGammaCorrect();
376 return HighContrastFilterEffect::Make(fConfig, linearize);
Dominic Mazzoni394d4142017-02-14 11:15:31 -0800377}
378#endif