aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2017-08-01 16:38:08 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-08-01 21:03:28 +0000
commit5a9a981edf54d203e06adab0909da03343a1c596 (patch)
tree27fff0d853f596559aef68ba7d3f5e59be8e0d49 /src
parentce06e261e68848ae21cac1052abc16bc07b961bf (diff)
Tiling support for SkSweepGradient
Expand the sweep gradient definition to include a color stop angular range ([0, 360] by default). Color stop positions in [0,1] are mapped to this range, and drawing outside is controlled by a tile mode param. This is closer to the CSS gradients spec and allows us to use fewer color stops in Blink conic gradients. Impl-wise, the remapping is effected after t calculation, and before tiling. Change-Id: I5d71be01d134404d6eb9d7e2a904ec636b39f855 Reviewed-on: https://skia-review.googlesource.com/27704 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Brian Salomon <bsalomon@google.com> Reviewed-by: Mike Klein <mtklein@chromium.org>
Diffstat (limited to 'src')
-rw-r--r--src/core/SkReadBuffer.h1
-rw-r--r--src/shaders/gradients/SkGradientShader.cpp26
-rw-r--r--src/shaders/gradients/SkSweepGradient.cpp131
-rw-r--r--src/shaders/gradients/SkSweepGradient.h6
4 files changed, 119 insertions, 45 deletions
diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h
index dd0484c450..0653ab6865 100644
--- a/src/core/SkReadBuffer.h
+++ b/src/core/SkReadBuffer.h
@@ -74,6 +74,7 @@ public:
kComposeShaderCanLerp_Version = 54,
kNoModesInMergeImageFilter_Verison = 55,
kTileModeInBlurImageFilter_Version = 56,
+ kTileInfoInSweepGradient_Version = 57,
};
/**
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index e2989e67f1..a203e88333 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -1228,11 +1228,14 @@ sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
const SkColor colors[],
const SkScalar pos[],
int colorCount,
+ SkShader::TileMode mode,
+ SkScalar startAngle,
+ SkScalar endAngle,
uint32_t flags,
const SkMatrix* localMatrix) {
ColorConverter converter(colors, colorCount);
- return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, flags,
- localMatrix);
+ return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
+ mode, startAngle, endAngle, flags, localMatrix);
}
sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
@@ -1240,26 +1243,39 @@ sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
sk_sp<SkColorSpace> colorSpace,
const SkScalar pos[],
int colorCount,
+ SkShader::TileMode mode,
+ SkScalar startAngle,
+ SkScalar endAngle,
uint32_t flags,
const SkMatrix* localMatrix) {
- if (!valid_grad(colors, pos, colorCount, SkShader::kClamp_TileMode)) {
+ if (!valid_grad(colors, pos, colorCount, mode)) {
return nullptr;
}
if (1 == colorCount) {
return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
}
+ if (startAngle >= endAngle) {
+ return nullptr;
+ }
if (localMatrix && !localMatrix->invert(nullptr)) {
return nullptr;
}
- auto mode = SkShader::kClamp_TileMode;
+ if (startAngle <= 0 && endAngle >= 360) {
+ // If the t-range includes [0,1], then we can always use clamping (presumably faster).
+ mode = SkShader::kClamp_TileMode;
+ }
ColorStopOptimizer opt(colors, pos, colorCount, mode);
SkGradientShaderBase::Descriptor desc;
desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
localMatrix);
- return sk_make_sp<SkSweepGradient>(cx, cy, desc);
+
+ const SkScalar t0 = startAngle / 360,
+ t1 = endAngle / 360;
+
+ return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
}
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
index e70066a4d4..c4003bfd42 100644
--- a/src/shaders/gradients/SkSweepGradient.cpp
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -11,18 +11,14 @@
#include "SkPM4fPriv.h"
#include "SkRasterPipeline.h"
-static SkMatrix translate(SkScalar dx, SkScalar dy) {
- SkMatrix matrix;
- matrix.setTranslate(dx, dy);
- return matrix;
-}
-
-SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor& desc)
- : SkGradientShaderBase(desc, translate(-cx, -cy))
- , fCenter(SkPoint::Make(cx, cy))
+SkSweepGradient::SkSweepGradient(const SkPoint& center, SkScalar t0, SkScalar t1,
+ const Descriptor& desc)
+ : SkGradientShaderBase(desc, SkMatrix::MakeTrans(-center.x(), -center.y()))
+ , fCenter(center)
+ , fTBias(-t0)
+ , fTScale(1 / (t1 - t0))
{
- // overwrite the tilemode to a canonical value (since sweep ignores it)
- fTileMode = SkShader::kClamp_TileMode;
+ SkASSERT(t0 < t1);
}
SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
@@ -39,14 +35,27 @@ sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) {
return nullptr;
}
const SkPoint center = buffer.readPoint();
+
+ SkScalar startAngle = 0,
+ endAngle = 360;
+ if (!buffer.isVersionLT(SkReadBuffer::kTileInfoInSweepGradient_Version)) {
+ const auto tBias = buffer.readScalar(),
+ tScale = buffer.readScalar();
+ startAngle = -tBias * 360;
+ endAngle = (1 / tScale - tBias) * 360;
+ }
+
return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors,
std::move(desc.fColorSpace), desc.fPos, desc.fCount,
+ desc.fTileMode, startAngle, endAngle,
desc.fGradFlags, desc.fLocalMatrix);
}
void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writePoint(fCenter);
+ buffer.writeScalar(fTBias);
+ buffer.writeScalar(fTScale);
}
/////////////////////////////////////////////////////////////////////
@@ -62,8 +71,9 @@ class GrSweepGradient : public GrGradientEffect {
public:
class GLSLSweepProcessor;
- static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
- auto processor = sk_sp<GrSweepGradient>(new GrSweepGradient(args));
+ static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar tBias,
+ SkScalar tScale) {
+ auto processor = sk_sp<GrSweepGradient>(new GrSweepGradient(args, tBias, tScale));
return processor->isValid() ? std::move(processor) : nullptr;
}
@@ -74,12 +84,17 @@ public:
}
private:
- explicit GrSweepGradient(const CreateArgs& args)
- : INHERITED(args, args.fShader->colorsAreOpaque()) {
+ explicit GrSweepGradient(const CreateArgs& args, SkScalar tBias, SkScalar tScale)
+ : INHERITED(args, args.fShader->colorsAreOpaque())
+ , fTBias(tBias)
+ , fTScale(tScale){
this->initClassID<GrSweepGradient>();
}
- explicit GrSweepGradient(const GrSweepGradient& that) : INHERITED(that) {
+ explicit GrSweepGradient(const GrSweepGradient& that)
+ : INHERITED(that)
+ , fTBias(that.fTBias)
+ , fTScale(that.fTScale) {
this->initClassID<GrSweepGradient>();
}
@@ -88,8 +103,18 @@ private:
virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
GrProcessorKeyBuilder* b) const override;
+ bool onIsEqual(const GrFragmentProcessor& base) const override {
+ const GrSweepGradient& fp = base.cast<GrSweepGradient>();
+ return INHERITED::onIsEqual(base)
+ && fTBias == fp.fTBias
+ && fTScale == fp.fTScale;
+ }
+
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+ SkScalar fTBias;
+ SkScalar fTScale;
+
typedef GrGradientEffect INHERITED;
};
@@ -97,17 +122,38 @@ private:
class GrSweepGradient::GLSLSweepProcessor : public GrGradientEffect::GLSLProcessor {
public:
- GLSLSweepProcessor(const GrProcessor&) {}
+ GLSLSweepProcessor(const GrProcessor&)
+ : fCachedTBias(SK_FloatNaN)
+ , fCachedTScale(SK_FloatNaN) {}
- virtual void emitCode(EmitArgs&) override;
+ void emitCode(EmitArgs&) override;
- static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+ static void GenKey(const GrProcessor& processor, const GrShaderCaps&,
+ GrProcessorKeyBuilder* b) {
b->add32(GenBaseGradientKey(processor));
}
+protected:
+ void onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& processor) override {
+ INHERITED::onSetData(pdman, processor);
+ const GrSweepGradient& data = processor.cast<GrSweepGradient>();
+
+ if (fCachedTBias != data.fTBias || fCachedTScale != data.fTScale) {
+ fCachedTBias = data.fTBias;
+ fCachedTScale = data.fTScale;
+ pdman.set2f(fTBiasScaleUni, fCachedTBias, fCachedTScale);
+ }
+ }
+
private:
- typedef GrGradientEffect::GLSLProcessor INHERITED;
+ UniformHandle fTBiasScaleUni;
+
+ // Uploaded uniform values.
+ float fCachedTBias,
+ fCachedTScale;
+ typedef GrGradientEffect::GLSLProcessor INHERITED;
};
/////////////////////////////////////////////////////////////////////
@@ -147,22 +193,28 @@ sk_sp<GrFragmentProcessor> GrSweepGradient::TestCreate(GrProcessorTestData* d) {
void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) {
const GrSweepGradient& ge = args.fFp.cast<GrSweepGradient>();
- this->emitUniforms(args.fUniformHandler, ge);
- SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
- SkString t;
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+ this->emitUniforms(uniformHandler, ge);
+ fTBiasScaleUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType,
+ kDefault_GrSLPrecision, "SweepFSParams");
+ const char* tBiasScaleV = uniformHandler->getUniformCStr(fTBiasScaleUni);
+
+ const SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+
+ // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
+ // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in
+ // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
+ // handle the undefined behavior of the second paramenter being 0 instead of doing the
+ // divide ourselves and using atan instead.
+ const SkString atan = args.fShaderCaps->atan2ImplementedAsAtanYOverX()
+ ? SkStringPrintf("2.0 * atan(- %s.y, length(%s) - %s.x)",
+ coords2D.c_str(), coords2D.c_str(), coords2D.c_str())
+ : SkStringPrintf("atan(- %s.y, - %s.x)", coords2D.c_str(), coords2D.c_str());
+
// 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
- if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) {
- // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
- // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in
- // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
- // handle the undefined behavior of the second paramenter being 0 instead of doing the
- // divide ourselves and using atan instead.
- t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)",
- coords2D.c_str(), coords2D.c_str(), coords2D.c_str());
- } else {
- t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
- coords2D.c_str(), coords2D.c_str());
- }
+ const SkString t = SkStringPrintf("((%s * 0.1591549430918 + 0.5 + %s[0]) * %s[1])",
+ atan.c_str(), tBiasScaleV, tBiasScaleV);
+
this->emitColor(args.fFragBuilder,
args.fUniformHandler,
args.fShaderCaps,
@@ -192,8 +244,9 @@ sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs&
sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
args.fDstColorSpace);
sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make(
- GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode,
- std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
+ GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
+ std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)),
+ fTBias, fTScale));
if (!inner) {
return nullptr;
}
@@ -224,9 +277,11 @@ void SkSweepGradient::toString(SkString* str) const {
str->append(")");
}
-void SkSweepGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline* p,
+void SkSweepGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* p,
SkRasterPipeline*) const {
p->append(SkRasterPipeline::xy_to_unit_angle);
+ p->append_matrix(alloc, SkMatrix::Concat(SkMatrix::MakeScale(fTScale, 1),
+ SkMatrix::MakeTrans(fTBias , 0)));
}
#endif
diff --git a/src/shaders/gradients/SkSweepGradient.h b/src/shaders/gradients/SkSweepGradient.h
index 647b454ab5..92d3411b01 100644
--- a/src/shaders/gradients/SkSweepGradient.h
+++ b/src/shaders/gradients/SkSweepGradient.h
@@ -12,7 +12,7 @@
class SkSweepGradient final : public SkGradientShaderBase {
public:
- SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&);
+ SkSweepGradient(const SkPoint& center, SkScalar t0, SkScalar t1, const Descriptor&);
GradientType asAGradient(GradientInfo* info) const override;
@@ -33,7 +33,9 @@ protected:
bool onIsRasterPipelineOnly() const override { return true; }
private:
- const SkPoint fCenter;
+ const SkPoint fCenter;
+ const SkScalar fTBias,
+ fTScale;
friend class SkGradientShader;
typedef SkGradientShaderBase INHERITED;