From 5a9a981edf54d203e06adab0909da03343a1c596 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Tue, 1 Aug 2017 16:38:08 -0400 Subject: 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 Reviewed-by: Brian Salomon Reviewed-by: Mike Klein --- src/core/SkReadBuffer.h | 1 + src/shaders/gradients/SkGradientShader.cpp | 26 ++++-- src/shaders/gradients/SkSweepGradient.cpp | 131 ++++++++++++++++++++--------- src/shaders/gradients/SkSweepGradient.h | 6 +- 4 files changed, 119 insertions(+), 45 deletions(-) (limited to 'src') 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 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 SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, @@ -1240,26 +1243,39 @@ sk_sp SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, sk_sp 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(cx, cy, desc); + + const SkScalar t0 = startAngle / 360, + t1 = endAngle / 360; + + return sk_make_sp(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 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 Make(const CreateArgs& args) { - auto processor = sk_sp(new GrSweepGradient(args)); + static sk_sp Make(const CreateArgs& args, SkScalar tBias, + SkScalar tScale) { + auto processor = sk_sp(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(); } - explicit GrSweepGradient(const GrSweepGradient& that) : INHERITED(that) { + explicit GrSweepGradient(const GrSweepGradient& that) + : INHERITED(that) + , fTBias(that.fTBias) + , fTScale(that.fTScale) { this->initClassID(); } @@ -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(); + 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(); + + 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 GrSweepGradient::TestCreate(GrProcessorTestData* d) { void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) { const GrSweepGradient& ge = args.fFp.cast(); - 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 SkSweepGradient::asFragmentProcessor(const AsFPArgs& sk_sp colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), args.fDstColorSpace); sk_sp 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; -- cgit v1.2.3