diff options
author | Florin Malita <fmalita@chromium.org> | 2018-07-09 16:20:47 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-07-10 13:28:15 +0000 |
commit | 418e658a6922923f8d1e1ce2cf00f0a0f6b18ff3 (patch) | |
tree | 3454ad2c2d0a10f2561511ab977753a7cffcc40e /modules/skottie/src | |
parent | 6d98257725b59e01a1a844b1b9c9e80912512b49 (diff) |
[skottie] Apply fully opaque masks as clips
We already have a clip optimization when there is only one opaque mask.
Extend to cover multiple opaque masks, using Merge scene nodes.
TBR=
Change-Id: I24b61f0c0d080b13438c6777e98a8e2fefd09fdd
Reviewed-on: https://skia-review.googlesource.com/140002
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
Diffstat (limited to 'modules/skottie/src')
-rw-r--r-- | modules/skottie/src/Skottie.cpp | 71 |
1 files changed, 52 insertions, 19 deletions
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp index 6cfeddcfce..60009b9bc0 100644 --- a/modules/skottie/src/Skottie.cpp +++ b/modules/skottie/src/Skottie.cpp @@ -376,11 +376,21 @@ sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke, return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx))); } +sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) { + std::vector<sksg::Merge::Rec> merge_recs; + merge_recs.reserve(geos.size()); + + for (const auto& geo : geos) { + merge_recs.push_back( + {std::move(geo), merge_recs.empty() ? sksg::Merge::Mode::kMerge : mode}); + } + + return sksg::Merge::Make(std::move(merge_recs)); +} + std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect( const skjson::ObjectValue& jmerge, AttachContext*, std::vector<sk_sp<sksg::GeometryNode>>&& geos) { - std::vector<sk_sp<sksg::GeometryNode>> merged; - static constexpr sksg::Merge::Mode gModes[] = { sksg::Merge::Mode::kMerge, // "mm": 1 sksg::Merge::Mode::kUnion, // "mm": 2 @@ -391,7 +401,9 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect( const auto mode = gModes[SkTPin<int>(ParseDefault<int>(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes) - 1)]; - merged.push_back(sksg::Merge::Make(std::move(geos), mode)); + + std::vector<sk_sp<sksg::GeometryNode>> merged; + merged.push_back(Merge(std::move(geos), mode)); return merged; } @@ -410,7 +422,7 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect( std::vector<sk_sp<sksg::GeometryNode>> inputs; if (mode == Mode::kMerged) { - inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge)); + inputs.push_back(Merge(std::move(geos), sksg::Merge::Mode::kMerge)); } else { inputs = std::move(geos); } @@ -661,7 +673,7 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape, AttachShap // If we still have multiple geos, reduce using 'merge'. auto geo = drawGeos.size() > 1 - ? sksg::Merge::Make(std::move(drawGeos), sksg::Merge::Mode::kMerge) + ? Merge(std::move(drawGeos), sksg::Merge::Mode::kMerge) : drawGeos[0]; SkASSERT(geo); @@ -974,17 +986,22 @@ private: }; struct MaskInfo { - SkBlendMode fBlendMode; - bool fInvertGeometry; + SkBlendMode fBlendMode; // used when masking with layers/blending + sksg::Merge::Mode fMergeMode; // used when clipping + bool fInvertGeometry; }; const MaskInfo* GetMaskInfo(char mode) { - static constexpr MaskInfo k_add_info = { SkBlendMode::kSrcOver , false }; - static constexpr MaskInfo k_int_info = { SkBlendMode::kSrcIn , false }; + static constexpr MaskInfo k_add_info = + { SkBlendMode::kSrcOver , sksg::Merge::Mode::kUnion , false }; + static constexpr MaskInfo k_int_info = + { SkBlendMode::kSrcIn , sksg::Merge::Mode::kIntersect , false }; // AE 'subtract' is the same as 'intersect' + inverted geometry // (draws the opacity-adjusted paint *outside* the shape). - static constexpr MaskInfo k_sub_info = { SkBlendMode::kSrcIn , true }; - static constexpr MaskInfo k_dif_info = { SkBlendMode::kDifference, false }; + static constexpr MaskInfo k_sub_info = + { SkBlendMode::kSrcIn , sksg::Merge::Mode::kIntersect , true }; + static constexpr MaskInfo k_dif_info = + { SkBlendMode::kDifference, sksg::Merge::Mode::kDifference, false }; switch (mode) { case 'a': return &k_add_info; @@ -1003,8 +1020,9 @@ sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask, if (!jmask) return childNode; struct MaskRecord { - sk_sp<sksg::Path> mask_path; - sk_sp<sksg::Color> mask_paint; + sk_sp<sksg::Path> mask_path; // for clipping and masking + sk_sp<sksg::Color> mask_paint; // for masking + sksg::Merge::Mode merge_mode; // for clipping }; SkSTArray<4, MaskRecord, true> mask_stack; @@ -1055,17 +1073,32 @@ sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask, mask_paint->setOpacity(o * 0.01f); }, &full_opacity); - mask_stack.push_back({mask_path, mask_paint}); + mask_stack.push_back({mask_path, mask_paint, mask_info->fMergeMode}); } if (mask_stack.empty()) return childNode; - if (mask_stack.count() == 1 && !has_opacity) { - // Single, fully-opaque mask => clip path. - return sksg::ClipEffect::Make(std::move(childNode), - std::move(mask_stack.front().mask_path), - true); + // If the masks are fully opaque, we can clip. + if (!has_opacity) { + sk_sp<sksg::GeometryNode> clip_node; + + if (mask_stack.count() == 1) { + // Single path -> just clip. + clip_node = std::move(mask_stack.front().mask_path); + } else { + // Multiple clip paths -> merge. + std::vector<sksg::Merge::Rec> merge_recs; + merge_recs.reserve(SkToSizeT(mask_stack.count())); + + for (const auto& mask : mask_stack) { + const auto mode = merge_recs.empty() ? sksg::Merge::Mode::kMerge : mask.merge_mode; + merge_recs.push_back({std::move(mask.mask_path), mode}); + } + clip_node = sksg::Merge::Make(std::move(merge_recs)); + } + + return sksg::ClipEffect::Make(std::move(childNode), std::move(clip_node), true); } auto mask_group = sksg::Group::Make(); |