[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>
This commit is contained in:
Florin Malita 2018-01-12 12:25:09 -05:00 committed by Skia Commit-Bot
parent 4f34fca3eb
commit 6aaee59c04
6 changed files with 364 additions and 34 deletions

View File

@ -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",

View File

@ -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());
auto color = AttachColorPaint(jfill, ctx);
if (color) {
LOG("** Attached color fill: 0x%x\n", color->getColor());
}
return color;
}
sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) {
SkASSERT(jstroke.isObject());
auto stroke_node = AttachColorPaint(jstroke, ctx);
if (!stroke_node)
const auto& stops = obj["g"];
if (!stops.isObject())
return nullptr;
LOG("** Attached color stroke: 0x%x\n", stroke_node->getColor());
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);
// TODO: highlight, angle
gradient_node = std::move(radial_node);
}
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> 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());
if (!stroke_node)
return nullptr;
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
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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