Decouple SkColorFilters and Ganesh GPU code
This moves all asFragmentProcessor code to GrFragmentProcessors.cpp
In order to do this, I needed to extract some class definitions
to headers and rename some files/filters to be more consistent
so we can use a macro like SK_ALL_COLOR_FILTERS.
I decided to expose some fields in the color filters as
public (but still internal to Skia) methods instead of defining
friend functions, as those definitions were long and unwieldy.
As I was doing this, I enforced IWYU on the files I modified
to help streamline #includes.
Suggested Review order:
- SkColorFIlterBase.h
- SkColorFilter.cpp -> SkComposeColorFilter,
SkWorkingFormatColorFilter, SkColorSpaceXformColorFilter
- SkTableColorFilter.cpp -> SkTableColorFilter.h,
GrColorTableEffect
- SkRuntimeEffect.cpp -> SkRuntimeColorFilter
- SkColorFilter_Matrix.cpp -> SkMatrixColorFilter
- Other colorfilters to see .cpp -> .h
- GrFragmentProcessors.cpp
- Remaining files.
Change-Id: Ifce96cf854166a46a81a4a354154a00d4c300e1d
Bug: skia:14317
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/703678
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Kevin Lubick <kjlubick@google.com>
diff --git a/gn/core.gni b/gn/core.gni
index 071972c..c8cf337 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -273,6 +273,8 @@
"$_src/core/SkBlendMode.cpp",
"$_src/core/SkBlendModeBlender.cpp",
"$_src/core/SkBlendModeBlender.h",
+ "$_src/core/SkBlendModeColorFilter.cpp",
+ "$_src/core/SkBlendModeColorFilter.h",
"$_src/core/SkBlendModePriv.h",
"$_src/core/SkBlenderBase.h",
"$_src/core/SkBlitBWMaskTemplate.h",
@@ -306,11 +308,14 @@
"$_src/core/SkColorFilter.cpp",
"$_src/core/SkColorFilterBase.h",
"$_src/core/SkColorFilterPriv.h",
- "$_src/core/SkColorFilter_Matrix.cpp",
"$_src/core/SkColorSpace.cpp",
"$_src/core/SkColorSpacePriv.h",
+ "$_src/core/SkColorSpaceXformColorFilter.cpp",
+ "$_src/core/SkColorSpaceXformColorFilter.h",
"$_src/core/SkColorSpaceXformSteps.cpp",
"$_src/core/SkColorSpaceXformSteps.h",
+ "$_src/core/SkComposeColorFilter.cpp",
+ "$_src/core/SkComposeColorFilter.h",
"$_src/core/SkCompressedDataUtils.cpp",
"$_src/core/SkCompressedDataUtils.h",
"$_src/core/SkContourMeasure.cpp",
@@ -420,6 +425,8 @@
"$_src/core/SkMaskGamma.cpp",
"$_src/core/SkMaskGamma.h",
"$_src/core/SkMatrix.cpp",
+ "$_src/core/SkMatrixColorFilter.cpp",
+ "$_src/core/SkMatrixColorFilter.h",
"$_src/core/SkMatrixInvert.cpp",
"$_src/core/SkMatrixInvert.h",
"$_src/core/SkMatrixPriv.h",
@@ -434,7 +441,6 @@
"$_src/core/SkMipmapAccessor.h",
"$_src/core/SkMipmapBuilder.cpp",
"$_src/core/SkMipmapBuilder.h",
- "$_src/core/SkModeColorFilter.cpp",
"$_src/core/SkNextID.h",
"$_src/core/SkOSFile.h",
"$_src/core/SkOpts.cpp",
@@ -505,6 +511,8 @@
"$_src/core/SkResourceCache.h",
"$_src/core/SkRuntimeBlender.cpp",
"$_src/core/SkRuntimeBlender.h",
+ "$_src/core/SkRuntimeColorFilter.cpp",
+ "$_src/core/SkRuntimeColorFilter.h",
"$_src/core/SkRuntimeEffect.cpp",
"$_src/core/SkRuntimeEffectPriv.h",
"$_src/core/SkSLTypeShared.cpp",
@@ -579,6 +587,8 @@
"$_src/core/SkVertState.h",
"$_src/core/SkVertices.cpp",
"$_src/core/SkVerticesPriv.h",
+ "$_src/core/SkWorkingFormatColorFilter.cpp",
+ "$_src/core/SkWorkingFormatColorFilter.h",
"$_src/core/SkWriteBuffer.cpp",
"$_src/core/SkWriteBuffer.h",
"$_src/core/SkWritePixelsRec.cpp",
diff --git a/gn/effects.gni b/gn/effects.gni
index 3ce20a9..ebb2ba5 100644
--- a/gn/effects.gni
+++ b/gn/effects.gni
@@ -64,6 +64,7 @@
"$_src/effects/SkShaderMaskFilterImpl.cpp",
"$_src/effects/SkShaderMaskFilterImpl.h",
"$_src/effects/SkTableColorFilter.cpp",
+ "$_src/effects/SkTableColorFilter.h",
"$_src/effects/SkTableMaskFilter.cpp",
"$_src/effects/SkTrimPE.h",
"$_src/effects/SkTrimPathEffect.cpp",
diff --git a/gn/gpu.gni b/gn/gpu.gni
index fa2858d..6753645 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -364,6 +364,8 @@
"$_src/gpu/ganesh/effects/GrBitmapTextGeoProc.h",
"$_src/gpu/ganesh/effects/GrBlendFragmentProcessor.cpp",
"$_src/gpu/ganesh/effects/GrBlendFragmentProcessor.h",
+ "$_src/gpu/ganesh/effects/GrColorTableEffect.cpp",
+ "$_src/gpu/ganesh/effects/GrColorTableEffect.h",
"$_src/gpu/ganesh/effects/GrConvexPolyEffect.cpp",
"$_src/gpu/ganesh/effects/GrConvexPolyEffect.h",
"$_src/gpu/ganesh/effects/GrCoverageSetOpXP.cpp",
diff --git a/gn/utils.gni b/gn/utils.gni
index 5b14b81..5023ebc 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -73,6 +73,7 @@
"$_src/utils/SkFloatToDecimal.h",
"$_src/utils/SkFloatUtils.h",
"$_src/utils/SkGaussianColorFilter.cpp",
+ "$_src/utils/SkGaussianColorFilter.h",
"$_src/utils/SkJSON.cpp",
"$_src/utils/SkJSON.h",
"$_src/utils/SkJSONWriter.cpp",
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 1e0f6ea..6e01a2f 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -8,12 +8,18 @@
#ifndef SkColorFilter_DEFINED
#define SkColorFilter_DEFINED
-#include "include/core/SkBlendMode.h"
#include "include/core/SkColor.h"
#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "include/private/base/SkAPI.h"
+
+#include <cstddef>
+#include <cstdint>
class SkColorMatrix;
class SkColorSpace;
+enum class SkBlendMode;
+struct SkDeserialProcs;
/**
* ColorFilters are optional objects in the drawing pipeline. When present in
diff --git a/modules/sksg/include/SkSGColorFilter.h b/modules/sksg/include/SkSGColorFilter.h
index a0f2c51..c7befe9 100644
--- a/modules/sksg/include/SkSGColorFilter.h
+++ b/modules/sksg/include/SkSGColorFilter.h
@@ -65,7 +65,7 @@
};
/**
- * Concrete SkModeColorFilter Effect node.
+ * Concrete SkBlendModeColorFilter Effect node.
*/
class ModeColorFilter final : public ColorFilter {
public:
diff --git a/public.bzl b/public.bzl
index dea2449..636be01 100644
--- a/public.bzl
+++ b/public.bzl
@@ -357,11 +357,11 @@
"src/core/SkATrace.h",
"src/core/SkAdvancedTypefaceMetrics.h",
"src/core/SkAlphaRuns.cpp",
+ "src/core/SkAlphaRuns.h",
"src/core/SkAnalyticEdge.cpp",
"src/core/SkAnalyticEdge.h",
"src/core/SkAnnotation.cpp",
"src/core/SkAnnotationKeys.h",
- "src/core/SkAlphaRuns.h",
"src/core/SkAutoBlitterChoose.h",
"src/core/SkAutoPixmapStorage.cpp",
"src/core/SkAutoPixmapStorage.h",
@@ -410,11 +410,14 @@
"src/core/SkColorFilter.cpp",
"src/core/SkColorFilterBase.h",
"src/core/SkColorFilterPriv.h",
- "src/core/SkColorFilter_Matrix.cpp",
"src/core/SkColorSpace.cpp",
"src/core/SkColorSpacePriv.h",
+ "src/core/SkColorSpaceXformColorFilter.cpp",
+ "src/core/SkColorSpaceXformColorFilter.h",
"src/core/SkColorSpaceXformSteps.cpp",
"src/core/SkColorSpaceXformSteps.h",
+ "src/core/SkComposeColorFilter.cpp",
+ "src/core/SkComposeColorFilter.h",
"src/core/SkCompressedDataUtils.cpp",
"src/core/SkCompressedDataUtils.h",
"src/core/SkContourMeasure.cpp",
@@ -524,6 +527,8 @@
"src/core/SkMaskGamma.cpp",
"src/core/SkMaskGamma.h",
"src/core/SkMatrix.cpp",
+ "src/core/SkMatrixColorFilter.cpp",
+ "src/core/SkMatrixColorFilter.h",
"src/core/SkMatrixInvert.cpp",
"src/core/SkMatrixInvert.h",
"src/core/SkMatrixPriv.h",
@@ -538,7 +543,8 @@
"src/core/SkMipmapAccessor.h",
"src/core/SkMipmapBuilder.cpp",
"src/core/SkMipmapBuilder.h",
- "src/core/SkModeColorFilter.cpp",
+ "src/core/SkBlendModeColorFilter.cpp",
+ "src/core/SkBlendModeColorFilter.h",
"src/core/SkNextID.h",
"src/core/SkOSFile.h",
"src/core/SkOpts.cpp",
@@ -624,6 +630,8 @@
"src/core/SkResourceCache.h",
"src/core/SkRuntimeBlender.cpp",
"src/core/SkRuntimeBlender.h",
+ "src/core/SkRuntimeColorFilter.cpp",
+ "src/core/SkRuntimeColorFilter.h",
"src/core/SkRuntimeEffect.cpp",
"src/core/SkRuntimeEffectPriv.h",
"src/core/SkSLTypeShared.cpp",
@@ -698,6 +706,8 @@
"src/core/SkVertState.h",
"src/core/SkVertices.cpp",
"src/core/SkVerticesPriv.h",
+ "src/core/SkWorkingFormatColorFilter.cpp",
+ "src/core/SkWorkingFormatColorFilter.h",
"src/core/SkWriteBuffer.cpp",
"src/core/SkWriteBuffer.h",
"src/core/SkWritePixelsRec.cpp",
@@ -736,6 +746,7 @@
"src/effects/SkShaderMaskFilterImpl.cpp",
"src/effects/SkShaderMaskFilterImpl.h",
"src/effects/SkTableColorFilter.cpp",
+ "src/effects/SkTableColorFilter.h",
"src/effects/SkTableMaskFilter.cpp",
"src/effects/SkTrimPE.h",
"src/effects/SkTrimPathEffect.cpp",
@@ -1041,6 +1052,8 @@
"src/gpu/ganesh/effects/GrBitmapTextGeoProc.h",
"src/gpu/ganesh/effects/GrBlendFragmentProcessor.cpp",
"src/gpu/ganesh/effects/GrBlendFragmentProcessor.h",
+ "src/gpu/ganesh/effects/GrColorTableEffect.cpp",
+ "src/gpu/ganesh/effects/GrColorTableEffect.h",
"src/gpu/ganesh/effects/GrConvexPolyEffect.cpp",
"src/gpu/ganesh/effects/GrConvexPolyEffect.h",
"src/gpu/ganesh/effects/GrCoverageSetOpXP.cpp",
@@ -1696,6 +1709,7 @@
"src/utils/SkFloatToDecimal.h",
"src/utils/SkFloatUtils.h",
"src/utils/SkGaussianColorFilter.cpp",
+ "src/utils/SkGaussianColorFilter.h",
"src/utils/SkJSON.cpp",
"src/utils/SkJSON.h",
"src/utils/SkJSONWriter.cpp",
diff --git a/src/core/BUILD.bazel b/src/core/BUILD.bazel
index 7b99be0..3e80d7d 100644
--- a/src/core/BUILD.bazel
+++ b/src/core/BUILD.bazel
@@ -86,11 +86,14 @@
"SkColorFilter.cpp",
"SkColorFilterBase.h",
"SkColorFilterPriv.h",
- "SkColorFilter_Matrix.cpp",
"SkColorSpace.cpp",
"SkColorSpacePriv.h",
+ "SkColorSpaceXformColorFilter.cpp",
+ "SkColorSpaceXformColorFilter.h",
"SkColorSpaceXformSteps.cpp",
"SkColorSpaceXformSteps.h",
+ "SkComposeColorFilter.cpp",
+ "SkComposeColorFilter.h",
"SkCompressedDataUtils.cpp",
"SkCompressedDataUtils.h",
"SkContourMeasure.cpp",
@@ -194,6 +197,8 @@
"SkMaskGamma.cpp",
"SkMaskGamma.h",
"SkMatrix.cpp",
+ "SkMatrixColorFilter.cpp",
+ "SkMatrixColorFilter.h",
"SkMatrixPriv.h",
"SkMatrixProvider.h",
"SkMatrixUtils.h",
@@ -206,7 +211,8 @@
"SkMipmapAccessor.h",
"SkMipmapBuilder.cpp",
"SkMipmapBuilder.h",
- "SkModeColorFilter.cpp",
+ "SkBlendModeColorFilter.cpp",
+ "SkBlendModeColorFilter.h",
"SkNextID.h",
"SkOSFile.h",
"SkOpts.cpp",
@@ -340,6 +346,8 @@
"SkVertState.h",
"SkVertices.cpp",
"SkVerticesPriv.h",
+ "SkWorkingFormatColorFilter.cpp",
+ "SkWorkingFormatColorFilter.h",
"SkWriteBuffer.cpp",
"SkWriteBuffer.h",
"SkWritePixelsRec.cpp",
@@ -370,6 +378,8 @@
"SkRuntimeEffect.cpp",
"SkRuntimeBlender.cpp",
"SkRuntimeBlender.h",
+ "SkRuntimeColorFilter.cpp",
+ "SkRuntimeColorFilter.h",
"SkSLTypeShared.cpp",
"SkSLTypeShared.h",
]
diff --git a/src/core/SkBlendModeColorFilter.cpp b/src/core/SkBlendModeColorFilter.cpp
new file mode 100644
index 0000000..a7a2ddd
--- /dev/null
+++ b/src/core/SkBlendModeColorFilter.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 "src/core/SkBlendModeColorFilter.h"
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkRefCnt.h"
+#include "include/private/SkColorData.h"
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkColorSpacePriv.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkPicturePriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkValidationUtils.h"
+#include "src/core/SkWriteBuffer.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+template <SkAlphaType kDstAT = kPremul_SkAlphaType>
+static SkRGBA4f<kDstAT> map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
+ SkRGBA4f<kDstAT> color = {c.fR, c.fG, c.fB, c.fA};
+ SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, dst, kDstAT).apply(color.vec());
+ return color;
+}
+
+SkBlendModeColorFilter::SkBlendModeColorFilter(const SkColor4f& color, SkBlendMode mode)
+ : fColor(color), fMode(mode) {}
+
+bool SkBlendModeColorFilter::onAsAColorMode(SkColor* color, SkBlendMode* mode) const {
+ if (color) {
+ *color = fColor.toSkColor();
+ }
+ if (mode) {
+ *mode = fMode;
+ }
+ return true;
+}
+
+bool SkBlendModeColorFilter::onIsAlphaUnchanged() const {
+ switch (fMode) {
+ case SkBlendMode::kDst: //!< [Da, Dc]
+ case SkBlendMode::kSrcATop: //!< [Da, Sc * Da + (1 - Sa) * Dc]
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+void SkBlendModeColorFilter::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeColor4f(fColor);
+ buffer.writeUInt((int)fMode);
+}
+
+sk_sp<SkFlattenable> SkBlendModeColorFilter::CreateProc(SkReadBuffer& buffer) {
+ if (buffer.isVersionLT(SkPicturePriv::kBlend4fColorFilter)) {
+ // Color is 8-bit, sRGB
+ SkColor color = buffer.readColor();
+ SkBlendMode mode = (SkBlendMode)buffer.readUInt();
+ return SkColorFilters::Blend(SkColor4f::FromColor(color), /*sRGB*/ nullptr, mode);
+ } else {
+ // Color is 32-bit, sRGB
+ SkColor4f color;
+ buffer.readColor4f(&color);
+ SkBlendMode mode = (SkBlendMode)buffer.readUInt();
+ return SkColorFilters::Blend(color, /*sRGB*/ nullptr, mode);
+ }
+}
+
+bool SkBlendModeColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ rec.fPipeline->append(SkRasterPipelineOp::move_src_dst);
+ SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), rec.fDstCS);
+ rec.fPipeline->append_constant_color(rec.fAlloc, color.vec());
+ SkBlendMode_AppendStages(fMode, rec.fPipeline);
+ return true;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkBlendModeColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dstInfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const {
+ SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), dstInfo.colorSpace());
+ // The blend program operates on this as if it were premul but the API takes an SkColor4f
+ skvm::Color dst = c, src = p->uniformColor({color.fR, color.fG, color.fB, color.fA}, uniforms);
+ return p->blend(fMode, src, dst);
+}
+#endif
+
+#if defined(SK_GRAPHITE)
+void SkBlendModeColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ SkPMColor4f color =
+ map_color(fColor, sk_srgb_singleton(), keyContext.dstColorInfo().colorSpace());
+ AddColorBlendBlock(keyContext, builder, gatherer, fMode, color);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkColorFilter> SkColorFilters::Blend(const SkColor4f& color,
+ sk_sp<SkColorSpace> colorSpace,
+ SkBlendMode mode) {
+ if (!SkIsValidMode(mode)) {
+ return nullptr;
+ }
+
+ // First map to sRGB to simplify storage in the actual SkColorFilter instance, staying unpremul
+ // until the final dst color space is known when actually filtering.
+ SkColor4f srgb = map_color<kUnpremul_SkAlphaType>(color, colorSpace.get(), sk_srgb_singleton());
+
+ // Next collapse some modes if possible
+ float alpha = srgb.fA;
+ if (SkBlendMode::kClear == mode) {
+ srgb = SkColors::kTransparent;
+ mode = SkBlendMode::kSrc;
+ } else if (SkBlendMode::kSrcOver == mode) {
+ if (0.f == alpha) {
+ mode = SkBlendMode::kDst;
+ } else if (1.f == alpha) {
+ mode = SkBlendMode::kSrc;
+ }
+ // else just stay srcover
+ }
+
+ // Finally weed out combinations that are noops, and just return null
+ if (SkBlendMode::kDst == mode ||
+ (0.f == alpha && (SkBlendMode::kSrcOver == mode ||
+ SkBlendMode::kDstOver == mode ||
+ SkBlendMode::kDstOut == mode ||
+ SkBlendMode::kSrcATop == mode ||
+ SkBlendMode::kXor == mode ||
+ SkBlendMode::kDarken == mode)) ||
+ (1.f == alpha && SkBlendMode::kDstIn == mode)) {
+ return nullptr;
+ }
+
+ return sk_sp<SkColorFilter>(new SkBlendModeColorFilter(srgb, mode));
+}
+
+sk_sp<SkColorFilter> SkColorFilters::Blend(SkColor color, SkBlendMode mode) {
+ return Blend(SkColor4f::FromColor(color), /*sRGB*/ nullptr, mode);
+}
+
+void SkRegisterModeColorFilterFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkBlendModeColorFilter);
+ // Previous name
+ SkFlattenable::Register("SkModeColorFilter", SkBlendModeColorFilter::CreateProc);
+}
diff --git a/src/core/SkBlendModeColorFilter.h b/src/core/SkBlendModeColorFilter.h
new file mode 100644
index 0000000..1097777
--- /dev/null
+++ b/src/core/SkBlendModeColorFilter.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkBlendModeColorFilter_DEFINED
+#define SkBlendModeColorFilter_DEFINED
+
+#include "include/core/SkColor.h"
+#include "include/core/SkFlattenable.h"
+#include "src/core/SkColorFilterBase.h"
+
+class SkReadBuffer;
+class SkWriteBuffer;
+enum class SkBlendMode;
+struct SkStageRec;
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+class SkBlendModeColorFilter final : public SkColorFilterBase {
+public:
+ SkBlendModeColorFilter(const SkColor4f& color, SkBlendMode mode);
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+ bool onIsAlphaUnchanged() const override;
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kBlendMode; }
+
+ SkColor4f color() const { return fColor; }
+ SkBlendMode mode() const { return fMode; }
+
+private:
+ friend void ::SkRegisterModeColorFilterFlattenable();
+ SK_FLATTENABLE_HOOKS(SkBlendModeColorFilter)
+
+ void flatten(SkWriteBuffer&) const override;
+ bool onAsAColorMode(SkColor*, SkBlendMode*) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder*,
+ skvm::Color,
+ const SkColorInfo&,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override;
+#endif
+
+ SkColor4f fColor; // always stored in sRGB
+ SkBlendMode fMode;
+};
+
+#endif
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 576bf38..c757474 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -4,31 +4,36 @@
* 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/SkString.h"
-#include "include/core/SkUnPreMultiply.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkSurfaceProps.h"
#include "include/effects/SkRuntimeEffect.h"
-#include "include/private/base/SkTDArray.h"
-#include "modules/skcms/skcms.h"
+#include "include/private/SkColorData.h"
+#include "include/private/base/SkAssert.h"
#include "src/base/SkArenaAlloc.h"
-#include "src/base/SkNoDestructor.h"
#include "src/core/SkColorFilterBase.h"
-#include "src/core/SkColorFilterPriv.h"
-#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/core/SkVM.h"
-#include "src/core/SkWriteBuffer.h"
-#if defined(SK_GANESH)
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrColorSpaceXform.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#endif
+#include <cstddef>
+#include <iterator>
+
+enum class SkBlendMode;
+struct SkDeserialProcs;
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
@@ -65,15 +70,6 @@
return false;
}
-#if defined(SK_GANESH)
-GrFPResult SkColorFilterBase::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo& dstColorInfo,
- const SkSurfaceProps& props) const {
- // This color filter doesn't implement `asFragmentProcessor`.
- return GrFPFailure(std::move(inputFP));
-}
-#endif
#if defined(SK_ENABLE_SKVM)
skvm::Color SkColorFilterBase::program(skvm::Builder* p, skvm::Color c,
const SkColorInfo& dst,
@@ -159,430 +155,6 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
-class SkComposeColorFilter final : public SkColorFilterBase {
-public:
- bool onIsAlphaUnchanged() const override {
- // Can only claim alphaunchanged support if both our proxys do.
- return fOuter->isAlphaUnchanged() && fInner->isAlphaUnchanged();
- }
-
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
- bool innerIsOpaque = shaderIsOpaque;
- if (!fInner->isAlphaUnchanged()) {
- innerIsOpaque = false;
- }
- return fInner->appendStages(rec, shaderIsOpaque) &&
- fOuter->appendStages(rec, innerIsOpaque);
- }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
- c = fInner->program(p, c, dst, uniforms, alloc);
- return c ? fOuter->program(p, c, dst, uniforms, alloc) : skvm::Color{};
- }
-#endif
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo& dstColorInfo,
- const SkSurfaceProps& props) const override {
- // Unfortunately, we need to clone the input before we know we need it. This lets us return
- // the original FP if either internal color filter fails.
- auto inputClone = inputFP ? inputFP->clone() : nullptr;
-
- auto [innerSuccess, innerFP] =
- fInner->asFragmentProcessor(std::move(inputFP), context, dstColorInfo, props);
- if (!innerSuccess) {
- return GrFPFailure(std::move(inputClone));
- }
-
- auto [outerSuccess, outerFP] =
- fOuter->asFragmentProcessor(std::move(innerFP), context, dstColorInfo, props);
- if (!outerSuccess) {
- return GrFPFailure(std::move(inputClone));
- }
-
- return GrFPSuccess(std::move(outerFP));
- }
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const override {
- using namespace skgpu::graphite;
-
- ComposeColorFilterBlock::BeginBlock(keyContext, builder, gatherer);
-
- as_CFB(fInner)->addToKey(keyContext, builder, gatherer);
- as_CFB(fOuter)->addToKey(keyContext, builder, gatherer);
-
- builder->endBlock();
- }
-#endif // SK_GRAPHITE
-
-protected:
- void flatten(SkWriteBuffer& buffer) const override {
- buffer.writeFlattenable(fOuter.get());
- buffer.writeFlattenable(fInner.get());
- }
-
-private:
- friend void ::SkRegisterComposeColorFilterFlattenable();
- SK_FLATTENABLE_HOOKS(SkComposeColorFilter)
-
- SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner)
- : fOuter(as_CFB_sp(std::move(outer)))
- , fInner(as_CFB_sp(std::move(inner)))
- {}
-
- sk_sp<SkColorFilterBase> fOuter;
- sk_sp<SkColorFilterBase> fInner;
-
- friend class SkColorFilter;
-
- using INHERITED = SkColorFilter;
-};
-
-sk_sp<SkFlattenable> SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) {
- sk_sp<SkColorFilter> outer(buffer.readColorFilter());
- sk_sp<SkColorFilter> inner(buffer.readColorFilter());
- return outer ? outer->makeComposed(std::move(inner)) : inner;
-}
-
-sk_sp<SkColorFilter> SkColorFilter::makeComposed(sk_sp<SkColorFilter> inner) const {
- if (!inner) {
- return sk_ref_sp(this);
- }
-
- return sk_sp<SkColorFilter>(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner)));
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class ColorSpaceXformColorFilter final : public SkColorFilterBase {
-public:
- ColorSpaceXformColorFilter(sk_sp<SkColorSpace> src, sk_sp<SkColorSpace> dst)
- : fSrc(std::move(src))
- , fDst(std::move(dst))
- , fSteps(
- // We handle premul/unpremul separately, so here just always upm->upm.
- fSrc.get(),
- kUnpremul_SkAlphaType,
- fDst.get(),
- kUnpremul_SkAlphaType)
-
- {}
-
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo& dstColorInfo,
- const SkSurfaceProps& props) const override {
- // wish our caller would let us know if our input was opaque...
- constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
- return GrFPSuccess(GrColorSpaceXformEffect::Make(
- std::move(inputFP), fSrc.get(), alphaType, fDst.get(), alphaType));
- SkUNREACHABLE;
- }
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const override {
- using namespace skgpu::graphite;
-
- constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
- ColorSpaceTransformBlock::ColorSpaceTransformData data(
- fSrc.get(), alphaType, fDst.get(), alphaType);
- ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
- builder->endBlock();
- }
-#endif
-
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
- if (!shaderIsOpaque) {
- rec.fPipeline->append(SkRasterPipelineOp::unpremul);
- }
-
- fSteps.apply(rec.fPipeline);
-
- if (!shaderIsOpaque) {
- rec.fPipeline->append(SkRasterPipelineOp::premul);
- }
- return true;
- }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& dst,
- skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
- return premul(fSteps.program(p, uniforms, unpremul(c)));
- }
-#endif
-
-protected:
- void flatten(SkWriteBuffer& buffer) const override {
- buffer.writeDataAsByteArray(fSrc->serialize().get());
- buffer.writeDataAsByteArray(fDst->serialize().get());
- }
-
-private:
- friend void ::SkRegisterColorSpaceXformColorFilterFlattenable();
- SK_FLATTENABLE_HOOKS(ColorSpaceXformColorFilter)
- static sk_sp<SkFlattenable> LegacyGammaOnlyCreateProc(SkReadBuffer& buffer);
-
- const sk_sp<SkColorSpace> fSrc;
- const sk_sp<SkColorSpace> fDst;
- SkColorSpaceXformSteps fSteps;
-
- friend class SkColorFilter;
- using INHERITED = SkColorFilterBase;
-};
-
-sk_sp<SkFlattenable> ColorSpaceXformColorFilter::LegacyGammaOnlyCreateProc(SkReadBuffer& buffer) {
- uint32_t dir = buffer.read32();
- if (!buffer.validate(dir <= 1)) {
- return nullptr;
- }
- if (dir == 0) {
- return SkColorFilters::LinearToSRGBGamma();
- }
- return SkColorFilters::SRGBToLinearGamma();
-}
-
-sk_sp<SkFlattenable> ColorSpaceXformColorFilter::CreateProc(SkReadBuffer& buffer) {
- sk_sp<SkColorSpace> colorSpaces[2];
- for (int i = 0; i < 2; ++i) {
- auto data = buffer.readByteArrayAsData();
- if (!buffer.validate(data != nullptr)) {
- return nullptr;
- }
- colorSpaces[i] = SkColorSpace::Deserialize(data->data(), data->size());
- if (!buffer.validate(colorSpaces[i] != nullptr)) {
- return nullptr;
- }
- }
- return sk_sp<SkFlattenable>(
- new ColorSpaceXformColorFilter(std::move(colorSpaces[0]), std::move(colorSpaces[1])));
-}
-
-sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() {
- static SkNoDestructor<ColorSpaceXformColorFilter> gSingleton(SkColorSpace::MakeSRGBLinear(),
- SkColorSpace::MakeSRGB());
- return sk_ref_sp(gSingleton.get());
-}
-
-sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() {
- static SkNoDestructor<ColorSpaceXformColorFilter> gSingleton(SkColorSpace::MakeSRGB(),
- SkColorSpace::MakeSRGBLinear());
- return sk_ref_sp(gSingleton.get());
-}
-
-sk_sp<SkColorFilter> SkColorFilterPriv::MakeColorSpaceXform(sk_sp<SkColorSpace> src,
- sk_sp<SkColorSpace> dst) {
- return sk_make_sp<ColorSpaceXformColorFilter>(std::move(src), std::move(dst));
-}
-
-class SkWorkingFormatColorFilter final : public SkColorFilterBase {
-public:
- SkWorkingFormatColorFilter(sk_sp<SkColorFilter> child,
- const skcms_TransferFunction* tf,
- const skcms_Matrix3x3* gamut,
- const SkAlphaType* at) {
- fChild = std::move(child);
- if (tf) { fTF = *tf; fUseDstTF = false; }
- if (gamut) { fGamut = *gamut; fUseDstGamut = false; }
- if (at) { fAT = *at; fUseDstAT = false; }
- }
-
- sk_sp<SkColorSpace> workingFormat(const sk_sp<SkColorSpace>& dstCS, SkAlphaType* at) const {
- skcms_TransferFunction tf = fTF;
- skcms_Matrix3x3 gamut = fGamut;
-
- if (fUseDstTF ) { SkAssertResult(dstCS->isNumericalTransferFn(&tf)); }
- if (fUseDstGamut) { SkAssertResult(dstCS->toXYZD50 (&gamut)); }
-
- *at = fUseDstAT ? kPremul_SkAlphaType : fAT;
- return SkColorSpace::MakeRGB(tf, gamut);
- }
-
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo& dstColorInfo,
- const SkSurfaceProps& props) const override {
- sk_sp<SkColorSpace> dstCS = dstColorInfo.refColorSpace();
- if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
-
- SkAlphaType workingAT;
- sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
-
- GrColorInfo dst = {dstColorInfo.colorType(), dstColorInfo.alphaType(), dstCS},
- working = {dstColorInfo.colorType(), workingAT, workingCS};
-
- auto [ok, fp] = as_CFB(fChild)->asFragmentProcessor(
- GrColorSpaceXformEffect::Make(std::move(inputFP), dst,working), context, working,
- props);
-
- return ok ? GrFPSuccess(GrColorSpaceXformEffect::Make(std::move(fp), working,dst))
- : GrFPFailure(std::move(fp));
- }
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const override {
- using namespace skgpu::graphite;
-
- const SkAlphaType dstAT = keyContext.dstColorInfo().alphaType();
- sk_sp<SkColorSpace> dstCS = keyContext.dstColorInfo().refColorSpace();
- if (!dstCS) {
- dstCS = SkColorSpace::MakeSRGB();
- }
-
- SkAlphaType workingAT;
- sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
-
- ColorSpaceTransformBlock::ColorSpaceTransformData data1(
- dstCS.get(), dstAT, workingCS.get(), workingAT);
- ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data1);
- builder->endBlock();
-
- as_CFB(fChild)->addToKey(keyContext, builder, gatherer);
-
- ColorSpaceTransformBlock::ColorSpaceTransformData data2(
- workingCS.get(), workingAT, dstCS.get(), dstAT);
- ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data2);
- builder->endBlock();
- }
-#endif
-
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
- sk_sp<SkColorSpace> dstCS = sk_ref_sp(rec.fDstCS);
-
- if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
-
- SkAlphaType workingAT;
- sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
-
- SkColorInfo dst = {rec.fDstColorType, kPremul_SkAlphaType, dstCS},
- working = {rec.fDstColorType, workingAT, workingCS};
-
- const auto* dstToWorking = rec.fAlloc->make<SkColorSpaceXformSteps>(dst, working);
- const auto* workingToDst = rec.fAlloc->make<SkColorSpaceXformSteps>(working, dst);
-
- // Any SkSL effects might reference the paint color, which is already in the destination
- // color space. We need to transform it to the working space for consistency.
- SkColor4f paintColorInWorkingSpace = rec.fPaintColor;
- dstToWorking->apply(paintColorInWorkingSpace.vec());
-
- SkStageRec workingRec = {rec.fPipeline,
- rec.fAlloc,
- rec.fDstColorType,
- workingCS.get(),
- paintColorInWorkingSpace,
- rec.fSurfaceProps};
-
- dstToWorking->apply(rec.fPipeline);
- if (!as_CFB(fChild)->appendStages(workingRec, shaderIsOpaque)) {
- return false;
- }
- workingToDst->apply(rec.fPipeline);
- return true;
- }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& rawDst,
- skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
- sk_sp<SkColorSpace> dstCS = rawDst.refColorSpace();
- if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
-
- SkAlphaType workingAT;
- sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
-
- SkColorInfo dst = {rawDst.colorType(), kPremul_SkAlphaType, dstCS},
- working = {rawDst.colorType(), workingAT, workingCS};
-
- c = SkColorSpaceXformSteps{dst,working}.program(p, uniforms, c);
- c = as_CFB(fChild)->program(p, c, working, uniforms, alloc);
- return c ? SkColorSpaceXformSteps{working,dst}.program(p, uniforms, c)
- : c;
- }
-#endif
-
- SkPMColor4f onFilterColor4f(const SkPMColor4f& origColor,
- SkColorSpace* rawDstCS) const override {
- sk_sp<SkColorSpace> dstCS = sk_ref_sp(rawDstCS);
- if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
-
- SkAlphaType workingAT;
- sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
-
- SkColorInfo dst = {kUnknown_SkColorType, kPremul_SkAlphaType, dstCS},
- working = {kUnknown_SkColorType, workingAT, workingCS};
-
- SkPMColor4f color = origColor;
- SkColorSpaceXformSteps{dst,working}.apply(color.vec());
- color = as_CFB(fChild)->onFilterColor4f(color, working.colorSpace());
- SkColorSpaceXformSteps{working,dst}.apply(color.vec());
- return color;
- }
-
- bool onIsAlphaUnchanged() const override { return fChild->isAlphaUnchanged(); }
-
-private:
- friend void ::SkRegisterWorkingFormatColorFilterFlattenable();
- SK_FLATTENABLE_HOOKS(SkWorkingFormatColorFilter)
-
- void flatten(SkWriteBuffer& buffer) const override {
- buffer.writeFlattenable(fChild.get());
- buffer.writeBool(fUseDstTF);
- buffer.writeBool(fUseDstGamut);
- buffer.writeBool(fUseDstAT);
- if (!fUseDstTF) { buffer.writeScalarArray(&fTF.g, 7); }
- if (!fUseDstGamut) { buffer.writeScalarArray(&fGamut.vals[0][0], 9); }
- if (!fUseDstAT) { buffer.writeInt(fAT); }
- }
-
- sk_sp<SkColorFilter> fChild;
- skcms_TransferFunction fTF; bool fUseDstTF = true;
- skcms_Matrix3x3 fGamut; bool fUseDstGamut = true;
- SkAlphaType fAT; bool fUseDstAT = true;
-};
-
-sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) {
- sk_sp<SkColorFilter> child = buffer.readColorFilter();
- bool useDstTF = buffer.readBool(),
- useDstGamut = buffer.readBool(),
- useDstAT = buffer.readBool();
-
- skcms_TransferFunction tf;
- skcms_Matrix3x3 gamut;
- SkAlphaType at;
-
- if (!useDstTF) { buffer.readScalarArray(&tf.g, 7); }
- if (!useDstGamut) { buffer.readScalarArray(&gamut.vals[0][0], 9); }
- if (!useDstAT) { at = buffer.read32LE(kLastEnum_SkAlphaType); }
-
- return SkColorFilterPriv::WithWorkingFormat(std::move(child),
- useDstTF ? nullptr : &tf,
- useDstGamut ? nullptr : &gamut,
- useDstAT ? nullptr : &at);
-}
-
-sk_sp<SkColorFilter> SkColorFilterPriv::WithWorkingFormat(sk_sp<SkColorFilter> child,
- const skcms_TransferFunction* tf,
- const skcms_Matrix3x3* gamut,
- const SkAlphaType* at) {
- return sk_make_sp<SkWorkingFormatColorFilter>(std::move(child), tf, gamut, at);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0,
sk_sp<SkColorFilter> cf1) {
#ifdef SK_ENABLE_SKSL
@@ -604,15 +176,15 @@
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();
+ 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};
@@ -624,19 +196,3 @@
#endif
}
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-void SkRegisterComposeColorFilterFlattenable() {
- SK_REGISTER_FLATTENABLE(SkComposeColorFilter);
-}
-
-void SkRegisterColorSpaceXformColorFilterFlattenable() {
- SK_REGISTER_FLATTENABLE(ColorSpaceXformColorFilter);
- // TODO(ccameron): Remove after grace period for SKPs to stop using old serialization.
- SkFlattenable::Register("SkSRGBGammaColorFilter",
- ColorSpaceXformColorFilter::LegacyGammaOnlyCreateProc);
-}
-
-void SkRegisterWorkingFormatColorFilterFlattenable() {
- SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter);
-}
diff --git a/src/core/SkColorFilterBase.h b/src/core/SkColorFilterBase.h
index a7eaae8..d7f0780 100644
--- a/src/core/SkColorFilterBase.h
+++ b/src/core/SkColorFilterBase.h
@@ -33,6 +33,16 @@
class PipelineDataGatherer;
}
+#define SK_ALL_COLOR_FILTERS(M) \
+ M(BlendMode) \
+ M(ColorSpaceXform) \
+ M(Compose) \
+ M(Gaussian) \
+ M(Matrix) \
+ M(Runtime) \
+ M(Table) \
+ M(WorkingFormat)
+
class SkColorFilterBase : public SkColorFilter {
public:
SK_WARN_UNUSED_RESULT
@@ -48,21 +58,16 @@
*/
virtual bool onIsAlphaUnchanged() const { return false; }
-#if defined(SK_GANESH)
- /**
- * A subclass may implement this factory function to work with the GPU backend. It returns
- * a GrFragmentProcessor that implements the color filter in GPU shader code.
- *
- * The fragment processor receives a input FP that generates a premultiplied input color, and
- * produces a premultiplied output color.
- *
- * A GrFPFailure indicates that the color filter isn't implemented for the GPU backend.
- */
- virtual GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo& dstColorInfo,
- const SkSurfaceProps& props) const;
-#endif
+ enum class Type {
+ // Used for stubs/tests
+ kNoop,
+#define M(type) k##type,
+ SK_ALL_COLOR_FILTERS(M)
+#undef M
+
+ };
+
+ virtual Type type() const = 0;
bool affectsTransparentBlack() const {
return this->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT;
@@ -134,11 +139,10 @@
return sk_sp<SkColorFilterBase>(static_cast<SkColorFilterBase*>(filter.release()));
}
-
void SkRegisterComposeColorFilterFlattenable();
void SkRegisterMatrixColorFilterFlattenable();
void SkRegisterModeColorFilterFlattenable();
-void SkRegisterColorSpaceXformColorFilterFlattenable();
+void SkRegisterSkColorSpaceXformColorFilterFlattenable();
void SkRegisterTableColorFilterFlattenable();
void SkRegisterWorkingFormatColorFilterFlattenable();
diff --git a/src/core/SkColorFilter_Matrix.cpp b/src/core/SkColorFilter_Matrix.cpp
deleted file mode 100644
index b71c422..0000000
--- a/src/core/SkColorFilter_Matrix.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/core/SkRefCnt.h"
-#include "include/core/SkUnPreMultiply.h"
-#include "include/effects/SkColorMatrix.h"
-#include "include/effects/SkRuntimeEffect.h"
-#include "include/private/SkColorData.h"
-#include "src/core/SkColorFilterBase.h"
-#include "src/core/SkColorSpacePriv.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/core/SkVM.h"
-#include "src/core/SkWriteBuffer.h"
-
-#if defined(SK_GRAPHITE)
-#include "src/gpu/graphite/KeyHelpers.h"
-#include "src/gpu/graphite/PaintParamsKey.h"
-#endif // SK_GRAPHITE
-
-static bool is_alpha_unchanged(const float matrix[20]) {
- const float* srcA = matrix + 15;
-
- return SkScalarNearlyZero (srcA[0])
- && SkScalarNearlyZero (srcA[1])
- && SkScalarNearlyZero (srcA[2])
- && SkScalarNearlyEqual(srcA[3], 1)
- && SkScalarNearlyZero (srcA[4]);
-}
-
-class SkColorFilter_Matrix final : public SkColorFilterBase {
-public:
- enum class Domain : uint8_t { kRGBA, kHSLA };
-
- explicit SkColorFilter_Matrix(const float array[20], Domain);
-
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
-
- bool onIsAlphaUnchanged() const override { return fAlphaIsUnchanged; }
-
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*,
- const GrColorInfo&,
- const SkSurfaceProps&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-private:
- friend void ::SkRegisterMatrixColorFilterFlattenable();
- SK_FLATTENABLE_HOOKS(SkColorFilter_Matrix)
-
- void flatten(SkWriteBuffer&) const override;
- bool onAsAColorMatrix(float matrix[20]) const override;
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder*, skvm::Color,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
-#endif
-
- float fMatrix[20];
- bool fAlphaIsUnchanged;
- Domain fDomain;
-};
-
-SkColorFilter_Matrix::SkColorFilter_Matrix(const float array[20], Domain domain)
- : fAlphaIsUnchanged(is_alpha_unchanged(array))
- , fDomain(domain) {
- memcpy(fMatrix, array, 20 * sizeof(float));
-}
-
-void SkColorFilter_Matrix::flatten(SkWriteBuffer& buffer) const {
- SkASSERT(sizeof(fMatrix)/sizeof(float) == 20);
- buffer.writeScalarArray(fMatrix, 20);
-
- // RGBA flag
- buffer.writeBool(fDomain == Domain::kRGBA);
-}
-
-sk_sp<SkFlattenable> SkColorFilter_Matrix::CreateProc(SkReadBuffer& buffer) {
- float matrix[20];
- if (!buffer.readScalarArray(matrix, 20)) {
- return nullptr;
- }
-
- auto is_rgba = buffer.readBool();
- return is_rgba ? SkColorFilters::Matrix(matrix)
- : SkColorFilters::HSLAMatrix(matrix);
-}
-
-bool SkColorFilter_Matrix::onAsAColorMatrix(float matrix[20]) const {
- if (matrix) {
- memcpy(matrix, fMatrix, 20 * sizeof(float));
- }
- return true;
-}
-
-bool SkColorFilter_Matrix::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
- const bool willStayOpaque = shaderIsOpaque && fAlphaIsUnchanged,
- hsla = fDomain == Domain::kHSLA;
-
- SkRasterPipeline* p = rec.fPipeline;
- if (!shaderIsOpaque) { p->append(SkRasterPipelineOp::unpremul); }
- if ( hsla) { p->append(SkRasterPipelineOp::rgb_to_hsl); }
- if ( true) { p->append(SkRasterPipelineOp::matrix_4x5, fMatrix); }
- if ( hsla) { p->append(SkRasterPipelineOp::hsl_to_rgb); }
- if ( true) { p->append(SkRasterPipelineOp::clamp_01); }
- if (!willStayOpaque) { p->append(SkRasterPipelineOp::premul); }
- return true;
-}
-
-#if defined(SK_ENABLE_SKVM)
-skvm::Color SkColorFilter_Matrix::onProgram(skvm::Builder* p, skvm::Color c,
- const SkColorInfo& /*dst*/,
- skvm::Uniforms* uniforms, SkArenaAlloc*) const {
- auto apply_matrix = [&](auto xyzw) {
- auto dot = [&](int j) {
- auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
- // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
- // While looking, also simplify f == ±1. Anything else becomes a uniform.
- return f == 0.0f ? a
- : f == +1.0f ? a + m
- : f == -1.0f ? a - m
- : m * p->uniformF(uniforms->pushF(f)) + a;
- };
-
- // Similarly, let skvm::Builder fold away the additive bias when zero.
- const float b = fMatrix[4+j*5];
- skvm::F32 bias = b == 0.0f ? p->splat(0.0f)
- : p->uniformF(uniforms->pushF(b));
-
- auto [x,y,z,w] = xyzw;
- return custom_mad(fMatrix[0+j*5], x,
- custom_mad(fMatrix[1+j*5], y,
- custom_mad(fMatrix[2+j*5], z,
- custom_mad(fMatrix[3+j*5], w, bias))));
- };
- return std::make_tuple(dot(0), dot(1), dot(2), dot(3));
- };
-
- c = unpremul(c);
-
- if (fDomain == Domain::kHSLA) {
- auto [h,s,l,a] = apply_matrix(p->to_hsla(c));
- c = p->to_rgba({h,s,l,a});
- } else {
- auto [r,g,b,a] = apply_matrix(c);
- c = {r,g,b,a};
- }
-
- return premul(clamp01(c));
-}
-#endif
-
-#if defined(SK_GANESH)
-#include "src/gpu/ganesh/effects/GrSkSLFP.h"
-
-static std::unique_ptr<GrFragmentProcessor> rgb_to_hsl(std::unique_ptr<GrFragmentProcessor> child) {
- static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
- "half4 main(half4 color) {"
- "return $rgb_to_hsl(color.rgb, color.a);"
- "}"
- );
- SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
- return GrSkSLFP::Make(effect, "RgbToHsl", std::move(child),
- GrSkSLFP::OptFlags::kPreservesOpaqueInput);
-}
-
-static std::unique_ptr<GrFragmentProcessor> hsl_to_rgb(std::unique_ptr<GrFragmentProcessor> child) {
- static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
- "half4 main(half4 color) {"
- "return $hsl_to_rgb(color.rgb, color.a);"
- "}"
- );
- SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
- return GrSkSLFP::Make(effect, "HslToRgb", std::move(child),
- GrSkSLFP::OptFlags::kPreservesOpaqueInput);
-}
-
-GrFPResult SkColorFilter_Matrix::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> fp,
- GrRecordingContext*,
- const GrColorInfo&,
- const SkSurfaceProps&) const {
- switch (fDomain) {
- case Domain::kRGBA:
- fp = GrFragmentProcessor::ColorMatrix(std::move(fp), fMatrix,
- /* unpremulInput = */ true,
- /* clampRGBOutput = */ true,
- /* premulOutput = */ true);
- break;
-
- case Domain::kHSLA:
- fp = rgb_to_hsl(std::move(fp));
- fp = GrFragmentProcessor::ColorMatrix(std::move(fp), fMatrix,
- /* unpremulInput = */ false,
- /* clampRGBOutput = */ false,
- /* premulOutput = */ false);
- fp = hsl_to_rgb(std::move(fp));
- break;
- }
-
- return GrFPSuccess(std::move(fp));
-}
-#endif // defined(SK_GANESH)
-
-#if defined(SK_GRAPHITE)
-void SkColorFilter_Matrix::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
- using namespace skgpu::graphite;
-
- MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(fMatrix,
- fDomain == Domain::kHSLA);
-
- MatrixColorFilterBlock::BeginBlock(keyContext, builder, gatherer, &matrixCFData);
- builder->endBlock();
-}
-#endif // SK_GRAPHITE
-
-///////////////////////////////////////////////////////////////////////////////
-
-static sk_sp<SkColorFilter> MakeMatrix(const float array[20],
- SkColorFilter_Matrix::Domain domain) {
- if (!sk_floats_are_finite(array, 20)) {
- return nullptr;
- }
- return sk_make_sp<SkColorFilter_Matrix>(array, domain);
-}
-
-sk_sp<SkColorFilter> SkColorFilters::Matrix(const float array[20]) {
- return MakeMatrix(array, SkColorFilter_Matrix::Domain::kRGBA);
-}
-
-sk_sp<SkColorFilter> SkColorFilters::Matrix(const SkColorMatrix& cm) {
- return MakeMatrix(cm.fMat.data(), SkColorFilter_Matrix::Domain::kRGBA);
-}
-
-sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const float array[20]) {
- return MakeMatrix(array, SkColorFilter_Matrix::Domain::kHSLA);
-}
-
-sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const SkColorMatrix& cm) {
- return MakeMatrix(cm.fMat.data(), SkColorFilter_Matrix::Domain::kHSLA);
-}
-
-void SkRegisterMatrixColorFilterFlattenable() {
- SK_REGISTER_FLATTENABLE(SkColorFilter_Matrix);
-}
diff --git a/src/core/SkColorSpaceXformColorFilter.cpp b/src/core/SkColorSpaceXformColorFilter.cpp
new file mode 100644
index 0000000..a914e55
--- /dev/null
+++ b/src/core/SkColorSpaceXformColorFilter.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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/core/SkColorSpaceXformColorFilter.h"
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkData.h"
+#include "include/core/SkRefCnt.h"
+#include "src/base/SkNoDestructor.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkColorFilterPriv.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+#include <cstdint>
+#include <utility>
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+SkColorSpaceXformColorFilter::SkColorSpaceXformColorFilter(sk_sp<SkColorSpace> src,
+ sk_sp<SkColorSpace> dst)
+ : fSrc(std::move(src))
+ , fDst(std::move(dst))
+ , fSteps( // We handle premul/unpremul separately, so here just always upm->upm.
+ fSrc.get(),
+ kUnpremul_SkAlphaType,
+ fDst.get(),
+ kUnpremul_SkAlphaType) {}
+
+#if defined(SK_GRAPHITE)
+void SkColorSpaceXformColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
+ ColorSpaceTransformBlock::ColorSpaceTransformData data(
+ fSrc.get(), alphaType, fDst.get(), alphaType);
+ ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
+ builder->endBlock();
+}
+#endif
+
+bool SkColorSpaceXformColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ if (!shaderIsOpaque) {
+ rec.fPipeline->append(SkRasterPipelineOp::unpremul);
+ }
+
+ fSteps.apply(rec.fPipeline);
+
+ if (!shaderIsOpaque) {
+ rec.fPipeline->append(SkRasterPipelineOp::premul);
+ }
+ return true;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkColorSpaceXformColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ return premul(fSteps.program(p, uniforms, unpremul(c)));
+}
+#endif
+
+void SkColorSpaceXformColorFilter::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeDataAsByteArray(fSrc->serialize().get());
+ buffer.writeDataAsByteArray(fDst->serialize().get());
+}
+
+sk_sp<SkFlattenable> SkColorSpaceXformColorFilter::LegacyGammaOnlyCreateProc(SkReadBuffer& buffer) {
+ uint32_t dir = buffer.read32();
+ if (!buffer.validate(dir <= 1)) {
+ return nullptr;
+ }
+ if (dir == 0) {
+ return SkColorFilters::LinearToSRGBGamma();
+ }
+ return SkColorFilters::SRGBToLinearGamma();
+}
+
+sk_sp<SkFlattenable> SkColorSpaceXformColorFilter::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkColorSpace> colorSpaces[2];
+ for (int i = 0; i < 2; ++i) {
+ auto data = buffer.readByteArrayAsData();
+ if (!buffer.validate(data != nullptr)) {
+ return nullptr;
+ }
+ colorSpaces[i] = SkColorSpace::Deserialize(data->data(), data->size());
+ if (!buffer.validate(colorSpaces[i] != nullptr)) {
+ return nullptr;
+ }
+ }
+ return sk_sp<SkFlattenable>(
+ new SkColorSpaceXformColorFilter(std::move(colorSpaces[0]), std::move(colorSpaces[1])));
+}
+
+sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() {
+ static SkNoDestructor<SkColorSpaceXformColorFilter> gSingleton(SkColorSpace::MakeSRGBLinear(),
+ SkColorSpace::MakeSRGB());
+ return sk_ref_sp(gSingleton.get());
+}
+
+sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() {
+ static SkNoDestructor<SkColorSpaceXformColorFilter> gSingleton(SkColorSpace::MakeSRGB(),
+ SkColorSpace::MakeSRGBLinear());
+ return sk_ref_sp(gSingleton.get());
+}
+
+sk_sp<SkColorFilter> SkColorFilterPriv::MakeColorSpaceXform(sk_sp<SkColorSpace> src,
+ sk_sp<SkColorSpace> dst) {
+ return sk_make_sp<SkColorSpaceXformColorFilter>(std::move(src), std::move(dst));
+}
+
+void SkRegisterSkColorSpaceXformColorFilterFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkColorSpaceXformColorFilter);
+ // Previous name
+ SkFlattenable::Register("ColorSpaceXformColorFilter", SkColorSpaceXformColorFilter::CreateProc);
+ // TODO(ccameron): Remove after grace period for SKPs to stop using old serialization.
+ SkFlattenable::Register("SkSRGBGammaColorFilter",
+ SkColorSpaceXformColorFilter::LegacyGammaOnlyCreateProc);
+}
diff --git a/src/core/SkColorSpaceXformColorFilter.h b/src/core/SkColorSpaceXformColorFilter.h
new file mode 100644
index 0000000..d1f8f7f
--- /dev/null
+++ b/src/core/SkColorSpaceXformColorFilter.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkColorSpaceXformColorFilter_DEFINED
+#define SkColorSpaceXformColorFilter_DEFINED
+
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+class SkColorSpaceXformColorFilter final : public SkColorFilterBase {
+public:
+ SkColorSpaceXformColorFilter(sk_sp<SkColorSpace> src, sk_sp<SkColorSpace> dst);
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const override;
+#endif
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+ SkColorFilterBase::Type type() const override {
+ return SkColorFilterBase::Type::kColorSpaceXform;
+ }
+
+ sk_sp<SkColorSpace> src() const { return fSrc; }
+ sk_sp<SkColorSpace> dst() const { return fDst; }
+
+protected:
+ void flatten(SkWriteBuffer& buffer) const override;
+
+private:
+ friend void ::SkRegisterSkColorSpaceXformColorFilterFlattenable();
+ SK_FLATTENABLE_HOOKS(SkColorSpaceXformColorFilter)
+ static sk_sp<SkFlattenable> LegacyGammaOnlyCreateProc(SkReadBuffer& buffer);
+
+ const sk_sp<SkColorSpace> fSrc;
+ const sk_sp<SkColorSpace> fDst;
+ SkColorSpaceXformSteps fSteps;
+
+ friend class SkColorFilter;
+ using INHERITED = SkColorFilterBase;
+};
+
+#endif
diff --git a/src/core/SkComposeColorFilter.cpp b/src/core/SkComposeColorFilter.cpp
new file mode 100644
index 0000000..5697add
--- /dev/null
+++ b/src/core/SkComposeColorFilter.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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/core/SkComposeColorFilter.h"
+
+#include "include/core/SkRefCnt.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+#include <utility>
+struct SkStageRec;
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+SkComposeColorFilter::SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner)
+ : fOuter(as_CFB_sp(std::move(outer))), fInner(as_CFB_sp(std::move(inner))) {}
+
+bool SkComposeColorFilter::onIsAlphaUnchanged() const {
+ // Can only claim alphaunchanged support if both our proxys do.
+ return fOuter->isAlphaUnchanged() && fInner->isAlphaUnchanged();
+}
+
+bool SkComposeColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ bool innerIsOpaque = shaderIsOpaque;
+ if (!fInner->isAlphaUnchanged()) {
+ innerIsOpaque = false;
+ }
+ return fInner->appendStages(rec, shaderIsOpaque) && fOuter->appendStages(rec, innerIsOpaque);
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkComposeColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ c = fInner->program(p, c, dst, uniforms, alloc);
+ return c ? fOuter->program(p, c, dst, uniforms, alloc) : skvm::Color{};
+}
+#endif
+
+#if defined(SK_GRAPHITE)
+void SkComposeColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ ComposeColorFilterBlock::BeginBlock(keyContext, builder, gatherer);
+
+ as_CFB(fInner)->addToKey(keyContext, builder, gatherer);
+ as_CFB(fOuter)->addToKey(keyContext, builder, gatherer);
+
+ builder->endBlock();
+}
+#endif // SK_GRAPHITE
+
+void SkComposeColorFilter::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeFlattenable(fOuter.get());
+ buffer.writeFlattenable(fInner.get());
+}
+
+sk_sp<SkFlattenable> SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkColorFilter> outer(buffer.readColorFilter());
+ sk_sp<SkColorFilter> inner(buffer.readColorFilter());
+ return outer ? outer->makeComposed(std::move(inner)) : inner;
+}
+
+sk_sp<SkColorFilter> SkColorFilter::makeComposed(sk_sp<SkColorFilter> inner) const {
+ if (!inner) {
+ return sk_ref_sp(this);
+ }
+
+ return sk_sp<SkColorFilter>(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner)));
+}
+
+void SkRegisterComposeColorFilterFlattenable() { SK_REGISTER_FLATTENABLE(SkComposeColorFilter); }
diff --git a/src/core/SkComposeColorFilter.h b/src/core/SkComposeColorFilter.h
new file mode 100644
index 0000000..42cb8bb
--- /dev/null
+++ b/src/core/SkComposeColorFilter.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkComposeColorFilter_DEFINED
+#define SkComposeColorFilter_DEFINED
+
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "src/core/SkColorFilterBase.h"
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+class SkComposeColorFilter final : public SkColorFilterBase {
+public:
+ bool onIsAlphaUnchanged() const override;
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kCompose; }
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const override;
+#endif // SK_GRAPHITE
+
+ sk_sp<SkColorFilterBase> outer() const { return fOuter; }
+ sk_sp<SkColorFilterBase> inner() const { return fInner; }
+
+protected:
+ void flatten(SkWriteBuffer& buffer) const override;
+
+private:
+ friend void ::SkRegisterComposeColorFilterFlattenable();
+ SK_FLATTENABLE_HOOKS(SkComposeColorFilter)
+
+ SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner);
+
+ sk_sp<SkColorFilterBase> fOuter;
+ sk_sp<SkColorFilterBase> fInner;
+
+ friend class SkColorFilter;
+
+ using INHERITED = SkColorFilter;
+};
+
+#endif
diff --git a/src/core/SkMatrixColorFilter.cpp b/src/core/SkMatrixColorFilter.cpp
new file mode 100644
index 0000000..9712011
--- /dev/null
+++ b/src/core/SkMatrixColorFilter.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/core/SkMatrixColorFilter.h"
+
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/effects/SkColorMatrix.h"
+#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkFloatingPoint.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+#include <array>
+#include <cstring>
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif // SK_GRAPHITE
+
+static bool is_alpha_unchanged(const float matrix[20]) {
+ const float* srcA = matrix + 15;
+
+ return SkScalarNearlyZero(srcA[0]) && SkScalarNearlyZero(srcA[1]) &&
+ SkScalarNearlyZero(srcA[2]) && SkScalarNearlyEqual(srcA[3], 1) &&
+ SkScalarNearlyZero(srcA[4]);
+}
+
+SkMatrixColorFilter::SkMatrixColorFilter(const float array[20], Domain domain)
+ : fAlphaIsUnchanged(is_alpha_unchanged(array)), fDomain(domain) {
+ memcpy(fMatrix, array, 20 * sizeof(float));
+}
+
+void SkMatrixColorFilter::flatten(SkWriteBuffer& buffer) const {
+ SkASSERT(sizeof(fMatrix) / sizeof(float) == 20);
+ buffer.writeScalarArray(fMatrix, 20);
+
+ // RGBA flag
+ buffer.writeBool(fDomain == Domain::kRGBA);
+}
+
+sk_sp<SkFlattenable> SkMatrixColorFilter::CreateProc(SkReadBuffer& buffer) {
+ float matrix[20];
+ if (!buffer.readScalarArray(matrix, 20)) {
+ return nullptr;
+ }
+
+ auto is_rgba = buffer.readBool();
+ return is_rgba ? SkColorFilters::Matrix(matrix) : SkColorFilters::HSLAMatrix(matrix);
+}
+
+bool SkMatrixColorFilter::onAsAColorMatrix(float matrix[20]) const {
+ if (matrix) {
+ memcpy(matrix, fMatrix, 20 * sizeof(float));
+ }
+ return true;
+}
+
+bool SkMatrixColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ const bool willStayOpaque = shaderIsOpaque && fAlphaIsUnchanged,
+ hsla = fDomain == Domain::kHSLA;
+
+ SkRasterPipeline* p = rec.fPipeline;
+ if (!shaderIsOpaque) {
+ p->append(SkRasterPipelineOp::unpremul);
+ }
+ if (hsla) {
+ p->append(SkRasterPipelineOp::rgb_to_hsl);
+ }
+ if (true) {
+ p->append(SkRasterPipelineOp::matrix_4x5, fMatrix);
+ }
+ if (hsla) {
+ p->append(SkRasterPipelineOp::hsl_to_rgb);
+ }
+ if (true) {
+ p->append(SkRasterPipelineOp::clamp_01);
+ }
+ if (!willStayOpaque) {
+ p->append(SkRasterPipelineOp::premul);
+ }
+ return true;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkMatrixColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& /*dst*/,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const {
+ auto apply_matrix = [&](auto xyzw) {
+ auto dot = [&](int j) {
+ auto custom_mad = [&](float f, skvm::F32 m, skvm::F32 a) {
+ // skvm::Builder won't fold f*0 == 0, but we shouldn't encounter NaN here.
+ // While looking, also simplify f == ±1. Anything else becomes a uniform.
+ return f == 0.0f
+ ? a
+ : f == +1.0f ? a + m
+ : f == -1.0f ? a - m
+ : m * p->uniformF(uniforms->pushF(f)) + a;
+ };
+
+ // Similarly, let skvm::Builder fold away the additive bias when zero.
+ const float b = fMatrix[4 + j * 5];
+ skvm::F32 bias = b == 0.0f ? p->splat(0.0f) : p->uniformF(uniforms->pushF(b));
+
+ auto [x, y, z, w] = xyzw;
+ return custom_mad(fMatrix[0 + j * 5],
+ x,
+ custom_mad(fMatrix[1 + j * 5],
+ y,
+ custom_mad(fMatrix[2 + j * 5],
+ z,
+ custom_mad(fMatrix[3 + j * 5], w, bias))));
+ };
+ return std::make_tuple(dot(0), dot(1), dot(2), dot(3));
+ };
+
+ c = unpremul(c);
+
+ if (fDomain == Domain::kHSLA) {
+ auto [h, s, l, a] = apply_matrix(p->to_hsla(c));
+ c = p->to_rgba({h, s, l, a});
+ } else {
+ auto [r, g, b, a] = apply_matrix(c);
+ c = {r, g, b, a};
+ }
+
+ return premul(clamp01(c));
+}
+#endif
+
+#if defined(SK_GRAPHITE)
+void SkMatrixColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(fMatrix, fDomain == Domain::kHSLA);
+
+ MatrixColorFilterBlock::BeginBlock(keyContext, builder, gatherer, &matrixCFData);
+ builder->endBlock();
+}
+#endif // SK_GRAPHITE
+
+///////////////////////////////////////////////////////////////////////////////
+
+static sk_sp<SkColorFilter> MakeMatrix(const float array[20], SkMatrixColorFilter::Domain domain) {
+ if (!sk_floats_are_finite(array, 20)) {
+ return nullptr;
+ }
+ return sk_make_sp<SkMatrixColorFilter>(array, domain);
+}
+
+sk_sp<SkColorFilter> SkColorFilters::Matrix(const float array[20]) {
+ return MakeMatrix(array, SkMatrixColorFilter::Domain::kRGBA);
+}
+
+sk_sp<SkColorFilter> SkColorFilters::Matrix(const SkColorMatrix& cm) {
+ return MakeMatrix(cm.fMat.data(), SkMatrixColorFilter::Domain::kRGBA);
+}
+
+sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const float array[20]) {
+ return MakeMatrix(array, SkMatrixColorFilter::Domain::kHSLA);
+}
+
+sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const SkColorMatrix& cm) {
+ return MakeMatrix(cm.fMat.data(), SkMatrixColorFilter::Domain::kHSLA);
+}
+
+void SkRegisterMatrixColorFilterFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkMatrixColorFilter);
+ // Previous name
+ SkFlattenable::Register("SkColorFilter_Matrix", SkMatrixColorFilter::CreateProc);
+}
diff --git a/src/core/SkMatrixColorFilter.h b/src/core/SkMatrixColorFilter.h
new file mode 100644
index 0000000..5b1cbe0
--- /dev/null
+++ b/src/core/SkMatrixColorFilter.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMatrixColorFilter_DEFINED
+#define SkMatrixColorFilter_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "src/core/SkColorFilterBase.h"
+
+#include <cstdint>
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif // SK_GRAPHITE
+
+class SkMatrixColorFilter final : public SkColorFilterBase {
+public:
+ enum class Domain : uint8_t { kRGBA, kHSLA };
+
+ explicit SkMatrixColorFilter(const float array[20], Domain);
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+ bool onIsAlphaUnchanged() const override { return fAlphaIsUnchanged; }
+
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kMatrix; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ Domain domain() const { return fDomain; }
+ const float* matrix() const { return fMatrix; }
+
+private:
+ friend void ::SkRegisterMatrixColorFilterFlattenable();
+ SK_FLATTENABLE_HOOKS(SkMatrixColorFilter)
+
+ void flatten(SkWriteBuffer&) const override;
+ bool onAsAColorMatrix(float matrix[20]) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder*,
+ skvm::Color,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const override;
+#endif
+
+ float fMatrix[20];
+ bool fAlphaIsUnchanged;
+ Domain fDomain;
+};
+
+#endif
diff --git a/src/core/SkModeColorFilter.cpp b/src/core/SkModeColorFilter.cpp
deleted file mode 100644
index 65b3baf..0000000
--- a/src/core/SkModeColorFilter.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * 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/private/SkColorData.h"
-#include "src/base/SkArenaAlloc.h"
-#include "src/core/SkBlendModePriv.h"
-#include "src/core/SkBlitRow.h"
-#include "src/core/SkColorFilterBase.h"
-#include "src/core/SkColorSpacePriv.h"
-#include "src/core/SkColorSpaceXformSteps.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkVM.h"
-#include "src/core/SkValidationUtils.h"
-#include "src/core/SkWriteBuffer.h"
-
-#if defined(SK_GRAPHITE)
-#include "src/gpu/graphite/KeyContext.h"
-#include "src/gpu/graphite/KeyHelpers.h"
-#include "src/gpu/graphite/PaintParamsKey.h"
-#endif
-
-template <SkAlphaType kDstAT = kPremul_SkAlphaType>
-static SkRGBA4f<kDstAT> map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
- SkRGBA4f<kDstAT> color = {c.fR, c.fG, c.fB, c.fA};
- SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType,
- dst, kDstAT).apply(color.vec());
- return color;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkModeColorFilter final : public SkColorFilterBase {
-public:
- SkModeColorFilter(const SkColor4f& color, SkBlendMode mode);
-
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
-
- bool onIsAlphaUnchanged() const override;
-
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*,
- const GrColorInfo&,
- const SkSurfaceProps&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-private:
- friend void ::SkRegisterModeColorFilterFlattenable();
- SK_FLATTENABLE_HOOKS(SkModeColorFilter)
-
- void flatten(SkWriteBuffer&) const override;
- bool onAsAColorMode(SkColor*, SkBlendMode*) const override;
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder*, skvm::Color,
- const SkColorInfo&, skvm::Uniforms*, SkArenaAlloc*) const override;
-#endif
-
- SkColor4f fColor; // always stored in sRGB
- SkBlendMode fMode;
-};
-
-SkModeColorFilter::SkModeColorFilter(const SkColor4f& color,
- SkBlendMode mode)
- : fColor(color)
- , fMode(mode) {}
-
-bool SkModeColorFilter::onAsAColorMode(SkColor* color, SkBlendMode* mode) const {
- if (color) {
- *color = fColor.toSkColor();
- }
- if (mode) {
- *mode = fMode;
- }
- return true;
-}
-
-bool SkModeColorFilter::onIsAlphaUnchanged() const {
- switch (fMode) {
- case SkBlendMode::kDst: //!< [Da, Dc]
- case SkBlendMode::kSrcATop: //!< [Da, Sc * Da + (1 - Sa) * Dc]
- return true;
- default:
- break;
- }
- return false;
-}
-
-void SkModeColorFilter::flatten(SkWriteBuffer& buffer) const {
- buffer.writeColor4f(fColor);
- buffer.writeUInt((int) fMode);
-}
-
-sk_sp<SkFlattenable> SkModeColorFilter::CreateProc(SkReadBuffer& buffer) {
- if (buffer.isVersionLT(SkPicturePriv::kBlend4fColorFilter)) {
- // Color is 8-bit, sRGB
- SkColor color = buffer.readColor();
- SkBlendMode mode = (SkBlendMode)buffer.readUInt();
- return SkColorFilters::Blend(SkColor4f::FromColor(color), /*sRGB*/nullptr, mode);
- } else {
- // Color is 32-bit, sRGB
- SkColor4f color;
- buffer.readColor4f(&color);
- SkBlendMode mode = (SkBlendMode)buffer.readUInt();
- return SkColorFilters::Blend(color, /*sRGB*/nullptr, mode);
- }
-}
-
-bool SkModeColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
- rec.fPipeline->append(SkRasterPipelineOp::move_src_dst);
- SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), rec.fDstCS);
- rec.fPipeline->append_constant_color(rec.fAlloc, color.vec());
- SkBlendMode_AppendStages(fMode, rec.fPipeline);
- return true;
-}
-
-#if defined(SK_ENABLE_SKVM)
-skvm::Color SkModeColorFilter::onProgram(skvm::Builder* p, skvm::Color c,
- const SkColorInfo& dstInfo,
- skvm::Uniforms* uniforms, SkArenaAlloc*) const {
- SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), dstInfo.colorSpace());
- // The blend program operates on this as if it were premul but the API takes an SkColor4f
- skvm::Color dst = c,
- src = p->uniformColor({color.fR, color.fG, color.fB, color.fA}, uniforms);
- return p->blend(fMode, src,dst);
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(SK_GANESH)
-#include "src/gpu/Blend.h"
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/SkGr.h"
-#include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
-
-GrFPResult SkModeColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*,
- const GrColorInfo& dstColorInfo,
- const SkSurfaceProps& props) const {
- if (fMode == SkBlendMode::kDst) {
- // If the blend mode is "dest," the blend color won't factor into it at all.
- // We can return the input FP as-is.
- return GrFPSuccess(std::move(inputFP));
- }
-
- SkDEBUGCODE(const bool fpHasConstIO = !inputFP || inputFP->hasConstantOutputForConstantInput();)
-
- SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), dstColorInfo.colorSpace());
-
- auto colorFP = GrFragmentProcessor::MakeColor(color);
- auto xferFP = GrBlendFragmentProcessor::Make(std::move(colorFP), std::move(inputFP), fMode);
-
- if (xferFP == nullptr) {
- // This is only expected to happen if the blend mode is "dest" and the input FP is null.
- // Since we already did an early-out in the "dest" blend mode case, we shouldn't get here.
- SkDEBUGFAIL("GrBlendFragmentProcessor::Make returned null unexpectedly");
- return GrFPFailure(nullptr);
- }
-
- // With a solid color input this should always be able to compute the blended color
- // (at least for coeff modes).
- // Occasionally, we even do better than we started; specifically, in "src" blend mode, we end up
- // ditching the input FP entirely, which turns a non-constant operation into a constant one.
- SkASSERT(fMode > SkBlendMode::kLastCoeffMode ||
- xferFP->hasConstantOutputForConstantInput() >= fpHasConstIO);
-
- return GrFPSuccess(std::move(xferFP));
-}
-
-#endif
-
-#if defined(SK_GRAPHITE)
-void SkModeColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
- using namespace skgpu::graphite;
-
- SkPMColor4f color = map_color(fColor, sk_srgb_singleton(),
- keyContext.dstColorInfo().colorSpace());
- AddColorBlendBlock(keyContext, builder, gatherer, fMode, color);
-}
-
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkColorFilter> SkColorFilters::Blend(const SkColor4f& color,
- sk_sp<SkColorSpace> colorSpace,
- SkBlendMode mode) {
- if (!SkIsValidMode(mode)) {
- return nullptr;
- }
-
- // First map to sRGB to simplify storage in the actual SkColorFilter instance, staying unpremul
- // until the final dst color space is known when actually filtering.
- SkColor4f srgb = map_color<kUnpremul_SkAlphaType>(
- color, colorSpace.get(), sk_srgb_singleton());
-
- // Next collapse some modes if possible
- float alpha = srgb.fA;
- if (SkBlendMode::kClear == mode) {
- srgb = SkColors::kTransparent;
- mode = SkBlendMode::kSrc;
- } else if (SkBlendMode::kSrcOver == mode) {
- if (0.f == alpha) {
- mode = SkBlendMode::kDst;
- } else if (1.f == alpha) {
- mode = SkBlendMode::kSrc;
- }
- // else just stay srcover
- }
-
- // Finally weed out combinations that are noops, and just return null
- if (SkBlendMode::kDst == mode ||
- (0.f == alpha && (SkBlendMode::kSrcOver == mode ||
- SkBlendMode::kDstOver == mode ||
- SkBlendMode::kDstOut == mode ||
- SkBlendMode::kSrcATop == mode ||
- SkBlendMode::kXor == mode ||
- SkBlendMode::kDarken == mode)) ||
- (1.f == alpha && SkBlendMode::kDstIn == mode)) {
- return nullptr;
- }
-
- return sk_sp<SkColorFilter>(new SkModeColorFilter(srgb, mode));
-}
-
-sk_sp<SkColorFilter> SkColorFilters::Blend(SkColor color, SkBlendMode mode) {
- return Blend(SkColor4f::FromColor(color), /*sRGB*/nullptr, mode);
-}
-
-void SkRegisterModeColorFilterFlattenable() {
- SK_REGISTER_FLATTENABLE(SkModeColorFilter);
-}
diff --git a/src/core/SkRuntimeColorFilter.cpp b/src/core/SkRuntimeColorFilter.cpp
new file mode 100644
index 0000000..8f39df2
--- /dev/null
+++ b/src/core/SkRuntimeColorFilter.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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/core/SkRuntimeColorFilter.h"
+
+#include "include/core/SkCapabilities.h"
+#include "include/core/SkData.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkString.h"
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/private/SkColorData.h"
+#include "include/private/SkSLSampleUsage.h"
+#include "include/private/base/SkDebug.h"
+#include "include/private/base/SkTArray.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkRuntimeEffectPriv.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/shaders/SkShaderBase.h"
+#include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
+
+#include <string>
+#include <utility>
+
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkFilterColorProgram.h"
+#endif
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+class SkColorSpace;
+
+SkRuntimeColorFilter::SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,
+ sk_sp<const SkData> uniforms,
+ SkSpan<SkRuntimeEffect::ChildPtr> children)
+ : fEffect(std::move(effect))
+ , fUniforms(std::move(uniforms))
+ , fChildren(children.begin(), children.end()) {}
+
+#if defined(SK_GRAPHITE)
+void SkRuntimeColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
+ fEffect->uniforms(), fUniforms, keyContext.dstColorInfo().colorSpace());
+ SkASSERT(uniforms);
+
+ RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, {fEffect, std::move(uniforms)});
+
+ SkRuntimeEffectPriv::AddChildrenToKey(
+ fChildren, fEffect->children(), keyContext, builder, gatherer);
+
+ builder->endBlock();
+}
+#endif
+
+bool SkRuntimeColorFilter::appendStages(const SkStageRec& rec, bool) const {
+#ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE
+ if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
+ // SkRP has support for many parts of #version 300 already, but for now, we restrict its
+ // usage in runtime effects to just #version 100.
+ return false;
+ }
+ if (const SkSL::RP::Program* program = fEffect->getRPProgram(/*debugTrace=*/nullptr)) {
+ SkSpan<const float> uniforms =
+ SkRuntimeEffectPriv::UniformsAsSpan(fEffect->uniforms(),
+ fUniforms,
+ /*alwaysCopyIntoAlloc=*/false,
+ rec.fDstCS,
+ rec.fAlloc);
+ SkShaderBase::MatrixRec matrix(SkMatrix::I());
+ matrix.markCTMApplied();
+ RuntimeEffectRPCallbacks callbacks(rec, matrix, fChildren, fEffect->fSampleUsages);
+ bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
+ return success;
+ }
+#endif
+ return false;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkRuntimeColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& colorInfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ SkASSERT(SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get()));
+
+ sk_sp<const SkData> inputs = SkRuntimeEffectPriv::TransformUniforms(
+ fEffect->uniforms(), fUniforms, colorInfo.colorSpace());
+ SkASSERT(inputs);
+
+ SkShaderBase::MatrixRec mRec(SkMatrix::I());
+ mRec.markTotalMatrixInvalid();
+ RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, mRec, c, colorInfo);
+ std::vector<skvm::Val> uniform =
+ SkRuntimeEffectPriv::MakeSkVMUniforms(p, uniforms, fEffect->uniformSize(), *inputs);
+
+ // There should be no way for the color filter to use device coords, but we need to supply
+ // something. (Uninitialized values can trigger asserts in skvm::Builder).
+ skvm::Coord zeroCoord = {p->splat(0.0f), p->splat(0.0f)};
+ return SkSL::ProgramToSkVM(*fEffect->fBaseProgram,
+ fEffect->fMain,
+ p,
+ /*debugTrace=*/nullptr,
+ SkSpan(uniform),
+ /*device=*/zeroCoord,
+ /*local=*/zeroCoord,
+ c,
+ c,
+ &callbacks);
+}
+#endif
+
+SkPMColor4f SkRuntimeColorFilter::onFilterColor4f(const SkPMColor4f& color,
+ SkColorSpace* dstCS) const {
+#if defined(SK_ENABLE_SKVM)
+ // Get the generic program for filtering a single color
+ if (const SkFilterColorProgram* program = fEffect->getFilterColorProgram()) {
+ // Get our specific uniform values
+ sk_sp<const SkData> inputs =
+ SkRuntimeEffectPriv::TransformUniforms(fEffect->uniforms(), fUniforms, dstCS);
+ SkASSERT(inputs);
+
+ auto evalChild = [&](int index, SkPMColor4f inColor) {
+ const auto& child = fChildren[index];
+
+ // SkFilterColorProgram::Make has guaranteed that any children will be color filters.
+ SkASSERT(!child.shader());
+ SkASSERT(!child.blender());
+ if (SkColorFilter* colorFilter = child.colorFilter()) {
+ return as_CFB(colorFilter)->onFilterColor4f(inColor, dstCS);
+ }
+ return inColor;
+ };
+
+ return program->eval(color, inputs->data(), evalChild);
+ }
+#endif
+ // We were unable to build a cached (per-effect) program. Use the base-class fallback,
+ // which builds a program for the specific filter instance.
+ return SkColorFilterBase::onFilterColor4f(color, dstCS);
+}
+
+bool SkRuntimeColorFilter::onIsAlphaUnchanged() const {
+#ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE
+ return fEffect->isAlphaUnchanged();
+#else
+ return fEffect->getFilterColorProgram() && fEffect->isAlphaUnchanged();
+#endif
+}
+
+void SkRuntimeColorFilter::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeString(fEffect->source().c_str());
+ buffer.writeDataAsByteArray(fUniforms.get());
+ SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren);
+}
+
+SkRuntimeEffect* SkRuntimeColorFilter::asRuntimeEffect() const { return fEffect.get(); }
+
+sk_sp<SkFlattenable> SkRuntimeColorFilter::CreateProc(SkReadBuffer& buffer) {
+ if (!buffer.validate(buffer.allowSkSL())) {
+ return nullptr;
+ }
+
+ SkString sksl;
+ buffer.readString(&sksl);
+ sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
+
+ auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, std::move(sksl));
+#if !SK_LENIENT_SKSL_DESERIALIZATION
+ if (!buffer.validate(effect != nullptr)) {
+ return nullptr;
+ }
+#endif
+
+ skia_private::STArray<4, SkRuntimeEffect::ChildPtr> children;
+ if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) {
+ return nullptr;
+ }
+
+#if SK_LENIENT_SKSL_DESERIALIZATION
+ if (!effect) {
+ SkDebugf("Serialized SkSL failed to compile. Ignoring/dropping SkSL color filter.\n");
+ return nullptr;
+ }
+#endif
+
+ return effect->makeColorFilter(std::move(uniforms), SkSpan(children));
+}
diff --git a/src/core/SkRuntimeColorFilter.h b/src/core/SkRuntimeColorFilter.h
new file mode 100644
index 0000000..2b0dd74
--- /dev/null
+++ b/src/core/SkRuntimeColorFilter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRuntimeColorFilter_DEFINED
+#define SkRuntimeColorFilter_DEFINED
+
+#include "include/core/SkData.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/private/SkColorData.h"
+#include "include/private/base/SkDebug.h"
+#include "include/private/base/SkSpan_impl.h"
+#include "src/core/SkColorFilterBase.h"
+
+#include <vector>
+
+class SkColorSpace;
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+class SkRuntimeColorFilter : public SkColorFilterBase {
+public:
+ SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,
+ sk_sp<const SkData> uniforms,
+ SkSpan<SkRuntimeEffect::ChildPtr> children);
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const override;
+#endif
+
+ bool appendStages(const SkStageRec& rec, bool) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& colorInfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+ SkPMColor4f onFilterColor4f(const SkPMColor4f& color, SkColorSpace* dstCS) const override;
+
+ bool onIsAlphaUnchanged() const override;
+
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ SkRuntimeEffect* asRuntimeEffect() const override;
+
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kRuntime; }
+
+ SK_FLATTENABLE_HOOKS(SkRuntimeColorFilter)
+
+ sk_sp<SkRuntimeEffect> effect() const { return fEffect; }
+ sk_sp<const SkData> uniforms() const { return fUniforms; }
+ std::vector<SkRuntimeEffect::ChildPtr> children() const { return fChildren; }
+
+private:
+ sk_sp<SkRuntimeEffect> fEffect;
+ sk_sp<const SkData> fUniforms;
+ std::vector<SkRuntimeEffect::ChildPtr> fChildren;
+};
+
+#endif
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index a2d3946..770f853 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -18,7 +18,6 @@
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSurface.h"
-#include "include/private/SkColorData.h"
#include "include/private/base/SkAlign.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkMutex.h"
@@ -40,6 +39,7 @@
#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkRuntimeBlender.h"
+#include "src/core/SkRuntimeColorFilter.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/core/SkStreamPriv.h"
#include "src/core/SkWriteBuffer.h"
@@ -69,7 +69,6 @@
#include <tuple>
class SkColorSpace;
-class SkSurfaceProps;
struct SkIPoint;
#if defined(SK_GANESH)
@@ -948,198 +947,6 @@
#endif // defined(SK_ENABLE_SKVM)
-class SkRuntimeColorFilter : public SkColorFilterBase {
-public:
- SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,
- sk_sp<const SkData> uniforms,
- SkSpan<SkRuntimeEffect::ChildPtr> children)
- : fEffect(std::move(effect))
- , fUniforms(std::move(uniforms))
- , fChildren(children.begin(), children.end()) {}
-
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo& colorInfo,
- const SkSurfaceProps& props) const override {
- sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
- fEffect->uniforms(),
- fUniforms,
- colorInfo.colorSpace());
- SkASSERT(uniforms);
-
- GrFPArgs childArgs(context, &colorInfo, props);
- return GrFragmentProcessors::make_effect_fp(fEffect,
- "runtime_color_filter",
- std::move(uniforms),
- std::move(inputFP),
- /*destColorFP=*/nullptr,
- SkSpan(fChildren),
- childArgs);
- }
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const override {
- using namespace skgpu::graphite;
-
- sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
- fEffect->uniforms(),
- fUniforms,
- keyContext.dstColorInfo().colorSpace());
- SkASSERT(uniforms);
-
- RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
- { fEffect, std::move(uniforms) });
-
- SkRuntimeEffectPriv::AddChildrenToKey(fChildren, fEffect->children(), keyContext, builder,
- gatherer);
-
- builder->endBlock();
- }
-#endif
-
- bool appendStages(const SkStageRec& rec, bool) const override {
-#ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE
- if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
- // SkRP has support for many parts of #version 300 already, but for now, we restrict its
- // usage in runtime effects to just #version 100.
- return false;
- }
- if (const SkSL::RP::Program* program = fEffect->getRPProgram(/*debugTrace=*/nullptr)) {
- SkSpan<const float> uniforms = SkRuntimeEffectPriv::UniformsAsSpan(
- fEffect->uniforms(),
- fUniforms,
- /*alwaysCopyIntoAlloc=*/false,
- rec.fDstCS,
- rec.fAlloc);
- SkShaderBase::MatrixRec matrix(SkMatrix::I());
- matrix.markCTMApplied();
- RuntimeEffectRPCallbacks callbacks(rec, matrix, fChildren, fEffect->fSampleUsages);
- bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
- return success;
- }
-#endif
- return false;
- }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
- const SkColorInfo& colorInfo,
- skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
- SkASSERT(SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(),
- fEffect.get()));
-
- sk_sp<const SkData> inputs = SkRuntimeEffectPriv::TransformUniforms(
- fEffect->uniforms(),
- fUniforms,
- colorInfo.colorSpace());
- SkASSERT(inputs);
-
- SkShaderBase::MatrixRec mRec(SkMatrix::I());
- mRec.markTotalMatrixInvalid();
- RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, mRec, c, colorInfo);
- std::vector<skvm::Val> uniform = SkRuntimeEffectPriv::MakeSkVMUniforms(
- p, uniforms, fEffect->uniformSize(), *inputs);
-
- // There should be no way for the color filter to use device coords, but we need to supply
- // something. (Uninitialized values can trigger asserts in skvm::Builder).
- skvm::Coord zeroCoord = { p->splat(0.0f), p->splat(0.0f) };
- return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p,/*debugTrace=*/nullptr,
- SkSpan(uniform), /*device=*/zeroCoord, /*local=*/zeroCoord,
- c, c, &callbacks);
- }
-#endif
-
- SkPMColor4f onFilterColor4f(const SkPMColor4f& color, SkColorSpace* dstCS) const override {
-#if defined(SK_ENABLE_SKVM)
- // Get the generic program for filtering a single color
- if (const SkFilterColorProgram* program = fEffect->getFilterColorProgram()) {
- // Get our specific uniform values
- sk_sp<const SkData> inputs = SkRuntimeEffectPriv::TransformUniforms(
- fEffect->uniforms(),
- fUniforms,
- dstCS);
- SkASSERT(inputs);
-
- auto evalChild = [&](int index, SkPMColor4f inColor) {
- const auto& child = fChildren[index];
-
- // SkFilterColorProgram::Make has guaranteed that any children will be color filters.
- SkASSERT(!child.shader());
- SkASSERT(!child.blender());
- if (SkColorFilter* colorFilter = child.colorFilter()) {
- return as_CFB(colorFilter)->onFilterColor4f(inColor, dstCS);
- }
- return inColor;
- };
-
- return program->eval(color, inputs->data(), evalChild);
- }
-#endif
- // We were unable to build a cached (per-effect) program. Use the base-class fallback,
- // which builds a program for the specific filter instance.
- return SkColorFilterBase::onFilterColor4f(color, dstCS);
- }
-
- bool onIsAlphaUnchanged() const override {
-#ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE
- return fEffect->isAlphaUnchanged();
-#else
- return fEffect->getFilterColorProgram() &&
- fEffect->isAlphaUnchanged();
-#endif
- }
-
- void flatten(SkWriteBuffer& buffer) const override {
- buffer.writeString(fEffect->source().c_str());
- buffer.writeDataAsByteArray(fUniforms.get());
- SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren);
- }
-
- SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
-
- SK_FLATTENABLE_HOOKS(SkRuntimeColorFilter)
-
-private:
- sk_sp<SkRuntimeEffect> fEffect;
- sk_sp<const SkData> fUniforms;
- std::vector<SkRuntimeEffect::ChildPtr> fChildren;
-};
-
-sk_sp<SkFlattenable> SkRuntimeColorFilter::CreateProc(SkReadBuffer& buffer) {
- if (!buffer.validate(buffer.allowSkSL())) {
- return nullptr;
- }
-
- SkString sksl;
- buffer.readString(&sksl);
- sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
-
- auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, std::move(sksl));
-#if !SK_LENIENT_SKSL_DESERIALIZATION
- if (!buffer.validate(effect != nullptr)) {
- return nullptr;
- }
-#endif
-
- STArray<4, SkRuntimeEffect::ChildPtr> children;
- if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) {
- return nullptr;
- }
-
-#if SK_LENIENT_SKSL_DESERIALIZATION
- if (!effect) {
- SkDebugf("Serialized SkSL failed to compile. Ignoring/dropping SkSL color filter.\n");
- return nullptr;
- }
-#endif
-
- return effect->makeColorFilter(std::move(uniforms), SkSpan(children));
-}
-
///////////////////////////////////////////////////////////////////////////////////////////////////
using UniformsCallback = SkRuntimeEffectPriv::UniformsCallback;
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index 284363e..710c4a7 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -53,6 +53,8 @@
return c;
}
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kNoop; }
+
bool appendStages(const SkStageRec&, bool) const override { return true; }
// Only created here, should never be flattened / unflattened.
diff --git a/src/core/SkWorkingFormatColorFilter.cpp b/src/core/SkWorkingFormatColorFilter.cpp
new file mode 100644
index 0000000..fefc7e2
--- /dev/null
+++ b/src/core/SkWorkingFormatColorFilter.cpp
@@ -0,0 +1,229 @@
+/*
+ * 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/core/SkWorkingFormatColorFilter.h"
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkColorType.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkRefCnt.h"
+#include "include/private/base/SkAssert.h"
+#include "modules/skcms/skcms.h"
+#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkColorFilterPriv.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+#include <utility>
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+SkWorkingFormatColorFilter::SkWorkingFormatColorFilter(sk_sp<SkColorFilter> child,
+ const skcms_TransferFunction* tf,
+ const skcms_Matrix3x3* gamut,
+ const SkAlphaType* at) {
+ fChild = std::move(child);
+ if (tf) {
+ fTF = *tf;
+ fUseDstTF = false;
+ }
+ if (gamut) {
+ fGamut = *gamut;
+ fUseDstGamut = false;
+ }
+ if (at) {
+ fAT = *at;
+ fUseDstAT = false;
+ }
+}
+
+sk_sp<SkColorSpace> SkWorkingFormatColorFilter::workingFormat(const sk_sp<SkColorSpace>& dstCS,
+ SkAlphaType* at) const {
+ skcms_TransferFunction tf = fTF;
+ skcms_Matrix3x3 gamut = fGamut;
+
+ if (fUseDstTF) {
+ SkAssertResult(dstCS->isNumericalTransferFn(&tf));
+ }
+ if (fUseDstGamut) {
+ SkAssertResult(dstCS->toXYZD50(&gamut));
+ }
+
+ *at = fUseDstAT ? kPremul_SkAlphaType : fAT;
+ return SkColorSpace::MakeRGB(tf, gamut);
+}
+
+#if defined(SK_GRAPHITE)
+void SkWorkingFormatColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ const SkAlphaType dstAT = keyContext.dstColorInfo().alphaType();
+ sk_sp<SkColorSpace> dstCS = keyContext.dstColorInfo().refColorSpace();
+ if (!dstCS) {
+ dstCS = SkColorSpace::MakeSRGB();
+ }
+
+ SkAlphaType workingAT;
+ sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
+
+ ColorSpaceTransformBlock::ColorSpaceTransformData data1(
+ dstCS.get(), dstAT, workingCS.get(), workingAT);
+ ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data1);
+ builder->endBlock();
+
+ as_CFB(fChild)->addToKey(keyContext, builder, gatherer);
+
+ ColorSpaceTransformBlock::ColorSpaceTransformData data2(
+ workingCS.get(), workingAT, dstCS.get(), dstAT);
+ ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data2);
+ builder->endBlock();
+}
+#endif
+
+bool SkWorkingFormatColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ sk_sp<SkColorSpace> dstCS = sk_ref_sp(rec.fDstCS);
+
+ if (!dstCS) {
+ dstCS = SkColorSpace::MakeSRGB();
+ }
+
+ SkAlphaType workingAT;
+ sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
+
+ SkColorInfo dst = {rec.fDstColorType, kPremul_SkAlphaType, dstCS},
+ working = {rec.fDstColorType, workingAT, workingCS};
+
+ const auto* dstToWorking = rec.fAlloc->make<SkColorSpaceXformSteps>(dst, working);
+ const auto* workingToDst = rec.fAlloc->make<SkColorSpaceXformSteps>(working, dst);
+
+ // Any SkSL effects might reference the paint color, which is already in the destination
+ // color space. We need to transform it to the working space for consistency.
+ SkColor4f paintColorInWorkingSpace = rec.fPaintColor;
+ dstToWorking->apply(paintColorInWorkingSpace.vec());
+
+ SkStageRec workingRec = {rec.fPipeline,
+ rec.fAlloc,
+ rec.fDstColorType,
+ workingCS.get(),
+ paintColorInWorkingSpace,
+ rec.fSurfaceProps};
+
+ dstToWorking->apply(rec.fPipeline);
+ if (!as_CFB(fChild)->appendStages(workingRec, shaderIsOpaque)) {
+ return false;
+ }
+ workingToDst->apply(rec.fPipeline);
+ return true;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkWorkingFormatColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& rawDst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ sk_sp<SkColorSpace> dstCS = rawDst.refColorSpace();
+ if (!dstCS) {
+ dstCS = SkColorSpace::MakeSRGB();
+ }
+
+ SkAlphaType workingAT;
+ sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
+
+ SkColorInfo dst = {rawDst.colorType(), kPremul_SkAlphaType, dstCS},
+ working = {rawDst.colorType(), workingAT, workingCS};
+
+ c = SkColorSpaceXformSteps{dst, working}.program(p, uniforms, c);
+ c = as_CFB(fChild)->program(p, c, working, uniforms, alloc);
+ return c ? SkColorSpaceXformSteps{working, dst}.program(p, uniforms, c) : c;
+}
+#endif
+
+SkPMColor4f SkWorkingFormatColorFilter::onFilterColor4f(const SkPMColor4f& origColor,
+ SkColorSpace* rawDstCS) const {
+ sk_sp<SkColorSpace> dstCS = sk_ref_sp(rawDstCS);
+ if (!dstCS) {
+ dstCS = SkColorSpace::MakeSRGB();
+ }
+
+ SkAlphaType workingAT;
+ sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
+
+ SkColorInfo dst = {kUnknown_SkColorType, kPremul_SkAlphaType, dstCS},
+ working = {kUnknown_SkColorType, workingAT, workingCS};
+
+ SkPMColor4f color = origColor;
+ SkColorSpaceXformSteps{dst, working}.apply(color.vec());
+ color = as_CFB(fChild)->onFilterColor4f(color, working.colorSpace());
+ SkColorSpaceXformSteps{working, dst}.apply(color.vec());
+ return color;
+}
+
+bool SkWorkingFormatColorFilter::onIsAlphaUnchanged() const { return fChild->isAlphaUnchanged(); }
+
+void SkWorkingFormatColorFilter::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeFlattenable(fChild.get());
+ buffer.writeBool(fUseDstTF);
+ buffer.writeBool(fUseDstGamut);
+ buffer.writeBool(fUseDstAT);
+ if (!fUseDstTF) {
+ buffer.writeScalarArray(&fTF.g, 7);
+ }
+ if (!fUseDstGamut) {
+ buffer.writeScalarArray(&fGamut.vals[0][0], 9);
+ }
+ if (!fUseDstAT) {
+ buffer.writeInt(fAT);
+ }
+}
+
+sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkColorFilter> child = buffer.readColorFilter();
+ bool useDstTF = buffer.readBool(), useDstGamut = buffer.readBool(),
+ useDstAT = buffer.readBool();
+
+ skcms_TransferFunction tf;
+ skcms_Matrix3x3 gamut;
+ SkAlphaType at;
+
+ if (!useDstTF) {
+ buffer.readScalarArray(&tf.g, 7);
+ }
+ if (!useDstGamut) {
+ buffer.readScalarArray(&gamut.vals[0][0], 9);
+ }
+ if (!useDstAT) {
+ at = buffer.read32LE(kLastEnum_SkAlphaType);
+ }
+
+ return SkColorFilterPriv::WithWorkingFormat(std::move(child),
+ useDstTF ? nullptr : &tf,
+ useDstGamut ? nullptr : &gamut,
+ useDstAT ? nullptr : &at);
+}
+
+sk_sp<SkColorFilter> SkColorFilterPriv::WithWorkingFormat(sk_sp<SkColorFilter> child,
+ const skcms_TransferFunction* tf,
+ const skcms_Matrix3x3* gamut,
+ const SkAlphaType* at) {
+ return sk_make_sp<SkWorkingFormatColorFilter>(std::move(child), tf, gamut, at);
+}
+
+void SkRegisterWorkingFormatColorFilterFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter);
+}
diff --git a/src/core/SkWorkingFormatColorFilter.h b/src/core/SkWorkingFormatColorFilter.h
new file mode 100644
index 0000000..1ce1692
--- /dev/null
+++ b/src/core/SkWorkingFormatColorFilter.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkWorkingFormatColorFilter_DEFINED
+#define SkWorkingFormatColorFilter_DEFINED
+
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "include/private/SkColorData.h"
+#include "modules/skcms/skcms.h"
+#include "src/core/SkColorFilterBase.h"
+
+class SkColorSpace;
+class SkReadBuffer;
+class SkWriteBuffer;
+enum SkAlphaType : int;
+struct SkStageRec;
+
+class SkWorkingFormatColorFilter final : public SkColorFilterBase {
+public:
+ SkWorkingFormatColorFilter(sk_sp<SkColorFilter> child,
+ const skcms_TransferFunction* tf,
+ const skcms_Matrix3x3* gamut,
+ const SkAlphaType* at);
+
+ sk_sp<SkColorSpace> workingFormat(const sk_sp<SkColorSpace>& dstCS, SkAlphaType* at) const;
+
+ SkColorFilterBase::Type type() const override {
+ return SkColorFilterBase::Type::kWorkingFormat;
+ }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const override;
+#endif
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& rawDst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+ SkPMColor4f onFilterColor4f(const SkPMColor4f& origColor,
+ SkColorSpace* rawDstCS) const override;
+
+ bool onIsAlphaUnchanged() const override;
+
+ sk_sp<SkColorFilter> child() const { return fChild; }
+
+private:
+ friend void ::SkRegisterWorkingFormatColorFilterFlattenable();
+ SK_FLATTENABLE_HOOKS(SkWorkingFormatColorFilter)
+
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ sk_sp<SkColorFilter> fChild;
+ skcms_TransferFunction fTF;
+ bool fUseDstTF = true;
+ skcms_Matrix3x3 fGamut;
+ bool fUseDstGamut = true;
+ SkAlphaType fAT;
+ bool fUseDstAT = true;
+};
+
+#endif
diff --git a/src/effects/BUILD.bazel b/src/effects/BUILD.bazel
index 691d50d..d98e818 100644
--- a/src/effects/BUILD.bazel
+++ b/src/effects/BUILD.bazel
@@ -26,6 +26,7 @@
"SkShaderMaskFilterImpl.cpp",
"SkShaderMaskFilterImpl.h",
"SkTableColorFilter.cpp",
+ "SkTableColorFilter.h",
"SkTableMaskFilter.cpp",
"SkTrimPE.h",
"SkTrimPathEffect.cpp",
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 7b869e0..265bcde 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -5,15 +5,14 @@
* found in the LICENSE file.
*/
-#include "include/core/SkAlphaType.h"
+#include "src/effects/SkTableColorFilter.h"
+
#include "include/core/SkBitmap.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkFlattenable.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkRefCnt.h"
-#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
-#include "include/private/SkSLSampleUsage.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkColorFilterBase.h"
#include "src/core/SkEffectPriv.h"
@@ -24,9 +23,6 @@
#include "src/core/SkWriteBuffer.h"
#include <cstdint>
-#include <memory>
-#include <tuple>
-#include <utility>
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/Image_Graphite.h"
@@ -41,119 +37,70 @@
}
#endif
-#if defined(SK_GANESH)
-#include "include/gpu/GpuTypes.h"
-#include "include/gpu/GrTypes.h"
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/GrProcessorUnitTest.h"
-#include "src/gpu/ganesh/GrSurfaceProxyView.h"
-#include "src/gpu/ganesh/SkGr.h"
-#include "src/gpu/ganesh/effects/GrTextureEffect.h"
-#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
-
-class GrRecordingContext;
-struct GrShaderCaps;
-namespace skgpu { class KeyBuilder; }
-#endif
-
-#if GR_TEST_UTILS
-#include "include/core/SkColorSpace.h"
-#include "include/core/SkSurfaceProps.h"
-#include "include/private/base/SkTo.h"
-#include "include/private/gpu/ganesh/GrTypesPriv.h"
-#include "src/base/SkRandom.h"
-#include "src/gpu/ganesh/GrTestUtils.h"
-#else
-class SkSurfaceProps;
-#endif
-
#if defined(SK_ENABLE_SKSL) && defined(SK_ENABLE_SKVM)
#include "src/core/SkVM.h"
#endif
-class SkTableColorFilter final : public SkColorFilterBase {
-public:
- SkTableColorFilter(const uint8_t tableA[],
- const uint8_t tableR[],
- const uint8_t tableG[],
- const uint8_t tableB[]) {
- fBitmap.allocPixels(SkImageInfo::MakeA8(256, 4));
- uint8_t *a = fBitmap.getAddr8(0,0),
- *r = fBitmap.getAddr8(0,1),
- *g = fBitmap.getAddr8(0,2),
- *b = fBitmap.getAddr8(0,3);
- for (int i = 0; i < 256; i++) {
- a[i] = tableA ? tableA[i] : i;
- r[i] = tableR ? tableR[i] : i;
- g[i] = tableG ? tableG[i] : i;
- b[i] = tableB ? tableB[i] : i;
- }
- fBitmap.setImmutable();
+SkTableColorFilter::SkTableColorFilter(const uint8_t tableA[],
+ const uint8_t tableR[],
+ const uint8_t tableG[],
+ const uint8_t tableB[]) {
+ fBitmap.allocPixels(SkImageInfo::MakeA8(256, 4));
+ uint8_t *a = fBitmap.getAddr8(0, 0), *r = fBitmap.getAddr8(0, 1), *g = fBitmap.getAddr8(0, 2),
+ *b = fBitmap.getAddr8(0, 3);
+ for (int i = 0; i < 256; i++) {
+ a[i] = tableA ? tableA[i] : i;
+ r[i] = tableR ? tableR[i] : i;
+ g[i] = tableG ? tableG[i] : i;
+ b[i] = tableB ? tableB[i] : i;
+ }
+ fBitmap.setImmutable();
+}
+
+bool SkTableColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ SkRasterPipeline* p = rec.fPipeline;
+ if (!shaderIsOpaque) {
+ p->append(SkRasterPipelineOp::unpremul);
}
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*, const GrColorInfo&,
- const SkSurfaceProps&) const override;
-#endif
+ SkRasterPipeline_TablesCtx* tables = rec.fAlloc->make<SkRasterPipeline_TablesCtx>();
+ tables->a = fBitmap.getAddr8(0, 0);
+ tables->r = fBitmap.getAddr8(0, 1);
+ tables->g = fBitmap.getAddr8(0, 2);
+ tables->b = fBitmap.getAddr8(0, 3);
+ p->append(SkRasterPipelineOp::byte_tables, tables);
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
- SkRasterPipeline* p = rec.fPipeline;
- if (!shaderIsOpaque) {
- p->append(SkRasterPipelineOp::unpremul);
- }
-
- SkRasterPipeline_TablesCtx* tables = rec.fAlloc->make<SkRasterPipeline_TablesCtx>();
- tables->a = fBitmap.getAddr8(0, 0);
- tables->r = fBitmap.getAddr8(0, 1);
- tables->g = fBitmap.getAddr8(0, 2);
- tables->b = fBitmap.getAddr8(0, 3);
- p->append(SkRasterPipelineOp::byte_tables, tables);
-
- bool definitelyOpaque = shaderIsOpaque && tables->a[0xff] == 0xff;
- if (!definitelyOpaque) {
- p->append(SkRasterPipelineOp::premul);
- }
- return true;
+ bool definitelyOpaque = shaderIsOpaque && tables->a[0xff] == 0xff;
+ if (!definitelyOpaque) {
+ p->append(SkRasterPipelineOp::premul);
}
+ return true;
+}
#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms, SkArenaAlloc*) const override {
+skvm::Color SkTableColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const {
+ auto apply_table_to_component = [&](skvm::F32 c, const uint8_t* bytePtr) -> skvm::F32 {
+ skvm::I32 index = to_unorm(8, clamp01(c));
+ skvm::Uniform table = uniforms->pushPtr(bytePtr);
+ return from_unorm(8, gather8(table, index));
+ };
- auto apply_table_to_component = [&](skvm::F32 c, const uint8_t* bytePtr) -> skvm::F32 {
- skvm::I32 index = to_unorm(8, clamp01(c));
- skvm::Uniform table = uniforms->pushPtr(bytePtr);
- return from_unorm(8, gather8(table, index));
- };
-
- c = unpremul(c);
- c.a = apply_table_to_component(c.a, fBitmap.getAddr8(0,0));
- c.r = apply_table_to_component(c.r, fBitmap.getAddr8(0,1));
- c.g = apply_table_to_component(c.g, fBitmap.getAddr8(0,2));
- c.b = apply_table_to_component(c.b, fBitmap.getAddr8(0,3));
- return premul(c);
- }
+ c = unpremul(c);
+ c.a = apply_table_to_component(c.a, fBitmap.getAddr8(0, 0));
+ c.r = apply_table_to_component(c.r, fBitmap.getAddr8(0, 1));
+ c.g = apply_table_to_component(c.g, fBitmap.getAddr8(0, 2));
+ c.b = apply_table_to_component(c.b, fBitmap.getAddr8(0, 3));
+ return premul(c);
+}
#endif
- void flatten(SkWriteBuffer& buffer) const override {
- buffer.writeByteArray(fBitmap.getAddr8(0,0), 4*256);
- }
-
-private:
- friend void ::SkRegisterTableColorFilterFlattenable();
- SK_FLATTENABLE_HOOKS(SkTableColorFilter)
-
- SkBitmap fBitmap;
-};
+void SkTableColorFilter::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeByteArray(fBitmap.getAddr8(0, 0), 4 * 256);
+}
sk_sp<SkFlattenable> SkTableColorFilter::CreateProc(SkReadBuffer& buffer) {
uint8_t argb[4*256];
@@ -163,141 +110,6 @@
return nullptr;
}
-#if defined(SK_GANESH)
-
-class ColorTableEffect : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const SkBitmap& bitmap);
-
- ~ColorTableEffect() override {}
-
- const char* name() const override { return "ColorTableEffect"; }
-
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(*this));
- }
-
- inline static constexpr int kTexEffectFPIndex = 0;
- inline static constexpr int kInputFPIndex = 1;
-
-private:
- std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
-
- void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
-
- bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
-
- ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP, GrSurfaceProxyView view);
-
- explicit ColorTableEffect(const ColorTableEffect& that);
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- using INHERITED = GrFragmentProcessor;
-};
-
-ColorTableEffect::ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrSurfaceProxyView view)
- // Not bothering with table-specific optimizations.
- : INHERITED(kColorTableEffect_ClassID, kNone_OptimizationFlags) {
- this->registerChild(GrTextureEffect::Make(std::move(view), kUnknown_SkAlphaType),
- SkSL::SampleUsage::Explicit());
- this->registerChild(std::move(inputFP));
-}
-
-ColorTableEffect::ColorTableEffect(const ColorTableEffect& that)
- : INHERITED(that) {}
-
-std::unique_ptr<GrFragmentProcessor::ProgramImpl> ColorTableEffect::onMakeProgramImpl() const {
- class Impl : public ProgramImpl {
- public:
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- SkString inputColor = this->invokeChild(kInputFPIndex, args);
- SkString a = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.a, 0.5)");
- SkString r = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.r, 1.5)");
- SkString g = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.g, 2.5)");
- SkString b = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.b, 3.5)");
- fragBuilder->codeAppendf(
- "half4 coord = 255 * unpremul(%s) + 0.5;\n"
- "half4 color = half4(%s.a, %s.a, %s.a, 1);\n"
- "return color * %s.a;\n",
- inputColor.c_str(), r.c_str(), g.c_str(), b.c_str(), a.c_str());
- }
- };
-
- return std::make_unique<Impl>();
-}
-
-std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(
- std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context, const SkBitmap& bitmap) {
- SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
- SkASSERT(bitmap.isImmutable());
-
- auto view = std::get<0>(GrMakeCachedBitmapProxyView(context,
- bitmap,
- /*label=*/"MakeColorTableEffect",
- GrMipmapped::kNo));
- if (!view) {
- return nullptr;
- }
-
- return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(std::move(inputFP),
- std::move(view)));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect)
-
-#if GR_TEST_UTILS
-
-
-std::unique_ptr<GrFragmentProcessor> ColorTableEffect::TestCreate(GrProcessorTestData* d) {
- int flags = 0;
- uint8_t luts[256][4];
- do {
- for (int i = 0; i < 4; ++i) {
- flags |= d->fRandom->nextBool() ? (1 << i): 0;
- }
- } while (!flags);
- for (int i = 0; i < 4; ++i) {
- if (flags & (1 << i)) {
- for (int j = 0; j < 256; ++j) {
- luts[j][i] = SkToU8(d->fRandom->nextBits(8));
- }
- }
- }
- auto filter(SkColorFilters::TableARGB(
- (flags & (1 << 0)) ? luts[0] : nullptr,
- (flags & (1 << 1)) ? luts[1] : nullptr,
- (flags & (1 << 2)) ? luts[2] : nullptr,
- (flags & (1 << 3)) ? luts[3] : nullptr
- ));
- sk_sp<SkColorSpace> colorSpace = GrTest::TestColorSpace(d->fRandom);
- SkSurfaceProps props; // default props for testing
- auto [success, fp] = as_CFB(filter)->asFragmentProcessor(
- d->inputFP(), d->context(),
- GrColorInfo(GrColorType::kRGBA_8888, kUnknown_SkAlphaType, std::move(colorSpace)),
- props);
- SkASSERT(success);
- return std::move(fp);
-}
-#endif
-
-GrFPResult SkTableColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrColorInfo&,
- const SkSurfaceProps&) const {
- auto cte = ColorTableEffect::Make(std::move(inputFP), context, fBitmap);
- return cte ? GrFPSuccess(std::move(cte)) : GrFPFailure(nullptr);
-}
-
-#endif // defined(SK_GANESH)
-
#if defined(SK_GRAPHITE)
void SkTableColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
@@ -342,5 +154,6 @@
void SkRegisterTableColorFilterFlattenable() {
SK_REGISTER_FLATTENABLE(SkTableColorFilter);
+ // Previous name
SkFlattenable::Register("SkTable_ColorFilter", SkTableColorFilter::CreateProc);
}
diff --git a/src/effects/SkTableColorFilter.h b/src/effects/SkTableColorFilter.h
new file mode 100644
index 0000000..9eedd78
--- /dev/null
+++ b/src/effects/SkTableColorFilter.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkTableColorFilter_DEFINED
+#define SkTableColorFilter_DEFINED
+
+#include "include/core/SkBitmap.h"
+#include "include/core/SkFlattenable.h"
+#include "include/private/base/SkDebug.h"
+#include "src/core/SkColorFilterBase.h"
+
+#include <cstdint>
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/Image_Graphite.h"
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/Log.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#include "src/gpu/graphite/RecorderPriv.h"
+
+namespace skgpu::graphite {
+class PipelineDataGatherer;
+}
+#endif
+
+#if defined(SK_ENABLE_SKSL) && defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+#endif
+
+class SkTableColorFilter final : public SkColorFilterBase {
+public:
+ SkTableColorFilter(const uint8_t tableA[],
+ const uint8_t tableR[],
+ const uint8_t tableG[],
+ const uint8_t tableB[]);
+
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kTable; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const override;
+#endif
+
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ SkBitmap bitmap() const { return fBitmap; }
+
+private:
+ friend void ::SkRegisterTableColorFilterFlattenable();
+ SK_FLATTENABLE_HOOKS(SkTableColorFilter)
+
+ SkBitmap fBitmap;
+};
+
+#endif
diff --git a/src/gpu/ganesh/GrFragmentProcessors.cpp b/src/gpu/ganesh/GrFragmentProcessors.cpp
index 6be7343..8102f06 100644
--- a/src/gpu/ganesh/GrFragmentProcessors.cpp
+++ b/src/gpu/ganesh/GrFragmentProcessors.cpp
@@ -7,29 +7,46 @@
#include "src/gpu/ganesh/GrFragmentProcessors.h"
-#include "include/core/SkColorSpace.h" // IWYU pragma: keep
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
#include "include/core/SkData.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRefCnt.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/GrRecordingContext.h"
+#include "include/private/SkColorData.h"
#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkDebug.h"
#include "include/private/base/SkTArray.h"
#include "src/core/SkBlendModeBlender.h"
+#include "src/core/SkBlendModeColorFilter.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkColorFilterBase.h"
+#include "src/core/SkColorSpacePriv.h"
+#include "src/core/SkColorSpaceXformColorFilter.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkComposeColorFilter.h"
#include "src/core/SkMaskFilterBase.h"
+#include "src/core/SkMatrixColorFilter.h"
#include "src/core/SkRuntimeBlender.h"
+#include "src/core/SkRuntimeColorFilter.h"
#include "src/core/SkRuntimeEffectPriv.h"
+#include "src/core/SkWorkingFormatColorFilter.h"
#include "src/effects/SkShaderMaskFilterImpl.h"
+#include "src/effects/SkTableColorFilter.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrColorInfo.h"
+#include "src/gpu/ganesh/GrColorSpaceXform.h"
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
+#include "src/gpu/ganesh/effects/GrColorTableEffect.h"
#include "src/gpu/ganesh/effects/GrSkSLFP.h"
#include "src/shaders/SkShaderBase.h"
+#include "src/utils/SkGaussianColorFilter.h"
#include <memory>
#include <optional>
@@ -89,11 +106,11 @@
childFPs.push_back(std::move(childFP));
} else if (type == ChildType::kColorFilter) {
// Convert a SkColorFilter into a child FP.
- auto [success, childFP] = as_CFB(child.colorFilter())
- ->asFragmentProcessor(/*inputFP=*/nullptr,
- childArgs.fContext,
- *childArgs.fDstColorInfo,
- childArgs.fSurfaceProps);
+ auto [success, childFP] = Make(childArgs.fContext,
+ child.colorFilter(),
+ /*inputFP=*/nullptr,
+ *childArgs.fDstColorInfo,
+ childArgs.fSurfaceProps);
if (!success) {
return GrFPFailure(std::move(inputFP));
}
@@ -180,6 +197,227 @@
SkUNREACHABLE;
}
+static SkPMColor4f map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
+ SkPMColor4f color = {c.fR, c.fG, c.fB, c.fA};
+ SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, dst, kPremul_SkAlphaType).apply(color.vec());
+ return color;
+}
+static GrFPResult make_colorfilter_fp(GrRecordingContext*,
+ const SkBlendModeColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo& dstColorInfo,
+ const SkSurfaceProps& props) {
+ if (filter->mode() == SkBlendMode::kDst) {
+ // If the blend mode is "dest," the blend color won't factor into it at all.
+ // We can return the input FP as-is.
+ return GrFPSuccess(std::move(inputFP));
+ }
+
+ SkDEBUGCODE(const bool fpHasConstIO = !inputFP || inputFP->hasConstantOutputForConstantInput();)
+
+ SkPMColor4f color = map_color(filter->color(), sk_srgb_singleton(), dstColorInfo.colorSpace());
+
+ auto colorFP = GrFragmentProcessor::MakeColor(color);
+ auto xferFP =
+ GrBlendFragmentProcessor::Make(std::move(colorFP), std::move(inputFP), filter->mode());
+
+ if (xferFP == nullptr) {
+ // This is only expected to happen if the blend mode is "dest" and the input FP is null.
+ // Since we already did an early-out in the "dest" blend mode case, we shouldn't get here.
+ SkDEBUGFAIL("GrBlendFragmentProcessor::Make returned null unexpectedly");
+ return GrFPFailure(nullptr);
+ }
+
+ // With a solid color input this should always be able to compute the blended color
+ // (at least for coeff modes).
+ // Occasionally, we even do better than we started; specifically, in "src" blend mode, we end up
+ // ditching the input FP entirely, which turns a non-constant operation into a constant one.
+ SkASSERT(filter->mode() > SkBlendMode::kLastCoeffMode ||
+ xferFP->hasConstantOutputForConstantInput() >= fpHasConstIO);
+
+ return GrFPSuccess(std::move(xferFP));
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext* context,
+ const SkComposeColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo& dstColorInfo,
+ const SkSurfaceProps& props) {
+ // Unfortunately, we need to clone the input before we know we need it. This lets us return
+ // the original FP if either internal color filter fails.
+ auto inputClone = inputFP ? inputFP->clone() : nullptr;
+
+ auto [innerSuccess, innerFP] =
+ Make(context, filter->inner().get(), std::move(inputFP), dstColorInfo, props);
+ if (!innerSuccess) {
+ return GrFPFailure(std::move(inputClone));
+ }
+
+ auto [outerSuccess, outerFP] =
+ Make(context, filter->outer().get(), std::move(innerFP), dstColorInfo, props);
+ if (!outerSuccess) {
+ return GrFPFailure(std::move(inputClone));
+ }
+
+ return GrFPSuccess(std::move(outerFP));
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext*,
+ const SkColorSpaceXformColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo&,
+ const SkSurfaceProps&) {
+ // wish our caller would let us know if our input was opaque...
+ constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
+ return GrFPSuccess(GrColorSpaceXformEffect::Make(
+ std::move(inputFP), filter->src().get(), alphaType, filter->dst().get(), alphaType));
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext*,
+ const SkGaussianColorFilter*,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo&,
+ const SkSurfaceProps&) {
+ static const SkRuntimeEffect* effect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
+ "half4 main(half4 inColor) {"
+ "half factor = 1 - inColor.a;"
+ "factor = exp(-factor * factor * 4) - 0.018;"
+ "return half4(factor);"
+ "}");
+ SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
+ return GrFPSuccess(
+ GrSkSLFP::Make(effect, "gaussian_fp", std::move(inputFP), GrSkSLFP::OptFlags::kNone));
+}
+
+static std::unique_ptr<GrFragmentProcessor> rgb_to_hsl(std::unique_ptr<GrFragmentProcessor> child) {
+ static const SkRuntimeEffect* effect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
+ "half4 main(half4 color) {"
+ "return $rgb_to_hsl(color.rgb, color.a);"
+ "}");
+ SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
+ return GrSkSLFP::Make(
+ effect, "RgbToHsl", std::move(child), GrSkSLFP::OptFlags::kPreservesOpaqueInput);
+}
+
+static std::unique_ptr<GrFragmentProcessor> hsl_to_rgb(std::unique_ptr<GrFragmentProcessor> child) {
+ static const SkRuntimeEffect* effect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
+ "half4 main(half4 color) {"
+ "return $hsl_to_rgb(color.rgb, color.a);"
+ "}");
+ SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
+ return GrSkSLFP::Make(
+ effect, "HslToRgb", std::move(child), GrSkSLFP::OptFlags::kPreservesOpaqueInput);
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext*,
+ const SkMatrixColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo&,
+ const SkSurfaceProps&) {
+ switch (filter->domain()) {
+ case SkMatrixColorFilter::Domain::kRGBA:
+ return GrFPSuccess(GrFragmentProcessor::ColorMatrix(std::move(inputFP),
+ filter->matrix(),
+ /* unpremulInput = */ true,
+ /* clampRGBOutput = */ true,
+ /* premulOutput = */ true));
+
+ case SkMatrixColorFilter::Domain::kHSLA: {
+ auto fp = rgb_to_hsl(std::move(inputFP));
+ fp = GrFragmentProcessor::ColorMatrix(std::move(fp),
+ filter->matrix(),
+ /* unpremulInput = */ false,
+ /* clampRGBOutput = */ false,
+ /* premulOutput = */ false);
+ return GrFPSuccess(hsl_to_rgb(std::move(fp)));
+ }
+ }
+ SkUNREACHABLE;
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext* context,
+ const SkRuntimeColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo& colorInfo,
+ const SkSurfaceProps& props) {
+ sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
+ filter->effect()->uniforms(), filter->uniforms(), colorInfo.colorSpace());
+ SkASSERT(uniforms);
+
+ GrFPArgs childArgs(context, &colorInfo, props);
+ auto children = filter->children();
+ return make_effect_fp(filter->effect(),
+ "runtime_color_filter",
+ std::move(uniforms),
+ std::move(inputFP),
+ /*destColorFP=*/nullptr,
+ SkSpan(children),
+ childArgs);
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext* context,
+ const SkTableColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo&,
+ const SkSurfaceProps&) {
+ auto cte = ColorTableEffect::Make(std::move(inputFP), context, filter->bitmap());
+ return cte ? GrFPSuccess(std::move(cte)) : GrFPFailure(nullptr);
+}
+
+static GrFPResult make_colorfilter_fp(GrRecordingContext* context,
+ const SkWorkingFormatColorFilter* filter,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo& dstColorInfo,
+ const SkSurfaceProps& props) {
+ sk_sp<SkColorSpace> dstCS = dstColorInfo.refColorSpace();
+ if (!dstCS) {
+ dstCS = SkColorSpace::MakeSRGB();
+ }
+
+ SkAlphaType workingAT;
+ sk_sp<SkColorSpace> workingCS = filter->workingFormat(dstCS, &workingAT);
+
+ GrColorInfo dst = {dstColorInfo.colorType(), dstColorInfo.alphaType(), dstCS},
+ working = {dstColorInfo.colorType(), workingAT, workingCS};
+
+ auto [ok, fp] = Make(context,
+ filter->child().get(),
+ GrColorSpaceXformEffect::Make(std::move(inputFP), dst, working),
+ working,
+ props);
+
+ return ok ? GrFPSuccess(GrColorSpaceXformEffect::Make(std::move(fp), working, dst))
+ : GrFPFailure(std::move(fp));
+}
+
+GrFPResult Make(GrRecordingContext* ctx,
+ const SkColorFilter* cf,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo& dstColorInfo,
+ const SkSurfaceProps& props) {
+ if (!cf) {
+ return GrFPFailure(nullptr);
+ }
+ auto cfb = as_CFB(cf);
+ switch (cfb->type()) {
+ case SkColorFilterBase::Type::kNoop:
+ return GrFPFailure(nullptr);
+#define M(type) \
+ case SkColorFilterBase::Type::k##type: \
+ return make_colorfilter_fp(ctx, \
+ static_cast<const Sk##type##ColorFilter*>(cf), \
+ std::move(inputFP), \
+ dstColorInfo, \
+ props);
+ SK_ALL_COLOR_FILTERS(M)
+#undef M
+ }
+ SkUNREACHABLE;
+}
+
bool IsSupported(const SkMaskFilter* maskfilter) {
if (!maskfilter) {
return false;
diff --git a/src/gpu/ganesh/GrFragmentProcessors.h b/src/gpu/ganesh/GrFragmentProcessors.h
index 0dbc37b..460c90e 100644
--- a/src/gpu/ganesh/GrFragmentProcessors.h
+++ b/src/gpu/ganesh/GrFragmentProcessors.h
@@ -15,20 +15,20 @@
#include <tuple>
#include <memory>
+class GrColorInfo;
class GrFragmentProcessor;
+class GrRecordingContext;
class SkBlenderBase;
+class SkColorFilter;
class SkData;
class SkMaskFilter;
class SkMatrix;
+class SkSurfaceProps;
struct GrFPArgs;
using GrFPResult = std::tuple<bool, std::unique_ptr<GrFragmentProcessor>>;
namespace GrFragmentProcessors {
-std::unique_ptr<GrFragmentProcessor> Make(const SkMaskFilter*,
- const GrFPArgs&,
- const SkMatrix& ctm);
-
/**
* Returns a GrFragmentProcessor that implements this blend for the Ganesh GPU backend.
* The GrFragmentProcessor expects premultiplied inputs and returns a premultiplied output.
@@ -38,6 +38,24 @@
std::unique_ptr<GrFragmentProcessor> dstFP,
const GrFPArgs& fpArgs);
+/**
+ * Returns a GrFragmentProcessor that implements the color filter in GPU shader code.
+ *
+ * The fragment processor receives a input FP that generates a premultiplied input color, and
+ * produces a premultiplied output color.
+ *
+ * A GrFPFailure indicates that the color filter isn't implemented for the GPU backend.
+ */
+GrFPResult Make(GrRecordingContext*,
+ const SkColorFilter*,
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ const GrColorInfo& dstColorInfo,
+ const SkSurfaceProps&);
+
+std::unique_ptr<GrFragmentProcessor> Make(const SkMaskFilter*,
+ const GrFPArgs&,
+ const SkMatrix& ctm);
+
bool IsSupported(const SkMaskFilter*);
// TODO(kjlubick, brianosman) remove this after all related effects have been migrated
diff --git a/src/gpu/ganesh/SkGr.cpp b/src/gpu/ganesh/SkGr.cpp
index 3199e1b..f4812ce 100644
--- a/src/gpu/ganesh/SkGr.cpp
+++ b/src/gpu/ganesh/SkGr.cpp
@@ -27,7 +27,6 @@
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/core/SkBlendModePriv.h"
#include "src/core/SkBlenderBase.h"
-#include "src/core/SkColorFilterBase.h"
#include "src/core/SkMessageBus.h"
#include "src/core/SkPaintPriv.h"
#include "src/core/SkRuntimeEffectPriv.h"
@@ -475,9 +474,8 @@
SkColorSpace* dstCS = dstColorInfo.colorSpace();
grPaint->setColor4f(colorFilter->filterColor4f(origColor, dstCS, dstCS).premul());
} else {
- auto [success, fp] = as_CFB(colorFilter)->asFragmentProcessor(std::move(paintFP),
- context, dstColorInfo,
- surfaceProps);
+ auto [success, fp] = GrFragmentProcessors::Make(
+ context, colorFilter, std::move(paintFP), dstColorInfo, surfaceProps);
if (!success) {
return false;
}
diff --git a/src/gpu/ganesh/effects/BUILD.bazel b/src/gpu/ganesh/effects/BUILD.bazel
index 36d36d6..c00129c 100644
--- a/src/gpu/ganesh/effects/BUILD.bazel
+++ b/src/gpu/ganesh/effects/BUILD.bazel
@@ -14,6 +14,8 @@
"GrBitmapTextGeoProc.h",
"GrBlendFragmentProcessor.cpp",
"GrBlendFragmentProcessor.h",
+ "GrColorTableEffect.cpp",
+ "GrColorTableEffect.h",
"GrConvexPolyEffect.cpp",
"GrConvexPolyEffect.h",
"GrCoverageSetOpXP.cpp",
diff --git a/src/gpu/ganesh/effects/GrColorTableEffect.cpp b/src/gpu/ganesh/effects/GrColorTableEffect.cpp
new file mode 100644
index 0000000..095b29e
--- /dev/null
+++ b/src/gpu/ganesh/effects/GrColorTableEffect.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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/gpu/ganesh/effects/GrColorTableEffect.h"
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBitmap.h"
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkString.h"
+#include "include/core/SkSurfaceProps.h"
+#include "include/gpu/GpuTypes.h"
+#include "include/gpu/GrTypes.h"
+#include "include/private/SkSLSampleUsage.h"
+#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkTo.h"
+#include "include/private/gpu/ganesh/GrTypesPriv.h"
+#include "src/base/SkRandom.h"
+#include "src/gpu/ganesh/GrColorInfo.h"
+#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
+#include "src/gpu/ganesh/GrProcessorUnitTest.h"
+#include "src/gpu/ganesh/GrSurfaceProxyView.h"
+#include "src/gpu/ganesh/GrTestUtils.h"
+#include "src/gpu/ganesh/SkGr.h"
+#include "src/gpu/ganesh/effects/GrTextureEffect.h"
+#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
+
+#include <cstdint>
+#include <tuple>
+#include <utility>
+
+class GrRecordingContext;
+
+ColorTableEffect::ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrSurfaceProxyView view)
+ // Not bothering with table-specific optimizations.
+ : GrFragmentProcessor(kColorTableEffect_ClassID, kNone_OptimizationFlags) {
+ this->registerChild(GrTextureEffect::Make(std::move(view), kUnknown_SkAlphaType),
+ SkSL::SampleUsage::Explicit());
+ this->registerChild(std::move(inputFP));
+}
+
+ColorTableEffect::ColorTableEffect(const ColorTableEffect& that) : GrFragmentProcessor(that) {}
+
+std::unique_ptr<GrFragmentProcessor::ProgramImpl> ColorTableEffect::onMakeProgramImpl() const {
+ class Impl : public ProgramImpl {
+ public:
+ void emitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ SkString inputColor = this->invokeChild(kInputFPIndex, args);
+ SkString a = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.a, 0.5)");
+ SkString r = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.r, 1.5)");
+ SkString g = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.g, 2.5)");
+ SkString b = this->invokeChild(kTexEffectFPIndex, args, "half2(coord.b, 3.5)");
+ fragBuilder->codeAppendf(
+ "half4 coord = 255 * unpremul(%s) + 0.5;\n"
+ "half4 color = half4(%s.a, %s.a, %s.a, 1);\n"
+ "return color * %s.a;\n",
+ inputColor.c_str(),
+ r.c_str(),
+ g.c_str(),
+ b.c_str(),
+ a.c_str());
+ }
+ };
+
+ return std::make_unique<Impl>();
+}
+
+std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(
+ std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrRecordingContext* context,
+ const SkBitmap& bitmap) {
+ SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
+ SkASSERT(bitmap.isImmutable());
+
+ auto view = std::get<0>(GrMakeCachedBitmapProxyView(context,
+ bitmap,
+ /*label=*/"MakeColorTableEffect",
+ GrMipmapped::kNo));
+ if (!view) {
+ return nullptr;
+ }
+
+ return std::unique_ptr<GrFragmentProcessor>(
+ new ColorTableEffect(std::move(inputFP), std::move(view)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect)
+
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> ColorTableEffect::TestCreate(GrProcessorTestData* d) {
+ int flags = 0;
+ uint8_t luts[256][4];
+ do {
+ for (int i = 0; i < 4; ++i) {
+ flags |= d->fRandom->nextBool() ? (1 << i) : 0;
+ }
+ } while (!flags);
+ for (int i = 0; i < 4; ++i) {
+ if (flags & (1 << i)) {
+ for (int j = 0; j < 256; ++j) {
+ luts[j][i] = SkToU8(d->fRandom->nextBits(8));
+ }
+ }
+ }
+ auto filter(SkColorFilters::TableARGB((flags & (1 << 0)) ? luts[0] : nullptr,
+ (flags & (1 << 1)) ? luts[1] : nullptr,
+ (flags & (1 << 2)) ? luts[2] : nullptr,
+ (flags & (1 << 3)) ? luts[3] : nullptr));
+ sk_sp<SkColorSpace> colorSpace = GrTest::TestColorSpace(d->fRandom);
+ SkSurfaceProps props; // default props for testing
+ auto [success, fp] = GrFragmentProcessors::Make(
+ d->context(),
+ filter.get(),
+ d->inputFP(),
+ GrColorInfo(GrColorType::kRGBA_8888, kUnknown_SkAlphaType, std::move(colorSpace)),
+ props);
+ SkASSERT(success);
+ return std::move(fp);
+}
+#endif
diff --git a/src/gpu/ganesh/effects/GrColorTableEffect.h b/src/gpu/ganesh/effects/GrColorTableEffect.h
new file mode 100644
index 0000000..823e68c
--- /dev/null
+++ b/src/gpu/ganesh/effects/GrColorTableEffect.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrColorTableEffect_DEFINED
+#define GrColorTableEffect_DEFINED
+
+#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrProcessorUnitTest.h"
+
+#include <memory>
+
+class GrRecordingContext;
+class GrSurfaceProxyView;
+class SkBitmap;
+struct GrShaderCaps;
+
+namespace skgpu {
+class KeyBuilder;
+}
+
+class ColorTableEffect : public GrFragmentProcessor {
+public:
+ static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrRecordingContext* context,
+ const SkBitmap& bitmap);
+
+ ~ColorTableEffect() override {}
+
+ const char* name() const override { return "ColorTableEffect"; }
+
+ std::unique_ptr<GrFragmentProcessor> clone() const override {
+ return std::unique_ptr<GrFragmentProcessor>(new ColorTableEffect(*this));
+ }
+
+ inline static constexpr int kTexEffectFPIndex = 0;
+ inline static constexpr int kInputFPIndex = 1;
+
+private:
+ std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
+
+ void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
+
+ bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
+
+ ColorTableEffect(std::unique_ptr<GrFragmentProcessor> inputFP, GrSurfaceProxyView view);
+
+ explicit ColorTableEffect(const ColorTableEffect& that);
+
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+};
+
+#endif
diff --git a/src/gpu/ganesh/effects/GrSkSLFP.cpp b/src/gpu/ganesh/effects/GrSkSLFP.cpp
index c060625..cc909d5 100644
--- a/src/gpu/ganesh/effects/GrSkSLFP.cpp
+++ b/src/gpu/ganesh/effects/GrSkSLFP.cpp
@@ -21,7 +21,6 @@
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/base/SkArenaAlloc.h"
#include "src/base/SkRandom.h"
-#include "src/core/SkColorFilterBase.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpContexts.h"
@@ -31,6 +30,7 @@
#include "src/gpu/KeyBuilder.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrColorSpaceXform.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrShaderVar.h"
#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
@@ -524,8 +524,8 @@
}
auto filter = SkOverdrawColorFilter::MakeWithSkColors(colors);
SkSurfaceProps props; // default props for testing
- auto [success, fp] = as_CFB(filter)->asFragmentProcessor(/*inputFP=*/nullptr, d->context(),
- GrColorInfo{}, props);
+ auto [success, fp] = GrFragmentProcessors::Make(
+ d->context(), filter.get(), /*inputFP=*/nullptr, GrColorInfo{}, props);
SkASSERT(success);
return std::move(fp);
}
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index 63c1d2b..5aec459 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -78,7 +78,7 @@
SkRegisterMatrixColorFilterFlattenable();
SkRegisterComposeColorFilterFlattenable();
SkRegisterModeColorFilterFlattenable();
- SkRegisterColorSpaceXformColorFilterFlattenable();
+ SkRegisterSkColorSpaceXformColorFilterFlattenable();
SkRegisterWorkingFormatColorFilterFlattenable();
SkRegisterTableColorFilterFlattenable();
diff --git a/src/shaders/SkColorFilterShader.cpp b/src/shaders/SkColorFilterShader.cpp
index eee266c..cd8ad50 100644
--- a/src/shaders/SkColorFilterShader.cpp
+++ b/src/shaders/SkColorFilterShader.cpp
@@ -18,6 +18,7 @@
#if defined(SK_GANESH)
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
#endif
#if defined(SK_GRAPHITE)
@@ -108,8 +109,11 @@
// TODO I guess, but it shouldn't come up as used today.
SkASSERT(fAlpha == 1.0f);
- auto [success, fp] = fFilter->asFragmentProcessor(std::move(shaderFP), args.fContext,
- *args.fDstColorInfo, args.fSurfaceProps);
+ auto [success, fp] = GrFragmentProcessors::Make(args.fContext,
+ fFilter.get(),
+ std::move(shaderFP),
+ *args.fDstColorInfo,
+ args.fSurfaceProps);
// If the filter FP could not be created, we still want to return the shader FP, so checking
// success can be omitted here.
return std::move(fp);
diff --git a/src/utils/BUILD.bazel b/src/utils/BUILD.bazel
index 6c6dc24..f63359a 100644
--- a/src/utils/BUILD.bazel
+++ b/src/utils/BUILD.bazel
@@ -38,6 +38,7 @@
"SkFloatToDecimal.h",
"SkFloatUtils.h",
"SkGaussianColorFilter.cpp",
+ "SkGaussianColorFilter.h",
"SkMatrix22.cpp",
"SkMatrix22.h",
"SkMultiPictureDocument.cpp",
diff --git a/src/utils/SkGaussianColorFilter.cpp b/src/utils/SkGaussianColorFilter.cpp
index 728ddfd..c80e599 100644
--- a/src/utils/SkGaussianColorFilter.cpp
+++ b/src/utils/SkGaussianColorFilter.cpp
@@ -5,6 +5,8 @@
* found in the LICENSE file.
*/
+#include "src/utils/SkGaussianColorFilter.h"
+
#include "include/core/SkColorFilter.h"
#include "include/core/SkFlattenable.h"
#include "include/core/SkRefCnt.h"
@@ -15,21 +17,6 @@
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpList.h"
-#if defined(SK_GANESH)
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-// This shouldn't be needed but IWYU needs both (identical) defs of GrFPResult.
-#include "src/shaders/SkShaderBase.h"
-#include <memory>
-#include <utility>
-
-class GrColorInfo;
-class GrRecordingContext;
-class SkSurfaceProps;
-#endif
-
-class SkReadBuffer;
-class SkWriteBuffer;
-
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/KeyHelpers.h"
@@ -46,82 +33,36 @@
class SkColorInfo;
#endif
-/**
- * Remaps the input color's alpha to a Gaussian ramp and then outputs premul white using the
- * remapped alpha.
- */
-class SkGaussianColorFilter final : public SkColorFilterBase {
-public:
- SkGaussianColorFilter() : SkColorFilterBase() {}
+SkGaussianColorFilter::SkGaussianColorFilter() : SkColorFilterBase() {}
- bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
- rec.fPipeline->append(SkRasterPipelineOp::gauss_a_to_rgba);
- return true;
- }
-
-#if defined(SK_GANESH)
- GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*,
- const GrColorInfo&,
- const SkSurfaceProps&) const override;
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-protected:
- void flatten(SkWriteBuffer&) const override {}
+bool SkGaussianColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
+ rec.fPipeline->append(SkRasterPipelineOp::gauss_a_to_rgba);
+ return true;
+}
#if defined(SK_ENABLE_SKVM)
- skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& dst, skvm::Uniforms*,
- SkArenaAlloc*) const override {
- // x = 1 - x;
- // exp(-x * x * 4) - 0.018f;
- // ... now approximate with quartic
- //
- skvm::F32 x = p->splat(-2.26661229133605957031f);
- x = c.a * x + 2.89795351028442382812f;
- x = c.a * x + 0.21345567703247070312f;
- x = c.a * x + 0.15489584207534790039f;
- x = c.a * x + 0.00030726194381713867f;
- return {x, x, x, x};
- }
+skvm::Color SkGaussianColorFilter::onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const {
+ // x = 1 - x;
+ // exp(-x * x * 4) - 0.018f;
+ // ... now approximate with quartic
+ //
+ skvm::F32 x = p->splat(-2.26661229133605957031f);
+ x = c.a * x + 2.89795351028442382812f;
+ x = c.a * x + 0.21345567703247070312f;
+ x = c.a * x + 0.15489584207534790039f;
+ x = c.a * x + 0.00030726194381713867f;
+ return {x, x, x, x};
+}
#endif
-private:
- SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
-};
-
sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
return SkColorFilterPriv::MakeGaussian();
}
-#if defined(SK_GANESH)
-
-#include "include/effects/SkRuntimeEffect.h"
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/gpu/ganesh/effects/GrSkSLFP.h"
-
-GrFPResult SkGaussianColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*,
- const GrColorInfo&,
- const SkSurfaceProps&) const {
- static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
- "half4 main(half4 inColor) {"
- "half factor = 1 - inColor.a;"
- "factor = exp(-factor * factor * 4) - 0.018;"
- "return half4(factor);"
- "}"
- );
- SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
- return GrFPSuccess(GrSkSLFP::Make(effect, "gaussian_fp", std::move(inputFP),
- GrSkSLFP::OptFlags::kNone));
-}
-#endif
-
#if defined(SK_GRAPHITE)
void SkGaussianColorFilter::addToKey(const skgpu::graphite::KeyContext& keyContext,
diff --git a/src/utils/SkGaussianColorFilter.h b/src/utils/SkGaussianColorFilter.h
new file mode 100644
index 0000000..2511763
--- /dev/null
+++ b/src/utils/SkGaussianColorFilter.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkGaussianColorFilter_DEFINED
+#define SkGaussianColorFilter_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkTypes.h"
+#include "src/core/SkColorFilterBase.h"
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+
+namespace skgpu::graphite {
+class PipelineDataGatherer;
+}
+#endif
+
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+class SkArenaAlloc;
+class SkColorInfo;
+#endif
+
+/**
+ * Remaps the input color's alpha to a Gaussian ramp and then outputs premul white using the
+ * remapped alpha.
+ */
+class SkGaussianColorFilter final : public SkColorFilterBase {
+public:
+ SkGaussianColorFilter();
+
+ bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kGaussian; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+protected:
+ void flatten(SkWriteBuffer&) const override {}
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color onProgram(skvm::Builder* p,
+ skvm::Color c,
+ const SkColorInfo& dst,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override;
+#endif
+
+private:
+ SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
+};
+
+#endif
diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp
index 420bc14..d1710bf 100644
--- a/tests/ColorFilterTest.cpp
+++ b/tests/ColorFilterTest.cpp
@@ -163,6 +163,8 @@
}
#endif
+ SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kNoop; }
+
bool appendStages(const SkStageRec&, bool) const override { return false; }
// Only created here, should never be flattened / unflattened.
diff --git a/toolchain/linux_trampolines/clang_trampoline_linux.sh b/toolchain/linux_trampolines/clang_trampoline_linux.sh
index 2207d9c..778a26e 100755
--- a/toolchain/linux_trampolines/clang_trampoline_linux.sh
+++ b/toolchain/linux_trampolines/clang_trampoline_linux.sh
@@ -49,8 +49,12 @@
"src/core/SkCanvas.cpp"
"src/core/SkCanvas_Raster.cpp"
"src/core/SkColor.cpp"
+ "src/core/SkColorFilter.cpp"
"src/core/SkColorSpace.cpp"
+ "src/core/SkColorSpaceXformColorFilter.cpp"
+ "src/core/SkComposeColorFilter.cpp"
"src/core/SkCompressedDataUtils.cpp"
+ "src/core/SkConvertPixels.cpp"
"src/core/SkCubicClipper.cpp"
"src/core/SkCubicMap.cpp"
"src/core/SkData.cpp"
@@ -63,12 +67,13 @@
"src/core/SkGlyph.cpp"
"src/core/SkGlyphRunPainter.cpp"
"src/core/SkICC.cpp"
- "src/core/SkImageInfo.cpp"
"src/core/SkImageGenerator.cpp"
+ "src/core/SkImageInfo.cpp"
"src/core/SkLineClipper.cpp"
"src/core/SkMD5.cpp"
"src/core/SkMaskFilter.cpp"
"src/core/SkMatrix.cpp"
+ "src/core/SkMatrixColorFilter.cpp"
"src/core/SkMipmapAccessor.cpp"
"src/core/SkMipmapBuilder.cpp"
"src/core/SkPaint.cpp"
@@ -78,9 +83,8 @@
"src/core/SkPathUtils.cpp"
"src/core/SkPictureData.cpp"
"src/core/SkPicturePlayback.cpp"
- "src/core/SkConvertPixels.cpp"
- "src/core/SkPixmap.cpp"
"src/core/SkPixelRef.cpp"
+ "src/core/SkPixmap.cpp"
"src/core/SkPixmapDraw.cpp"
"src/core/SkPoint.cpp"
"src/core/SkRRect.cpp"
@@ -91,8 +95,9 @@
"src/core/SkRuntime"
"src/core/SkScalar.cpp"
"src/core/SkStream.cpp"
- "src/core/SkString.cpp"
"src/core/SkStrike"
+ "src/core/SkString.cpp"
+ "src/core/SkWorkingFormatColorFilter.cpp"
"src/core/SkWriteBuffer.cpp"
"src/core/SkWritePixelsRec.cpp"
"src/core/SkYUVAInfo.cpp"