xref: /third_party/skia/src/shaders/SkShader.cpp (revision cb93a386)
1/*
2 * Copyright 2006 The Android Open Source Project
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 "include/core/SkMallocPixelRef.h"
9#include "include/core/SkPaint.h"
10#include "include/core/SkPicture.h"
11#include "include/core/SkScalar.h"
12#include "src/core/SkArenaAlloc.h"
13#include "src/core/SkColorSpacePriv.h"
14#include "src/core/SkColorSpaceXformSteps.h"
15#include "src/core/SkMatrixProvider.h"
16#include "src/core/SkRasterPipeline.h"
17#include "src/core/SkReadBuffer.h"
18#include "src/core/SkTLazy.h"
19#include "src/core/SkVM.h"
20#include "src/core/SkWriteBuffer.h"
21#include "src/shaders/SkBitmapProcShader.h"
22#include "src/shaders/SkColorShader.h"
23#include "src/shaders/SkEmptyShader.h"
24#include "src/shaders/SkImageShader.h"
25#include "src/shaders/SkPictureShader.h"
26#include "src/shaders/SkShaderBase.h"
27#include "src/shaders/SkTransformShader.h"
28
29#if SK_SUPPORT_GPU
30#include "src/gpu/GrFragmentProcessor.h"
31#endif
32
33SkShaderBase::SkShaderBase(const SkMatrix* localMatrix)
34    : fLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I()) {
35    // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe.
36    (void)fLocalMatrix.getType();
37}
38
39SkShaderBase::~SkShaderBase() {}
40
41void SkShaderBase::flatten(SkWriteBuffer& buffer) const {
42    this->INHERITED::flatten(buffer);
43    bool hasLocalM = !fLocalMatrix.isIdentity();
44    buffer.writeBool(hasLocalM);
45    if (hasLocalM) {
46        buffer.writeMatrix(fLocalMatrix);
47    }
48}
49
50SkTCopyOnFirstWrite<SkMatrix>
51SkShaderBase::totalLocalMatrix(const SkMatrix* preLocalMatrix) const {
52    SkTCopyOnFirstWrite<SkMatrix> m(fLocalMatrix);
53
54    if (preLocalMatrix) {
55        m.writable()->preConcat(*preLocalMatrix);
56    }
57
58    return m;
59}
60
61bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm,
62                                       const SkMatrix* outerLocalMatrix,
63                                       SkMatrix* totalInverse) const {
64    return SkMatrix::Concat(ctm, *this->totalLocalMatrix(outerLocalMatrix)).invert(totalInverse);
65}
66
67bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const {
68    SkColor storage;
69    if (nullptr == colorPtr) {
70        colorPtr = &storage;
71    }
72    if (this->onAsLuminanceColor(colorPtr)) {
73        *colorPtr = SkColorSetA(*colorPtr, 0xFF);   // we only return opaque
74        return true;
75    }
76    return false;
77}
78
79SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
80#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
81    // We always fall back to raster pipeline when perspective is present.
82    if (rec.fMatrix->hasPerspective() ||
83        fLocalMatrix.hasPerspective() ||
84        (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective()) ||
85        !this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) {
86        return nullptr;
87    }
88
89    return this->onMakeContext(rec, alloc);
90#else
91    return nullptr;
92#endif
93}
94
95SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
96    : fShader(shader), fCTM(*rec.fMatrix)
97{
98    // We should never use a context with perspective.
99    SkASSERT(!rec.fMatrix->hasPerspective());
100    SkASSERT(!rec.fLocalMatrix || !rec.fLocalMatrix->hasPerspective());
101    SkASSERT(!shader.getLocalMatrix().hasPerspective());
102
103    // Because the context parameters must be valid at this point, we know that the matrix is
104    // invertible.
105    SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse));
106
107    fPaintAlpha = rec.fPaintAlpha;
108}
109
110SkShaderBase::Context::~Context() {}
111
112bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
113    // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
114    // always premul (or opaque).  (And those "or opaque" caveats won't make any difference here.)
115    SkAlphaType shaderAT = kPremul_SkAlphaType,
116                   dstAT = kPremul_SkAlphaType;
117    return 0 == SkColorSpaceXformSteps{shaderColorSpace, shaderAT,
118                                         fDstColorSpace,    dstAT}.flags.mask();
119}
120
121SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const {
122    return as_SB(this)->onIsAImage(localMatrix, xy);
123}
124
125SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
126    return kNone_GradientType;
127}
128
129#if SK_SUPPORT_GPU
130std::unique_ptr<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const GrFPArgs&) const {
131    return nullptr;
132}
133#endif
134
135sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const {
136    return nullptr;
137}
138
139SkUpdatableShader* SkShaderBase::updatableShader(SkArenaAlloc* alloc) const {
140    if (auto updatable = this->onUpdatableShader(alloc)) {
141        return updatable;
142    }
143
144    return alloc->make<SkTransformShader>(*as_SB(this));
145}
146
147SkUpdatableShader* SkShaderBase::onUpdatableShader(SkArenaAlloc* alloc) const {
148    return nullptr;
149}
150
151sk_sp<SkShader> SkShaders::Empty() { return sk_make_sp<SkEmptyShader>(); }
152sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
153
154sk_sp<SkShader> SkBitmap::makeShader(SkTileMode tmx, SkTileMode tmy,
155                                     const SkSamplingOptions& sampling,
156                                     const SkMatrix* lm) const {
157    if (lm && !lm->invert(nullptr)) {
158        return nullptr;
159    }
160    return SkImageShader::Make(SkMakeImageFromRasterBitmap(*this, kIfMutable_SkCopyPixelsMode),
161                               tmx, tmy, sampling, lm);
162}
163
164bool SkShaderBase::appendStages(const SkStageRec& rec) const {
165    return this->onAppendStages(rec);
166}
167
168bool SkShaderBase::onAppendStages(const SkStageRec& rec) const {
169    // SkShader::Context::shadeSpan() handles the paint opacity internally,
170    // but SkRasterPipelineBlitter applies it as a separate stage.
171    // We skip the internal shadeSpan() step by forcing the paint opaque.
172    SkTCopyOnFirstWrite<SkPaint> opaquePaint(rec.fPaint);
173    if (rec.fPaint.getAlpha() != SK_AlphaOPAQUE) {
174        opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE);
175    }
176
177    ContextRec cr(*opaquePaint, rec.fMatrixProvider.localToDevice(), rec.fLocalM, rec.fDstColorType,
178                  sk_srgb_singleton());
179
180    struct CallbackCtx : SkRasterPipeline_CallbackCtx {
181        sk_sp<const SkShader> shader;
182        Context*              ctx;
183    };
184    auto cb = rec.fAlloc->make<CallbackCtx>();
185    cb->shader = sk_ref_sp(this);
186    cb->ctx = as_SB(this)->makeContext(cr, rec.fAlloc);
187    cb->fn  = [](SkRasterPipeline_CallbackCtx* self, int active_pixels) {
188        auto c = (CallbackCtx*)self;
189        int x = (int)c->rgba[0],
190            y = (int)c->rgba[1];
191        SkPMColor tmp[SkRasterPipeline_kMaxStride];
192        c->ctx->shadeSpan(x,y, tmp, active_pixels);
193
194        for (int i = 0; i < active_pixels; i++) {
195            auto rgba_4f = SkPMColor4f::FromPMColor(tmp[i]);
196            memcpy(c->rgba + 4*i, rgba_4f.vec(), 4*sizeof(float));
197        }
198    };
199
200    if (cb->ctx) {
201        rec.fPipeline->append(SkRasterPipeline::seed_shader);
202        rec.fPipeline->append(SkRasterPipeline::callback, cb);
203        rec.fAlloc->make<SkColorSpaceXformSteps>(sk_srgb_singleton(), kPremul_SkAlphaType,
204                                                 rec.fDstCS,          kPremul_SkAlphaType)
205            ->apply(rec.fPipeline);
206        return true;
207    }
208    return false;
209}
210
211skvm::Color SkShaderBase::program(skvm::Builder* p,
212                                  skvm::Coord device, skvm::Coord local, skvm::Color paint,
213                                  const SkMatrixProvider& matrices, const SkMatrix* localM,
214                                  const SkColorInfo& dst,
215                                  skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
216    // Shader subclasses should always act as if the destination were premul or opaque.
217    // SkVMBlitter handles all the coordination of unpremul itself, via premul.
218    SkColorInfo tweaked = dst.alphaType() == kUnpremul_SkAlphaType
219                           ? dst.makeAlphaType(kPremul_SkAlphaType)
220                           : dst;
221
222    // Force opaque alpha for all opaque shaders.
223    //
224    // This is primarily nice in that we usually have a 1.0f constant splat
225    // somewhere in the program anyway, and this will let us drop the work the
226    // shader notionally does to produce alpha, p->extract(...), etc. in favor
227    // of that simple hoistable splat.
228    //
229    // More subtly, it makes isOpaque() a parameter to all shader program
230    // generation, guaranteeing that is-opaque bit is mixed into the overall
231    // shader program hash and blitter Key.  This makes it safe for us to use
232    // that bit to make decisions when constructing an SkVMBlitter, like doing
233    // SrcOver -> Src strength reduction.
234    if (auto color = this->onProgram(p, device,local, paint, matrices,localM, tweaked,
235                                     uniforms,alloc)) {
236        if (this->isOpaque()) {
237            color.a = p->splat(1.0f);
238        }
239        return color;
240    }
241    return {};
242}
243
244// need a cheap way to invert the alpha channel of a shader (i.e. 1 - a)
245sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
246    return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
247}
248
249
250skvm::Coord SkShaderBase::ApplyMatrix(skvm::Builder* p, const SkMatrix& m,
251                                      skvm::Coord coord, skvm::Uniforms* uniforms) {
252    skvm::F32 x = coord.x,
253              y = coord.y;
254    if (m.isIdentity()) {
255        // That was easy.
256    } else if (m.isTranslate()) {
257        x = p->add(x, p->uniformF(uniforms->pushF(m[2])));
258        y = p->add(y, p->uniformF(uniforms->pushF(m[5])));
259    } else if (m.isScaleTranslate()) {
260        x = p->mad(x, p->uniformF(uniforms->pushF(m[0])), p->uniformF(uniforms->pushF(m[2])));
261        y = p->mad(y, p->uniformF(uniforms->pushF(m[4])), p->uniformF(uniforms->pushF(m[5])));
262    } else {  // Affine or perspective.
263        auto dot = [&,x,y](int row) {
264            return p->mad(x, p->uniformF(uniforms->pushF(m[3*row+0])),
265                   p->mad(y, p->uniformF(uniforms->pushF(m[3*row+1])),
266                             p->uniformF(uniforms->pushF(m[3*row+2]))));
267        };
268        x = dot(0);
269        y = dot(1);
270        if (m.hasPerspective()) {
271            x = x * (1.0f / dot(2));
272            y = y * (1.0f / dot(2));
273        }
274    }
275    return {x,y};
276}
277
278///////////////////////////////////////////////////////////////////////////////////////////////////
279
280skvm::Color SkEmptyShader::onProgram(skvm::Builder*, skvm::Coord, skvm::Coord, skvm::Color,
281                                     const SkMatrixProvider&, const SkMatrix*, const SkColorInfo&,
282                                     skvm::Uniforms*, SkArenaAlloc*) const {
283    return {};  // signal failure
284}
285
286sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
287    return SkShaders::Empty();
288}
289