• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkottieAdapter.h"
9 
10 #include "SkFont.h"
11 #include "SkMatrix.h"
12 #include "SkMatrix44.h"
13 #include "SkPath.h"
14 #include "SkRRect.h"
15 #include "SkSGColor.h"
16 #include "SkSGDraw.h"
17 #include "SkSGGradient.h"
18 #include "SkSGGroup.h"
19 #include "SkSGPath.h"
20 #include "SkSGRect.h"
21 #include "SkSGRenderEffect.h"
22 #include "SkSGText.h"
23 #include "SkSGTransform.h"
24 #include "SkSGTrimEffect.h"
25 #include "SkShaper.h"
26 #include "SkTextBlob.h"
27 #include "SkTextUtils.h"
28 #include "SkTo.h"
29 #include "SkUTF.h"
30 #include "SkottieValue.h"
31 
32 #include <cmath>
33 #include <utility>
34 
35 namespace skottie {
36 
RRectAdapter(sk_sp<sksg::RRect> wrapped_node)37 RRectAdapter::RRectAdapter(sk_sp<sksg::RRect> wrapped_node)
38     : fRRectNode(std::move(wrapped_node)) {}
39 
40 RRectAdapter::~RRectAdapter() = default;
41 
apply()42 void RRectAdapter::apply() {
43     // BM "position" == "center position"
44     auto rr = SkRRect::MakeRectXY(SkRect::MakeXYWH(fPosition.x() - fSize.width() / 2,
45                                                    fPosition.y() - fSize.height() / 2,
46                                                    fSize.width(), fSize.height()),
47                                   fRadius.width(),
48                                   fRadius.height());
49    fRRectNode->setRRect(rr);
50 }
51 
TransformAdapter2D(sk_sp<sksg::Matrix<SkMatrix>> matrix)52 TransformAdapter2D::TransformAdapter2D(sk_sp<sksg::Matrix<SkMatrix>> matrix)
53     : fMatrixNode(std::move(matrix)) {}
54 
55 TransformAdapter2D::~TransformAdapter2D() = default;
56 
totalMatrix() const57 SkMatrix TransformAdapter2D::totalMatrix() const {
58     SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y());
59 
60     t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based
61     t.postRotate(fRotation);
62     t.postTranslate(fPosition.x(), fPosition.y());
63     // TODO: skew
64 
65     return t;
66 }
67 
apply()68 void TransformAdapter2D::apply() {
69     fMatrixNode->setMatrix(this->totalMatrix());
70 }
71 
Vec3(const VectorValue & v)72 TransformAdapter3D::Vec3::Vec3(const VectorValue& v) {
73     fX = v.size() > 0 ? v[0] : 0;
74     fY = v.size() > 1 ? v[1] : 0;
75     fZ = v.size() > 2 ? v[2] : 0;
76 }
77 
TransformAdapter3D(sk_sp<sksg::Matrix<SkMatrix44>> matrix)78 TransformAdapter3D::TransformAdapter3D(sk_sp<sksg::Matrix<SkMatrix44>> matrix)
79     : fMatrixNode(std::move(matrix)) {}
80 
81 TransformAdapter3D::~TransformAdapter3D() = default;
82 
totalMatrix() const83 SkMatrix44 TransformAdapter3D::totalMatrix() const {
84     SkMatrix44 t;
85 
86     t.setTranslate(-fAnchorPoint.fX, -fAnchorPoint.fY, -fAnchorPoint.fZ);
87     t.postScale(fScale.fX / 100, fScale.fY / 100, fScale.fZ / 100);
88 
89     // TODO: SkMatrix44:postRotate()?
90     SkMatrix44 r;
91     r.setRotateDegreesAbout(1, 0, 0, fRotation.fX);
92     t.postConcat(r);
93     r.setRotateDegreesAbout(0, 1, 0, fRotation.fY);
94     t.postConcat(r);
95     r.setRotateDegreesAbout(0, 0, 1, fRotation.fZ);
96     t.postConcat(r);
97 
98     t.postTranslate(fPosition.fX, fPosition.fY, fPosition.fZ);
99 
100     return t;
101 }
102 
apply()103 void TransformAdapter3D::apply() {
104     fMatrixNode->setMatrix(this->totalMatrix());
105 }
106 
RepeaterAdapter(sk_sp<sksg::RenderNode> repeater_node,Composite composite)107 RepeaterAdapter::RepeaterAdapter(sk_sp<sksg::RenderNode> repeater_node, Composite composite)
108     : fRepeaterNode(repeater_node)
109     , fComposite(composite)
110     , fRoot(sksg::Group::Make()) {}
111 
112 RepeaterAdapter::~RepeaterAdapter() = default;
113 
apply()114 void RepeaterAdapter::apply() {
115     static constexpr SkScalar kMaxCount = 512;
116     const auto count = static_cast<size_t>(SkTPin(fCount, 0.0f, kMaxCount) + 0.5f);
117 
118     const auto& compute_transform = [this] (size_t index) {
119         const auto t = fOffset + index;
120 
121         // Position, scale & rotation are "scaled" by index/offset.
122         SkMatrix m = SkMatrix::MakeTrans(-fAnchorPoint.x(),
123                                          -fAnchorPoint.y());
124         m.postScale(std::pow(fScale.x() * .01f, fOffset),
125                     std::pow(fScale.y() * .01f, fOffset));
126         m.postRotate(t * fRotation);
127         m.postTranslate(t * fPosition.x() + fAnchorPoint.x(),
128                         t * fPosition.y() + fAnchorPoint.y());
129 
130         return m;
131     };
132 
133     // TODO: start/end opacity support.
134 
135     // TODO: we can avoid rebuilding all the fragments in most cases.
136     fRoot->clear();
137     for (size_t i = 0; i < count; ++i) {
138         const auto insert_index = (fComposite == Composite::kAbove) ? i : count - i - 1;
139         fRoot->addChild(sksg::TransformEffect::Make(fRepeaterNode,
140                                                     compute_transform(insert_index)));
141     }
142 }
143 
PolyStarAdapter(sk_sp<sksg::Path> wrapped_node,Type t)144 PolyStarAdapter::PolyStarAdapter(sk_sp<sksg::Path> wrapped_node, Type t)
145     : fPathNode(std::move(wrapped_node))
146     , fType(t) {}
147 
148 PolyStarAdapter::~PolyStarAdapter() = default;
149 
apply()150 void PolyStarAdapter::apply() {
151     static constexpr int kMaxPointCount = 100000;
152     const auto count = SkToUInt(SkTPin(SkScalarRoundToInt(fPointCount), 0, kMaxPointCount));
153     const auto arc   = sk_ieee_float_divide(SK_ScalarPI * 2, count);
154 
155     const auto pt_on_circle = [](const SkPoint& c, SkScalar r, SkScalar a) {
156         return SkPoint::Make(c.x() + r * std::cos(a),
157                              c.y() + r * std::sin(a));
158     };
159 
160     // TODO: inner/outer "roundness"?
161 
162     SkPath poly;
163 
164     auto angle = SkDegreesToRadians(fRotation - 90);
165     poly.moveTo(pt_on_circle(fPosition, fOuterRadius, angle));
166     poly.incReserve(fType == Type::kStar ? count * 2 : count);
167 
168     for (unsigned i = 0; i < count; ++i) {
169         if (fType == Type::kStar) {
170             poly.lineTo(pt_on_circle(fPosition, fInnerRadius, angle + arc * 0.5f));
171         }
172         angle += arc;
173         poly.lineTo(pt_on_circle(fPosition, fOuterRadius, angle));
174     }
175 
176     poly.close();
177     fPathNode->setPath(poly);
178 }
179 
GradientAdapter(sk_sp<sksg::Gradient> grad,size_t stopCount)180 GradientAdapter::GradientAdapter(sk_sp<sksg::Gradient> grad, size_t stopCount)
181     : fGradient(std::move(grad))
182     , fStopCount(stopCount) {}
183 
apply()184 void GradientAdapter::apply() {
185     this->onApply();
186 
187     // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ]
188 
189     if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) {
190         // apply() may get called before the stops are set, so only log when we have some stops.
191         if (!fColorStops.empty()) {
192             SkDebugf("!! Invalid gradient stop array size: %zu\n", fColorStops.size());
193         }
194         return;
195     }
196 
197     std::vector<sksg::Gradient::ColorStop> stops;
198 
199     // TODO: merge/lerp opacity stops
200     const auto csEnd = fColorStops.cbegin() + fStopCount * 4;
201     for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) {
202         const auto pos = cs[0];
203         const VectorValue rgb({ cs[1], cs[2], cs[3] });
204 
205         stops.push_back({ pos, ValueTraits<VectorValue>::As<SkColor>(rgb) });
206     }
207 
208     fGradient->setColorStops(std::move(stops));
209 }
210 
LinearGradientAdapter(sk_sp<sksg::LinearGradient> grad,size_t stopCount)211 LinearGradientAdapter::LinearGradientAdapter(sk_sp<sksg::LinearGradient> grad, size_t stopCount)
212     : INHERITED(std::move(grad), stopCount) {}
213 
onApply()214 void LinearGradientAdapter::onApply() {
215     auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
216     grad->setStartPoint(this->startPoint());
217     grad->setEndPoint(this->endPoint());
218 }
219 
RadialGradientAdapter(sk_sp<sksg::RadialGradient> grad,size_t stopCount)220 RadialGradientAdapter::RadialGradientAdapter(sk_sp<sksg::RadialGradient> grad, size_t stopCount)
221     : INHERITED(std::move(grad), stopCount) {}
222 
onApply()223 void RadialGradientAdapter::onApply() {
224     auto* grad = static_cast<sksg::RadialGradient*>(fGradient.get());
225     grad->setStartCenter(this->startPoint());
226     grad->setEndCenter(this->startPoint());
227     grad->setStartRadius(0);
228     grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint()));
229 }
230 
TrimEffectAdapter(sk_sp<sksg::TrimEffect> trimEffect)231 TrimEffectAdapter::TrimEffectAdapter(sk_sp<sksg::TrimEffect> trimEffect)
232     : fTrimEffect(std::move(trimEffect)) {
233     SkASSERT(fTrimEffect);
234 }
235 
236 TrimEffectAdapter::~TrimEffectAdapter() = default;
237 
apply()238 void TrimEffectAdapter::apply() {
239     // BM semantics: start/end are percentages, offset is "degrees" (?!).
240     const auto  start = fStart  / 100,
241                   end = fEnd    / 100,
242                offset = fOffset / 360;
243 
244     auto startT = SkTMin(start, end) + offset,
245           stopT = SkTMax(start, end) + offset;
246     auto   mode = SkTrimPathEffect::Mode::kNormal;
247 
248     if (stopT - startT < 1) {
249         startT -= SkScalarFloorToScalar(startT);
250         stopT  -= SkScalarFloorToScalar(stopT);
251 
252         if (startT > stopT) {
253             using std::swap;
254             swap(startT, stopT);
255             mode = SkTrimPathEffect::Mode::kInverted;
256         }
257     } else {
258         startT = 0;
259         stopT  = 1;
260     }
261 
262     fTrimEffect->setStart(startT);
263     fTrimEffect->setStop(stopT);
264     fTrimEffect->setMode(mode);
265 }
266 
DropShadowEffectAdapter(sk_sp<sksg::DropShadowImageFilter> dropShadow)267 DropShadowEffectAdapter::DropShadowEffectAdapter(sk_sp<sksg::DropShadowImageFilter> dropShadow)
268     : fDropShadow(std::move(dropShadow)) {
269     SkASSERT(fDropShadow);
270 }
271 
272 DropShadowEffectAdapter::~DropShadowEffectAdapter() = default;
273 
apply()274 void DropShadowEffectAdapter::apply() {
275     // fColor -> RGB, fOpacity -> A
276     fDropShadow->setColor(SkColorSetA(fColor, SkTPin(SkScalarRoundToInt(fOpacity), 0, 255)));
277 
278     // The offset is specified in terms of a bearing angle + distance.
279     SkScalar sinV, cosV;
280     sinV = SkScalarSinCos(SkDegreesToRadians(90 - fDirection), &cosV);
281     fDropShadow->setOffset(SkVector::Make(fDistance * cosV, -fDistance * sinV));
282 
283     // Close enough to AE.
284     static constexpr SkScalar kSoftnessToSigmaFactor = 0.3f;
285     const auto sigma = fSoftness * kSoftnessToSigmaFactor;
286     fDropShadow->setSigma(SkVector::Make(sigma, sigma));
287 
288     fDropShadow->setMode(fShadowOnly ? sksg::DropShadowImageFilter::Mode::kShadowOnly
289                                      : sksg::DropShadowImageFilter::Mode::kShadowAndForeground);
290 }
291 
GaussianBlurEffectAdapter(sk_sp<sksg::BlurImageFilter> blur)292 GaussianBlurEffectAdapter::GaussianBlurEffectAdapter(sk_sp<sksg::BlurImageFilter> blur)
293     : fBlur(std::move(blur)) {
294     SkASSERT(fBlur);
295 }
296 
297 GaussianBlurEffectAdapter::~GaussianBlurEffectAdapter() = default;
298 
apply()299 void GaussianBlurEffectAdapter::apply() {
300     static constexpr SkVector kDimensionsMap[] = {
301         { 1, 1 }, // 1 -> horizontal and vertical
302         { 1, 0 }, // 2 -> horizontal
303         { 0, 1 }, // 3 -> vertical
304     };
305 
306     const auto dim_index = SkTPin<size_t>(static_cast<size_t>(fDimensions),
307                                           1, SK_ARRAY_COUNT(kDimensionsMap)) - 1;
308 
309     // Close enough to AE.
310     static constexpr SkScalar kBlurrinessToSigmaFactor = 0.3f;
311     const auto sigma = fBlurriness * kBlurrinessToSigmaFactor;
312 
313     fBlur->setSigma({ sigma * kDimensionsMap[dim_index].x(),
314                       sigma * kDimensionsMap[dim_index].y() });
315 
316     static constexpr SkBlurImageFilter::TileMode kRepeatEdgeMap[] = {
317         SkBlurImageFilter::kClampToBlack_TileMode, // 0 -> repeat edge pixels: off
318         SkBlurImageFilter::       kClamp_TileMode, // 1 -> repeat edge pixels: on
319     };
320 
321     const auto repeat_index = SkTPin<size_t>(static_cast<size_t>(fRepeatEdge),
322                                              0, SK_ARRAY_COUNT(kRepeatEdgeMap) - 1);
323     fBlur->setTileMode(kRepeatEdgeMap[repeat_index]);
324 }
325 
TextAdapter(sk_sp<sksg::Group> root)326 TextAdapter::TextAdapter(sk_sp<sksg::Group> root)
327     : fRoot(std::move(root))
328     , fTextNode(sksg::TextBlob::Make())
329     , fFillColor(sksg::Color::Make(SK_ColorTRANSPARENT))
330     , fStrokeColor(sksg::Color::Make(SK_ColorTRANSPARENT))
331     , fFillNode(sksg::Draw::Make(fTextNode, fFillColor))
332     , fStrokeNode(sksg::Draw::Make(fTextNode, fStrokeColor))
333     , fHadFill(false)
334     , fHadStroke(false) {
335     // Build a SG fragment with the following general format:
336     //
337     // [Group]
338     //   [Draw]
339     //     [FillPaint]
340     //     [Text]*
341     //   [Draw]
342     //     [StrokePaint]
343     //     [Text]*
344     //
345     // * where the text node is shared
346 
347     fFillColor->setAntiAlias(true);
348     fStrokeColor->setAntiAlias(true);
349     fStrokeColor->setStyle(SkPaint::kStroke_Style);
350 }
351 
352 TextAdapter::~TextAdapter() = default;
353 
makeBlob() const354 sk_sp<SkTextBlob> TextAdapter::makeBlob() const {
355     SkFont font(fText.fTypeface, fText.fTextSize);
356     font.setHinting(kNo_SkFontHinting);
357     font.setSubpixel(true);
358     font.setEdging(SkFont::Edging::kAntiAlias);
359 
360     // Helper for interfacing with SkShaper: buffers shaper-fed runs and performs
361     // per-line position adjustments (for external line breaking, horizontal alignment, etc).
362     class BlobMaker final : public SkShaper::RunHandler {
363     public:
364         BlobMaker(SkTextUtils::Align align)
365             : fAlignFactor(AlignFactor(align)) {}
366 
367         Buffer newRunBuffer(const RunInfo& info, const SkFont& font, int glyphCount,
368                             SkSpan<const char> utf8) override {
369             fPendingLineAdvance += info.fAdvance;
370 
371             auto& run = fPendingLineRuns.emplace_back(font, info, glyphCount);
372 
373             return {
374                 run.fGlyphs   .data(),
375                 run.fPositions.data(),
376                 nullptr,
377             };
378         }
379 
380         void commitRun() override { }
381 
382         void commitLine() override {
383             SkScalar line_spacing = 0;
384 
385             for (const auto& run : fPendingLineRuns) {
386                 const auto runSize = run.size();
387                 const auto& blobBuffer = fBuilder.allocRunPos(run.fFont, SkToInt(runSize));
388 
389                 sk_careful_memcpy(blobBuffer.glyphs,
390                                   run.fGlyphs.data(),
391                                   runSize * sizeof(SkGlyphID));
392 
393                 // For each buffered line, perform the following position adjustments:
394                 //   1) horizontal alignment
395                 //   2) vertical advance (based on line number/offset)
396                 //   3) baseline/ascent adjustment
397                 const auto offset = SkVector::Make(fAlignFactor * fPendingLineAdvance.x(),
398                                                    fPendingLineVOffset + run.fInfo.fAscent);
399                 for (size_t i = 0; i < runSize; ++i) {
400                     blobBuffer.points()[i] = run.fPositions[SkToInt(i)] + offset;
401                 }
402 
403                 line_spacing = SkTMax(line_spacing,
404                                       run.fInfo.fDescent - run.fInfo.fAscent + run.fInfo.fLeading);
405             }
406 
407             fPendingLineRuns.reset();
408             fPendingLineVOffset += line_spacing;
409             fPendingLineAdvance  = { 0, 0 };
410         }
411 
412         sk_sp<SkTextBlob> makeBlob() {
413             return fBuilder.make();
414         }
415 
416     private:
417         static float AlignFactor(SkTextUtils::Align align) {
418             switch (align) {
419             case SkTextUtils::kLeft_Align:   return  0.0f;
420             case SkTextUtils::kCenter_Align: return -0.5f;
421             case SkTextUtils::kRight_Align:  return -1.0f;
422             }
423             return 0.0f; // go home, msvc...
424         }
425 
426         struct Run {
427             SkFont                          fFont;
428             SkShaper::RunHandler::RunInfo   fInfo;
429             SkSTArray<128, SkGlyphID, true> fGlyphs;
430             SkSTArray<128, SkPoint  , true> fPositions;
431 
432             Run(const SkFont& font, const SkShaper::RunHandler::RunInfo& info, int count)
433                 : fFont(font)
434                 , fInfo(info)
435                 , fGlyphs   (count)
436                 , fPositions(count) {
437                 fGlyphs   .push_back_n(count);
438                 fPositions.push_back_n(count);
439             }
440 
441             size_t size() const {
442                 SkASSERT(fGlyphs.size() == fPositions.size());
443                 return fGlyphs.size();
444             }
445         };
446 
447         const float fAlignFactor;
448 
449         SkTextBlobBuilder        fBuilder;
450         SkSTArray<2, Run, false> fPendingLineRuns;
451         SkScalar                 fPendingLineVOffset = 0;
452         SkVector                 fPendingLineAdvance = { 0, 0 };
453     };
454 
455     BlobMaker blobMaker(fText.fAlign);
456 
457     const auto& push_line = [&](const char* start, const char* end) {
458         std::unique_ptr<SkShaper> shaper = SkShaper::Make();
459         if (!shaper) {
460             return;
461         }
462 
463         shaper->shape(&blobMaker, font, start, SkToSizeT(end - start), true, { 0, 0 }, SK_ScalarMax);
464     };
465 
466     const auto& is_line_break = [](SkUnichar uch) {
467         // TODO: other explicit breaks?
468         return uch == '\r';
469     };
470 
471     const char* ptr        = fText.fText.c_str();
472     const char* line_start = ptr;
473     const char* end        = ptr + fText.fText.size();
474 
475     while (ptr < end) {
476         if (is_line_break(SkUTF::NextUTF8(&ptr, end))) {
477             push_line(line_start, ptr - 1);
478             line_start = ptr;
479         }
480     }
481     push_line(line_start, ptr);
482 
483     return blobMaker.makeBlob();
484 }
485 
apply()486 void TextAdapter::apply() {
487     fTextNode->setBlob(this->makeBlob());
488     fFillColor->setColor(fText.fFillColor);
489     fStrokeColor->setColor(fText.fStrokeColor);
490     fStrokeColor->setStrokeWidth(fText.fStrokeWidth);
491 
492     // Turn the state transition into a tri-state value:
493     //   -1: detach node
494     //    0: no change
495     //    1: attach node
496     const auto   fill_change = SkToInt(fText.fHasFill) - SkToInt(fHadFill);
497     const auto stroke_change = SkToInt(fText.fHasStroke) - SkToInt(fHadStroke);
498 
499     // Sync SG topology.
500     if (fill_change || stroke_change) {
501         // This is trickier than it should be because sksg::Group only allows adding children
502         // in paint-order.
503         if (stroke_change < 0 || (fHadStroke && fill_change > 0)) {
504             fRoot->removeChild(fStrokeNode);
505         }
506 
507         if (fill_change < 0) {
508             fRoot->removeChild(fFillNode);
509         } else if (fill_change > 0) {
510             fRoot->addChild(fFillNode);
511         }
512 
513         if (stroke_change > 0 || (fHadStroke && fill_change > 0)) {
514             fRoot->addChild(fStrokeNode);
515         }
516     }
517 
518     // Track current state.
519     fHadFill   = fText.fHasFill;
520     fHadStroke = fText.fHasStroke;
521 }
522 
523 } // namespace skottie
524