let color filters tell us their alpha format

There are a bunch of native premul->premul color filters, and about an
equal number of native unpremul->unpremul filters that all have roughly
the same logic to interoperate with a premul color pipeline.  I haven't
seen anything that wants premul in and unpremul out or vice versa.

(It's easy to find by grepping for ::unpremul.)

This CL flags the natively unpremul color filters and centralizes the
logic to handle premul interop.  This mostly eliminates the need for
SkColorFilter subclasses to know shaderIsOpaque, but not quite entirely.
It's of course used in the centralized SkColorFilter::appendStages(),
but is still also needed by any subclass that calls that, any subclass
that composes other color filters. I've commented out any unused
shaderIsOpaque arguments.

In the future we could imagine handling this outside in a more
sophisticated way, like eliding unnecessary alpha format conversions.
We'd need to move this logic out of SkColorFilter::appendStages() to
it's callers... the blitter, composing subclasses, and any other misc.

Should be no diffs and no interesting perf change.

I couldn't help but correct some misuse of "protected" where I was
editing anyway.  I'm feeling a mega CL coming...

Change-Id: If3528820ca639357864b8b99c8fe11ab60c1ae0d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/231465
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Mike Klein 2019-08-01 10:26:24 -05:00 committed by Skia Commit-Bot
parent c711a86493
commit 0355f05b26
9 changed files with 61 additions and 71 deletions

View File

@ -117,9 +117,6 @@ public:
protected:
SkColorFilter() {}
virtual bool onAsAColorMatrix(float[20]) const;
virtual bool onAsAColorMode(SkColor* color, SkBlendMode* bmode) const;
private:
/*
* Returns 1 if this is a single filter (not a composition of other filters), otherwise it
@ -130,8 +127,14 @@ private:
*/
virtual int privateComposedFilterCount() const { return 1; }
virtual bool onAsAColorMatrix(float[20]) const;
virtual bool onAsAColorMode(SkColor* color, SkBlendMode* bmode) const;
virtual bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const = 0;
// Return the alpha type this effect most naturally operates on (default premul).
// Inputs to onAppendStages() will be in this format, and its outputs should be left that way.
virtual SkAlphaType onAlphaType() const;
friend class SkComposeColorFilter;
typedef SkFlattenable INHERITED;

View File

@ -32,6 +32,8 @@ bool SkColorFilter::onAsAColorMatrix(float matrix[20]) const {
return false;
}
SkAlphaType SkColorFilter::onAlphaType() const { return kPremul_SkAlphaType; }
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> SkColorFilter::asFragmentProcessor(
GrRecordingContext*, const GrColorSpaceInfo&) const {
@ -39,8 +41,18 @@ std::unique_ptr<GrFragmentProcessor> SkColorFilter::asFragmentProcessor(
}
#endif
bool SkColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
return this->onAppendStages(rec, shaderIsOpaque);
bool SkColorFilter::appendStages(const SkStageRec& rec, bool opaque) const {
const bool unpremul = this->onAlphaType() == kUnpremul_SkAlphaType;
if (unpremul && !opaque) { rec.fPipeline->append(SkRasterPipeline::unpremul); }
const bool ok = this->onAppendStages(rec, opaque);
opaque = opaque && (this->getFlags() & kAlphaUnchanged_Flag) != 0;
if (unpremul && !opaque) { rec.fPipeline->append(SkRasterPipeline::premul); }
return ok;
}
SkColor SkColorFilter::filterColor(SkColor c) const {
@ -186,6 +198,8 @@ public:
}
}()) {}
private:
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
GrRecordingContext*, const GrColorSpaceInfo&) const override {
@ -201,27 +215,20 @@ public:
}
#endif
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
if (!shaderIsOpaque) {
rec.fPipeline->append(SkRasterPipeline::unpremul);
}
uint32_t getFlags() const override { return kAlphaUnchanged_Flag; }
SkAlphaType onAlphaType() const override { return kUnpremul_SkAlphaType; }
bool onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const override {
// TODO: is it valuable to thread this through appendStages()?
bool shaderIsNormalized = false;
fSteps.apply(rec.fPipeline, shaderIsNormalized);
if (!shaderIsOpaque) {
rec.fPipeline->append(SkRasterPipeline::premul);
}
return true;
}
protected:
void flatten(SkWriteBuffer& buffer) const override {
buffer.write32(static_cast<uint32_t>(fDir));
}
private:
SK_FLATTENABLE_HOOKS(SkSRGBGammaColorFilter)
const Direction fDir;
@ -387,7 +394,7 @@ public:
}
#endif
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
bool onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const override {
if (fCpuFunction) {
struct CpuFuncCtx : public SkRasterPipeline_CallbackCtx {
SkRuntimeColorFilterFn cpuFn;

View File

@ -52,15 +52,11 @@ bool SkColorFilter_Matrix::onAsAColorMatrix(float matrix[20]) const {
}
bool SkColorFilter_Matrix::onAppendStages(const SkStageRec& rec,
bool shaderIsOpaque) const {
const bool willStayOpaque = shaderIsOpaque && (fFlags & kAlphaUnchanged_Flag);
bool /*shaderIsOpaque*/) const {
SkRasterPipeline* p = rec.fPipeline;
if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); }
if ( true) { p->append(SkRasterPipeline::matrix_4x5, fMatrix); }
if ( true) { p->append(SkRasterPipeline::clamp_0); }
if ( true) { p->append(SkRasterPipeline::clamp_1); }
if (!willStayOpaque) { p->append(SkRasterPipeline::premul); }
p->append(SkRasterPipeline::matrix_4x5, fMatrix);
p->append(SkRasterPipeline::clamp_0);
p->append(SkRasterPipeline::clamp_1);
return true;
}

View File

@ -25,14 +25,13 @@ public:
static void RegisterFlattenables();
protected:
void flatten(SkWriteBuffer&) const override;
bool onAsAColorMatrix(float matrix[20]) const override;
private:
SK_FLATTENABLE_HOOKS(SkColorFilter_Matrix)
void flatten(SkWriteBuffer&) const override;
bool onAsAColorMatrix(float matrix[20]) const override;
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
SkAlphaType onAlphaType() const override { return kUnpremul_SkAlphaType; }
float fMatrix[20];
uint32_t fFlags;

View File

@ -61,7 +61,7 @@ sk_sp<SkFlattenable> SkModeColorFilter::CreateProc(SkReadBuffer& buffer) {
return SkColorFilters::Blend(color, mode);
}
bool SkModeColorFilter::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
bool SkModeColorFilter::onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const {
rec.fPipeline->append(SkRasterPipeline::move_src_dst);
SkColor4f color = SkColor4f::FromColor(fColor);
SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,

View File

@ -25,44 +25,36 @@ using InvertStyle = SkHighContrastConfig::InvertStyle;
class SkHighContrast_Filter : public SkColorFilter {
public:
SkHighContrast_Filter(const SkHighContrastConfig& config) {
fConfig = config;
SkHighContrast_Filter(const SkHighContrastConfig& config) : fConfig(config) {
// Clamp contrast to just inside -1 to 1 to avoid division by zero.
fConfig.fContrast = SkScalarPin(fConfig.fContrast,
-1.0f + FLT_EPSILON,
1.0f - FLT_EPSILON);
+1.0f - FLT_EPSILON);
}
~SkHighContrast_Filter() override {}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
GrRecordingContext*, const GrColorSpaceInfo&) const override;
#endif
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
protected:
void flatten(SkWriteBuffer&) const override;
private:
SK_FLATTENABLE_HOOKS(SkHighContrast_Filter)
void flatten(SkWriteBuffer&) const override;
uint32_t getFlags() const override { return kAlphaUnchanged_Flag; }
SkAlphaType onAlphaType() const override { return kUnpremul_SkAlphaType; }
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
SkHighContrastConfig fConfig;
friend class SkHighContrastFilter;
typedef SkColorFilter INHERITED;
};
bool SkHighContrast_Filter::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
bool SkHighContrast_Filter::onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const {
SkRasterPipeline* p = rec.fPipeline;
SkArenaAlloc* alloc = rec.fAlloc;
if (!shaderIsOpaque) {
p->append(SkRasterPipeline::unpremul);
}
// Linearize before applying high-contrast filter.
auto tf = alloc->make<skcms_TransferFunction>();
if (rec.fDstCS) {
@ -122,9 +114,6 @@ bool SkHighContrast_Filter::onAppendStages(const SkStageRec& rec, bool shaderIsO
}
p->append(SkRasterPipeline::parametric, invTF);
if (!shaderIsOpaque) {
p->append(SkRasterPipeline::premul);
}
return true;
}

View File

@ -18,7 +18,7 @@
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#endif
bool SkLumaColorFilter::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
bool SkLumaColorFilter::onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const {
rec.fPipeline->append(SkRasterPipeline::bt709_luminance_or_luma_to_alpha);
rec.fPipeline->append(SkRasterPipeline::clamp_0);
rec.fPipeline->append(SkRasterPipeline::clamp_1);

View File

@ -89,6 +89,12 @@ public:
GrRecordingContext*, const GrColorSpaceInfo&) const override;
#endif
private:
void flatten(SkWriteBuffer&) const override;
SK_FLATTENABLE_HOOKS(SkTable_ColorFilter)
void getTableAsBitmap(SkBitmap* table) const;
enum {
kA_Flag = 1 << 0,
kR_Flag = 1 << 1,
@ -96,7 +102,14 @@ public:
kB_Flag = 1 << 3,
};
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
uint32_t getFlags() const override {
return (fFlags & kA_Flag) == 0 ? kAlphaUnchanged_Flag
: 0;
}
SkAlphaType onAlphaType() const override { return kUnpremul_SkAlphaType; }
bool onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const override {
const uint8_t *r = gIdentityTable,
*g = gIdentityTable,
*b = gIdentityTable,
@ -107,28 +120,12 @@ public:
if (fFlags & kG_Flag) { g = ptr; ptr += 256; }
if (fFlags & kB_Flag) { b = ptr; }
SkRasterPipeline* p = rec.fPipeline;
if (!shaderIsOpaque) {
p->append(SkRasterPipeline::unpremul);
}
struct Tables { const uint8_t *r, *g, *b, *a; };
p->append(SkRasterPipeline::byte_tables, rec.fAlloc->make<Tables>(Tables{r,g,b,a}));
bool definitelyOpaque = shaderIsOpaque && a[0xff] == 0xff;
if (!definitelyOpaque) {
p->append(SkRasterPipeline::premul);
}
rec.fPipeline->append(SkRasterPipeline::byte_tables,
rec.fAlloc->make<Tables>(Tables{r,g,b,a}));
return true;
}
protected:
void flatten(SkWriteBuffer&) const override;
private:
SK_FLATTENABLE_HOOKS(SkTable_ColorFilter)
void getTableAsBitmap(SkBitmap* table) const;
mutable const SkBitmap* fBitmap; // lazily allocated

View File

@ -46,13 +46,12 @@ public:
GrRecordingContext*, const GrColorSpaceInfo&) const override;
#endif
protected:
private:
void flatten(SkWriteBuffer&) const override {}
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
bool onAppendStages(const SkStageRec& rec, bool /*shaderIsOpaque*/) const override {
rec.fPipeline->append(SkRasterPipeline::gauss_a_to_rgba);
return true;
}
private:
SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
SkGaussianColorFilter() : INHERITED() {}