aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2018-01-12 12:25:09 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-01-12 19:20:55 +0000
commit6aaee59c041ce86d88954b32ba14c6fc29410e9c (patch)
tree52e1f68c38b524b237700c66a3213ad37024059d
parent4f34fca3eb7b8551d3b0ec19217afb1437658d2c (diff)
[skotty,sksg] Initial gradient support
TBR= Change-Id: I61e4d46ac14660f4c9ea757be2278e4098131a6b Reviewed-on: https://skia-review.googlesource.com/94121 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Florin Malita <fmalita@chromium.org>
-rw-r--r--BUILD.gn1
-rw-r--r--experimental/skotty/Skotty.cpp106
-rw-r--r--experimental/skotty/SkottyProperties.cpp70
-rw-r--r--experimental/skotty/SkottyProperties.h46
-rw-r--r--experimental/sksg/paint/SkSGGradient.cpp60
-rw-r--r--experimental/sksg/paint/SkSGGradient.h105
6 files changed, 359 insertions, 29 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 16e6819abd..c27af9468f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1392,6 +1392,7 @@ if (skia_enable_tools) {
"experimental/sksg/geometry/SkSGRect.cpp",
"experimental/sksg/geometry/SkSGTrimEffect.cpp",
"experimental/sksg/paint/SkSGColor.cpp",
+ "experimental/sksg/paint/SkSGGradient.cpp",
]
deps = [
":skia",
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<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, Attac
return path_node;
}
-sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx) {
+sk_sp<sksg::Color> 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<CompositeColor>(color_node);
+ auto composite = sk_make_sp<CompositeColor>(color_node);
auto color_attached = BindProperty<VectorValue>(obj["c"], ctx, composite,
[](CompositeColor* node, const VectorValue& c) {
node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
@@ -312,29 +311,70 @@ sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx)
return (color_attached || opacity_attached) ? color_node : nullptr;
}
-sk_sp<sksg::PaintNode> AttachFillPaint(const Json::Value& jfill, AttachContext* ctx) {
- SkASSERT(jfill.isObject());
+sk_sp<sksg::Gradient> 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<sksg::Gradient> gradient_node;
+ sk_sp<CompositeGradient> composite;
+
+ if (ParseInt(obj["t"], 1) == 1) {
+ auto linear_node = sksg::LinearGradient::Make();
+ composite = sk_make_sp<CompositeLinearGradient>(linear_node, stopCount);
+ gradient_node = std::move(linear_node);
+ } else {
+ auto radial_node = sksg::RadialGradient::Make();
+ composite = sk_make_sp<CompositeRadialGradient>(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<VectorValue>(stops["k"], ctx, composite,
+ [](CompositeGradient* node, const VectorValue& stops) {
+ node->setColorStops(stops);
+ });
+ BindProperty<VectorValue>(obj["s"], ctx, composite,
+ [](CompositeGradient* node, const VectorValue& s) {
+ node->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
+ });
+ BindProperty<VectorValue>(obj["e"], ctx, composite,
+ [](CompositeGradient* node, const VectorValue& e) {
+ node->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
+ });
+
+ return gradient_node;
}
-sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) {
+sk_sp<sksg::PaintNode> AttachPaint(const Json::Value& jfill, AttachContext* ctx,
+ sk_sp<sksg::PaintNode> paint_node) {
+ if (paint_node) {
+ paint_node->setAntiAlias(true);
+
+ // TODO: refactor opacity
+ }
+
+ return paint_node;
+}
+
+sk_sp<sksg::PaintNode> AttachStroke(const Json::Value& jstroke, AttachContext* ctx,
+ sk_sp<sksg::PaintNode> 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<ScalarValue>(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<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachConte
return stroke_node;
}
+sk_sp<sksg::PaintNode> AttachColorFill(const Json::Value& jfill, AttachContext* ctx) {
+ SkASSERT(jfill.isObject());
+
+ return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
+}
+
+sk_sp<sksg::PaintNode> AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) {
+ SkASSERT(jfill.isObject());
+
+ return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
+}
+
+sk_sp<sksg::PaintNode> AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) {
+ SkASSERT(jstroke.isObject());
+
+ return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
+}
+
+sk_sp<sksg::PaintNode> AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) {
+ SkASSERT(jstroke.isObject());
+
+ return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
+}
+
std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
std::vector<sk_sp<sksg::GeometryNode>> merged;
@@ -433,8 +497,10 @@ static constexpr GeometryAttacherT gGeometryAttachers[] = {
using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
static constexpr PaintAttacherT gPaintAttachers[] = {
- AttachFillPaint,
- AttachStrokePaint,
+ AttachColorFill,
+ AttachColorStroke,
+ AttachGradientFill,
+ AttachGradientStroke,
};
using GroupAttacherT = sk_sp<sksg::RenderNode> (*)(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<SkScalar>(a, 0, 1) * 255,
+ SkTPin<SkScalar>(r, 0, 1) * 255,
+ SkTPin<SkScalar>(g, 0, 1) * 255,
+ SkTPin<SkScalar>(b, 0, 1) * 255);
+}
+
} // namespace
template <>
@@ -94,16 +108,7 @@ size_t ValueTraits<VectorValue>::Cardinality(const VectorValue& vec) {
template <>
template <>
SkColor ValueTraits<VectorValue>::As<SkColor>(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<SkScalar>(a, 0, 1) * 255,
- SkTPin<SkScalar>(r, 0, 1) * 255,
- SkTPin<SkScalar>(g, 0, 1) * 255,
- SkTPin<SkScalar>(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<sksg::Gradient> 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<sksg::Gradient::ColorStop> 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<sksg::LinearGradient> grad, size_t stopCount)
+ : INHERITED(std::move(grad), stopCount) {}
+
+void CompositeLinearGradient::onApply() {
+ auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
+ grad->setStartPoint(this->startPoint());
+ grad->setEndPoint(this->endPoint());
+}
+
+CompositeRadialGradient::CompositeRadialGradient(sk_sp<sksg::RadialGradient> grad, size_t stopCount)
+ : INHERITED(std::move(grad), stopCount) {}
+
+void CompositeRadialGradient::onApply() {
+ auto* grad = static_cast<sksg::RadialGradient*>(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<SkScalar>, std::vector<SkScalar>())
+
+protected:
+ CompositeGradient(sk_sp<sksg::Gradient>, size_t stopCount);
+
+ const SkPoint& startPoint() const { return fStartPoint; }
+ const SkPoint& endPoint() const { return fEndPoint; }
+
+ sk_sp<sksg::Gradient> fGradient;
+ size_t fStopCount;
+
+ virtual void onApply() = 0;
+
+private:
+ void apply();
+
+ using INHERITED = SkRefCnt;
+};
+
+class CompositeLinearGradient final : public CompositeGradient {
+public:
+ CompositeLinearGradient(sk_sp<sksg::LinearGradient>, size_t stopCount);
+
+private:
+ void onApply() override;
+
+ using INHERITED = CompositeGradient;
+};
+
+class CompositeRadialGradient final : public CompositeGradient {
+public:
+ CompositeRadialGradient(sk_sp<sksg::RadialGradient>, 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<SkColor> colors;
+ std::vector<SkScalar> 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<SkShader> LinearGradient::onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& 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<SkShader> RadialGradient::onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& 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 <vector>
+
+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<ColorStop>, fColorStops)
+ SG_ATTRIBUTE(TileMode , SkShader::TileMode , fTileMode )
+
+protected:
+ void onApplyToPaint(SkPaint*) const final;
+
+ virtual sk_sp<SkShader> onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& positions) const = 0;
+
+protected:
+ Gradient() = default;
+
+private:
+ std::vector<ColorStop> fColorStops;
+ SkShader::TileMode fTileMode = SkShader::kClamp_TileMode;
+
+ using INHERITED = PaintNode;
+};
+
+class LinearGradient final : public Gradient {
+public:
+ static sk_sp<LinearGradient> Make() {
+ return sk_sp<LinearGradient>(new LinearGradient());
+ }
+
+ SG_ATTRIBUTE(StartPoint, SkPoint, fStartPoint)
+ SG_ATTRIBUTE(EndPoint , SkPoint, fEndPoint )
+
+protected:
+ sk_sp<SkShader> onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& 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<RadialGradient> Make() {
+ return sk_sp<RadialGradient>(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<SkShader> onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& 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