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 "GrNonlinearColorSpaceXformEffect.h"
      9 #include "GrColorSpaceXform.h"
     10 #include "GrProcessor.h"
     11 #include "SkColorSpace_Base.h"
     12 #include "glsl/GrGLSLFragmentProcessor.h"
     13 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     14 
     15 class GrGLNonlinearColorSpaceXformEffect : public GrGLSLFragmentProcessor {
     16 public:
     17     void emitCode(EmitArgs& args) override {
     18         const GrNonlinearColorSpaceXformEffect& csxe =
     19                 args.fFp.cast<GrNonlinearColorSpaceXformEffect>();
     20         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     21         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
     22 
     23         const char* srcCoeffsName = nullptr;
     24         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
     25             fSrcTransferFnUni = uniformHandler->addUniformArray(
     26                     kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
     27                     "SrcTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
     28                     &srcCoeffsName);
     29         }
     30 
     31         const char* dstCoeffsName = nullptr;
     32         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
     33             fDstTransferFnUni = uniformHandler->addUniformArray(
     34                     kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
     35                     "DstTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
     36                     &dstCoeffsName);
     37         }
     38 
     39         const char* gamutXformName = nullptr;
     40         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
     41             fGamutXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
     42                                                         kDefault_GrSLPrecision, "GamutXform",
     43                                                         &gamutXformName);
     44         }
     45 
     46         // Helper function to apply the src or dst transfer function to a single value
     47         SkString tfFuncNames[2];
     48         for (size_t i = 0; i < 2; ++i) {
     49             const char* coeffsName = i ? dstCoeffsName : srcCoeffsName;
     50             if (!coeffsName) {
     51                 continue;
     52             }
     53             const char* fnName = i ? "dst_transfer_fn" : "src_transfer_fn";
     54             static const GrShaderVar gTransferFnFuncArgs[] = {
     55                     GrShaderVar("x", kFloat_GrSLType),
     56             };
     57             SkString transferFnBody;
     58             // Temporaries to make evaluation line readable
     59             transferFnBody.printf("float A = %s[0];", coeffsName);
     60             transferFnBody.appendf("float B = %s[1];", coeffsName);
     61             transferFnBody.appendf("float C = %s[2];", coeffsName);
     62             transferFnBody.appendf("float D = %s[3];", coeffsName);
     63             transferFnBody.appendf("float E = %s[4];", coeffsName);
     64             transferFnBody.appendf("float F = %s[5];", coeffsName);
     65             transferFnBody.appendf("float G = %s[6];", coeffsName);
     66             transferFnBody.appendf("return (x < D) ? (C * x) + F : pow(A * x + B, G) + E;");
     67             fragBuilder->emitFunction(kFloat_GrSLType, fnName, SK_ARRAY_COUNT(gTransferFnFuncArgs),
     68                                       gTransferFnFuncArgs, transferFnBody.c_str(), &tfFuncNames[i]);
     69         }
     70 
     71         if (nullptr == args.fInputColor) {
     72             args.fInputColor = "vec4(1)";
     73         }
     74         fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
     75 
     76         // 1: Un-premultiply the input color (if necessary)
     77         fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);");
     78         fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
     79 
     80         // 2: Apply src transfer function (to get to linear RGB)
     81         if (srcCoeffsName) {
     82             fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[0].c_str());
     83             fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[0].c_str());
     84             fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[0].c_str());
     85         }
     86 
     87         // 3: Apply gamut matrix
     88         if (gamutXformName) {
     89             // Color is unpremultiplied at this point, so clamp to [0, 1]
     90             fragBuilder->codeAppendf(
     91                 "color.rgb = clamp((%s * vec4(color.rgb, 1.0)).rgb, 0.0, 1.0);", gamutXformName);
     92         }
     93 
     94         // 4: Apply dst transfer fn
     95         if (dstCoeffsName) {
     96             fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[1].c_str());
     97             fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[1].c_str());
     98             fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[1].c_str());
     99         }
    100 
    101         // 5: Premultiply again
    102         fragBuilder->codeAppendf("%s = vec4(color.rgb * color.a, color.a);", args.fOutputColor);
    103     }
    104 
    105     static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
    106                               GrProcessorKeyBuilder* b) {
    107         const GrNonlinearColorSpaceXformEffect& csxe =
    108                 processor.cast<GrNonlinearColorSpaceXformEffect>();
    109         b->add32(csxe.ops());
    110     }
    111 
    112 protected:
    113     void onSetData(const GrGLSLProgramDataManager& pdman,
    114                    const GrFragmentProcessor& processor) override {
    115         const GrNonlinearColorSpaceXformEffect& csxe =
    116                 processor.cast<GrNonlinearColorSpaceXformEffect>();
    117         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
    118             pdman.set1fv(fSrcTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
    119                          csxe.srcTransferFnCoeffs());
    120         }
    121         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
    122             pdman.set1fv(fDstTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
    123                          csxe.dstTransferFnCoeffs());
    124         }
    125         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
    126             pdman.setSkMatrix44(fGamutXformUni, csxe.gamutXform());
    127         }
    128     }
    129 
    130 private:
    131     GrGLSLProgramDataManager::UniformHandle fSrcTransferFnUni;
    132     GrGLSLProgramDataManager::UniformHandle fDstTransferFnUni;
    133     GrGLSLProgramDataManager::UniformHandle fGamutXformUni;
    134 
    135     typedef GrGLSLFragmentProcessor INHERITED;
    136 };
    137 
    138 ///////////////////////////////////////////////////////////////////////////////
    139 
    140 GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
    141     uint32_t ops, const SkColorSpaceTransferFn& srcTransferFn,
    142     const SkColorSpaceTransferFn& dstTransferFn, const SkMatrix44& gamutXform)
    143         : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
    144         , fGamutXform(gamutXform)
    145         , fOps(ops) {
    146     this->initClassID<GrNonlinearColorSpaceXformEffect>();
    147 
    148     fSrcTransferFnCoeffs[0] = srcTransferFn.fA;
    149     fSrcTransferFnCoeffs[1] = srcTransferFn.fB;
    150     fSrcTransferFnCoeffs[2] = srcTransferFn.fC;
    151     fSrcTransferFnCoeffs[3] = srcTransferFn.fD;
    152     fSrcTransferFnCoeffs[4] = srcTransferFn.fE;
    153     fSrcTransferFnCoeffs[5] = srcTransferFn.fF;
    154     fSrcTransferFnCoeffs[6] = srcTransferFn.fG;
    155 
    156     fDstTransferFnCoeffs[0] = dstTransferFn.fA;
    157     fDstTransferFnCoeffs[1] = dstTransferFn.fB;
    158     fDstTransferFnCoeffs[2] = dstTransferFn.fC;
    159     fDstTransferFnCoeffs[3] = dstTransferFn.fD;
    160     fDstTransferFnCoeffs[4] = dstTransferFn.fE;
    161     fDstTransferFnCoeffs[5] = dstTransferFn.fF;
    162     fDstTransferFnCoeffs[6] = dstTransferFn.fG;
    163 }
    164 
    165 bool GrNonlinearColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const {
    166     const GrNonlinearColorSpaceXformEffect& other = s.cast<GrNonlinearColorSpaceXformEffect>();
    167     if (other.fOps != fOps) {
    168         return false;
    169     }
    170     if (SkToBool(fOps & kSrcTransfer_Op) &&
    171         memcmp(&other.fSrcTransferFnCoeffs, &fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs))) {
    172         return false;
    173     }
    174     if (SkToBool(fOps & kDstTransfer_Op) &&
    175         memcmp(&other.fDstTransferFnCoeffs, &fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs))) {
    176         return false;
    177     }
    178     if (SkToBool(fOps & kGamutXform_Op) && other.fGamutXform != fGamutXform) {
    179         return false;
    180     }
    181     return true;
    182 }
    183 
    184 ///////////////////////////////////////////////////////////////////////////////
    185 
    186 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrNonlinearColorSpaceXformEffect);
    187 
    188 #if GR_TEST_UTILS
    189 sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::TestCreate(GrProcessorTestData* d) {
    190     // TODO: Generate a random variety of color spaces for this effect (it can handle wacky
    191     // transfer functions, etc...)
    192     sk_sp<SkColorSpace> srcSpace = SkColorSpace::MakeSRGBLinear();
    193     sk_sp<SkColorSpace> dstSpace = SkColorSpace::MakeSRGB();
    194     return GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), dstSpace.get());
    195 }
    196 #endif
    197 
    198 ///////////////////////////////////////////////////////////////////////////////
    199 
    200 void GrNonlinearColorSpaceXformEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    201                                                              GrProcessorKeyBuilder* b) const {
    202     GrGLNonlinearColorSpaceXformEffect::GenKey(*this, caps, b);
    203 }
    204 
    205 GrGLSLFragmentProcessor* GrNonlinearColorSpaceXformEffect::onCreateGLSLInstance() const {
    206     return new GrGLNonlinearColorSpaceXformEffect();
    207 }
    208 
    209 sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make(const SkColorSpace* src,
    210                                                                   const SkColorSpace* dst) {
    211     if (!src || !dst || SkColorSpace::Equals(src, dst)) {
    212         // No conversion possible (or necessary)
    213         return nullptr;
    214     }
    215 
    216     uint32_t ops = 0;
    217 
    218     // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching)
    219     auto gamutXform = GrColorSpaceXform::Make(src, dst);
    220     SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor);
    221     if (gamutXform) {
    222         ops |= kGamutXform_Op;
    223         srcToDstMtx = gamutXform->srcToDst();
    224     }
    225 
    226     SkColorSpaceTransferFn srcTransferFn;
    227     if (!src->gammaIsLinear()) {
    228         if (src->isNumericalTransferFn(&srcTransferFn)) {
    229             ops |= kSrcTransfer_Op;
    230         } else {
    231             return nullptr;
    232         }
    233     }
    234 
    235     SkColorSpaceTransferFn dstTransferFn;
    236     if (!dst->gammaIsLinear()) {
    237         if (dst->isNumericalTransferFn(&dstTransferFn)) {
    238             dstTransferFn = dstTransferFn.invert();
    239             ops |= kDstTransfer_Op;
    240         } else {
    241             return nullptr;
    242         }
    243     }
    244 
    245     return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(
    246             ops, srcTransferFn, dstTransferFn, srcToDstMtx));
    247 }
    248