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