1/* 2 * Copyright 2014 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 "src/core/SkMatrixProvider.h" 9#include "src/core/SkTLazy.h" 10#include "src/core/SkVM.h" 11#include "src/shaders/SkLocalMatrixShader.h" 12 13#if SK_SUPPORT_GPU 14#include "src/gpu/GrFragmentProcessor.h" 15#include "src/gpu/effects/GrMatrixEffect.h" 16#endif 17 18#if SK_SUPPORT_GPU 19std::unique_ptr<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor( 20 const GrFPArgs& args) const { 21 return as_SB(fProxyShader)->asFragmentProcessor( 22 GrFPArgs::WithPreLocalMatrix(args, this->getLocalMatrix())); 23} 24#endif 25 26sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) { 27 SkMatrix lm; 28 buffer.readMatrix(&lm); 29 auto baseShader(buffer.readShader()); 30 if (!baseShader) { 31 return nullptr; 32 } 33 return baseShader->makeWithLocalMatrix(lm); 34} 35 36void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const { 37 buffer.writeMatrix(this->getLocalMatrix()); 38 buffer.writeFlattenable(fProxyShader.get()); 39} 40 41#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 42SkShaderBase::Context* SkLocalMatrixShader::onMakeContext( 43 const ContextRec& rec, SkArenaAlloc* alloc) const 44{ 45 SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix()); 46 if (rec.fLocalMatrix) { 47 lm.writable()->preConcat(*rec.fLocalMatrix); 48 } 49 50 ContextRec newRec(rec); 51 newRec.fLocalMatrix = lm; 52 53 return as_SB(fProxyShader)->makeContext(newRec, alloc); 54} 55#endif 56 57SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, SkTileMode* mode) const { 58 SkMatrix imageMatrix; 59 SkImage* image = fProxyShader->isAImage(&imageMatrix, mode); 60 if (image && outMatrix) { 61 // Local matrix must be applied first so it is on the right side of the concat. 62 *outMatrix = SkMatrix::Concat(imageMatrix, this->getLocalMatrix()); 63 } 64 65 return image; 66} 67 68bool SkLocalMatrixShader::onAppendStages(const SkStageRec& rec) const { 69 SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix()); 70 if (rec.fLocalM) { 71 lm.writable()->preConcat(*rec.fLocalM); 72 } 73 74 SkStageRec newRec = rec; 75 newRec.fLocalM = lm; 76 return as_SB(fProxyShader)->appendStages(newRec); 77} 78 79 80skvm::Color SkLocalMatrixShader::onProgram(skvm::Builder* p, 81 skvm::Coord device, skvm::Coord local, skvm::Color paint, 82 const SkMatrixProvider& matrices, const SkMatrix* localM, 83 const SkColorInfo& dst, 84 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { 85 SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix()); 86 if (localM) { 87 lm.writable()->preConcat(*localM); 88 } 89 return as_SB(fProxyShader)->program(p, device,local, paint, 90 matrices,lm.get(), dst, 91 uniforms,alloc); 92} 93 94sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const { 95 if (localMatrix.isIdentity()) { 96 return sk_ref_sp(const_cast<SkShader*>(this)); 97 } 98 99 const SkMatrix* lm = &localMatrix; 100 101 sk_sp<SkShader> baseShader; 102 SkMatrix otherLocalMatrix; 103 sk_sp<SkShader> proxy(as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix)); 104 if (proxy) { 105 otherLocalMatrix.preConcat(localMatrix); 106 lm = &otherLocalMatrix; 107 baseShader = proxy; 108 } else { 109 baseShader = sk_ref_sp(const_cast<SkShader*>(this)); 110 } 111 112 return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm); 113} 114 115//////////////////////////////////////////////////////////////////// 116 117/** 118 * Replaces the CTM when used. Created to support clipShaders, which have to be evaluated 119 * using the CTM that was present at the time they were specified (which may be different 120 * from the CTM at the time something is drawn through the clip. 121 */ 122class SkCTMShader final : public SkShaderBase { 123public: 124 SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm) 125 : fProxyShader(std::move(proxy)) 126 , fCTM(ctm) 127 {} 128 129 GradientType asAGradient(GradientInfo* info) const override { 130 return fProxyShader->asAGradient(info); 131 } 132 133#if SK_SUPPORT_GPU 134 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override; 135#endif 136 137protected: 138 void flatten(SkWriteBuffer&) const override { SkASSERT(false); } 139 140#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 141 Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override { return nullptr; } 142#endif 143 144 bool onAppendStages(const SkStageRec& rec) const override { 145 SkOverrideDeviceMatrixProvider matrixProvider(rec.fMatrixProvider, fCTM); 146 SkStageRec newRec = { 147 rec.fPipeline, 148 rec.fAlloc, 149 rec.fDstColorType, 150 rec.fDstCS, 151 rec.fPaint, 152 rec.fLocalM, 153 matrixProvider, 154 }; 155 return as_SB(fProxyShader)->appendStages(newRec); 156 } 157 158 skvm::Color onProgram(skvm::Builder* p, 159 skvm::Coord device, skvm::Coord local, skvm::Color paint, 160 const SkMatrixProvider& matrices, const SkMatrix* localM, 161 const SkColorInfo& dst, 162 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override { 163 SkOverrideDeviceMatrixProvider matrixProvider(matrices, fCTM); 164 return as_SB(fProxyShader)->program(p, device,local, paint, 165 matrixProvider,localM, dst, 166 uniforms,alloc); 167 } 168 169private: 170 SK_FLATTENABLE_HOOKS(SkCTMShader) 171 172 sk_sp<SkShader> fProxyShader; 173 SkMatrix fCTM; 174 175 using INHERITED = SkShaderBase; 176}; 177 178 179#if SK_SUPPORT_GPU 180std::unique_ptr<GrFragmentProcessor> SkCTMShader::asFragmentProcessor( 181 const GrFPArgs& args) const { 182 SkMatrix ctmInv; 183 if (!fCTM.invert(&ctmInv)) { 184 return nullptr; 185 } 186 187 auto ctmProvider = SkOverrideDeviceMatrixProvider(args.fMatrixProvider, fCTM); 188 auto base = as_SB(fProxyShader)->asFragmentProcessor( 189 GrFPArgs::WithPreLocalMatrix(args.withNewMatrixProvider(ctmProvider), 190 this->getLocalMatrix())); 191 if (!base) { 192 return nullptr; 193 } 194 195 // In order for the shader to be evaluated with the original CTM, we explicitly evaluate it 196 // at sk_FragCoord, and pass that through the inverse of the original CTM. This avoids requiring 197 // local coords for the shader and mapping from the draw's local to device and then back. 198 return GrFragmentProcessor::DeviceSpace(GrMatrixEffect::Make(ctmInv, std::move(base))); 199} 200#endif 201 202sk_sp<SkFlattenable> SkCTMShader::CreateProc(SkReadBuffer& buffer) { 203 SkASSERT(false); 204 return nullptr; 205} 206 207sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const { 208 return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM)); 209} 210