Factor out common code from sweep and linear.
And, some file cleanup. Change-Id: I804db924bce3b5834f6cda481315dd2da38df5ca Reviewed-on: https://skia-review.googlesource.com/15226 Commit-Queue: Herb Derby <herb@google.com> Reviewed-by: Mike Klein <mtklein@chromium.org> Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
e7705780c9
commit
b7a330ff00
@ -12,8 +12,8 @@
|
||||
#include "SkLinearGradient.h"
|
||||
#include "SkMallocPixelRef.h"
|
||||
#include "SkRadialGradient.h"
|
||||
#include "SkTwoPointConicalGradient.h"
|
||||
#include "SkSweepGradient.h"
|
||||
#include "SkTwoPointConicalGradient.h"
|
||||
|
||||
enum GradientSerializationFlags {
|
||||
// Bits 29:31 used for various boolean flags
|
||||
@ -347,6 +347,155 @@ void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst,
|
||||
memcpy(colorDst, colorsTemp.get(), count * sizeof(SkColor));
|
||||
}
|
||||
|
||||
bool SkGradientShaderBase::onAppendStages(
|
||||
SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
|
||||
const SkMatrix& ctm, const SkPaint& paint,
|
||||
const SkMatrix* localM) const
|
||||
{
|
||||
// Local matrix not supported currently. Remove once we have a generic RP wrapper.
|
||||
if (localM || !getLocalMatrix().isIdentity()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkMatrix matrix;
|
||||
if (!ctm.invert(&matrix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkRasterPipeline p;
|
||||
if (!this->adjustMatrixAndAppendStages(alloc, &matrix, &p)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* m = alloc->makeArrayDefault<float>(9);
|
||||
if (matrix.asAffine(m)) {
|
||||
// TODO: mapping y is not needed; split the matrix stages to save some math?
|
||||
pipeline->append(SkRasterPipeline::matrix_2x3, m);
|
||||
} else {
|
||||
matrix.get9(m);
|
||||
pipeline->append(SkRasterPipeline::matrix_perspective, m);
|
||||
}
|
||||
|
||||
pipeline->extend(p);
|
||||
|
||||
const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
|
||||
auto prepareColor = [premulGrad, dstCS, this](int i) {
|
||||
SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS)
|
||||
: SkColor4f_from_SkColor(fOrigColors[i], nullptr);
|
||||
return premulGrad ? c.premul()
|
||||
: SkPM4f::From4f(Sk4f::Load(&c));
|
||||
};
|
||||
|
||||
// The two-stop case with stops at 0 and 1.
|
||||
if (fColorCount == 2 && fOrigPos == nullptr) {
|
||||
const SkPM4f c_l = prepareColor(0),
|
||||
c_r = prepareColor(1);
|
||||
|
||||
// See F and B below.
|
||||
auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
|
||||
f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
|
||||
f_and_b[1] = c_l;
|
||||
|
||||
pipeline->append(SkRasterPipeline::linear_gradient_2stops, f_and_b);
|
||||
} else {
|
||||
|
||||
struct Stop { float t; SkPM4f f, b; };
|
||||
struct Ctx { size_t n; Stop* stops; SkPM4f start; };
|
||||
|
||||
auto* ctx = alloc->make<Ctx>();
|
||||
ctx->start = prepareColor(0);
|
||||
|
||||
// For each stop we calculate a bias B and a scale factor F, such that
|
||||
// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
|
||||
auto init_stop = [](float t_l, float t_r, SkPM4f c_l, SkPM4f c_r, Stop *stop) {
|
||||
auto F = SkPM4f::From4f((c_r.to4f() - c_l.to4f()) / (t_r - t_l));
|
||||
auto B = SkPM4f::From4f(c_l.to4f() - (F.to4f() * t_l));
|
||||
*stop = {t_l, F, B};
|
||||
};
|
||||
|
||||
if (fOrigPos == nullptr) {
|
||||
// Handle evenly distributed stops.
|
||||
|
||||
float dt = 1.0f / (fColorCount - 1);
|
||||
// In the evenly distributed case, fColorCount is the number of stops. There are no
|
||||
// dummy entries.
|
||||
auto* stopsArray = alloc->makeArrayDefault<Stop>(fColorCount);
|
||||
|
||||
float t_l = 0;
|
||||
SkPM4f c_l = ctx->start;
|
||||
for (int i = 0; i < fColorCount - 1; i++) {
|
||||
// Use multiply instead of accumulating error using repeated addition.
|
||||
float t_r = (i + 1) * dt;
|
||||
SkPM4f c_r = prepareColor(i + 1);
|
||||
init_stop(t_l, t_r, c_l, c_r, &stopsArray[i]);
|
||||
|
||||
t_l = t_r;
|
||||
c_l = c_r;
|
||||
}
|
||||
|
||||
// Force the last stop.
|
||||
stopsArray[fColorCount - 1].t = 1;
|
||||
stopsArray[fColorCount - 1].f = SkPM4f::From4f(Sk4f{0});
|
||||
stopsArray[fColorCount - 1].b = prepareColor(fColorCount - 1);
|
||||
|
||||
ctx->n = fColorCount;
|
||||
ctx->stops = stopsArray;
|
||||
} else {
|
||||
// Handle arbitrary stops.
|
||||
|
||||
// Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
|
||||
// because they are naturally handled by the search method.
|
||||
int firstStop;
|
||||
int lastStop;
|
||||
if (fColorCount > 2) {
|
||||
firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
|
||||
lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
|
||||
? fColorCount - 1 : fColorCount - 2;
|
||||
} else {
|
||||
firstStop = 0;
|
||||
lastStop = 1;
|
||||
}
|
||||
int realCount = lastStop - firstStop + 1;
|
||||
|
||||
// This is the maximum number of stops. There may be fewer stops because the duplicate
|
||||
// points of hard stops are removed.
|
||||
auto* stopsArray = alloc->makeArrayDefault<Stop>(realCount);
|
||||
|
||||
size_t stopCount = 0;
|
||||
float t_l = fOrigPos[firstStop];
|
||||
SkPM4f c_l = prepareColor(firstStop);
|
||||
// N.B. lastStop is the index of the last stop, not one after.
|
||||
for (int i = firstStop; i < lastStop; i++) {
|
||||
float t_r = fOrigPos[i + 1];
|
||||
SkPM4f c_r = prepareColor(i + 1);
|
||||
if (t_l < t_r) {
|
||||
init_stop(t_l, t_r, c_l, c_r, &stopsArray[stopCount]);
|
||||
stopCount += 1;
|
||||
}
|
||||
t_l = t_r;
|
||||
c_l = c_r;
|
||||
}
|
||||
|
||||
stopsArray[stopCount].t = fOrigPos[lastStop];
|
||||
stopsArray[stopCount].f = SkPM4f::From4f(Sk4f{0});
|
||||
stopsArray[stopCount].b = prepareColor(lastStop);
|
||||
stopCount += 1;
|
||||
|
||||
ctx->n = stopCount;
|
||||
ctx->stops = stopsArray;
|
||||
}
|
||||
|
||||
pipeline->append(SkRasterPipeline::linear_gradient, ctx);
|
||||
}
|
||||
|
||||
if (!premulGrad && !this->colorsAreOpaque()) {
|
||||
pipeline->append(SkRasterPipeline::premul);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SkGradientShaderBase::isOpaque() const {
|
||||
return fColorsAreOpaque;
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkColorSpace.h"
|
||||
#include "SkOnce.h"
|
||||
#include "SkPM4fPriv.h"
|
||||
#include "SkRasterPipeline.h"
|
||||
#include "SkReadBuffer.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkUtils.h"
|
||||
@ -201,28 +203,21 @@ public:
|
||||
uint32_t getGradFlags() const { return fGradFlags; }
|
||||
|
||||
protected:
|
||||
struct Rec {
|
||||
SkFixed fPos; // 0...1
|
||||
uint32_t fScale; // (1 << 24) / range
|
||||
};
|
||||
|
||||
class GradientShaderBase4fContext;
|
||||
|
||||
SkGradientShaderBase(SkReadBuffer& );
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
SK_TO_STRING_OVERRIDE()
|
||||
|
||||
const SkMatrix fPtsToUnit;
|
||||
TileMode fTileMode;
|
||||
TileProc fTileProc;
|
||||
uint8_t fGradFlags;
|
||||
|
||||
struct Rec {
|
||||
SkFixed fPos; // 0...1
|
||||
uint32_t fScale; // (1 << 24) / range
|
||||
};
|
||||
Rec* fRecs;
|
||||
|
||||
void commonAsAGradient(GradientInfo*, bool flipGrad = false) const;
|
||||
|
||||
bool onAsLuminanceColor(SkColor*) const override;
|
||||
|
||||
|
||||
void initLinearBitmap(SkBitmap* bitmap) const;
|
||||
|
||||
/*
|
||||
@ -236,6 +231,14 @@ protected:
|
||||
SkColor* colorSrc, Rec* recSrc,
|
||||
int count);
|
||||
|
||||
bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
|
||||
const SkMatrix& ctm, const SkPaint& paint,
|
||||
const SkMatrix* localM) const final;
|
||||
|
||||
virtual bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const = 0;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {
|
||||
auto* ctx = alloc->make<T>(std::forward<Args>(args)...);
|
||||
@ -245,6 +248,12 @@ protected:
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const SkMatrix fPtsToUnit;
|
||||
TileMode fTileMode;
|
||||
TileProc fTileProc;
|
||||
uint8_t fGradFlags;
|
||||
Rec* fRecs;
|
||||
|
||||
private:
|
||||
enum {
|
||||
kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
|
||||
@ -277,6 +286,7 @@ private:
|
||||
typedef SkShader INHERITED;
|
||||
};
|
||||
|
||||
|
||||
static inline int init_dither_toggle(int x, int y) {
|
||||
x &= 1;
|
||||
y = (y & 1) << 1;
|
||||
|
@ -83,76 +83,22 @@ SkShader::Context* SkLinearGradient::onMakeContext(
|
||||
: CheckedMakeContext< LinearGradientContext>(alloc, *this, rec);
|
||||
}
|
||||
|
||||
//
|
||||
// Stages:
|
||||
//
|
||||
// * matrix (map dst -> grad space)
|
||||
// * clamp/repeat/mirror (tiling)
|
||||
// * linear_gradient_2stops (lerp c0/c1)
|
||||
// * optional premul
|
||||
//
|
||||
bool SkLinearGradient::onAppendStages(SkRasterPipeline* p,
|
||||
SkColorSpace* dstCS,
|
||||
SkArenaAlloc* alloc,
|
||||
const SkMatrix& ctm,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix* localM) const {
|
||||
// Local matrix not supported currently. Remove once we have a generic RP wrapper.
|
||||
if (localM || !getLocalMatrix().isIdentity()) {
|
||||
return false;
|
||||
}
|
||||
bool SkLinearGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const {
|
||||
*matrix = SkMatrix::Concat(fPtsToUnit, *matrix);
|
||||
// If the gradient is less than a quarter of a pixel, this falls into the
|
||||
// subpixel gradient code handled on a different path.
|
||||
SkVector dx = matrix->mapVector(1, 0);
|
||||
if (dx.fX >= 4) { return false; }
|
||||
|
||||
SkMatrix dstToPts;
|
||||
if (!ctm.invert(&dstToPts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto dstToUnit = SkMatrix::Concat(fPtsToUnit, dstToPts);
|
||||
|
||||
// If the gradient is less than a quarter of a pixel, this falls into the subpixel gradient code
|
||||
// handled on a different path.
|
||||
SkVector dx = dstToUnit.mapVector(1, 0);
|
||||
if (dx.fX >= 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* m = alloc->makeArrayDefault<float>(9);
|
||||
if (dstToUnit.asAffine(m)) {
|
||||
// TODO: mapping y is not needed; split the matrix stages to save some math?
|
||||
p->append(SkRasterPipeline::matrix_2x3, m);
|
||||
} else {
|
||||
dstToUnit.get9(m);
|
||||
p->append(SkRasterPipeline::matrix_perspective, m);
|
||||
}
|
||||
|
||||
// TODO: clamp/repeat/mirror const 1f stages?
|
||||
auto* limit = alloc->make<float>(1.0f);
|
||||
|
||||
const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
|
||||
auto prepareColor = [premulGrad, dstCS, this](int i) {
|
||||
SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS)
|
||||
: SkColor4f_from_SkColor(fOrigColors[i], nullptr);
|
||||
return premulGrad ? c.premul()
|
||||
: SkPM4f::From4f(Sk4f::Load(&c));
|
||||
};
|
||||
|
||||
// The two-stop case with stops at 0 and 1.
|
||||
if (fColorCount == 2 && fOrigPos == nullptr) {
|
||||
switch (fTileMode) {
|
||||
case kClamp_TileMode: p->append(SkRasterPipeline:: clamp_x, limit); break;
|
||||
case kClamp_TileMode: p->append(SkRasterPipeline::clamp_x, limit); break;
|
||||
case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
|
||||
case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
|
||||
}
|
||||
|
||||
const SkPM4f c_l = prepareColor(0),
|
||||
c_r = prepareColor(1);
|
||||
|
||||
// See F and B below.
|
||||
auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
|
||||
f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
|
||||
f_and_b[1] = c_l;
|
||||
|
||||
p->append(SkRasterPipeline::linear_gradient_2stops, f_and_b);
|
||||
} else {
|
||||
switch (fTileMode) {
|
||||
// The search strategy does not need clamping. It has implicit hard stops at the
|
||||
@ -161,100 +107,7 @@ bool SkLinearGradient::onAppendStages(SkRasterPipeline* p,
|
||||
case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
|
||||
case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
|
||||
}
|
||||
|
||||
struct Stop { float t; SkPM4f f, b; };
|
||||
struct Ctx { size_t n; Stop* stops; SkPM4f start; };
|
||||
|
||||
auto* ctx = alloc->make<Ctx>();
|
||||
ctx->start = prepareColor(0);
|
||||
|
||||
// For each stop we calculate a bias B and a scale factor F, such that
|
||||
// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
|
||||
auto init_stop = [](float t_l, float t_r, SkPM4f c_l, SkPM4f c_r, Stop *stop) {
|
||||
auto F = SkPM4f::From4f((c_r.to4f() - c_l.to4f()) / (t_r - t_l));
|
||||
auto B = SkPM4f::From4f(c_l.to4f() - (F.to4f() * t_l));
|
||||
*stop = {t_l, F, B};
|
||||
};
|
||||
|
||||
if (fOrigPos == nullptr) {
|
||||
// Handle evenly distributed stops.
|
||||
|
||||
float dt = 1.0f / (fColorCount - 1);
|
||||
// In the evenly distributed case, fColorCount is the number of stops. There are no
|
||||
// dummy entries.
|
||||
auto* stopsArray = alloc->makeArrayDefault<Stop>(fColorCount);
|
||||
|
||||
float t_l = 0;
|
||||
SkPM4f c_l = ctx->start;
|
||||
for (int i = 0; i < fColorCount - 1; i++) {
|
||||
// Use multiply instead of accumulating error using repeated addition.
|
||||
float t_r = (i + 1) * dt;
|
||||
SkPM4f c_r = prepareColor(i + 1);
|
||||
init_stop(t_l, t_r, c_l, c_r, &stopsArray[i]);
|
||||
|
||||
t_l = t_r;
|
||||
c_l = c_r;
|
||||
}
|
||||
|
||||
// Force the last stop.
|
||||
stopsArray[fColorCount - 1].t = 1;
|
||||
stopsArray[fColorCount - 1].f = SkPM4f::From4f(Sk4f{0});
|
||||
stopsArray[fColorCount - 1].b = prepareColor(fColorCount - 1);
|
||||
|
||||
ctx->n = fColorCount;
|
||||
ctx->stops = stopsArray;
|
||||
} else {
|
||||
// Handle arbitrary stops.
|
||||
|
||||
// Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
|
||||
// because they are naturally handled by the search method.
|
||||
int firstStop;
|
||||
int lastStop;
|
||||
if (fColorCount > 2) {
|
||||
firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
|
||||
lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
|
||||
? fColorCount - 1 : fColorCount - 2;
|
||||
} else {
|
||||
firstStop = 0;
|
||||
lastStop = 1;
|
||||
}
|
||||
int realCount = lastStop - firstStop + 1;
|
||||
|
||||
// This is the maximum number of stops. There may be fewer stops because the duplicate
|
||||
// points of hard stops are removed.
|
||||
auto* stopsArray = alloc->makeArrayDefault<Stop>(realCount);
|
||||
|
||||
size_t stopCount = 0;
|
||||
float t_l = fOrigPos[firstStop];
|
||||
SkPM4f c_l = prepareColor(firstStop);
|
||||
// N.B. lastStop is the index of the last stop, not one after.
|
||||
for (int i = firstStop; i < lastStop; i++) {
|
||||
float t_r = fOrigPos[i + 1];
|
||||
SkPM4f c_r = prepareColor(i + 1);
|
||||
if (t_l < t_r) {
|
||||
init_stop(t_l, t_r, c_l, c_r, &stopsArray[stopCount]);
|
||||
stopCount += 1;
|
||||
}
|
||||
t_l = t_r;
|
||||
c_l = c_r;
|
||||
}
|
||||
|
||||
stopsArray[stopCount].t = fOrigPos[lastStop];
|
||||
stopsArray[stopCount].f = SkPM4f::From4f(Sk4f{0});
|
||||
stopsArray[stopCount].b = prepareColor(lastStop);
|
||||
stopCount += 1;
|
||||
|
||||
ctx->n = stopCount;
|
||||
ctx->stops = stopsArray;
|
||||
}
|
||||
|
||||
p->append(SkRasterPipeline::linear_gradient, ctx);
|
||||
}
|
||||
|
||||
if (!premulGrad && !this->colorsAreOpaque()) {
|
||||
p->append(SkRasterPipeline::premul);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,10 @@ protected:
|
||||
void flatten(SkWriteBuffer& buffer) const override;
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
|
||||
|
||||
bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
|
||||
const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
|
||||
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const final;
|
||||
|
||||
|
||||
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
|
||||
|
||||
|
@ -38,6 +38,12 @@ protected:
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
|
||||
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
|
||||
|
||||
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const final {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkPoint fCenter;
|
||||
const SkScalar fRadius;
|
||||
|
@ -311,150 +311,12 @@ void SkSweepGradient::toString(SkString* str) const {
|
||||
str->append(")");
|
||||
}
|
||||
|
||||
bool SkSweepGradient::onAppendStages(SkRasterPipeline* p,
|
||||
SkColorSpace* dstCS,
|
||||
SkArenaAlloc* alloc,
|
||||
const SkMatrix& ctm,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix* localM) const {
|
||||
// Local matrix not supported currently. Remove once we have a generic RP wrapper.
|
||||
if (localM || !getLocalMatrix().isIdentity()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkMatrix dstToSrc;
|
||||
if (!ctm.invert(&dstToSrc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto dstToCenter = SkMatrix::Concat(
|
||||
SkMatrix::MakeTrans(-fCenter.fX, -fCenter.fY), dstToSrc);
|
||||
|
||||
auto* m = alloc->makeArrayDefault<float>(9);
|
||||
if (dstToCenter.asAffine(m)) {
|
||||
// TODO: mapping y is not needed; split the matrix stages to save some math?
|
||||
p->append(SkRasterPipeline::matrix_2x3, m);
|
||||
} else {
|
||||
dstToCenter.get9(m);
|
||||
p->append(SkRasterPipeline::matrix_perspective, m);
|
||||
}
|
||||
|
||||
bool SkSweepGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const {
|
||||
matrix->postTranslate(-fCenter.fX, -fCenter.fY);
|
||||
p->append(SkRasterPipeline::xy_to_polar_unit);
|
||||
|
||||
const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
|
||||
auto prepareColor = [premulGrad, dstCS, this](int i) {
|
||||
SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS)
|
||||
: SkColor4f_from_SkColor(fOrigColors[i], nullptr);
|
||||
return premulGrad ? c.premul()
|
||||
: SkPM4f::From4f(Sk4f::Load(&c));
|
||||
};
|
||||
|
||||
// The two-stop case with stops at 0 and 1.
|
||||
if (fColorCount == 2 && fOrigPos == nullptr) {
|
||||
const SkPM4f c_l = prepareColor(0),
|
||||
c_r = prepareColor(1);
|
||||
|
||||
// See F and B below.
|
||||
auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
|
||||
f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
|
||||
f_and_b[1] = c_l;
|
||||
|
||||
p->append(SkRasterPipeline::linear_gradient_2stops, f_and_b);
|
||||
} else {
|
||||
|
||||
struct Stop { float t; SkPM4f f, b; };
|
||||
struct Ctx { size_t n; Stop* stops; SkPM4f start; };
|
||||
|
||||
auto* ctx = alloc->make<Ctx>();
|
||||
ctx->start = prepareColor(0);
|
||||
|
||||
// For each stop we calculate a bias B and a scale factor F, such that
|
||||
// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
|
||||
auto init_stop = [](float t_l, float t_r, SkPM4f c_l, SkPM4f c_r, Stop *stop) {
|
||||
auto F = SkPM4f::From4f((c_r.to4f() - c_l.to4f()) / (t_r - t_l));
|
||||
auto B = SkPM4f::From4f(c_l.to4f() - (F.to4f() * t_l));
|
||||
*stop = {t_l, F, B};
|
||||
};
|
||||
|
||||
if (fOrigPos == nullptr) {
|
||||
// Handle evenly distributed stops.
|
||||
|
||||
float dt = 1.0f / (fColorCount - 1);
|
||||
// In the evenly distributed case, fColorCount is the number of stops. There are no
|
||||
// dummy entries.
|
||||
auto* stopsArray = alloc->makeArrayDefault<Stop>(fColorCount);
|
||||
|
||||
float t_l = 0;
|
||||
SkPM4f c_l = ctx->start;
|
||||
for (int i = 0; i < fColorCount - 1; i++) {
|
||||
// Use multiply instead of accumulating error using repeated addition.
|
||||
float t_r = (i + 1) * dt;
|
||||
SkPM4f c_r = prepareColor(i + 1);
|
||||
init_stop(t_l, t_r, c_l, c_r, &stopsArray[i]);
|
||||
|
||||
t_l = t_r;
|
||||
c_l = c_r;
|
||||
}
|
||||
|
||||
// Force the last stop.
|
||||
stopsArray[fColorCount - 1].t = 1;
|
||||
stopsArray[fColorCount - 1].f = SkPM4f::From4f(Sk4f{0});
|
||||
stopsArray[fColorCount - 1].b = prepareColor(fColorCount - 1);
|
||||
|
||||
ctx->n = fColorCount;
|
||||
ctx->stops = stopsArray;
|
||||
} else {
|
||||
// Handle arbitrary stops.
|
||||
|
||||
// Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
|
||||
// because they are naturally handled by the search method.
|
||||
int firstStop;
|
||||
int lastStop;
|
||||
if (fColorCount > 2) {
|
||||
firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
|
||||
lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
|
||||
? fColorCount - 1 : fColorCount - 2;
|
||||
} else {
|
||||
firstStop = 0;
|
||||
lastStop = 1;
|
||||
}
|
||||
int realCount = lastStop - firstStop + 1;
|
||||
|
||||
// This is the maximum number of stops. There may be fewer stops because the duplicate
|
||||
// points of hard stops are removed.
|
||||
auto* stopsArray = alloc->makeArrayDefault<Stop>(realCount);
|
||||
|
||||
size_t stopCount = 0;
|
||||
float t_l = fOrigPos[firstStop];
|
||||
SkPM4f c_l = prepareColor(firstStop);
|
||||
// N.B. lastStop is the index of the last stop, not one after.
|
||||
for (int i = firstStop; i < lastStop; i++) {
|
||||
float t_r = fOrigPos[i + 1];
|
||||
SkPM4f c_r = prepareColor(i + 1);
|
||||
if (t_l < t_r) {
|
||||
init_stop(t_l, t_r, c_l, c_r, &stopsArray[stopCount]);
|
||||
stopCount += 1;
|
||||
}
|
||||
t_l = t_r;
|
||||
c_l = c_r;
|
||||
}
|
||||
|
||||
stopsArray[stopCount].t = fOrigPos[lastStop];
|
||||
stopsArray[stopCount].f = SkPM4f::From4f(Sk4f{0});
|
||||
stopsArray[stopCount].b = prepareColor(lastStop);
|
||||
stopCount += 1;
|
||||
|
||||
ctx->n = stopCount;
|
||||
ctx->stops = stopsArray;
|
||||
}
|
||||
|
||||
p->append(SkRasterPipeline::linear_gradient, ctx);
|
||||
}
|
||||
|
||||
if (!premulGrad && !this->colorsAreOpaque()) {
|
||||
p->append(SkRasterPipeline::premul);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -38,9 +38,9 @@ protected:
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
|
||||
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
|
||||
|
||||
bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* space, SkArenaAlloc* alloc,
|
||||
const SkMatrix& matrix, const SkPaint& paint,
|
||||
const SkMatrix* localM) const override;
|
||||
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const final;
|
||||
|
||||
private:
|
||||
const SkPoint fCenter;
|
||||
|
@ -78,6 +78,11 @@ protected:
|
||||
void flatten(SkWriteBuffer& buffer) const override;
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
|
||||
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
|
||||
bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
|
||||
SkMatrix* matrix,
|
||||
SkRasterPipeline* p) const final {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
SkPoint fCenter1;
|
||||
|
Loading…
Reference in New Issue
Block a user