• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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