blob: f274420bf495982a149912579f9368fcbea73698 [file] [log] [blame]
* Copyright 2023 Google LLC
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
#include "src/shaders/SkShaderBase.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkColorFilter.h"
#include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkEffectPriv.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpList.h"
#include "src/shaders/SkLocalMatrixShader.h"
class SkWriteBuffer;
namespace SkShaders {
MatrixRec::MatrixRec(const SkMatrix& ctm) : fCTM(ctm) {}
std::optional<MatrixRec> MatrixRec::apply(const SkStageRec& rec, const SkMatrix& postInv) const {
SkMatrix total = fPendingLocalMatrix;
if (!fCTMApplied) {
total = SkMatrix::Concat(fCTM, total);
if (!total.invert(&total)) {
return {};
total = SkMatrix::Concat(postInv, total);
if (!fCTMApplied) {
// appendMatrix is a no-op if total worked out to identity.
rec.fPipeline->appendMatrix(rec.fAlloc, total);
return MatrixRec{fCTM,
std::tuple<SkMatrix, bool> MatrixRec::applyForFragmentProcessor(const SkMatrix& postInv) const {
SkMatrix total;
if (!fPendingLocalMatrix.invert(&total)) {
return {SkMatrix::I(), false};
return {SkMatrix::Concat(postInv, total), true};
MatrixRec MatrixRec::applied() const {
// We mark the CTM as "not applied" because we *never* apply the CTM for FPs. Their starting
// coords are local, not device, coords.
return MatrixRec{fCTM,
MatrixRec MatrixRec::concat(const SkMatrix& m) const {
return {fCTM,
SkShaderBase::ConcatLocalMatrices(fTotalLocalMatrix, m),
SkShaderBase::ConcatLocalMatrices(fPendingLocalMatrix, m),
} // namespace SkShaders
SkShaderBase::SkShaderBase() = default;
SkShaderBase::~SkShaderBase() = default;
void SkShaderBase::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); }
bool SkShaderBase::asLuminanceColor(SkColor4f* colorPtr) const {
SkColor4f storage;
if (nullptr == colorPtr) {
colorPtr = &storage;
if (this->onAsLuminanceColor(colorPtr)) {
colorPtr->fA = 1.0f; // we only return opaque
return true;
return false;
SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
// We always fall back to raster pipeline when perspective is present.
auto totalMatrix = rec.fMatrixRec.totalMatrix();
if (totalMatrix.hasPerspective() || !totalMatrix.invert(nullptr)) {
return nullptr;
return this->onMakeContext(rec, alloc);
return nullptr;
SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
: fShader(shader) {
// We should never use a context with perspective.
// Because the context parameters must be valid at this point, we know that the matrix is
// invertible.
fPaintAlpha = rec.fPaintAlpha;
SkShaderBase::Context::~Context() {}
bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
// In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
// always premul (or opaque). (And those "or opaque" caveats won't make any difference here.)
SkAlphaType shaderAT = kPremul_SkAlphaType, dstAT = kPremul_SkAlphaType;
return 0 ==
SkColorSpaceXformSteps{shaderColorSpace, shaderAT, fDstColorSpace, dstAT}.flags.mask();
sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const { return nullptr; }
bool SkShaderBase::appendRootStages(const SkStageRec& rec, const SkMatrix& ctm) const {
return this->appendStages(rec, SkShaders::MatrixRec(ctm));
sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
// need a cheap way to invert the alpha channel of a shader (i.e. 1 - a)
sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));