blob: d66bdf4632b2b833f1f35a4b59704a07a354765e [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
Herb Derby83e939b2017-02-07 14:25:11 -05008#include "SkArenaAlloc.h"
Mike Reed9959f722017-05-15 09:34:22 -04009#include "SkBlendModePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkComposeShader.h"
11#include "SkColorFilter.h"
12#include "SkColorPriv.h"
reed@google.com573f22b2011-11-30 19:17:15 +000013#include "SkColorShader.h"
Mike Reed9959f722017-05-15 09:34:22 -040014#include "SkRasterPipeline.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000015#include "SkReadBuffer.h"
16#include "SkWriteBuffer.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +000017#include "SkString.h"
Mike Reed9959f722017-05-15 09:34:22 -040018#include "../jumper/SkJumper.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019
Mike Reed7d954ad2016-10-28 15:42:34 -040020sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
21 SkBlendMode mode) {
22 if (!src || !dst) {
23 return nullptr;
24 }
25 if (SkBlendMode::kSrc == mode) {
26 return src;
27 }
28 if (SkBlendMode::kDst == mode) {
29 return dst;
30 }
31 return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
32}
33
reed@google.com82065d62011-02-07 15:30:46 +000034///////////////////////////////////////////////////////////////////////////////
35
reed60c9b582016-04-03 09:11:13 -070036sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
reed8a21c9f2016-03-08 18:50:00 -080037 sk_sp<SkShader> shaderA(buffer.readShader());
38 sk_sp<SkShader> shaderB(buffer.readShader());
Mike Reed7d954ad2016-10-28 15:42:34 -040039 SkBlendMode mode;
40 if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) {
41 sk_sp<SkXfermode> xfer = buffer.readXfermode();
42 mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver;
43 } else {
44 mode = (SkBlendMode)buffer.read32();
45 }
reed8a21c9f2016-03-08 18:50:00 -080046 if (!shaderA || !shaderB) {
halcanary96fcdcc2015-08-27 07:41:13 -070047 return nullptr;
reed9fa60da2014-08-21 07:59:51 -070048 }
Mike Reed7d954ad2016-10-28 15:42:34 -040049 return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
reed9fa60da2014-08-21 07:59:51 -070050}
51
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000052void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
reed8a21c9f2016-03-08 18:50:00 -080053 buffer.writeFlattenable(fShaderA.get());
54 buffer.writeFlattenable(fShaderB.get());
Mike Reed7d954ad2016-10-28 15:42:34 -040055 buffer.write32((int)fMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000056}
57
Florin Malita4aed1382017-05-25 10:38:07 -040058SkShaderBase::Context* SkComposeShader::onMakeContext(
Herb Derby83e939b2017-02-07 14:25:11 -050059 const ContextRec& rec, SkArenaAlloc* alloc) const
60{
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +000061 // we preconcat our localMatrix (if any) with the device matrix
62 // before calling our sub-shaders
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +000063 SkMatrix tmpM;
64 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
skia.committer@gmail.com9a408032014-05-02 03:03:47 +000065
commit-bot@chromium.org1ab536f2014-05-05 16:45:01 +000066 // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
67 // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
68 // sub-shaders.
69 SkPaint opaquePaint(*rec.fPaint);
70 opaquePaint.setAlpha(0xFF);
skia.committer@gmail.comedda70e2014-05-06 03:06:53 +000071
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +000072 ContextRec newRec(rec);
73 newRec.fMatrix = &tmpM;
commit-bot@chromium.org1ab536f2014-05-05 16:45:01 +000074 newRec.fPaint = &opaquePaint;
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +000075
Florin Malita4aed1382017-05-25 10:38:07 -040076 SkShaderBase::Context* contextA = as_SB(fShaderA)->makeContext(newRec, alloc);
77 SkShaderBase::Context* contextB = as_SB(fShaderB)->makeContext(newRec, alloc);
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +000078 if (!contextA || !contextB) {
halcanary96fcdcc2015-08-27 07:41:13 -070079 return nullptr;
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +000080 }
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000081
Herb Derby83e939b2017-02-07 14:25:11 -050082 return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB);
reed@google.coma641f3f2012-12-13 22:16:30 +000083}
84
Mike Klein2814d912017-05-10 12:35:51 -040085sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
86 return SkShader::MakeComposeShader(xformer->apply(fShaderA.get()),
87 xformer->apply(fShaderB.get()), fMode);
88}
89
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000090SkComposeShader::ComposeShaderContext::ComposeShaderContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +000091 const SkComposeShader& shader, const ContextRec& rec,
Florin Malita4aed1382017-05-25 10:38:07 -040092 SkShaderBase::Context* contextA, SkShaderBase::Context* contextB)
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +000093 : INHERITED(shader, rec)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +000094 , fShaderContextA(contextA)
95 , fShaderContextB(contextB) {}
96
commit-bot@chromium.org79590552014-05-13 18:14:45 +000097bool SkComposeShader::asACompose(ComposeRec* rec) const {
98 if (rec) {
Mike Reedfaba3712016-11-03 14:45:31 -040099 rec->fShaderA = fShaderA.get();
100 rec->fShaderB = fShaderB.get();
Mike Reed7d954ad2016-10-28 15:42:34 -0400101 rec->fBlendMode = fMode;
commit-bot@chromium.org79590552014-05-13 18:14:45 +0000102 }
103 return true;
104}
105
Florin Malita7d022e02017-05-15 15:06:39 -0400106bool SkComposeShader::isRasterPipelineOnly() const {
Florin Malita4aed1382017-05-25 10:38:07 -0400107 return as_SB(fShaderA)->isRasterPipelineOnly() || as_SB(fShaderB)->isRasterPipelineOnly();
Florin Malita7d022e02017-05-15 15:06:39 -0400108}
109
Mike Reed9959f722017-05-15 09:34:22 -0400110bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS,
111 SkArenaAlloc* alloc, const SkMatrix& ctm,
112 const SkPaint& paint, const SkMatrix* localM) const {
113 struct Storage {
Mike Reed9959f722017-05-15 09:34:22 -0400114 float fRGBA[4 * SkJumper_kMaxStride];
115 float fAlpha;
116 };
117 auto storage = alloc->make<Storage>();
118
Florin Malita4aed1382017-05-25 10:38:07 -0400119 if (!as_SB(fShaderB)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // SRC
Mike Reed9959f722017-05-15 09:34:22 -0400120 return false;
121 }
122 // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now
123 // since fShaderB will overwrite them.
124 pipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
Mike Klein5df94d52017-06-01 14:43:51 -0400125
Florin Malita4aed1382017-05-25 10:38:07 -0400126 if (!as_SB(fShaderA)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // DST
Mike Reed9959f722017-05-15 09:34:22 -0400127 return false;
128 }
129 // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode
130 // so we have to shuttle them. If we had a stage the would load_into_dst, then we could
131 // reverse the two shader invocations, and avoid this move...
132 pipeline->append(SkRasterPipeline::move_src_dst);
133 pipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
134
135 // Idea: should time this, and see if it helps to have custom versions of the overflow modes
136 // that do their own clamping, avoiding the overhead of an extra stage.
137 SkBlendMode_AppendStages(fMode, pipeline);
138 if (SkBlendMode_CanOverflow(fMode)) {
139 pipeline->append(SkRasterPipeline::clamp_a);
140 }
141 return true;
142}
commit-bot@chromium.org79590552014-05-13 18:14:45 +0000143
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144// larger is better (fewer times we have to loop), but we shouldn't
145// take up too much stack-space (each element is 4 bytes)
146#define TMP_COLOR_COUNT 64
147
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000148void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
Florin Malita4aed1382017-05-25 10:38:07 -0400149 auto* shaderContextA = fShaderContextA;
150 auto* shaderContextB = fShaderContextB;
Mike Reed7d954ad2016-10-28 15:42:34 -0400151 SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000152 unsigned scale = SkAlpha255To256(this->getPaintAlpha());
reed@google.com82065d62011-02-07 15:30:46 +0000153
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 SkPMColor tmp[TMP_COLOR_COUNT];
155
Mike Reed7d954ad2016-10-28 15:42:34 -0400156 SkXfermode* xfer = SkXfermode::Peek(mode);
157 if (nullptr == xfer) { // implied SRC_OVER
reed@android.comc4cae852009-09-23 15:06:10 +0000158 // TODO: when we have a good test-case, should use SkBlitRow::Proc32
159 // for these loops
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 do {
161 int n = count;
reed@google.com82065d62011-02-07 15:30:46 +0000162 if (n > TMP_COLOR_COUNT) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 n = TMP_COLOR_COUNT;
reed@google.com82065d62011-02-07 15:30:46 +0000164 }
165
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000166 shaderContextA->shadeSpan(x, y, result, n);
167 shaderContextB->shadeSpan(x, y, tmp, n);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reed@google.com82065d62011-02-07 15:30:46 +0000169 if (256 == scale) {
170 for (int i = 0; i < n; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 result[i] = SkPMSrcOver(tmp[i], result[i]);
reed@google.com82065d62011-02-07 15:30:46 +0000172 }
173 } else {
174 for (int i = 0; i < n; i++) {
175 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
176 scale);
177 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 }
reed@google.com82065d62011-02-07 15:30:46 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 result += n;
181 x += n;
182 count -= n;
183 } while (count > 0);
reed@google.com82065d62011-02-07 15:30:46 +0000184 } else { // use mode for the composition
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 do {
186 int n = count;
reed@google.com82065d62011-02-07 15:30:46 +0000187 if (n > TMP_COLOR_COUNT) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 n = TMP_COLOR_COUNT;
reed@google.com82065d62011-02-07 15:30:46 +0000189 }
190
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000191 shaderContextA->shadeSpan(x, y, result, n);
192 shaderContextB->shadeSpan(x, y, tmp, n);
Mike Reed7d954ad2016-10-28 15:42:34 -0400193 xfer->xfer32(result, tmp, n, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
commit-bot@chromium.org1ab536f2014-05-05 16:45:01 +0000195 if (256 != scale) {
reed@google.com82065d62011-02-07 15:30:46 +0000196 for (int i = 0; i < n; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 result[i] = SkAlphaMulQ(result[i], scale);
reed@google.com82065d62011-02-07 15:30:46 +0000198 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 }
200
201 result += n;
202 x += n;
203 count -= n;
204 } while (count > 0);
205 }
206}
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000207
Mike Reed273eead2017-05-12 13:07:46 -0400208void SkComposeShader::ComposeShaderContext::shadeSpan4f(int x, int y, SkPM4f result[], int count) {
Florin Malita4aed1382017-05-25 10:38:07 -0400209 auto* shaderContextA = fShaderContextA;
210 auto* shaderContextB = fShaderContextB;
Mike Reed273eead2017-05-12 13:07:46 -0400211 SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode;
212 unsigned alpha = this->getPaintAlpha();
213 Sk4f scale(alpha * (1.0f / 255));
214
215 SkPM4f tmp[TMP_COLOR_COUNT];
216
217 SkXfermodeProc4f xfer = SkXfermode::GetProc4f(mode);
218 do {
219 int n = SkTMin(count, TMP_COLOR_COUNT);
220
221 shaderContextA->shadeSpan4f(x, y, result, n);
222 shaderContextB->shadeSpan4f(x, y, tmp, n);
223 if (255 == alpha) {
224 for (int i = 0; i < n; ++i) {
225 result[i] = xfer(tmp[i], result[i]);
226 }
227 } else {
228 for (int i = 0; i < n; ++i) {
229 (xfer(tmp[i], result[i]).to4f() * scale).store(result + i);
230 }
231 }
232 result += n;
233 x += n;
234 count -= n;
235 } while (count > 0);
236}
237
wangyix73fa6162015-09-01 09:45:08 -0700238#if SK_SUPPORT_GPU
239
wangyix036fd8e2015-09-08 15:23:34 -0700240#include "effects/GrConstColorProcessor.h"
wangyix809e5af2015-09-09 12:58:32 -0700241#include "effects/GrXfermodeFragmentProcessor.h"
wangyix73fa6162015-09-01 09:45:08 -0700242
243/////////////////////////////////////////////////////////////////////
244
brianosman839345d2016-07-22 11:04:53 -0700245sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
Mike Reed7d954ad2016-10-28 15:42:34 -0400246 switch (fMode) {
247 case SkBlendMode::kClear:
Brian Osman618d3042016-10-25 10:51:28 -0400248 return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
bungeman06ca8ec2016-06-09 08:01:03 -0700249 GrConstColorProcessor::kIgnore_InputMode);
wangyix73fa6162015-09-01 09:45:08 -0700250 break;
Mike Reed7d954ad2016-10-28 15:42:34 -0400251 case SkBlendMode::kSrc:
Florin Malita4aed1382017-05-25 10:38:07 -0400252 return as_SB(fShaderB)->asFragmentProcessor(args);
wangyix73fa6162015-09-01 09:45:08 -0700253 break;
Mike Reed7d954ad2016-10-28 15:42:34 -0400254 case SkBlendMode::kDst:
Florin Malita4aed1382017-05-25 10:38:07 -0400255 return as_SB(fShaderA)->asFragmentProcessor(args);
wangyix73fa6162015-09-01 09:45:08 -0700256 break;
257 default:
Florin Malita4aed1382017-05-25 10:38:07 -0400258 sk_sp<GrFragmentProcessor> fpA(as_SB(fShaderA)->asFragmentProcessor(args));
bungeman06ca8ec2016-06-09 08:01:03 -0700259 if (!fpA) {
wangyix73fa6162015-09-01 09:45:08 -0700260 return nullptr;
261 }
Florin Malita4aed1382017-05-25 10:38:07 -0400262 sk_sp<GrFragmentProcessor> fpB(as_SB(fShaderB)->asFragmentProcessor(args));
bungeman06ca8ec2016-06-09 08:01:03 -0700263 if (!fpB) {
wangyix73fa6162015-09-01 09:45:08 -0700264 return nullptr;
265 }
bungeman06ca8ec2016-06-09 08:01:03 -0700266 return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
Mike Reed7d954ad2016-10-28 15:42:34 -0400267 std::move(fpA), fMode);
wangyix73fa6162015-09-01 09:45:08 -0700268 }
269}
270#endif
271
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000272#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000273void SkComposeShader::toString(SkString* str) const {
274 str->append("SkComposeShader: (");
275
276 str->append("ShaderA: ");
Florin Malita4aed1382017-05-25 10:38:07 -0400277 as_SB(fShaderA)->toString(str);
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000278 str->append(" ShaderB: ");
Florin Malita4aed1382017-05-25 10:38:07 -0400279 as_SB(fShaderB)->toString(str);
Mike Reed7d954ad2016-10-28 15:42:34 -0400280 if (SkBlendMode::kSrcOver != fMode) {
281 str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode));
halcanary8464a962015-06-16 11:53:26 -0700282 }
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000283
284 this->INHERITED::toString(str);
285
286 str->append(")");
287}
288#endif