/* * 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::MakeComposeShader(sk_sp dst, sk_sp src, SkBlendMode mode) { if (!src || !dst) { return nullptr; } if (SkBlendMode::kSrc == mode) { return src; } if (SkBlendMode::kDst == mode) { return dst; } return sk_sp(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 SkComposeShader::CreateProc(SkReadBuffer& buffer) { sk_sp shaderA(buffer.readShader()); sk_sp shaderB(buffer.readShader()); SkBlendMode mode; if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) { sk_sp xfer = buffer.readXfermode(); mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver; } else { mode = (SkBlendMode)buffer.read32(); } if (!shaderA || !shaderB) { return nullptr; } return sk_make_sp(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(*this, rec, contextA, contextB); } sk_sp 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(); // 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(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(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 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 fpA(as_SB(fShaderA)->asFragmentProcessor(args)); if (!fpA) { return nullptr; } sk_sp 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