blob: f60c6ffbca992037cd59caa0f128b693fe5e1632 [file] [log] [blame]
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "modules/sksg/include/SkSGGroup.h"
#include "include/core/SkCanvas.h"
#include <algorithm>
namespace sksg {
Group::Group() = default;
Group::Group(std::vector<sk_sp<RenderNode>> children)
: fChildren(std::move(children)) {
for (const auto& child : fChildren) {
this->observeInval(child);
}
}
Group::~Group() {
for (const auto& child : fChildren) {
this->unobserveInval(child);
}
}
void Group::clear() {
for (const auto& child : fChildren) {
this->unobserveInval(child);
}
fChildren.clear();
}
void Group::addChild(sk_sp<RenderNode> node) {
// should we allow duplicates?
for (const auto& child : fChildren) {
if (child == node) {
return;
}
}
this->observeInval(node);
fChildren.push_back(std::move(node));
this->invalidate();
}
void Group::removeChild(const sk_sp<RenderNode>& node) {
SkDEBUGCODE(const auto origSize = fChildren.size());
fChildren.erase(std::remove(fChildren.begin(), fChildren.end(), node), fChildren.end());
SkASSERT(fChildren.size() == origSize - 1);
this->unobserveInval(node);
this->invalidate();
}
void Group::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
const auto local_ctx = ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
canvas->getTotalMatrix(),
fRequiresIsolation);
for (const auto& child : fChildren) {
child->render(canvas, local_ctx);
}
}
const RenderNode* Group::onNodeAt(const SkPoint& p) const {
for (auto it = fChildren.crbegin(); it != fChildren.crend(); ++it) {
if (const auto* node = (*it)->nodeAt(p)) {
return node;
}
}
return nullptr;
}
SkRect Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
SkASSERT(this->hasInval());
SkRect bounds = SkRect::MakeEmpty();
fRequiresIsolation = false;
for (size_t i = 0; i < fChildren.size(); ++i) {
const auto child_bounds = fChildren[i]->revalidate(ic, ctm);
// If any of the child nodes overlap, group effects require layer isolation.
if (!fRequiresIsolation && i > 0 && child_bounds.intersects(bounds)) {
#if 1
// Testing conservatively against the union of prev bounds is cheap and good enough.
fRequiresIsolation = true;
#else
// Testing exhaustively doesn't seem to increase the layer elision rate in practice.
for (size_t j = 0; j < i; ++ j) {
if (child_bounds.intersects(fChildren[i]->bounds())) {
fRequiresIsolation = true;
break;
}
}
#endif
}
bounds.join(child_bounds);
}
return bounds;
}
} // namespace sksg