text: /*
   * Copyright 2006 The Android Open Source Project
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "SkArenaAlloc.h"
  #include "SkBlendModePriv.h"
  #include "SkComposeShader.h"
  #include "SkColorFilter.h"
  #include "SkColorData.h"
  #include "SkColorShader.h"
  #include "SkRasterPipeline.h"
  #include "SkReadBuffer.h"
  #include "SkWriteBuffer.h"
  #include "SkString.h"
  
  sk_sp<SkShader> SkShader::MakeCompose(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode mode,
                                        float lerpT) {
      if (!src || !dst || SkScalarIsNaN(lerpT)) {
          return nullptr;
      }
      lerpT = SkScalarPin(lerpT, 0, 1);
  
      if (lerpT == 0) {
          return dst;
      } else if (lerpT == 1) {
          if (mode == SkBlendMode::kSrc) {
              return src;
          }
          if (mode == SkBlendMode::kDst) {
              return dst;
          }
      }
      return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode, lerpT));
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
      sk_sp<SkShader> dst(buffer.readShader());
      sk_sp<SkShader> src(buffer.readShader());
      unsigned        mode = buffer.read32();
      float           lerp = buffer.readScalar();
  
      // check for valid mode before we cast to the enum type
      if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
          return nullptr;
      }
      return MakeCompose(std::move(dst), std::move(src), static_cast<SkBlendMode>(mode), lerp);
  }
  
  void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
      buffer.writeFlattenable(fDst.get());
      buffer.writeFlattenable(fSrc.get());
      buffer.write32((int)fMode);
      buffer.writeScalar(fLerpT);
  }
  
  sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
      return MakeCompose(xformer->apply(fDst.get()), xformer->apply(fSrc.get()),
                         fMode, fLerpT);
  }
  
  #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
  bool SkComposeShader::asACompose(ComposeRec* rec) const {
      if (!this->isJustMode()) {
          return false;
      }
  
      if (rec) {
          rec->fShaderA   = fDst.get();
          rec->fShaderB   = fSrc.get();
          rec->fBlendMode = fMode;
      }
      return true;
  }
  #endif
  
  bool SkComposeShader::onAppendStages(const StageRec& rec) const {
      struct Storage {
          float   fRGBA[4 * SkRasterPipeline_kMaxStride];
          float   fAlpha;
      };
      auto storage = rec.fAlloc->make<Storage>();
  
      if (!as_SB(fSrc)->appendStages(rec)) {
          return false;
      }
      // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now
      // since fShaderB will overwrite them.
      rec.fPipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
  
      if (!as_SB(fDst)->appendStages(rec)) {
          return false;
      }
      // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode/lerp
      // so we have to shuttle them. If we had a stage the would load_into_dst, then we could
      // reverse the two shader invocations, and avoid this move...
      rec.fPipeline->append(SkRasterPipeline::move_src_dst);
      rec.fPipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
  
      if (!this->isJustLerp()) {
          SkBlendMode_AppendStages(fMode, rec.fPipeline);
      }
      if (!this->isJustMode()) {
          rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fLerpT);
      }
      return true;
  }
  
  #if SK_SUPPORT_GPU
  
  #include "effects/GrConstColorProcessor.h"
  #include "effects/GrXfermodeFragmentProcessor.h"
  
  /////////////////////////////////////////////////////////////////////
  
  std::unique_ptr<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(
          const GrFPArgs& args) const {
      if (this->isJustMode()) {
          SkASSERT(fMode != SkBlendMode::kSrc && fMode != SkBlendMode::kDst); // caught in factory
          if (fMode == SkBlendMode::kClear) {
              return GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT,
                                                 GrConstColorProcessor::InputMode::kIgnore);
          }
      }
  
      std::unique_ptr<GrFragmentProcessor> fpA(as_SB(fDst)->asFragmentProcessor(args));
      if (!fpA) {
          return nullptr;
      }
      std::unique_ptr<GrFragmentProcessor> fpB(as_SB(fSrc)->asFragmentProcessor(args));
      if (!fpB) {
          return nullptr;
      }
      // TODO: account for fLerpT when it is < 1
      return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
                                                                std::move(fpA), fMode);
  }
  #endif
  