blob: 5c77cc8744949713d590fb767b79a1b9b6cf9595 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkMallocPixelRef.h"
9#include "include/core/SkPaint.h"
10#include "include/core/SkPicture.h"
11#include "include/core/SkScalar.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040012#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/core/SkColorSpacePriv.h"
14#include "src/core/SkColorSpaceXformSteps.h"
15#include "src/core/SkRasterPipeline.h"
16#include "src/core/SkReadBuffer.h"
17#include "src/core/SkTLazy.h"
Mike Klein8e717442020-01-07 10:22:33 -060018#include "src/core/SkVM.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/core/SkWriteBuffer.h"
20#include "src/shaders/SkBitmapProcShader.h"
21#include "src/shaders/SkColorShader.h"
22#include "src/shaders/SkEmptyShader.h"
23#include "src/shaders/SkPictureShader.h"
24#include "src/shaders/SkShaderBase.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025
bungeman06ca8ec2016-06-09 08:01:03 -070026#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/gpu/GrFragmentProcessor.h"
bungeman06ca8ec2016-06-09 08:01:03 -070028#endif
29
Florin Malita4aed1382017-05-25 10:38:07 -040030SkShaderBase::SkShaderBase(const SkMatrix* localMatrix)
31 : fLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I()) {
mtklein435eba72014-12-01 12:06:24 -080032 // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe.
33 (void)fLocalMatrix.getType();
reed@android.com8a1c16f2008-12-17 15:59:43 +000034}
35
Mike Kleinde2244c2018-12-04 11:16:08 -050036SkShaderBase::~SkShaderBase() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000037
Florin Malita4aed1382017-05-25 10:38:07 -040038void SkShaderBase::flatten(SkWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 this->INHERITED::flatten(buffer);
commit-bot@chromium.org5970f622014-05-12 20:42:21 +000040 bool hasLocalM = !fLocalMatrix.isIdentity();
bsalomon@google.comf94b3a42012-10-31 18:09:01 +000041 buffer.writeBool(hasLocalM);
42 if (hasLocalM) {
43 buffer.writeMatrix(fLocalMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 }
45}
46
Florin Malitac6c5ead2018-04-11 15:33:40 -040047SkTCopyOnFirstWrite<SkMatrix>
48SkShaderBase::totalLocalMatrix(const SkMatrix* preLocalMatrix,
49 const SkMatrix* postLocalMatrix) const {
50 SkTCopyOnFirstWrite<SkMatrix> m(fLocalMatrix);
51
52 if (preLocalMatrix) {
53 m.writable()->preConcat(*preLocalMatrix);
54 }
55
56 if (postLocalMatrix) {
57 m.writable()->postConcat(*postLocalMatrix);
58 }
59
60 return m;
61}
62
Florin Malita4aed1382017-05-25 10:38:07 -040063bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm,
64 const SkMatrix* outerLocalMatrix,
65 SkMatrix* totalInverse) const {
Florin Malitac6c5ead2018-04-11 15:33:40 -040066 return SkMatrix::Concat(ctm, *this->totalLocalMatrix(outerLocalMatrix)).invert(totalInverse);
reed@android.com8a1c16f2008-12-17 15:59:43 +000067}
68
Florin Malita4aed1382017-05-25 10:38:07 -040069bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const {
reed8367b8c2014-08-22 08:30:20 -070070 SkColor storage;
halcanary96fcdcc2015-08-27 07:41:13 -070071 if (nullptr == colorPtr) {
reed8367b8c2014-08-22 08:30:20 -070072 colorPtr = &storage;
73 }
74 if (this->onAsLuminanceColor(colorPtr)) {
75 *colorPtr = SkColorSetA(*colorPtr, 0xFF); // we only return opaque
76 return true;
77 }
78 return false;
79}
80
Florin Malita4aed1382017-05-25 10:38:07 -040081SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
Mike Reede92aae62018-10-17 10:21:51 -040082#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Florin Malitaaf2769d2018-04-04 13:46:35 -040083 // We always fall back to raster pipeline when perspective is present.
84 if (rec.fMatrix->hasPerspective() ||
85 fLocalMatrix.hasPerspective() ||
86 (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective()) ||
87 !this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) {
88 return nullptr;
89 }
90
91 return this->onMakeContext(rec, alloc);
Mike Reede92aae62018-10-17 10:21:51 -040092#else
93 return nullptr;
94#endif
commit-bot@chromium.orgf3e50592014-04-30 23:29:02 +000095}
96
Florin Malita4aed1382017-05-25 10:38:07 -040097SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +000098 : fShader(shader), fCTM(*rec.fMatrix)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000099{
Florin Malitaaf2769d2018-04-04 13:46:35 -0400100 // We should never use a context with perspective.
Florin Malita5769dd22017-07-12 13:31:25 -0400101 SkASSERT(!rec.fMatrix->hasPerspective());
102 SkASSERT(!rec.fLocalMatrix || !rec.fLocalMatrix->hasPerspective());
Florin Malitaaf2769d2018-04-04 13:46:35 -0400103 SkASSERT(!shader.getLocalMatrix().hasPerspective());
Florin Malita7d022e02017-05-15 15:06:39 -0400104
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000105 // Because the context parameters must be valid at this point, we know that the matrix is
106 // invertible.
Florin Malita26368c32017-05-08 13:03:24 -0400107 SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000108
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000109 fPaintAlpha = rec.fPaint->getAlpha();
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000110}
111
Florin Malita4aed1382017-05-25 10:38:07 -0400112SkShaderBase::Context::~Context() {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000113
Mike Reed011d1662019-02-28 17:19:25 -0500114bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
Mike Kleina7bb87e2019-12-03 11:04:54 -0600115 // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
116 // always premul (or opaque). (And those "or opaque" caveats won't make any difference here.)
117 SkAlphaType shaderAT = kPremul_SkAlphaType,
118 dstAT = kPremul_SkAlphaType;
119 return 0 == SkColorSpaceXformSteps{shaderColorSpace, shaderAT,
120 fDstColorSpace, dstAT}.flags.mask();
Mike Reed011d1662019-02-28 17:19:25 -0500121}
122
Mike Reedfae8fce2019-04-03 10:27:45 -0400123SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const {
Florin Malita4aed1382017-05-25 10:38:07 -0400124 return as_SB(this)->onIsAImage(localMatrix, xy);
125}
126
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000127SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
128 return kNone_GradientType;
129}
130
bungeman06ca8ec2016-06-09 08:01:03 -0700131#if SK_SUPPORT_GPU
Mike Reede3429e62018-01-19 11:43:34 -0500132std::unique_ptr<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const GrFPArgs&) const {
bsalomonc21b09e2015-08-28 18:46:56 -0700133 return nullptr;
rileya@google.com03c1c352012-07-20 20:02:43 +0000134}
bungeman06ca8ec2016-06-09 08:01:03 -0700135#endif
rileya@google.com03c1c352012-07-20 20:02:43 +0000136
Mike Reed7656b2c2019-04-08 11:48:20 -0400137sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700138 return nullptr;
commit-bot@chromium.org8fae2132014-05-07 22:26:37 +0000139}
140
Mike Reedc8bea7d2019-04-09 13:55:36 -0400141sk_sp<SkShader> SkShaders::Empty() { return sk_make_sp<SkEmptyShader>(); }
142sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
reed8367b8c2014-08-22 08:30:20 -0700143
Mike Reed50acf8f2019-04-08 13:20:23 -0400144sk_sp<SkShader> SkBitmap::makeShader(SkTileMode tmx, SkTileMode tmy, const SkMatrix* lm) const {
145 if (lm && !lm->invert(nullptr)) {
146 return nullptr;
147 }
148 return SkMakeBitmapShader(*this, tmx, tmy, lm, kIfMutable_SkCopyPixelsMode);
149}
150
151sk_sp<SkShader> SkBitmap::makeShader(const SkMatrix* lm) const {
152 return this->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, lm);
153}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
Mike Reed1386b2d2019-03-13 21:15:05 -0400155bool SkShaderBase::appendStages(const SkStageRec& rec) const {
Mike Reed1d8c42e2017-08-29 14:58:19 -0400156 return this->onAppendStages(rec);
Florin Malita9206c762017-01-30 12:08:05 -0500157}
158
Mike Reed1386b2d2019-03-13 21:15:05 -0400159bool SkShaderBase::onAppendStages(const SkStageRec& rec) const {
Mike Klein541cbd42019-01-30 13:11:33 -0500160 // SkShader::Context::shadeSpan() handles the paint opacity internally,
Mike Reed6867eee2017-06-02 13:25:15 -0400161 // but SkRasterPipelineBlitter applies it as a separate stage.
Mike Klein541cbd42019-01-30 13:11:33 -0500162 // We skip the internal shadeSpan() step by forcing the paint opaque.
Mike Reed1d8c42e2017-08-29 14:58:19 -0400163 SkTCopyOnFirstWrite<SkPaint> opaquePaint(rec.fPaint);
164 if (rec.fPaint.getAlpha() != SK_AlphaOPAQUE) {
Mike Reed6867eee2017-06-02 13:25:15 -0400165 opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE);
166 }
167
Mike Klein894328d2019-03-21 10:06:38 -0500168 ContextRec cr(*opaquePaint, rec.fCTM, rec.fLocalM, rec.fDstColorType, sk_srgb_singleton());
Mike Reed6867eee2017-06-02 13:25:15 -0400169
Mike Kleinb11ab572018-10-24 06:42:14 -0400170 struct CallbackCtx : SkRasterPipeline_CallbackCtx {
Mike Kleinb0b2c962019-03-21 10:33:51 -0500171 sk_sp<const SkShader> shader;
172 Context* ctx;
Mike Reed6867eee2017-06-02 13:25:15 -0400173 };
Mike Reed1d8c42e2017-08-29 14:58:19 -0400174 auto cb = rec.fAlloc->make<CallbackCtx>();
Mike Kleinb0b2c962019-03-21 10:33:51 -0500175 cb->shader = sk_ref_sp(this);
176 cb->ctx = as_SB(this)->makeContext(cr, rec.fAlloc);
Mike Kleinb11ab572018-10-24 06:42:14 -0400177 cb->fn = [](SkRasterPipeline_CallbackCtx* self, int active_pixels) {
Mike Reed6867eee2017-06-02 13:25:15 -0400178 auto c = (CallbackCtx*)self;
179 int x = (int)c->rgba[0],
Mike Klein541cbd42019-01-30 13:11:33 -0500180 y = (int)c->rgba[1];
181 SkPMColor tmp[SkRasterPipeline_kMaxStride];
182 c->ctx->shadeSpan(x,y, tmp, active_pixels);
183
184 for (int i = 0; i < active_pixels; i++) {
185 auto rgba_4f = SkPMColor4f::FromPMColor(tmp[i]);
186 memcpy(c->rgba + 4*i, rgba_4f.vec(), 4*sizeof(float));
187 }
Mike Reed6867eee2017-06-02 13:25:15 -0400188 };
189
190 if (cb->ctx) {
Mike Kleine8de0242018-03-10 12:37:11 -0500191 rec.fPipeline->append(SkRasterPipeline::seed_shader);
Mike Reed1d8c42e2017-08-29 14:58:19 -0400192 rec.fPipeline->append(SkRasterPipeline::callback, cb);
Mike Klein894328d2019-03-21 10:06:38 -0500193 rec.fAlloc->make<SkColorSpaceXformSteps>(sk_srgb_singleton(), kPremul_SkAlphaType,
194 rec.fDstCS, kPremul_SkAlphaType)
195 ->apply(rec.fPipeline, true);
Mike Reed6867eee2017-06-02 13:25:15 -0400196 return true;
197 }
Mike Klein44d32792017-05-10 12:29:38 -0400198 return false;
199}
200
Mike Klein03935052019-11-01 11:36:55 -0500201bool SkShaderBase::program(skvm::Builder* p,
Mike Kleinf6a715b2019-12-30 15:24:18 -0600202 const SkMatrix& ctm, const SkMatrix* localM,
203 SkFilterQuality quality, SkColorSpace* dstCS,
Mike Klein3ab71282020-01-06 19:53:39 -0600204 skvm::Uniforms* uniforms, SkArenaAlloc* alloc,
Mike Kleinf3d41092019-11-04 13:22:02 -0600205 skvm::F32 x, skvm::F32 y,
Mike Kleinb9f20882019-11-08 12:14:15 -0600206 skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
Mike Klein03935052019-11-01 11:36:55 -0500207 // Force opaque alpha for all opaque shaders.
208 //
Mike Kleinb9f20882019-11-08 12:14:15 -0600209 // This is primarily nice in that we usually have a 1.0f constant splat
Mike Klein03935052019-11-01 11:36:55 -0500210 // somewhere in the program anyway, and this will let us drop the work the
211 // shader notionally does to produce alpha, p->extract(...), etc. in favor
212 // of that simple hoistable splat.
213 //
214 // More subtly, it makes isOpaque() a parameter to all shader program
215 // generation, guaranteeing that is-opaque bit is mixed into the overall
216 // shader program hash and blitter Key. This makes it safe for us to use
217 // that bit to make decisions when constructing an SkVMBlitter, like doing
218 // SrcOver -> Src strength reduction.
Mike Klein3ab71282020-01-06 19:53:39 -0600219 if (this->onProgram(p, ctm,localM, quality,dstCS, uniforms,alloc, x,y, r,g,b,a)) {
Mike Kleinfb9146f2019-11-05 05:49:34 -0600220 if (this->isOpaque()) {
Mike Kleinb9f20882019-11-08 12:14:15 -0600221 *a = p->splat(1.0f);
Mike Klein03935052019-11-01 11:36:55 -0500222 }
223 return true;
224 }
225 return false;
226}
227
Mike Klein8e717442020-01-07 10:22:33 -0600228bool SkShaderBase::onProgram(skvm::Builder*,
229 const SkMatrix& ctm, const SkMatrix* localM,
230 SkFilterQuality quality, SkColorSpace* dstCS,
231 skvm::Uniforms* uniforms, SkArenaAlloc* alloc,
232 skvm::F32 x, skvm::F32 y,
233 skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
234 return false;
235}
236
Mike Klein85754d52020-01-22 10:04:11 -0600237void SkShaderBase::ApplyMatrix(skvm::Builder* p, const SkMatrix& m,
238 skvm::F32* x, skvm::F32* y, skvm::Uniforms* uniforms) {
239 if (m.isIdentity()) {
240 // That was easy.
241 } else if (m.isTranslate()) {
242 *x = p->add(*x, p->uniformF(uniforms->pushF(m[2])));
243 *y = p->add(*y, p->uniformF(uniforms->pushF(m[5])));
244 } else if (m.isScaleTranslate()) {
245 *x = p->mad(*x, p->uniformF(uniforms->pushF(m[0])), p->uniformF(uniforms->pushF(m[2])));
246 *y = p->mad(*y, p->uniformF(uniforms->pushF(m[4])), p->uniformF(uniforms->pushF(m[5])));
247 } else { // Affine or perspective.
248 auto dot = [&,X=*x,Y=*y](int row) {
249 return p->mad(X, p->uniformF(uniforms->pushF(m[3*row+0])),
250 p->mad(Y, p->uniformF(uniforms->pushF(m[3*row+1])),
251 p->uniformF(uniforms->pushF(m[3*row+2]))));
252 };
253 *x = dot(0);
254 *y = dot(1);
255 if (m.hasPerspective()) {
256 *x = p->div(*x, dot(2));
257 *y = p->div(*y, dot(2));
258 }
259 }
260}
261
reed0ccc62d2016-05-04 13:09:39 -0700262///////////////////////////////////////////////////////////////////////////////////////////////////
reed830dfd82016-03-16 12:29:01 -0700263
reed60c9b582016-04-03 09:11:13 -0700264sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
Mike Reedc8bea7d2019-04-09 13:55:36 -0400265 return SkShaders::Empty();
reed9fa60da2014-08-21 07:59:51 -0700266}