blob: c75747455ac9a29fab1e79acd8bc1abf00783334 [file] [log] [blame]
/*
* 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 "include/core/SkColorFilter.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h" // IWYU pragma: keep
#include "include/core/SkColorType.h"
#include "include/core/SkData.h"
#include "include/core/SkFlattenable.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSurfaceProps.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkColorData.h"
#include "include/private/base/SkAssert.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkColorFilterBase.h"
#include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkEffectPriv.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpContexts.h"
#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include <cstddef>
#include <iterator>
enum class SkBlendMode;
struct SkDeserialProcs;
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
bool SkColorFilter::asAColorMode(SkColor* color, SkBlendMode* mode) const {
return as_CFB(this)->onAsAColorMode(color, mode);
}
bool SkColorFilter::asAColorMatrix(float matrix[20]) const {
return as_CFB(this)->onAsAColorMatrix(matrix);
}
bool SkColorFilter::isAlphaUnchanged() const {
return as_CFB(this)->onIsAlphaUnchanged();
}
sk_sp<SkColorFilter> SkColorFilter::Deserialize(const void* data, size_t size,
const SkDeserialProcs* procs) {
return sk_sp<SkColorFilter>(static_cast<SkColorFilter*>(
SkFlattenable::Deserialize(
kSkColorFilter_Type, data, size, procs).release()));
}
//////////////////////////////////////////////////////////////////////////////////////////////////
bool SkColorFilterBase::onAsAColorMode(SkColor*, SkBlendMode*) const {
return false;
}
bool SkColorFilterBase::onAsAColorMatrix(float matrix[20]) const {
return false;
}
#if defined(SK_ENABLE_SKVM)
skvm::Color SkColorFilterBase::program(skvm::Builder* p, skvm::Color c,
const SkColorInfo& dst,
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
skvm::F32 original = c.a;
if ((c = this->onProgram(p,c, dst, uniforms,alloc))) {
if (this->isAlphaUnchanged()) {
c.a = original;
}
return c;
}
//SkDebugf("cannot program %s\n", this->getTypeName());
return {};
}
#endif
SkColor SkColorFilter::filterColor(SkColor c) const {
// This is mostly meaningless. We should phase-out this call entirely.
SkColorSpace* cs = nullptr;
return this->filterColor4f(SkColor4f::FromColor(c), cs, cs).toSkColor();
}
SkColor4f SkColorFilter::filterColor4f(const SkColor4f& origSrcColor, SkColorSpace* srcCS,
SkColorSpace* dstCS) const {
SkPMColor4f color = { origSrcColor.fR, origSrcColor.fG, origSrcColor.fB, origSrcColor.fA };
SkColorSpaceXformSteps(srcCS, kUnpremul_SkAlphaType,
dstCS, kPremul_SkAlphaType).apply(color.vec());
return as_CFB(this)->onFilterColor4f(color, dstCS).unpremul();
}
SkPMColor4f SkColorFilterBase::onFilterColor4f(const SkPMColor4f& color,
SkColorSpace* dstCS) const {
constexpr size_t kEnoughForCommonFilters = 2048; // big enough for a tiny SkSL program
SkSTArenaAlloc<kEnoughForCommonFilters> alloc;
SkRasterPipeline pipeline(&alloc);
pipeline.append_constant_color(&alloc, color.vec());
SkMatrixProvider matrixProvider(SkMatrix::I());
SkSurfaceProps props{}; // default OK; colorFilters don't render text
SkStageRec rec = {&pipeline, &alloc, kRGBA_F32_SkColorType, dstCS, color.unpremul(), props};
if (as_CFB(this)->appendStages(rec, color.fA == 1)) {
SkPMColor4f dst;
SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 };
pipeline.append(SkRasterPipelineOp::store_f32, &dstPtr);
pipeline.run(0,0, 1,1);
return dst;
}
#if defined(SK_ENABLE_SKVM)
// This filter doesn't support SkRasterPipeline... try skvm.
skvm::Builder b;
skvm::Uniforms uni(b.uniform(), 4);
SkColor4f uniColor = {color.fR, color.fG, color.fB, color.fA};
SkColorInfo dstInfo = {kRGBA_F32_SkColorType, kPremul_SkAlphaType, sk_ref_sp(dstCS)};
if (skvm::Color filtered =
as_CFB(this)->program(&b, b.uniformColor(uniColor, &uni), dstInfo, &uni, &alloc)) {
b.store({skvm::PixelFormat::FLOAT, 32,32,32,32, 0,32,64,96},
b.varying<SkColor4f>(), filtered);
const bool allow_jit = false; // We're only filtering one color, no point JITing.
b.done("filterColor4f", allow_jit).eval(1, uni.buf.data(), &color);
return color;
}
#endif
SkDEBUGFAIL("onFilterColor4f unimplemented for this filter");
return SkPMColor4f{0,0,0,0};
}
#if defined(SK_GRAPHITE)
void SkColorFilterBase::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
skgpu::graphite::PipelineDataGatherer* gatherer) const {
using namespace skgpu::graphite;
// Return the input color as-is.
PriorOutputBlock::BeginBlock(keyContext, builder, gatherer);
builder->endBlock();
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0,
sk_sp<SkColorFilter> cf1) {
#ifdef SK_ENABLE_SKSL
if (!cf0 && !cf1) {
return nullptr;
}
if (SkScalarIsNaN(weight)) {
return nullptr;
}
if (cf0 == cf1) {
return cf0; // or cf1
}
if (weight <= 0) {
return cf0;
}
if (weight >= 1) {
return cf1;
}
static const SkRuntimeEffect* effect =
SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
"uniform colorFilter cf0;"
"uniform colorFilter cf1;"
"uniform half weight;"
"half4 main(half4 color) {"
"return mix(cf0.eval(color), cf1.eval(color), weight);"
"}")
.release();
SkASSERT(effect);
sk_sp<SkColorFilter> inputs[] = {cf0,cf1};
return effect->makeColorFilter(SkData::MakeWithCopy(&weight, sizeof(weight)),
inputs, std::size(inputs));
#else
// TODO(skia:12197)
return nullptr;
#endif
}