Home | History | Annotate | Download | only in effects
      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 
     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 
     22 using InvertStyle = SkHighContrastConfig::InvertStyle;
     23 
     24 namespace {
     25 
     26 SkScalar 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 
     48 uint8_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 
     57 SkScalar 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 
     63 static 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 
    151 class SkHighContrast_Filter : public SkColorFilter {
    152 public:
    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     ~SkHighContrast_Filter() override {}
    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 
    178 protected:
    179     void flatten(SkWriteBuffer&) const override;
    180 
    181 private:
    182     SkHighContrastConfig fConfig;
    183 
    184     friend class SkHighContrastFilter;
    185 
    186     typedef SkColorFilter INHERITED;
    187 };
    188 
    189 void 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 
    195 bool 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 
    248 void 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 
    254 sk_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 
    262 sk_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
    270 void SkHighContrast_Filter::toString(SkString* str) const {
    271     str->append("SkHighContrastColorFilter ");
    272 }
    273 #endif
    274 
    275 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter)
    276     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter)
    277 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
    278 
    279 #if SK_SUPPORT_GPU
    280 class HighContrastFilterEffect : public GrFragmentProcessor {
    281 public:
    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 
    290 private:
    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 
    314 class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor {
    315 public:
    316     static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
    317 
    318     GLHighContrastFilterEffect(const SkHighContrastConfig& config);
    319 
    320 protected:
    321     void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
    322     void emitCode(EmitArgs& args) override;
    323 
    324 private:
    325     UniformHandle fContrastUni;
    326     SkHighContrastConfig fConfig;
    327 
    328     typedef GrGLSLFragmentProcessor INHERITED;
    329 };
    330 
    331 GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const {
    332     return new GLHighContrastFilterEffect(fConfig);
    333 }
    334 
    335 void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    336                                                      GrProcessorKeyBuilder* b) const {
    337     GLHighContrastFilterEffect::GenKey(*this, caps, b);
    338 }
    339 
    340 void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) {
    341     const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
    342     pdm.set1f(fContrastUni, hcfe.config().fContrast);
    343 }
    344 
    345 GLHighContrastFilterEffect::GLHighContrastFilterEffect(const SkHighContrastConfig& config)
    346     : INHERITED()
    347     , fConfig(config) {
    348 }
    349 
    350 void 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 
    357 void 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 
    466 sk_sp<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(GrContext*, SkColorSpace*) const {
    467     return HighContrastFilterEffect::Make(fConfig);
    468 }
    469 #endif
    470