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 #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 "GrColorSpaceInfo.h"
     19 #include "GrContext.h"
     20 #include "glsl/GrGLSLFragmentProcessor.h"
     21 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     22 #endif
     23 
     24 using InvertStyle = SkHighContrastConfig::InvertStyle;
     25 
     26 class SkHighContrast_Filter : public SkColorFilter {
     27 public:
     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 
     36     ~SkHighContrast_Filter() override {}
     37 
     38 #if SK_SUPPORT_GPU
     39     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
     40             GrContext*, const GrColorSpaceInfo&) const override;
     41  #endif
     42 
     43     void onAppendStages(SkRasterPipeline* p,
     44                         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 
     52 protected:
     53     void flatten(SkWriteBuffer&) const override;
     54 
     55 private:
     56     SkHighContrastConfig fConfig;
     57 
     58     friend class SkHighContrastFilter;
     59 
     60     typedef SkColorFilter INHERITED;
     61 };
     62 
     63 void SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p,
     64                                            SkColorSpace* dstCS,
     65                                            SkArenaAlloc* alloc,
     66                                            bool shaderIsOpaque) const {
     67     if (!shaderIsOpaque) {
     68         p->append(SkRasterPipeline::unpremul);
     69     }
     70 
     71     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 
     83     if (fConfig.fGrayscale) {
     84         float r = SK_LUM_COEFF_R;
     85         float g = SK_LUM_COEFF_G;
     86         float b = SK_LUM_COEFF_B;
     87         float* matrix = alloc->makeArray<float>(12);
     88         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) {
     95         float* matrix = alloc->makeArray<float>(12);
     96         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);
    101         float* matrix = alloc->makeArray<float>(12);
    102         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) {
    109         float* matrix = alloc->makeArray<float>(12);
    110         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 
    121     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 
    132     if (!shaderIsOpaque) {
    133         p->append(SkRasterPipeline::premul);
    134     }
    135 }
    136 
    137 void 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 
    143 sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) {
    144     SkHighContrastConfig config;
    145     config.fGrayscale = buffer.readBool();
    146     config.fInvertStyle = buffer.read32LE(InvertStyle::kLast);
    147     config.fContrast = buffer.readScalar();
    148 
    149     return SkHighContrastFilter::Make(config);
    150 }
    151 
    152 sk_sp<SkColorFilter> SkHighContrastFilter::Make(
    153     const SkHighContrastConfig& config) {
    154     if (!config.isValid()) {
    155         return nullptr;
    156     }
    157     return sk_make_sp<SkHighContrast_Filter>(config);
    158 }
    159 
    160 #ifndef SK_IGNORE_TO_STRING
    161 void SkHighContrast_Filter::toString(SkString* str) const {
    162     str->append("SkHighContrastColorFilter ");
    163 }
    164 #endif
    165 
    166 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter)
    167     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter)
    168 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
    169 
    170 #if SK_SUPPORT_GPU
    171 class HighContrastFilterEffect : public GrFragmentProcessor {
    172 public:
    173     static std::unique_ptr<GrFragmentProcessor> Make(const SkHighContrastConfig& config,
    174                                                      bool linearize) {
    175         return std::unique_ptr<GrFragmentProcessor>(new HighContrastFilterEffect(config,
    176                                                                                  linearize));
    177     }
    178 
    179     const char* name() const override { return "HighContrastFilter"; }
    180 
    181     const SkHighContrastConfig& config() const { return fConfig; }
    182     bool linearize() const { return fLinearize; }
    183 
    184     std::unique_ptr<GrFragmentProcessor> clone() const override {
    185         return Make(fConfig, fLinearize);
    186     }
    187 
    188 private:
    189     HighContrastFilterEffect(const SkHighContrastConfig& config, bool linearize)
    190         : INHERITED(kHighContrastFilterEffect_ClassID, kNone_OptimizationFlags)
    191         , fConfig(config)
    192         , fLinearize(linearize) {
    193     }
    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 &&
    204             fConfig.fContrast == that.fConfig.fContrast &&
    205             fLinearize == that.fLinearize;
    206     }
    207 
    208     SkHighContrastConfig fConfig;
    209     bool fLinearize;
    210 
    211     typedef GrFragmentProcessor INHERITED;
    212 };
    213 
    214 class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor {
    215 public:
    216     static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
    217 
    218 protected:
    219     void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
    220     void emitCode(EmitArgs& args) override;
    221 
    222 private:
    223     UniformHandle fContrastUni;
    224 
    225     typedef GrGLSLFragmentProcessor INHERITED;
    226 };
    227 
    228 GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const {
    229     return new GLHighContrastFilterEffect();
    230 }
    231 
    232 void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    233                                                      GrProcessorKeyBuilder* b) const {
    234     GLHighContrastFilterEffect::GenKey(*this, caps, b);
    235 }
    236 
    237 void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm,
    238                                            const GrFragmentProcessor& proc) {
    239     const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
    240     pdm.set1f(fContrastUni, hcfe.config().fContrast);
    241 }
    242 
    243 void 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));
    248   b->add32(hcfe.linearize() ? 1 : 0);
    249 }
    250 
    251 void GLHighContrastFilterEffect::emitCode(EmitArgs& args) {
    252     const HighContrastFilterEffect& hcfe = args.fFp.cast<HighContrastFilterEffect>();
    253     const SkHighContrastConfig& config = hcfe.config();
    254 
    255     const char* contrast;
    256     fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
    257                                                     "contrast", &contrast);
    258 
    259     if (nullptr == args.fInputColor) {
    260         args.fInputColor = "half4(1)";
    261     }
    262 
    263     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
    264 
    265     fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor);
    266 
    267     // Unpremultiply. The max() is to guard against 0 / 0.
    268     fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.00001);");
    269     fragBuilder->codeAppendf("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
    270 
    271     if (hcfe.linearize()) {
    272         fragBuilder->codeAppend("color.rgb = color.rgb * color.rgb;");
    273     }
    274 
    275     // Grayscale.
    276     if (config.fGrayscale) {
    277         fragBuilder->codeAppendf("half luma = dot(color, half4(%f, %f, %f, 0));",
    278                                  SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B);
    279         fragBuilder->codeAppendf("color = half4(luma, luma, luma, 0);");
    280     }
    281 
    282     if (config.fInvertStyle == InvertStyle::kInvertBrightness) {
    283         fragBuilder->codeAppendf("color = half4(1, 1, 1, 1) - color;");
    284     }
    285 
    286     if (config.fInvertStyle == InvertStyle::kInvertLightness) {
    287         // Convert from RGB to HSL.
    288         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;");
    291 
    292         fragBuilder->codeAppendf("half h;");
    293         fragBuilder->codeAppendf("half s;");
    294 
    295         fragBuilder->codeAppendf("if (fmax == fmin) {");
    296         fragBuilder->codeAppendf("  h = 0;");
    297         fragBuilder->codeAppendf("  s = 0;");
    298         fragBuilder->codeAppendf("} else {");
    299         fragBuilder->codeAppendf("  half d = fmax - fmin;");
    300         fragBuilder->codeAppendf("  s = l > 0.5 ?");
    301         fragBuilder->codeAppendf("      d / (2 - fmax - fmin) :");
    302         fragBuilder->codeAppendf("      d / (fmax + fmin);");
    303         // 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) {");
    307         fragBuilder->codeAppendf("    h = (color.g - color.b) / d + ");
    308         fragBuilder->codeAppendf("        (color.g < color.b ? 6 : 0);");
    309         fragBuilder->codeAppendf("  } else if (color.g >= color.b) {");
    310         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[] = {
    320             GrShaderVar("p", kHalf_GrSLType),
    321             GrShaderVar("q", kHalf_GrSLType),
    322             GrShaderVar("t", kHalf_GrSLType),
    323         };
    324         fragBuilder->emitFunction(kHalf_GrSLType,
    325                                   "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) {");
    341         fragBuilder->codeAppendf("  color = half4(l, l, l, 0);");
    342         fragBuilder->codeAppendf("} else {");
    343         fragBuilder->codeAppendf("  half q = l < 0.5 ? l * (1 + s) : l + s - l * s;");
    344         fragBuilder->codeAppendf("  half p = 2 * l - q;");
    345         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);
    353     fragBuilder->codeAppendf("  half m = (1 + %s) / (1 - %s);", contrast, contrast);
    354     fragBuilder->codeAppendf("  half off = (-0.5 * m + 0.5);");
    355     fragBuilder->codeAppendf("  color = m * color + off;");
    356     fragBuilder->codeAppendf("}");
    357 
    358     // Clamp.
    359     fragBuilder->codeAppendf("color = clamp(color, 0, 1);");
    360 
    361     if (hcfe.linearize()) {
    362         fragBuilder->codeAppend("color.rgb = sqrt(color.rgb);");
    363     }
    364 
    365     // 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 
    373 std::unique_ptr<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(
    374         GrContext*, const GrColorSpaceInfo& csi) const {
    375     bool linearize = !csi.isGammaCorrect();
    376     return HighContrastFilterEffect::Make(fConfig, linearize);
    377 }
    378 #endif
    379