From 6aaee59c041ce86d88954b32ba14c6fc29410e9c Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Fri, 12 Jan 2018 12:25:09 -0500 Subject: [skotty,sksg] Initial gradient support TBR= Change-Id: I61e4d46ac14660f4c9ea757be2278e4098131a6b Reviewed-on: https://skia-review.googlesource.com/94121 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- experimental/skotty/Skotty.cpp | 106 +++++++++++++++++++++++++------ experimental/skotty/SkottyProperties.cpp | 70 +++++++++++++++++--- experimental/skotty/SkottyProperties.h | 46 ++++++++++++++ experimental/sksg/paint/SkSGGradient.cpp | 60 +++++++++++++++++ experimental/sksg/paint/SkSGGradient.h | 105 ++++++++++++++++++++++++++++++ 5 files changed, 358 insertions(+), 29 deletions(-) create mode 100644 experimental/sksg/paint/SkSGGradient.cpp create mode 100644 experimental/sksg/paint/SkSGGradient.h (limited to 'experimental') diff --git a/experimental/skotty/Skotty.cpp b/experimental/skotty/Skotty.cpp index 284b37704a..ce2644ea51 100644 --- a/experimental/skotty/Skotty.cpp +++ b/experimental/skotty/Skotty.cpp @@ -21,6 +21,7 @@ #include "SkPoint.h" #include "SkSGColor.h" #include "SkSGDraw.h" +#include "SkSGGradient.h" #include "SkSGGroup.h" #include "SkSGImage.h" #include "SkSGInvalidationController.h" @@ -293,13 +294,11 @@ sk_sp AttachPolystarGeometry(const Json::Value& jstar, Attac return path_node; } -sk_sp AttachColorPaint(const Json::Value& obj, AttachContext* ctx) { +sk_sp AttachColor(const Json::Value& obj, AttachContext* ctx) { SkASSERT(obj.isObject()); auto color_node = sksg::Color::Make(SK_ColorBLACK); - color_node->setAntiAlias(true); - - auto composite = sk_make_sp(color_node); + auto composite = sk_make_sp(color_node); auto color_attached = BindProperty(obj["c"], ctx, composite, [](CompositeColor* node, const VectorValue& c) { node->setColor(ValueTraits::As(c)); @@ -312,29 +311,70 @@ sk_sp AttachColorPaint(const Json::Value& obj, AttachContext* ctx) return (color_attached || opacity_attached) ? color_node : nullptr; } -sk_sp AttachFillPaint(const Json::Value& jfill, AttachContext* ctx) { - SkASSERT(jfill.isObject()); +sk_sp AttachGradient(const Json::Value& obj, AttachContext* ctx) { + SkASSERT(obj.isObject()); + + const auto& stops = obj["g"]; + if (!stops.isObject()) + return nullptr; + + const auto stopCount = ParseInt(stops["p"], -1); + if (stopCount < 0) + return nullptr; + + sk_sp gradient_node; + sk_sp composite; + + if (ParseInt(obj["t"], 1) == 1) { + auto linear_node = sksg::LinearGradient::Make(); + composite = sk_make_sp(linear_node, stopCount); + gradient_node = std::move(linear_node); + } else { + auto radial_node = sksg::RadialGradient::Make(); + composite = sk_make_sp(radial_node, stopCount); - auto color = AttachColorPaint(jfill, ctx); - if (color) { - LOG("** Attached color fill: 0x%x\n", color->getColor()); + // TODO: highlight, angle + gradient_node = std::move(radial_node); } - return color; + + BindProperty(stops["k"], ctx, composite, + [](CompositeGradient* node, const VectorValue& stops) { + node->setColorStops(stops); + }); + BindProperty(obj["s"], ctx, composite, + [](CompositeGradient* node, const VectorValue& s) { + node->setStartPoint(ValueTraits::As(s)); + }); + BindProperty(obj["e"], ctx, composite, + [](CompositeGradient* node, const VectorValue& e) { + node->setEndPoint(ValueTraits::As(e)); + }); + + return gradient_node; } -sk_sp AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) { +sk_sp AttachPaint(const Json::Value& jfill, AttachContext* ctx, + sk_sp paint_node) { + if (paint_node) { + paint_node->setAntiAlias(true); + + // TODO: refactor opacity + } + + return paint_node; +} + +sk_sp AttachStroke(const Json::Value& jstroke, AttachContext* ctx, + sk_sp stroke_node) { SkASSERT(jstroke.isObject()); - auto stroke_node = AttachColorPaint(jstroke, ctx); if (!stroke_node) return nullptr; - LOG("** Attached color stroke: 0x%x\n", stroke_node->getColor()); - stroke_node->setStyle(SkPaint::kStroke_Style); auto width_attached = BindProperty(jstroke["w"], ctx, stroke_node, - [](sksg::Color* node, const ScalarValue& w) { + [](sksg::PaintNode* node, const ScalarValue& w) { node->setStrokeWidth(w); }); if (!width_attached) @@ -361,6 +401,30 @@ sk_sp AttachStrokePaint(const Json::Value& jstroke, AttachConte return stroke_node; } +sk_sp AttachColorFill(const Json::Value& jfill, AttachContext* ctx) { + SkASSERT(jfill.isObject()); + + return AttachPaint(jfill, ctx, AttachColor(jfill, ctx)); +} + +sk_sp AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) { + SkASSERT(jfill.isObject()); + + return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx)); +} + +sk_sp AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) { + SkASSERT(jstroke.isObject()); + + return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx))); +} + +sk_sp AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) { + SkASSERT(jstroke.isObject()); + + return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx))); +} + std::vector> AttachMergeGeometryEffect( const Json::Value& jmerge, AttachContext* ctx, std::vector>&& geos) { std::vector> merged; @@ -433,8 +497,10 @@ static constexpr GeometryAttacherT gGeometryAttachers[] = { using PaintAttacherT = sk_sp (*)(const Json::Value&, AttachContext*); static constexpr PaintAttacherT gPaintAttachers[] = { - AttachFillPaint, - AttachStrokePaint, + AttachColorFill, + AttachColorStroke, + AttachGradientFill, + AttachGradientStroke, }; using GroupAttacherT = sk_sp (*)(const Json::Value&, AttachContext*); @@ -468,13 +534,15 @@ struct ShapeInfo { const ShapeInfo* FindShapeInfo(const Json::Value& shape) { static constexpr ShapeInfo gShapeInfo[] = { { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry - { "fl", ShapeType::kPaint , 0 }, // fill -> AttachFillPaint + { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill + { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill { "gr", ShapeType::kGroup , 0 }, // group -> AttachShapeGroup + { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry - { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint + { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect { "tr", ShapeType::kTransform , 0 }, // transform -> In-place handler }; diff --git a/experimental/skotty/SkottyProperties.cpp b/experimental/skotty/SkottyProperties.cpp index 579823b24c..e962a7d538 100644 --- a/experimental/skotty/SkottyProperties.cpp +++ b/experimental/skotty/SkottyProperties.cpp @@ -11,6 +11,7 @@ #include "SkottyPriv.h" #include "SkPath.h" #include "SkSGColor.h" +#include "SkSGGradient.h" #include "SkSGPath.h" #include "SkSGRect.h" #include "SkSGTransform.h" @@ -41,6 +42,19 @@ bool ParsePoints(const Json::Value& v, PointArray* pts) { return true; } +SkColor VecToColor(const float* v, size_t size) { + // best effort to turn this into a color + const auto r = size > 0 ? v[0] : 0, + g = size > 1 ? v[1] : 0, + b = size > 2 ? v[2] : 0, + a = size > 3 ? v[3] : 1; + + return SkColorSetARGB(SkTPin(a, 0, 1) * 255, + SkTPin(r, 0, 1) * 255, + SkTPin(g, 0, 1) * 255, + SkTPin(b, 0, 1) * 255); +} + } // namespace template <> @@ -94,16 +108,7 @@ size_t ValueTraits::Cardinality(const VectorValue& vec) { template <> template <> SkColor ValueTraits::As(const VectorValue& vec) { - // best effort to turn this into a color - const auto r = vec.size() > 0 ? vec[0] : 0, - g = vec.size() > 1 ? vec[1] : 0, - b = vec.size() > 2 ? vec[2] : 0, - a = vec.size() > 3 ? vec[3] : 1; - - return SkColorSetARGB(SkTPin(a, 0, 1) * 255, - SkTPin(r, 0, 1) * 255, - SkTPin(g, 0, 1) * 255, - SkTPin(b, 0, 1) * 255); + return VecToColor(vec.data(), vec.size()); } template <> @@ -251,4 +256,49 @@ void CompositePolyStar::apply() { fPathNode->setPath(poly); } +CompositeGradient::CompositeGradient(sk_sp grad, size_t stopCount) + : fGradient(std::move(grad)) + , fStopCount(stopCount) {} + +void CompositeGradient::apply() { + this->onApply(); + + // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ] + + if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) { + LOG("!! Invalid gradient stop array size: %zu", fColorStops.size()); + return; + } + + std::vector stops; + + // TODO: merge/lerp opacity stops + const auto csEnd = fColorStops.cbegin() + fStopCount * 4; + for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) { + stops.push_back({ *cs, VecToColor(&*(cs + 1), 3) }); + } + + fGradient->setColorStops(std::move(stops)); +} + +CompositeLinearGradient::CompositeLinearGradient(sk_sp grad, size_t stopCount) + : INHERITED(std::move(grad), stopCount) {} + +void CompositeLinearGradient::onApply() { + auto* grad = static_cast(fGradient.get()); + grad->setStartPoint(this->startPoint()); + grad->setEndPoint(this->endPoint()); +} + +CompositeRadialGradient::CompositeRadialGradient(sk_sp grad, size_t stopCount) + : INHERITED(std::move(grad), stopCount) {} + +void CompositeRadialGradient::onApply() { + auto* grad = static_cast(fGradient.get()); + grad->setStartCenter(this->startPoint()); + grad->setEndCenter(this->startPoint()); + grad->setStartRadius(0); + grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint())); +} + } // namespace skotty diff --git a/experimental/skotty/SkottyProperties.h b/experimental/skotty/SkottyProperties.h index cb64ed748b..cd22810c2e 100644 --- a/experimental/skotty/SkottyProperties.h +++ b/experimental/skotty/SkottyProperties.h @@ -22,8 +22,11 @@ namespace sksg { class Color; +class Gradient; +class LinearGradient; class Matrix; class Path; +class RadialGradient; class RRect; class RenderNode;; } @@ -130,6 +133,49 @@ private: using INHERITED = SkRefCnt; }; +class CompositeGradient : public SkRefCnt { +public: + COMPOSITE_PROPERTY(StartPoint, SkPoint , SkPoint::Make(0, 0) ) + COMPOSITE_PROPERTY(EndPoint , SkPoint , SkPoint::Make(0, 0) ) + COMPOSITE_PROPERTY(ColorStops, std::vector, std::vector()) + +protected: + CompositeGradient(sk_sp, size_t stopCount); + + const SkPoint& startPoint() const { return fStartPoint; } + const SkPoint& endPoint() const { return fEndPoint; } + + sk_sp fGradient; + size_t fStopCount; + + virtual void onApply() = 0; + +private: + void apply(); + + using INHERITED = SkRefCnt; +}; + +class CompositeLinearGradient final : public CompositeGradient { +public: + CompositeLinearGradient(sk_sp, size_t stopCount); + +private: + void onApply() override; + + using INHERITED = CompositeGradient; +}; + +class CompositeRadialGradient final : public CompositeGradient { +public: + CompositeRadialGradient(sk_sp, size_t stopCount); + +private: + void onApply() override; + + using INHERITED = CompositeGradient; +}; + #undef COMPOSITE_PROPERTY } // namespace skotty diff --git a/experimental/sksg/paint/SkSGGradient.cpp b/experimental/sksg/paint/SkSGGradient.cpp new file mode 100644 index 0000000000..98e7f395f8 --- /dev/null +++ b/experimental/sksg/paint/SkSGGradient.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSGGradient.h" + +#include "SkGradientShader.h" +#include "SkPaint.h" + +namespace sksg { + +void Gradient::onApplyToPaint(SkPaint* paint) const { + if (fColorStops.empty()) { + paint->setShader(nullptr); + return; + } + + std::vector colors; + std::vector positions; + colors.reserve(fColorStops.size()); + positions.reserve(fColorStops.size()); + + SkScalar position = 0; + for (const auto& stop : fColorStops) { + colors.push_back(stop.fColor); + position = SkTPin(stop.fPosition, position, 1.0f); + positions.push_back(position); + } + + // TODO: detect even stop distributions, pass null for positions. + paint->setShader(this->onMakeShader(colors, positions)); +} + +sk_sp LinearGradient::onMakeShader(const std::vector& colors, + const std::vector& positions) const { + SkASSERT(colors.size() == positions.size()); + + const SkPoint pts[] = { fStartPoint, fEndPoint }; + return SkGradientShader::MakeLinear(pts, colors.data(), positions.data(), colors.size(), + this->getTileMode()); +} + +sk_sp RadialGradient::onMakeShader(const std::vector& colors, + const std::vector& positions) const { + SkASSERT(colors.size() == positions.size()); + + return (fStartRadius <= 0 && fStartCenter == fEndCenter) + ? SkGradientShader::MakeRadial(fEndCenter, fEndRadius, + colors.data(), positions.data(), colors.size(), + this->getTileMode()) + : SkGradientShader::MakeTwoPointConical(fStartCenter, fStartRadius, + fEndCenter, fEndRadius, + colors.data(), positions.data(), colors.size(), + this->getTileMode()); +} + +} //namespace sksg diff --git a/experimental/sksg/paint/SkSGGradient.h b/experimental/sksg/paint/SkSGGradient.h new file mode 100644 index 0000000000..d69cb1495c --- /dev/null +++ b/experimental/sksg/paint/SkSGGradient.h @@ -0,0 +1,105 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSGGradient_DEFINED +#define SkSGGradient_DEFINED + +#include "SkSGPaintNode.h" + +#include "SkColor.h" +#include "SkPoint.h" +#include "SkScalar.h" +#include "SkShader.h" + +#include + +namespace sksg { + +/** + * Gradient base class. + */ +class Gradient : public PaintNode { +public: + struct ColorStop { + SkScalar fPosition; + SkColor fColor; + + bool operator==(const ColorStop& other) const { + return fPosition == other.fPosition && fColor == other.fColor; + } + }; + + SG_ATTRIBUTE(ColorStops, std::vector, fColorStops) + SG_ATTRIBUTE(TileMode , SkShader::TileMode , fTileMode ) + +protected: + void onApplyToPaint(SkPaint*) const final; + + virtual sk_sp onMakeShader(const std::vector& colors, + const std::vector& positions) const = 0; + +protected: + Gradient() = default; + +private: + std::vector fColorStops; + SkShader::TileMode fTileMode = SkShader::kClamp_TileMode; + + using INHERITED = PaintNode; +}; + +class LinearGradient final : public Gradient { +public: + static sk_sp Make() { + return sk_sp(new LinearGradient()); + } + + SG_ATTRIBUTE(StartPoint, SkPoint, fStartPoint) + SG_ATTRIBUTE(EndPoint , SkPoint, fEndPoint ) + +protected: + sk_sp onMakeShader(const std::vector& colors, + const std::vector& positions) const override; + +private: + LinearGradient() = default; + + SkPoint fStartPoint = SkPoint::Make(0, 0), + fEndPoint = SkPoint::Make(0, 0); + + using INHERITED = Gradient; +}; + +class RadialGradient final : public Gradient { +public: + static sk_sp Make() { + return sk_sp(new RadialGradient()); + } + + SG_ATTRIBUTE(StartCenter, SkPoint , fStartCenter) + SG_ATTRIBUTE(EndCenter , SkPoint , fEndCenter ) + SG_ATTRIBUTE(StartRadius, SkScalar, fStartRadius) + SG_ATTRIBUTE(EndRadius , SkScalar, fEndRadius ) + +protected: + sk_sp onMakeShader(const std::vector& colors, + const std::vector& positions) const override; + +private: + RadialGradient() = default; + + SkPoint fStartCenter = SkPoint::Make(0, 0), + fEndCenter = SkPoint::Make(0, 0); + SkScalar fStartRadius = 0, + fEndRadius = 0; + + using INHERITED = Gradient; +}; + +} // namespace sksg + +#endif // SkSGGradient_DEFINED -- cgit v1.2.3