SkColorfilters::HSLAMatrix
Introduce an SkColorFilter_Matrix flavor operating in HSLA space.
(CPU-only for now)
Change-Id: If081de062b9e920c3365bd7b281e45bb069c3d1a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/231259
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index ec97a87..7e5f871 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -8,9 +8,11 @@
* Added RELEASE_NOTES.txt file
-SkDrawLooper is no longer supported in SkPaint or SkCanvas.
+ * SkDrawLooper is no longer supported in SkPaint or SkCanvas.
-SkImageFilter API refactor started:
- - Consolidated enum types to use SkTileMode and SkColorChannel
- - Hide filter implementation classes
- - Bumps SkPicture version number
+ * SkImageFilter API refactor started:
+ - Consolidated enum types to use SkTileMode and SkColorChannel
+ - Hide filter implementation classes
+ - Bumps SkPicture version number
+
+ * SkColorFilters::HSLAMatrix - new matrix color filter operating in HSLA space.
diff --git a/gm/colorfilters.cpp b/gm/colorfilters.cpp
index e2b4b23..0c547a1 100644
--- a/gm/colorfilters.cpp
+++ b/gm/colorfilters.cpp
@@ -20,6 +20,10 @@
#include "include/core/SkTypes.h"
#include "include/effects/SkColorMatrixFilter.h"
#include "include/effects/SkGradientShader.h"
+#include "tools/Resources.h"
+
+#include <vector>
+#include <tuple>
static sk_sp<SkShader> make_shader(const SkRect& bounds) {
const SkPoint pts[] = {
@@ -78,3 +82,104 @@
};
DEF_GM(return new ColorFiltersGM;)
+
+class HSLColorFilterGM : public skiagm::GM {
+protected:
+ SkString onShortName() override { return SkString("hslcolorfilter"); }
+
+ SkISize onISize() override { return { 840, 1100 }; }
+
+ void onOnceBeforeDraw() override {
+ sk_sp<SkImage> mandrill = GetResourceAsImage("images/mandrill_256.png");
+ const auto lm = SkMatrix::MakeRectToRect(SkRect::MakeWH(mandrill->width(),
+ mandrill->height()),
+ SkRect::MakeWH(kWheelSize, kWheelSize),
+ SkMatrix::kFill_ScaleToFit);
+ fShaders.push_back(mandrill->makeShader(&lm));
+
+ static constexpr SkColor gGrads[][4] = {
+ { 0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000 },
+ { 0xdfc08040, 0xdf8040c0, 0xdf40c080, 0xdfc08040 },
+ };
+
+ for (const auto& cols : gGrads) {
+ fShaders.push_back(SkGradientShader::MakeSweep(kWheelSize / 2, kWheelSize / 2,
+ cols, nullptr, SK_ARRAY_COUNT(cols),
+ SkTileMode::kRepeat, -90, 270, 0,
+ nullptr));
+ }
+ }
+
+ void onDraw(SkCanvas* canvas) override {
+ using std::make_tuple;
+
+ static constexpr struct {
+ std::tuple<float, float> h, s, l;
+ } gTests[] = {
+ { make_tuple(-0.5f, 0.5f), make_tuple( 0.0f, 0.0f), make_tuple( 0.0f, 0.0f) },
+ { make_tuple( 0.0f, 0.0f), make_tuple(-1.0f, 1.0f), make_tuple( 0.0f, 0.0f) },
+ { make_tuple( 0.0f, 0.0f), make_tuple( 0.0f, 0.0f), make_tuple(-1.0f, 1.0f) },
+ };
+
+ const auto rect = SkRect::MakeWH(kWheelSize, kWheelSize);
+
+ canvas->drawColor(0xffcccccc);
+ SkPaint paint;
+
+ for (const auto& shader : fShaders) {
+ paint.setShader(shader);
+
+ for (const auto& tst: gTests) {
+ canvas->translate(0, kWheelSize * 0.1f);
+
+ const auto dh = (std::get<1>(tst.h) - std::get<0>(tst.h)) / (kSteps - 1),
+ ds = (std::get<1>(tst.s) - std::get<0>(tst.s)) / (kSteps - 1),
+ dl = (std::get<1>(tst.l) - std::get<0>(tst.l)) / (kSteps - 1);
+ auto h = std::get<0>(tst.h),
+ s = std::get<0>(tst.s),
+ l = std::get<0>(tst.l);
+ {
+ SkAutoCanvasRestore acr(canvas, true);
+ for (size_t i = 0; i < kSteps; ++i) {
+ paint.setColorFilter(make_filter(h, s, l));
+ canvas->translate(kWheelSize * 0.1f, 0);
+ canvas->drawRect(rect, paint);
+ canvas->translate(kWheelSize * 1.1f, 0);
+ h += dh;
+ s += ds;
+ l += dl;
+ }
+ }
+ canvas->translate(0, kWheelSize * 1.1f);
+ }
+ canvas->translate(0, kWheelSize * 0.1f);
+ }
+ }
+
+private:
+ static constexpr SkScalar kWheelSize = 100;
+ static constexpr size_t kSteps = 7;
+
+ static sk_sp<SkColorFilter> make_filter(float h, float s, float l) {
+ // These are roughly AE semantics.
+ const auto h_bias = h,
+ h_scale = 1.0f,
+ s_bias = std::max(s, 0.0f),
+ s_scale = 1 - std::abs(s),
+ l_bias = std::max(l, 0.0f),
+ l_scale = 1 - std::abs(l);
+
+ const float cm[20] = {
+ h_scale, 0, 0, 0, h_bias,
+ 0, s_scale, 0, 0, s_bias,
+ 0, 0, l_scale, 0, l_bias,
+ 0, 0, 0, 1, 0,
+ };
+
+ return SkColorFilters::HSLAMatrix(cm);
+ }
+
+ std::vector<sk_sp<SkShader>> fShaders;
+};
+
+DEF_GM(return new HSLColorFilterGM;)
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 5d73774..ee01f62 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -149,6 +149,10 @@
static sk_sp<SkColorFilter> Matrix(const SkColorMatrix&);
static sk_sp<SkColorFilter> Matrix(const float rowMajor[20]);
+ // A version of Matrix which operates in HSLA space instead of RGBA.
+ // I.e. HSLA-to-RGBA(Matrix(RGBA-to-HSLA(input))).
+ static sk_sp<SkColorFilter> HSLAMatrix(const float rowMajor[20]);
+
static sk_sp<SkColorFilter> LinearToSRGBGamma();
static sk_sp<SkColorFilter> SRGBToLinearGamma();
static sk_sp<SkColorFilter> Lerp(float t, sk_sp<SkColorFilter> dst, sk_sp<SkColorFilter> src);
diff --git a/src/core/SkColorFilter_Matrix.cpp b/src/core/SkColorFilter_Matrix.cpp
index 1e331bc..52d4b75 100644
--- a/src/core/SkColorFilter_Matrix.cpp
+++ b/src/core/SkColorFilter_Matrix.cpp
@@ -16,15 +16,21 @@
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
-void SkColorFilter_Matrix::initState() {
- const float* srcA = fMatrix + 15;
- fFlags = (srcA[0] == 0 && srcA[1] == 0 && srcA[2] == 0 && srcA[3] == 1 && srcA[4] == 0)
- ? kAlphaUnchanged_Flag : 0;
+static uint16_t ComputeFlags(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])
+ ? SkColorFilter::kAlphaUnchanged_Flag : 0;
}
-SkColorFilter_Matrix::SkColorFilter_Matrix(const float array[20]) {
+SkColorFilter_Matrix::SkColorFilter_Matrix(const float array[20], Domain domain)
+ : fFlags(ComputeFlags(array))
+ , fDomain(domain) {
memcpy(fMatrix, array, 20 * sizeof(float));
- this->initState();
}
uint32_t SkColorFilter_Matrix::getFlags() const {
@@ -34,14 +40,21 @@
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 SkColorFilters::Matrix(matrix);
+ if (!buffer.readScalarArray(matrix, 20)) {
+ return nullptr;
}
- return nullptr;
+
+ auto is_rgba = buffer.isVersionLT(SkPicturePriv::kMatrixColorFilterDomain_Version) ||
+ buffer.readBool();
+ return is_rgba ? SkColorFilters::Matrix(matrix)
+ : SkColorFilters::HSLAMatrix(matrix);
}
bool SkColorFilter_Matrix::onAsAColorMatrix(float matrix[20]) const {
@@ -53,10 +66,14 @@
bool SkColorFilter_Matrix::onAppendStages(const SkStageRec& rec,
bool /*shaderIsOpaque*/) const {
+ const bool hsla = fDomain == Domain::kHSLA;
+
SkRasterPipeline* p = rec.fPipeline;
- p->append(SkRasterPipeline::matrix_4x5, fMatrix);
- p->append(SkRasterPipeline::clamp_0);
- p->append(SkRasterPipeline::clamp_1);
+ if (hsla) { p->append(SkRasterPipeline::rgb_to_hsl); }
+ p->append(SkRasterPipeline::matrix_4x5, fMatrix);
+ if (hsla) { p->append(SkRasterPipeline::hsl_to_rgb); }
+ p->append(SkRasterPipeline::clamp_0);
+ p->append(SkRasterPipeline::clamp_1);
return true;
}
@@ -64,6 +81,10 @@
#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
std::unique_ptr<GrFragmentProcessor> SkColorFilter_Matrix::asFragmentProcessor(
GrRecordingContext*, const GrColorSpaceInfo&) const {
+ if (fDomain == Domain::kHSLA) {
+ // TODO
+ return nullptr;
+ }
return GrColorMatrixFragmentProcessor::Make(fMatrix,
/* premulInput = */ true,
/* clampRGBOutput = */ true,
@@ -74,15 +95,23 @@
///////////////////////////////////////////////////////////////////////////////
+static sk_sp<SkColorFilter> MakeMatrix(const float array[20],
+ SkColorFilter_Matrix::Domain domain) {
+ return sk_floats_are_finite(array, 20)
+ ? sk_make_sp<SkColorFilter_Matrix>(array, domain)
+ : nullptr;
+}
+
sk_sp<SkColorFilter> SkColorFilters::Matrix(const float array[20]) {
- if (!sk_floats_are_finite(array, 20)) {
- return nullptr;
- }
- return sk_sp<SkColorFilter>(new SkColorFilter_Matrix(array));
+ return MakeMatrix(array, SkColorFilter_Matrix::Domain::kRGBA);
}
sk_sp<SkColorFilter> SkColorFilters::Matrix(const SkColorMatrix& cm) {
- return Matrix(cm.fMat);
+ return MakeMatrix(cm.fMat, SkColorFilter_Matrix::Domain::kRGBA);
+}
+
+sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const float array[20]) {
+ return MakeMatrix(array, SkColorFilter_Matrix::Domain::kHSLA);
}
void SkColorFilter_Matrix::RegisterFlattenables() {
diff --git a/src/core/SkColorFilter_Matrix.h b/src/core/SkColorFilter_Matrix.h
index bc2d1b4..bab111e 100644
--- a/src/core/SkColorFilter_Matrix.h
+++ b/src/core/SkColorFilter_Matrix.h
@@ -13,8 +13,9 @@
class SkColorFilter_Matrix : public SkColorFilter {
public:
- SkColorFilter_Matrix() {}
- explicit SkColorFilter_Matrix(const float array[20]);
+ enum class Domain : uint8_t { kRGBA, kHSLA };
+
+ explicit SkColorFilter_Matrix(const float array[20], Domain);
uint32_t getFlags() const override;
@@ -34,9 +35,8 @@
SkAlphaType onAlphaType() const override { return kUnpremul_SkAlphaType; }
float fMatrix[20];
- uint32_t fFlags;
-
- void initState();
+ uint16_t fFlags;
+ Domain fDomain;
typedef SkColorFilter INHERITED;
};
diff --git a/src/core/SkPicturePriv.h b/src/core/SkPicturePriv.h
index c4c9e59..c14751c 100644
--- a/src/core/SkPicturePriv.h
+++ b/src/core/SkPicturePriv.h
@@ -72,27 +72,29 @@
// V69: Clean up duplicated and redundant SkImageFilter related enums
// V70: Image filters definitions hidden, registered names updated to include "Impl"
// V71: Unify erode and dilate image filters
+ // V72: SkColorFilter_Matrix domain (rgba vs. hsla)
enum Version {
- kTileModeInBlurImageFilter_Version = 56,
- kTileInfoInSweepGradient_Version = 57,
- k2PtConicalNoFlip_Version = 58,
+ kTileModeInBlurImageFilter_Version = 56,
+ kTileInfoInSweepGradient_Version = 57,
+ k2PtConicalNoFlip_Version = 58,
kRemovePictureImageFilterLocalSpace = 59,
- kRemoveHeaderFlags_Version = 60,
- kTwoColorDrawShadow_Version = 61,
- kDontNegateImageSize_Version = 62,
- kStoreImageBounds_Version = 63,
- kRemoveOccluderFromBlurMaskFilter = 64,
- kFloat4PaintColor_Version = 65,
- kSaveBehind_Version = 66,
- kSerializeFonts_Version = 67,
- kPaintDoesntSerializeFonts_Version = 68,
- kCleanupImageFilterEnums_Version = 69,
- kHideImageFilterImpls_Version = 70,
- kUnifyErodeDilateImpls_Version = 71,
+ kRemoveHeaderFlags_Version = 60,
+ kTwoColorDrawShadow_Version = 61,
+ kDontNegateImageSize_Version = 62,
+ kStoreImageBounds_Version = 63,
+ kRemoveOccluderFromBlurMaskFilter = 64,
+ kFloat4PaintColor_Version = 65,
+ kSaveBehind_Version = 66,
+ kSerializeFonts_Version = 67,
+ kPaintDoesntSerializeFonts_Version = 68,
+ kCleanupImageFilterEnums_Version = 69,
+ kHideImageFilterImpls_Version = 70,
+ kUnifyErodeDilateImpls_Version = 71,
+ kMatrixColorFilterDomain_Version = 72,
// Only SKPs within the min/current picture version range (inclusive) can be read.
kMin_Version = kTileModeInBlurImageFilter_Version,
- kCurrent_Version = kUnifyErodeDilateImpls_Version
+ kCurrent_Version = kMatrixColorFilterDomain_Version
};
static_assert(kMin_Version <= 62, "Remove kFontAxes_bad from SkFontDescriptor.cpp");