• 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 "SkArenaAlloc.h"
10 #include "SkColorData.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 "GrColorSpaceInfo.h"
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     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
39             GrRecordingContext*, const GrColorSpaceInfo&) const override;
40  #endif
41 
42     void onAppendStages(SkRasterPipeline* p,
43                         SkColorSpace* dst,
44                         SkArenaAlloc* scratch,
45                         bool shaderIsOpaque) const override;
46 
47 protected:
48     void flatten(SkWriteBuffer&) const override;
49 
50 private:
51     SK_FLATTENABLE_HOOKS(SkHighContrast_Filter)
52 
53     SkHighContrastConfig fConfig;
54 
55     friend class SkHighContrastFilter;
56 
57     typedef SkColorFilter INHERITED;
58 };
59 
onAppendStages(SkRasterPipeline * p,SkColorSpace * dstCS,SkArenaAlloc * alloc,bool shaderIsOpaque) const60 void SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p,
61                                            SkColorSpace* dstCS,
62                                            SkArenaAlloc* alloc,
63                                            bool shaderIsOpaque) const {
64     if (!shaderIsOpaque) {
65         p->append(SkRasterPipeline::unpremul);
66     }
67 
68     // Linearize before applying high-contrast filter.
69     auto tf = alloc->make<skcms_TransferFunction>();
70     if (dstCS) {
71         dstCS->transferFn(&tf->g);
72     } else {
73         // Historically we approximate untagged destinations as gamma 2.
74         // TODO: sRGB?
75         *tf = {2,1, 0,0,0,0,0};
76     }
77     p->append(SkRasterPipeline::parametric, tf);
78 
79     if (fConfig.fGrayscale) {
80         float r = SK_LUM_COEFF_R;
81         float g = SK_LUM_COEFF_G;
82         float b = SK_LUM_COEFF_B;
83         float* matrix = alloc->makeArray<float>(12);
84         matrix[0] = matrix[1] = matrix[2] = r;
85         matrix[3] = matrix[4] = matrix[5] = g;
86         matrix[6] = matrix[7] = matrix[8] = b;
87         p->append(SkRasterPipeline::matrix_3x4, matrix);
88     }
89 
90     if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) {
91         float* matrix = alloc->makeArray<float>(12);
92         matrix[0] = matrix[4] = matrix[8] = -1;
93         matrix[9] = matrix[10] = matrix[11] = 1;
94         p->append(SkRasterPipeline::matrix_3x4, matrix);
95     } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) {
96         p->append(SkRasterPipeline::rgb_to_hsl);
97         float* matrix = alloc->makeArray<float>(12);
98         matrix[0] = matrix[4] = matrix[11] = 1;
99         matrix[8] = -1;
100         p->append(SkRasterPipeline::matrix_3x4, matrix);
101         p->append(SkRasterPipeline::hsl_to_rgb);
102     }
103 
104     if (fConfig.fContrast != 0.0) {
105         float* matrix = alloc->makeArray<float>(12);
106         float c = fConfig.fContrast;
107         float m = (1 + c) / (1 - c);
108         float b = (-0.5f * m + 0.5f);
109         matrix[0] = matrix[4] = matrix[8] = m;
110         matrix[9] = matrix[10] = matrix[11] = b;
111         p->append(SkRasterPipeline::matrix_3x4, matrix);
112     }
113 
114     p->append(SkRasterPipeline::clamp_0);
115     p->append(SkRasterPipeline::clamp_1);
116 
117     // Re-encode back from linear.
118     auto invTF = alloc->make<skcms_TransferFunction>();
119     if (dstCS) {
120         dstCS->invTransferFn(&invTF->g);
121     } else {
122         // See above... historically untagged == gamma 2 in this filter.
123         *invTF ={0.5f,1, 0,0,0,0,0};
124     }
125     p->append(SkRasterPipeline::parametric, invTF);
126 
127     if (!shaderIsOpaque) {
128         p->append(SkRasterPipeline::premul);
129     }
130 }
131 
flatten(SkWriteBuffer & buffer) const132 void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const {
133     buffer.writeBool(fConfig.fGrayscale);
134     buffer.writeInt(static_cast<int>(fConfig.fInvertStyle));
135     buffer.writeScalar(fConfig.fContrast);
136 }
137 
CreateProc(SkReadBuffer & buffer)138 sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) {
139     SkHighContrastConfig config;
140     config.fGrayscale = buffer.readBool();
141     config.fInvertStyle = buffer.read32LE(InvertStyle::kLast);
142     config.fContrast = buffer.readScalar();
143 
144     return SkHighContrastFilter::Make(config);
145 }
146 
Make(const SkHighContrastConfig & config)147 sk_sp<SkColorFilter> SkHighContrastFilter::Make(
148     const SkHighContrastConfig& config) {
149     if (!config.isValid()) {
150         return nullptr;
151     }
152     return sk_make_sp<SkHighContrast_Filter>(config);
153 }
154 
RegisterFlattenables()155 void SkHighContrastFilter::RegisterFlattenables() {
156     SK_REGISTER_FLATTENABLE(SkHighContrast_Filter);
157 }
158 
159 #if SK_SUPPORT_GPU
160 class HighContrastFilterEffect : public GrFragmentProcessor {
161 public:
Make(const SkHighContrastConfig & config,bool linearize)162     static std::unique_ptr<GrFragmentProcessor> Make(const SkHighContrastConfig& config,
163                                                      bool linearize) {
164         return std::unique_ptr<GrFragmentProcessor>(new HighContrastFilterEffect(config,
165                                                                                  linearize));
166     }
167 
name() const168     const char* name() const override { return "HighContrastFilter"; }
169 
config() const170     const SkHighContrastConfig& config() const { return fConfig; }
linearize() const171     bool linearize() const { return fLinearize; }
172 
clone() const173     std::unique_ptr<GrFragmentProcessor> clone() const override {
174         return Make(fConfig, fLinearize);
175     }
176 
177 private:
HighContrastFilterEffect(const SkHighContrastConfig & config,bool linearize)178     HighContrastFilterEffect(const SkHighContrastConfig& config, bool linearize)
179         : INHERITED(kHighContrastFilterEffect_ClassID, kNone_OptimizationFlags)
180         , fConfig(config)
181         , fLinearize(linearize) {}
182 
183     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
184 
185     virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
186                                        GrProcessorKeyBuilder* b) const override;
187 
onIsEqual(const GrFragmentProcessor & other) const188     bool onIsEqual(const GrFragmentProcessor& other) const override {
189         const HighContrastFilterEffect& that = other.cast<HighContrastFilterEffect>();
190         return fConfig.fGrayscale == that.fConfig.fGrayscale &&
191             fConfig.fInvertStyle == that.fConfig.fInvertStyle &&
192             fConfig.fContrast == that.fConfig.fContrast &&
193             fLinearize == that.fLinearize;
194     }
195 
196     SkHighContrastConfig fConfig;
197     bool fLinearize;
198 
199     typedef GrFragmentProcessor INHERITED;
200 };
201 
202 class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor {
203 public:
204     static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
205 
206 protected:
207     void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
208     void emitCode(EmitArgs& args) override;
209 
210 private:
211     UniformHandle fContrastUni;
212 
213     typedef GrGLSLFragmentProcessor INHERITED;
214 };
215 
onCreateGLSLInstance() const216 GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const {
217     return new GLHighContrastFilterEffect();
218 }
219 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const220 void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
221                                                      GrProcessorKeyBuilder* b) const {
222     GLHighContrastFilterEffect::GenKey(*this, caps, b);
223 }
224 
onSetData(const GrGLSLProgramDataManager & pdm,const GrFragmentProcessor & proc)225 void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm,
226                                            const GrFragmentProcessor& proc) {
227     const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
228     pdm.set1f(fContrastUni, hcfe.config().fContrast);
229 }
230 
GenKey(const GrProcessor & proc,const GrShaderCaps &,GrProcessorKeyBuilder * b)231 void GLHighContrastFilterEffect::GenKey(
232     const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
233   const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
234   b->add32(static_cast<uint32_t>(hcfe.config().fGrayscale));
235   b->add32(static_cast<uint32_t>(hcfe.config().fInvertStyle));
236   b->add32(hcfe.linearize() ? 1 : 0);
237 }
238 
emitCode(EmitArgs & args)239 void GLHighContrastFilterEffect::emitCode(EmitArgs& args) {
240     const HighContrastFilterEffect& hcfe = args.fFp.cast<HighContrastFilterEffect>();
241     const SkHighContrastConfig& config = hcfe.config();
242 
243     const char* contrast;
244     fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
245                                                     "contrast", &contrast);
246 
247     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
248 
249     fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor);
250 
251     // Unpremultiply. The max() is to guard against 0 / 0.
252     fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.00001);");
253     fragBuilder->codeAppendf("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
254 
255     if (hcfe.linearize()) {
256         fragBuilder->codeAppend("color.rgb = color.rgb * color.rgb;");
257     }
258 
259     // Grayscale.
260     if (config.fGrayscale) {
261         fragBuilder->codeAppendf("half luma = dot(color, half4(%f, %f, %f, 0));",
262                                  SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B);
263         fragBuilder->codeAppendf("color = half4(luma, luma, luma, 0);");
264     }
265 
266     if (config.fInvertStyle == InvertStyle::kInvertBrightness) {
267         fragBuilder->codeAppendf("color = half4(1, 1, 1, 1) - color;");
268     }
269 
270     if (config.fInvertStyle == InvertStyle::kInvertLightness) {
271         // Convert from RGB to HSL.
272         fragBuilder->codeAppendf("half fmax = max(color.r, max(color.g, color.b));");
273         fragBuilder->codeAppendf("half fmin = min(color.r, min(color.g, color.b));");
274         fragBuilder->codeAppendf("half l = (fmax + fmin) / 2;");
275 
276         fragBuilder->codeAppendf("half h;");
277         fragBuilder->codeAppendf("half s;");
278 
279         fragBuilder->codeAppendf("if (fmax == fmin) {");
280         fragBuilder->codeAppendf("  h = 0;");
281         fragBuilder->codeAppendf("  s = 0;");
282         fragBuilder->codeAppendf("} else {");
283         fragBuilder->codeAppendf("  half d = fmax - fmin;");
284         fragBuilder->codeAppendf("  s = l > 0.5 ?");
285         fragBuilder->codeAppendf("      d / (2 - fmax - fmin) :");
286         fragBuilder->codeAppendf("      d / (fmax + fmin);");
287         // We'd like to just write "if (color.r == fmax) { ... }". On many GPUs, running the
288         // angle_d3d9_es2 config, that failed. It seems that max(x, y) is not necessarily equal
289         // to either x or y. Tried several ways to fix it, but this was the only reasonable fix.
290         fragBuilder->codeAppendf("  if (color.r >= color.g && color.r >= color.b) {");
291         fragBuilder->codeAppendf("    h = (color.g - color.b) / d + ");
292         fragBuilder->codeAppendf("        (color.g < color.b ? 6 : 0);");
293         fragBuilder->codeAppendf("  } else if (color.g >= color.b) {");
294         fragBuilder->codeAppendf("    h = (color.b - color.r) / d + 2;");
295         fragBuilder->codeAppendf("  } else {");
296         fragBuilder->codeAppendf("    h = (color.r - color.g) / d + 4;");
297         fragBuilder->codeAppendf("  }");
298         fragBuilder->codeAppendf("}");
299         fragBuilder->codeAppendf("h /= 6;");
300         fragBuilder->codeAppendf("l = 1.0 - l;");
301         // Convert back from HSL to RGB.
302         SkString hue2rgbFuncName;
303         const GrShaderVar gHue2rgbArgs[] = {
304             GrShaderVar("p", kHalf_GrSLType),
305             GrShaderVar("q", kHalf_GrSLType),
306             GrShaderVar("t", kHalf_GrSLType),
307         };
308         fragBuilder->emitFunction(kHalf_GrSLType,
309                                   "hue2rgb",
310                                   SK_ARRAY_COUNT(gHue2rgbArgs),
311                                   gHue2rgbArgs,
312                                   "if (t < 0)"
313                                   "  t += 1;"
314                                   "if (t > 1)"
315                                   "  t -= 1;"
316                                   "if (t < 1/6.)"
317                                   "  return p + (q - p) * 6 * t;"
318                                   "if (t < 1/2.)"
319                                   "  return q;"
320                                   "if (t < 2/3.)"
321                                   "  return p + (q - p) * (2/3. - t) * 6;"
322                                   "return p;",
323                                   &hue2rgbFuncName);
324         fragBuilder->codeAppendf("if (s == 0) {");
325         fragBuilder->codeAppendf("  color = half4(l, l, l, 0);");
326         fragBuilder->codeAppendf("} else {");
327         fragBuilder->codeAppendf("  half q = l < 0.5 ? l * (1 + s) : l + s - l * s;");
328         fragBuilder->codeAppendf("  half p = 2 * l - q;");
329         fragBuilder->codeAppendf("  color.r = %s(p, q, h + 1/3.);", hue2rgbFuncName.c_str());
330         fragBuilder->codeAppendf("  color.g = %s(p, q, h);", hue2rgbFuncName.c_str());
331         fragBuilder->codeAppendf("  color.b = %s(p, q, h - 1/3.);", hue2rgbFuncName.c_str());
332         fragBuilder->codeAppendf("}");
333     }
334 
335     // Contrast.
336     fragBuilder->codeAppendf("if (%s != 0) {", contrast);
337     fragBuilder->codeAppendf("  half m = (1 + %s) / (1 - %s);", contrast, contrast);
338     fragBuilder->codeAppendf("  half off = (-0.5 * m + 0.5);");
339     fragBuilder->codeAppendf("  color = m * color + off;");
340     fragBuilder->codeAppendf("}");
341 
342     // Clamp.
343     fragBuilder->codeAppendf("color = saturate(color);");
344 
345     if (hcfe.linearize()) {
346         fragBuilder->codeAppend("color.rgb = sqrt(color.rgb);");
347     }
348 
349     // Restore the original alpha and premultiply.
350     fragBuilder->codeAppendf("color.a = %s.a;", args.fInputColor);
351     fragBuilder->codeAppendf("color.rgb *= color.a;");
352 
353     // Copy to the output color.
354     fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
355 }
356 
asFragmentProcessor(GrRecordingContext *,const GrColorSpaceInfo & csi) const357 std::unique_ptr<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(
358         GrRecordingContext*, const GrColorSpaceInfo& csi) const {
359     bool linearize = !csi.isLinearlyBlended();
360     return HighContrastFilterEffect::Make(fConfig, linearize);
361 }
362 #endif
363