experiment: float color components
BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1617813002 Review URL: https://codereview.chromium.org/1617813002
This commit is contained in:
parent
07caf56e72
commit
353c148d8e
@ -160,4 +160,44 @@ SK_API SkPMColor SkPreMultiplyColor(SkColor c);
|
||||
*/
|
||||
typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* The float values are 0...1 premultiplied
|
||||
*/
|
||||
struct SK_ATTRIBUTE(aligned(16)) SkPM4f {
|
||||
float fVec[4];
|
||||
|
||||
float a() const { return fVec[SK_A32_SHIFT/8]; }
|
||||
};
|
||||
|
||||
/*
|
||||
* The float values are 0...1 unpremultiplied
|
||||
*/
|
||||
struct SkColor4f {
|
||||
float fA;
|
||||
float fR;
|
||||
float fG;
|
||||
float fB;
|
||||
|
||||
bool operator==(const SkColor4f& other) const {
|
||||
return fA == other.fA && fR == other.fR && fG == other.fG && fB == other.fB;
|
||||
}
|
||||
bool operator!=(const SkColor4f& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const float* vec() const { return &fA; }
|
||||
float* vec() { return &fA; }
|
||||
|
||||
static SkColor4f Pin(float a, float r, float g, float b);
|
||||
static SkColor4f FromColor(SkColor);
|
||||
|
||||
SkColor4f pin() const {
|
||||
return Pin(fA, fR, fG, fB);
|
||||
}
|
||||
|
||||
SkPM4f premul() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -68,10 +68,13 @@ public:
|
||||
*/
|
||||
virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) const = 0;
|
||||
|
||||
virtual void filterSpan4f(const SkPM4f src[], int count, SkPM4f result[]) const;
|
||||
|
||||
enum Flags {
|
||||
/** If set the filter methods will not change the alpha channel of the colors.
|
||||
*/
|
||||
kAlphaUnchanged_Flag = 0x01,
|
||||
kAlphaUnchanged_Flag = 1 << 0,
|
||||
kSupports4f_Flag = 1 << 1,
|
||||
};
|
||||
|
||||
/** Returns the flags for this filter. Override in subclasses to return custom flags.
|
||||
|
@ -71,6 +71,11 @@
|
||||
SK_B32_SHIFT == SK_BGRA_B32_SHIFT)
|
||||
|
||||
|
||||
#define SK_A_INDEX (SK_A32_SHIFT/8)
|
||||
#define SK_R_INDEX (SK_R32_SHIFT/8)
|
||||
#define SK_G_INDEX (SK_G32_SHIFT/8)
|
||||
#define SK_B_INDEX (SK_B32_SHIFT/8)
|
||||
|
||||
#if defined(SK_PMCOLOR_IS_RGBA) && !LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA
|
||||
#error "SK_PMCOLOR_IS_RGBA does not match SK_*32_SHIFT values"
|
||||
#endif
|
||||
|
@ -81,6 +81,7 @@ public:
|
||||
shadeSpan().
|
||||
*/
|
||||
kConstInY32_Flag = 1 << 1,
|
||||
kSupports4f_Flag = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -127,6 +128,8 @@ public:
|
||||
*/
|
||||
virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0;
|
||||
|
||||
virtual void shadeSpan4f(int x, int y, SkPM4f[], int count);
|
||||
|
||||
/**
|
||||
* The const void* ctx is only const because all the implementations are const.
|
||||
* This can be changed to non-const if a new shade proc needs to change the ctx.
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add);
|
||||
|
||||
void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override;
|
||||
void filterSpan4f(const SkPM4f src[], int count, SkPM4f[]) const override;
|
||||
uint32_t getFlags() const override;
|
||||
bool asColorMatrix(SkScalar matrix[20]) const override;
|
||||
SkColorFilter* newComposed(const SkColorFilter*) const override;
|
||||
|
@ -100,3 +100,37 @@ SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) {
|
||||
}
|
||||
return SkColorSetARGB(a, r, g, b);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "SkNx.h"
|
||||
|
||||
SkColor4f SkColor4f::Pin(float a, float r, float g, float b) {
|
||||
SkColor4f c4;
|
||||
Sk4f::Min(Sk4f::Max(Sk4f(a, r, g, b), Sk4f(0)), Sk4f(1)).store(c4.vec());
|
||||
return c4;
|
||||
}
|
||||
|
||||
SkColor4f SkColor4f::FromColor(SkColor c) {
|
||||
Sk4f value = SkNx_shuffle<3,2,1,0>(SkNx_cast<float>(Sk4b::Load((const uint8_t*)&c)));
|
||||
SkColor4f c4;
|
||||
(value * Sk4f(1.0f / 255)).store(c4.vec());
|
||||
return c4;
|
||||
}
|
||||
|
||||
SkPM4f SkColor4f::premul() const {
|
||||
auto src = Sk4f::Load(this->pin().vec());
|
||||
float srcAlpha = src.kth<0>(); // need the pinned version of our alpha
|
||||
src = src * Sk4f(1, srcAlpha, srcAlpha, srcAlpha);
|
||||
|
||||
#ifdef SK_PMCOLOR_IS_BGRA
|
||||
// ARGB -> BGRA
|
||||
Sk4f dst = SkNx_shuffle<3,2,1,0>(src);
|
||||
#else
|
||||
// ARGB -> RGBA
|
||||
Sk4f dst = SkNx_shuffle<1,2,3,0>(src);
|
||||
#endif
|
||||
|
||||
SkPM4f pm4;
|
||||
dst.store(pm4.fVec);
|
||||
return pm4;
|
||||
}
|
||||
|
@ -35,6 +35,10 @@ SkColor SkColorFilter::filterColor(SkColor c) const {
|
||||
return SkUnPreMultiply::PMColorToColor(dst);
|
||||
}
|
||||
|
||||
void SkColorFilter::filterSpan4f(const SkPM4f[], int count, SkPM4f[]) const {
|
||||
SkASSERT(false && "filterSpan4f called but not implemented");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
|
@ -36,8 +36,10 @@ public:
|
||||
uint32_t getFlags() const override;
|
||||
void shadeSpan(int x, int y, SkPMColor span[], int count) override;
|
||||
void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override;
|
||||
void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
|
||||
|
||||
private:
|
||||
SkPM4f fPM4f;
|
||||
SkPMColor fPMColor;
|
||||
uint32_t fFlags;
|
||||
|
||||
|
@ -78,4 +78,8 @@ static inline unsigned SkDiv255Round(unsigned prod) {
|
||||
return (prod + (prod >> 8)) >> 8;
|
||||
}
|
||||
|
||||
static inline float SkPinToUnitFloat(float x) {
|
||||
return SkTMin(SkTMax(x, 0.0f), 1.0f);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -117,6 +117,10 @@ SkShader::Context::ShadeProc SkShader::Context::asAShadeProc(void** ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SkShader::Context::shadeSpan4f(int x, int y, SkPM4f[], int count) {
|
||||
SkASSERT(false && "shadeSpan4f called but not implemented");
|
||||
}
|
||||
|
||||
#include "SkColorPriv.h"
|
||||
|
||||
#define kTempColorQuadCount 6 // balance between speed (larger) and saving stack-space
|
||||
@ -279,7 +283,11 @@ SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shade
|
||||
}
|
||||
fPMColor = SkPackARGB32(a, r, g, b);
|
||||
|
||||
fFlags = kConstInY32_Flag;
|
||||
SkColor4f c4 = SkColor4f::FromColor(shader.fColor);
|
||||
c4.fA *= rec.fPaint->getAlpha() / 255.0f;
|
||||
fPM4f = c4.premul();
|
||||
|
||||
fFlags = kConstInY32_Flag | kSupports4f_Flag;
|
||||
if (255 == a) {
|
||||
fFlags |= kOpaqueAlpha_Flag;
|
||||
}
|
||||
@ -293,6 +301,12 @@ void SkColorShader::ColorShaderContext::shadeSpanAlpha(int x, int y, uint8_t alp
|
||||
memset(alpha, SkGetPackedA32(fPMColor), count);
|
||||
}
|
||||
|
||||
void SkColorShader::ColorShaderContext::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
span[i] = fPM4f;
|
||||
}
|
||||
}
|
||||
|
||||
SkShader::GradientType SkColorShader::asAGradient(GradientInfo* info) const {
|
||||
if (info) {
|
||||
if (info->fColors && info->fColorCount >= 1) {
|
||||
|
@ -45,10 +45,11 @@ void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
|
||||
bool usesAlpha = (array[3] || array[8] || array[13]);
|
||||
|
||||
if (changesAlpha || usesAlpha) {
|
||||
fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
|
||||
fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag;
|
||||
} else {
|
||||
fFlags = SkColorFilter::kAlphaUnchanged_Flag;
|
||||
fFlags = kAlphaUnchanged_Flag;
|
||||
}
|
||||
fFlags |= kSupports4f_Flag;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -89,28 +90,28 @@ static SkPMColor round(const Sk4f& x) {
|
||||
return c;
|
||||
}
|
||||
|
||||
void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
|
||||
template <typename Adaptor, typename T>
|
||||
void filter_span(const float array[], const T src[], int count, T dst[]) {
|
||||
// c0-c3 are already in [0,1].
|
||||
const Sk4f c0 = Sk4f::Load(fTranspose + 0);
|
||||
const Sk4f c1 = Sk4f::Load(fTranspose + 4);
|
||||
const Sk4f c2 = Sk4f::Load(fTranspose + 8);
|
||||
const Sk4f c3 = Sk4f::Load(fTranspose + 12);
|
||||
const Sk4f c0 = Sk4f::Load(array + 0);
|
||||
const Sk4f c1 = Sk4f::Load(array + 4);
|
||||
const Sk4f c2 = Sk4f::Load(array + 8);
|
||||
const Sk4f c3 = Sk4f::Load(array + 12);
|
||||
// c4 (the translate vector) is in [0, 255]. Bring it back to [0,1].
|
||||
const Sk4f c4 = Sk4f::Load(fTranspose + 16)*Sk4f(1.0f/255);
|
||||
const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255);
|
||||
|
||||
// todo: we could cache this in the constructor...
|
||||
SkPMColor matrix_translate_pmcolor = round(premul(clamp_0_1(c4)));
|
||||
T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4)));
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
const SkPMColor src_c = src[i];
|
||||
if (0 == src_c) {
|
||||
Sk4f srcf = Adaptor::To4f(src[i]);
|
||||
float srcA = srcf.kth<SK_A32_SHIFT/8>();
|
||||
|
||||
if (0 == srcA) {
|
||||
dst[i] = matrix_translate_pmcolor;
|
||||
continue;
|
||||
}
|
||||
|
||||
Sk4f srcf = SkNx_cast<float>(Sk4b::Load((const uint8_t*)&src_c)) * Sk4f(1.0f/255);
|
||||
|
||||
if (0xFF != SkGetPackedA32(src_c)) {
|
||||
if (1 == srcA) {
|
||||
srcf = unpremul(srcf);
|
||||
}
|
||||
|
||||
@ -122,11 +123,36 @@ void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor
|
||||
// apply matrix
|
||||
Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4;
|
||||
|
||||
// clamp, re-premul, and write
|
||||
dst[i] = round(premul(clamp_0_1(dst4)));
|
||||
dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4)));
|
||||
}
|
||||
}
|
||||
|
||||
struct SkPMColorAdaptor {
|
||||
static SkPMColor From4f(const Sk4f& c4) {
|
||||
return round(c4);
|
||||
}
|
||||
static Sk4f To4f(SkPMColor c) {
|
||||
return SkNx_cast<float>(Sk4b::Load((const uint8_t*)&c)) * Sk4f(1.0f/255);
|
||||
}
|
||||
};
|
||||
void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
|
||||
filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst);
|
||||
}
|
||||
|
||||
struct SkPM4fAdaptor {
|
||||
static SkPM4f From4f(const Sk4f& c4) {
|
||||
SkPM4f c;
|
||||
c4.store(c.fVec);
|
||||
return c;
|
||||
}
|
||||
static Sk4f To4f(const SkPM4f& c) {
|
||||
return Sk4f::Load(c.fVec);
|
||||
}
|
||||
};
|
||||
void SkColorMatrixFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
|
||||
filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const {
|
||||
|
62
tests/SkColor4fTest.cpp
Normal file
62
tests/SkColor4fTest.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkColor.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkColorMatrixFilter.h"
|
||||
#include "Test.h"
|
||||
#include "SkRandom.h"
|
||||
|
||||
DEF_TEST(SkColor4f_FromColor, reporter) {
|
||||
const struct {
|
||||
SkColor fC;
|
||||
SkColor4f fC4;
|
||||
} recs[] = {
|
||||
{ SK_ColorBLACK, { 1, 0, 0, 0 } },
|
||||
{ SK_ColorWHITE, { 1, 1, 1, 1 } },
|
||||
{ SK_ColorRED, { 1, 1, 0, 0 } },
|
||||
{ SK_ColorGREEN, { 1, 0, 1, 0 } },
|
||||
{ SK_ColorBLUE, { 1, 0, 0, 1 } },
|
||||
{ 0, { 0, 0, 0, 0 } },
|
||||
{ 0x55AAFF00, { 1/3.0f, 2/3.0f, 1, 0 } },
|
||||
};
|
||||
|
||||
for (const auto& r : recs) {
|
||||
SkColor4f c4 = SkColor4f::FromColor(r.fC);
|
||||
REPORTER_ASSERT(reporter, c4 == r.fC4);
|
||||
}
|
||||
}
|
||||
|
||||
static bool nearly_equal(float a, float b) {
|
||||
const float kTolerance = 1.0f / (1 << 20);
|
||||
return fabsf(a - b) < kTolerance;
|
||||
}
|
||||
|
||||
DEF_TEST(SkColor4f_premul, reporter) {
|
||||
SkRandom rand;
|
||||
|
||||
for (int i = 0; i < 1000000; ++i) {
|
||||
// First just test opaque colors, so that the premul should be exact
|
||||
SkColor4f c4 {
|
||||
1, rand.nextUScalar1(), rand.nextUScalar1(), rand.nextUScalar1()
|
||||
};
|
||||
SkPM4f pm4 = c4.premul();
|
||||
REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
|
||||
REPORTER_ASSERT(reporter, pm4.fVec[SK_R_INDEX] == c4.fA * c4.fR);
|
||||
REPORTER_ASSERT(reporter, pm4.fVec[SK_G_INDEX] == c4.fA * c4.fG);
|
||||
REPORTER_ASSERT(reporter, pm4.fVec[SK_B_INDEX] == c4.fA * c4.fB);
|
||||
|
||||
// We compare with a tolerance, in case our premul multiply is implemented at slightly
|
||||
// different precision than the test code.
|
||||
c4.fA = rand.nextUScalar1();
|
||||
pm4 = c4.premul();
|
||||
REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
|
||||
REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_R_INDEX], c4.fA * c4.fR));
|
||||
REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_G_INDEX], c4.fA * c4.fG));
|
||||
REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_B_INDEX], c4.fA * c4.fB));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user