aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/shaders/SkComposeShader.cpp
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2017-05-25 15:29:13 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-05-25 20:05:08 +0000
commitfabe0b26d05624ce7374f6ca89bd66df6142534e (patch)
treef32b5873b31185d7e86e6c48fbdbd654efb8af7a /src/shaders/SkComposeShader.cpp
parent1c214313248a4b5a69af14539608c54fb67c2bf8 (diff)
Relocate shaders to own dir
Consolidate all shader impls under src/shaders/. Change-Id: I450e37541214704c1ad9e379d9d753b7cc62fac3 Reviewed-on: https://skia-review.googlesource.com/17927 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Herb Derby <herb@google.com>
Diffstat (limited to 'src/shaders/SkComposeShader.cpp')
-rw-r--r--src/shaders/SkComposeShader.cpp311
1 files changed, 311 insertions, 0 deletions
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp
new file mode 100644
index 0000000000..7735494291
--- /dev/null
+++ b/src/shaders/SkComposeShader.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "SkColorPriv.h"
+#include "SkColorShader.h"
+#include "SkRasterPipeline.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkString.h"
+#include "../jumper/SkJumper.h"
+
+sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
+ SkBlendMode mode) {
+ if (!src || !dst) {
+ return nullptr;
+ }
+ if (SkBlendMode::kSrc == mode) {
+ return src;
+ }
+ if (SkBlendMode::kDst == mode) {
+ return dst;
+ }
+ return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoAlphaRestore {
+public:
+ SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
+ fAlpha = paint->getAlpha();
+ fPaint = paint;
+ paint->setAlpha(newAlpha);
+ }
+
+ ~SkAutoAlphaRestore() {
+ fPaint->setAlpha(fAlpha);
+ }
+private:
+ SkPaint* fPaint;
+ uint8_t fAlpha;
+};
+#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
+
+sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkShader> shaderA(buffer.readShader());
+ sk_sp<SkShader> shaderB(buffer.readShader());
+ SkBlendMode mode;
+ if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) {
+ sk_sp<SkXfermode> xfer = buffer.readXfermode();
+ mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver;
+ } else {
+ mode = (SkBlendMode)buffer.read32();
+ }
+ if (!shaderA || !shaderB) {
+ return nullptr;
+ }
+ return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
+}
+
+void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeFlattenable(fShaderA.get());
+ buffer.writeFlattenable(fShaderB.get());
+ buffer.write32((int)fMode);
+}
+
+SkShaderBase::Context* SkComposeShader::onMakeContext(
+ const ContextRec& rec, SkArenaAlloc* alloc) const
+{
+ // we preconcat our localMatrix (if any) with the device matrix
+ // before calling our sub-shaders
+ SkMatrix tmpM;
+ tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
+
+ // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
+ // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
+ // sub-shaders.
+ SkPaint opaquePaint(*rec.fPaint);
+ opaquePaint.setAlpha(0xFF);
+
+ ContextRec newRec(rec);
+ newRec.fMatrix = &tmpM;
+ newRec.fPaint = &opaquePaint;
+
+ SkShaderBase::Context* contextA = as_SB(fShaderA)->makeContext(newRec, alloc);
+ SkShaderBase::Context* contextB = as_SB(fShaderB)->makeContext(newRec, alloc);
+ if (!contextA || !contextB) {
+ return nullptr;
+ }
+
+ return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB);
+}
+
+sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+ return SkShader::MakeComposeShader(xformer->apply(fShaderA.get()),
+ xformer->apply(fShaderB.get()), fMode);
+}
+
+SkComposeShader::ComposeShaderContext::ComposeShaderContext(
+ const SkComposeShader& shader, const ContextRec& rec,
+ SkShaderBase::Context* contextA, SkShaderBase::Context* contextB)
+ : INHERITED(shader, rec)
+ , fShaderContextA(contextA)
+ , fShaderContextB(contextB) {}
+
+bool SkComposeShader::asACompose(ComposeRec* rec) const {
+ if (rec) {
+ rec->fShaderA = fShaderA.get();
+ rec->fShaderB = fShaderB.get();
+ rec->fBlendMode = fMode;
+ }
+ return true;
+}
+
+bool SkComposeShader::isRasterPipelineOnly() const {
+ return as_SB(fShaderA)->isRasterPipelineOnly() || as_SB(fShaderB)->isRasterPipelineOnly();
+}
+
+bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS,
+ SkArenaAlloc* alloc, const SkMatrix& ctm,
+ const SkPaint& paint, const SkMatrix* localM) const {
+ struct Storage {
+ float fXY[4 * SkJumper_kMaxStride];
+ float fRGBA[4 * SkJumper_kMaxStride];
+ float fAlpha;
+ };
+ auto storage = alloc->make<Storage>();
+
+ // We need to save off device x,y (inputs to shader), since after calling fShaderA they
+ // will be smashed, and I'll need them again for fShaderB. store_rgba saves off 4 registers
+ // even though we only need to save r,g.
+ pipeline->append(SkRasterPipeline::store_rgba, storage->fXY);
+ if (!as_SB(fShaderB)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // SRC
+ 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.
+ pipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
+ // Now we restore the device x,y for the next shader
+ pipeline->append(SkRasterPipeline::load_rgba, storage->fXY);
+ if (!as_SB(fShaderA)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // DST
+ 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
+ // 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...
+ pipeline->append(SkRasterPipeline::move_src_dst);
+ pipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
+
+ // Idea: should time this, and see if it helps to have custom versions of the overflow modes
+ // that do their own clamping, avoiding the overhead of an extra stage.
+ SkBlendMode_AppendStages(fMode, pipeline);
+ if (SkBlendMode_CanOverflow(fMode)) {
+ pipeline->append(SkRasterPipeline::clamp_a);
+ }
+ return true;
+}
+
+// larger is better (fewer times we have to loop), but we shouldn't
+// take up too much stack-space (each element is 4 bytes)
+#define TMP_COLOR_COUNT 64
+
+void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
+ auto* shaderContextA = fShaderContextA;
+ auto* shaderContextB = fShaderContextB;
+ SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode;
+ unsigned scale = SkAlpha255To256(this->getPaintAlpha());
+
+ SkPMColor tmp[TMP_COLOR_COUNT];
+
+ SkXfermode* xfer = SkXfermode::Peek(mode);
+ if (nullptr == xfer) { // implied SRC_OVER
+ // TODO: when we have a good test-case, should use SkBlitRow::Proc32
+ // for these loops
+ do {
+ int n = count;
+ if (n > TMP_COLOR_COUNT) {
+ n = TMP_COLOR_COUNT;
+ }
+
+ shaderContextA->shadeSpan(x, y, result, n);
+ shaderContextB->shadeSpan(x, y, tmp, n);
+
+ if (256 == scale) {
+ for (int i = 0; i < n; i++) {
+ result[i] = SkPMSrcOver(tmp[i], result[i]);
+ }
+ } else {
+ for (int i = 0; i < n; i++) {
+ result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
+ scale);
+ }
+ }
+
+ result += n;
+ x += n;
+ count -= n;
+ } while (count > 0);
+ } else { // use mode for the composition
+ do {
+ int n = count;
+ if (n > TMP_COLOR_COUNT) {
+ n = TMP_COLOR_COUNT;
+ }
+
+ shaderContextA->shadeSpan(x, y, result, n);
+ shaderContextB->shadeSpan(x, y, tmp, n);
+ xfer->xfer32(result, tmp, n, nullptr);
+
+ if (256 != scale) {
+ for (int i = 0; i < n; i++) {
+ result[i] = SkAlphaMulQ(result[i], scale);
+ }
+ }
+
+ result += n;
+ x += n;
+ count -= n;
+ } while (count > 0);
+ }
+}
+
+void SkComposeShader::ComposeShaderContext::shadeSpan4f(int x, int y, SkPM4f result[], int count) {
+ auto* shaderContextA = fShaderContextA;
+ auto* shaderContextB = fShaderContextB;
+ SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode;
+ unsigned alpha = this->getPaintAlpha();
+ Sk4f scale(alpha * (1.0f / 255));
+
+ SkPM4f tmp[TMP_COLOR_COUNT];
+
+ SkXfermodeProc4f xfer = SkXfermode::GetProc4f(mode);
+ do {
+ int n = SkTMin(count, TMP_COLOR_COUNT);
+
+ shaderContextA->shadeSpan4f(x, y, result, n);
+ shaderContextB->shadeSpan4f(x, y, tmp, n);
+ if (255 == alpha) {
+ for (int i = 0; i < n; ++i) {
+ result[i] = xfer(tmp[i], result[i]);
+ }
+ } else {
+ for (int i = 0; i < n; ++i) {
+ (xfer(tmp[i], result[i]).to4f() * scale).store(result + i);
+ }
+ }
+ result += n;
+ x += n;
+ count -= n;
+ } while (count > 0);
+}
+
+#if SK_SUPPORT_GPU
+
+#include "effects/GrConstColorProcessor.h"
+#include "effects/GrXfermodeFragmentProcessor.h"
+
+/////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
+ switch (fMode) {
+ case SkBlendMode::kClear:
+ return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
+ GrConstColorProcessor::kIgnore_InputMode);
+ break;
+ case SkBlendMode::kSrc:
+ return as_SB(fShaderB)->asFragmentProcessor(args);
+ break;
+ case SkBlendMode::kDst:
+ return as_SB(fShaderA)->asFragmentProcessor(args);
+ break;
+ default:
+ sk_sp<GrFragmentProcessor> fpA(as_SB(fShaderA)->asFragmentProcessor(args));
+ if (!fpA) {
+ return nullptr;
+ }
+ sk_sp<GrFragmentProcessor> fpB(as_SB(fShaderB)->asFragmentProcessor(args));
+ if (!fpB) {
+ return nullptr;
+ }
+ return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
+ std::move(fpA), fMode);
+ }
+}
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkComposeShader::toString(SkString* str) const {
+ str->append("SkComposeShader: (");
+
+ str->append("ShaderA: ");
+ as_SB(fShaderA)->toString(str);
+ str->append(" ShaderB: ");
+ as_SB(fShaderB)->toString(str);
+ if (SkBlendMode::kSrcOver != fMode) {
+ str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode));
+ }
+
+ this->INHERITED::toString(str);
+
+ str->append(")");
+}
+#endif