[sksg] Hit-testing API
Introduce RenderNode::nodeAt(const SkPoint&) as the entry point for the hit-testing API. This is backed by a onNodeAt() virtual, which gets dispatched throughout the render DAG, and normally stops at the first leaf Draw node in encounters. To support the implementation, introduce a GeometryNode::contains(const SkPoint&) API. This is backed by a onContains() virtual, overridden in each concrete geometry class. Expose nodeAt() on sksg::Scene, and add some basic unit tests. Change-Id: I0c8abd9d1e51ecf2d8b4dd699f325cd636e21084 Reviewed-on: https://skia-review.googlesource.com/c/191296 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
parent
01d5ec8b9f
commit
eb46bd892d
@ -187,6 +187,8 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name
|
||||
return SkRect::MakeSize(fAnimation->size());
|
||||
}
|
||||
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
|
||||
|
||||
void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
|
||||
const auto local_scope =
|
||||
ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(), true);
|
||||
|
@ -33,6 +33,7 @@ protected:
|
||||
ClipEffect(sk_sp<RenderNode>, sk_sp<GeometryNode>, bool aa);
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -27,6 +27,7 @@ protected:
|
||||
explicit ColorFilter(sk_sp<RenderNode>);
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const final;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const final;
|
||||
|
||||
sk_sp<SkColorFilter> fColorFilter;
|
||||
|
||||
|
@ -33,6 +33,7 @@ protected:
|
||||
~Draw() override;
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -24,6 +24,7 @@ protected:
|
||||
~EffectNode() override;
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -27,6 +27,8 @@ public:
|
||||
void clip(SkCanvas*, bool antiAlias) const;
|
||||
void draw(SkCanvas*, const SkPaint&) const;
|
||||
|
||||
bool contains(const SkPoint&) const;
|
||||
|
||||
SkPath asPath() const;
|
||||
|
||||
protected:
|
||||
@ -36,6 +38,8 @@ protected:
|
||||
|
||||
virtual void onDraw(SkCanvas*, const SkPaint&) const = 0;
|
||||
|
||||
virtual bool onContains(const SkPoint&) const = 0;
|
||||
|
||||
virtual SkPath onAsPath() const = 0;
|
||||
|
||||
private:
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -39,6 +39,8 @@ protected:
|
||||
~Group() override;
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
private:
|
||||
|
@ -34,6 +34,7 @@ protected:
|
||||
explicit Image(sk_sp<SkImage>);
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -36,6 +36,7 @@ protected:
|
||||
MaskEffect(sk_sp<RenderNode>, sk_sp<RenderNode> mask, Mode);
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -50,6 +50,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -28,6 +28,7 @@ protected:
|
||||
OpacityEffect(sk_sp<RenderNode>, float);
|
||||
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -25,6 +25,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
@ -80,6 +81,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -64,6 +64,7 @@ public:
|
||||
|
||||
protected:
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -29,10 +29,15 @@ public:
|
||||
// Render the node and its descendants to the canvas.
|
||||
void render(SkCanvas*, const RenderContext* = nullptr) const;
|
||||
|
||||
// Perform a front-to-back hit-test, and return the RenderNode located at |point|.
|
||||
// Normally, hit-testing stops at leaf Draw nodes.
|
||||
const RenderNode* nodeAt(const SkPoint& point) const;
|
||||
|
||||
protected:
|
||||
explicit RenderNode(uint32_t inval_traits = 0);
|
||||
|
||||
virtual void onRender(SkCanvas*, const RenderContext*) const = 0;
|
||||
virtual const RenderNode* onNodeAt(const SkPoint& p) const = 0;
|
||||
|
||||
// Paint property overrides.
|
||||
// These are deferred until we can determine whether they can be applied to the individual
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <vector>
|
||||
|
||||
class SkCanvas;
|
||||
struct SkPoint;
|
||||
|
||||
namespace sksg {
|
||||
|
||||
@ -67,6 +68,7 @@ public:
|
||||
|
||||
void render(SkCanvas*) const;
|
||||
void animate(float t);
|
||||
const RenderNode* nodeAt(const SkPoint&) const;
|
||||
|
||||
void setShowInval(bool show) { fShowInval = show; }
|
||||
|
||||
|
@ -45,6 +45,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
@ -83,6 +84,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -97,6 +97,7 @@ public:
|
||||
|
||||
protected:
|
||||
void onRender(SkCanvas*, const RenderContext*) const override;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
protected:
|
||||
void onClip(SkCanvas*, bool antiAlias) const override;
|
||||
void onDraw(SkCanvas*, const SkPaint&) const override;
|
||||
bool onContains(const SkPoint&) const override;
|
||||
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
SkPath onAsPath() const override;
|
||||
|
@ -36,6 +36,10 @@ void ClipEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
this->INHERITED::onRender(canvas, ctx);
|
||||
}
|
||||
|
||||
const RenderNode* ClipEffect::onNodeAt(const SkPoint& p) const {
|
||||
return fClipNode->contains(p) ? this->INHERITED::onNodeAt(p) : nullptr;
|
||||
}
|
||||
|
||||
SkRect ClipEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -24,6 +24,11 @@ void ColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
this->INHERITED::onRender(canvas, local_ctx);
|
||||
}
|
||||
|
||||
const RenderNode* ColorFilter::onNodeAt(const SkPoint& p) const {
|
||||
// TODO: we likely need to do something more sophisticated than delegate to descendants here.
|
||||
return this->INHERITED::onNodeAt(p);
|
||||
}
|
||||
|
||||
ColorModeFilter::ColorModeFilter(sk_sp<RenderNode> child, sk_sp<Color> color, SkBlendMode mode)
|
||||
: INHERITED(std::move(child))
|
||||
, fColor(std::move(color))
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "SkSGDraw.h"
|
||||
|
||||
#include "SkPath.h"
|
||||
#include "SkSGGeometryNode.h"
|
||||
#include "SkSGInvalidationController.h"
|
||||
#include "SkSGPaintNode.h"
|
||||
@ -40,6 +41,25 @@ void Draw::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
}
|
||||
}
|
||||
|
||||
const RenderNode* Draw::onNodeAt(const SkPoint& p) const {
|
||||
const auto paint = fPaint->makePaint();
|
||||
|
||||
if (!paint.getAlpha()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (paint.getStyle() == SkPaint::Style::kFill_Style && fGeometry->contains(p)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
SkPath stroke_path;
|
||||
if (!paint.getFillPath(fGeometry->asPath(), &stroke_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return stroke_path.contains(p.x(), p.y()) ? this : nullptr;
|
||||
}
|
||||
|
||||
SkRect Draw::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -23,6 +23,10 @@ void EffectNode::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
fChild->render(canvas, ctx);
|
||||
}
|
||||
|
||||
const RenderNode* EffectNode::onNodeAt(const SkPoint& p) const {
|
||||
return fChild->nodeAt(p);
|
||||
}
|
||||
|
||||
SkRect EffectNode::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -24,6 +24,11 @@ void GeometryNode::draw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
this->onDraw(canvas, paint);
|
||||
}
|
||||
|
||||
bool GeometryNode::contains(const SkPoint& p) const {
|
||||
SkASSERT(!this->hasInval());
|
||||
return this->bounds().contains(p.x(), p.y()) ? this->onContains(p) : false;
|
||||
}
|
||||
|
||||
SkPath GeometryNode::asPath() const {
|
||||
SkASSERT(!this->hasInval());
|
||||
return this->onAsPath();
|
||||
|
@ -33,6 +33,10 @@ void GeometryTransform::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawPath(fTransformedPath, paint);
|
||||
}
|
||||
|
||||
bool GeometryTransform::onContains(const SkPoint& p) const {
|
||||
return fTransformedPath.contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkRect GeometryTransform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -66,6 +66,16 @@ void Group::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
}
|
||||
}
|
||||
|
||||
const RenderNode* Group::onNodeAt(const SkPoint& p) const {
|
||||
for (const auto& child : fChildren) {
|
||||
if (const auto* node = child->nodeAt(p)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkRect Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -30,6 +30,11 @@ void Image::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
canvas->drawImage(fImage, 0, 0, &paint);
|
||||
}
|
||||
|
||||
const RenderNode* Image::onNodeAt(const SkPoint& p) const {
|
||||
SkASSERT(this->bounds().contains(p.x(), p.y()));
|
||||
return this;
|
||||
}
|
||||
|
||||
SkRect Image::onRevalidate(InvalidationController*, const SkMatrix& ctm) {
|
||||
return fImage ? SkRect::Make(fImage->bounds()) : SkRect::MakeEmpty();
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ void MaskEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
// Note: the paint overrides in ctx don't apply to the mask.
|
||||
fMaskNode->render(canvas);
|
||||
|
||||
|
||||
SkPaint p;
|
||||
p.setBlendMode(fMaskMode == Mode::kNormal ? SkBlendMode::kSrcIn : SkBlendMode::kSrcOut);
|
||||
canvas->saveLayer(this->bounds(), &p);
|
||||
@ -40,6 +39,11 @@ void MaskEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
this->INHERITED::onRender(canvas, ctx);
|
||||
}
|
||||
|
||||
const RenderNode* MaskEffect::onNodeAt(const SkPoint& p) const {
|
||||
const auto mask_hit = (!!fMaskNode->nodeAt(p) == (fMaskMode == Mode::kNormal));
|
||||
|
||||
return mask_hit ? this->INHERITED::onNodeAt(p) : nullptr;
|
||||
}
|
||||
|
||||
SkRect MaskEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
@ -33,6 +33,10 @@ void Merge::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawPath(fMerged, paint);
|
||||
}
|
||||
|
||||
bool Merge::onContains(const SkPoint& p) const {
|
||||
return fMerged.contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkPath Merge::onAsPath() const {
|
||||
return fMerged;
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ void OpacityEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
this->INHERITED::onRender(canvas, local_context);
|
||||
}
|
||||
|
||||
const RenderNode* OpacityEffect::onNodeAt(const SkPoint& p) const {
|
||||
return (fOpacity > 0) ? this->INHERITED::onNodeAt(p) : nullptr;
|
||||
}
|
||||
|
||||
SkRect OpacityEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -23,6 +23,10 @@ void Path::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawPath(fPath, paint);
|
||||
}
|
||||
|
||||
bool Path::onContains(const SkPoint& p) const {
|
||||
return fPath.contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkRect Path::onRevalidate(InvalidationController*, const SkMatrix&) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -20,6 +20,8 @@ void Plane::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawPaint(paint);
|
||||
}
|
||||
|
||||
bool Plane::onContains(const SkPoint&) const { return true; }
|
||||
|
||||
SkRect Plane::onRevalidate(InvalidationController*, const SkMatrix&) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -23,6 +23,10 @@ void Rect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawRect(fRect, paint);
|
||||
}
|
||||
|
||||
bool Rect::onContains(const SkPoint& p) const {
|
||||
return fRect.contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkRect Rect::onRevalidate(InvalidationController*, const SkMatrix&) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
@ -45,6 +49,22 @@ void RRect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawRRect(fRRect, paint);
|
||||
}
|
||||
|
||||
bool RRect::onContains(const SkPoint& p) const {
|
||||
if (!fRRect.rect().contains(p.x(), p.y())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fRRect.isRect()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: no SkRRect::contains(x, y)
|
||||
return fRRect.contains(SkRect::MakeLTRB(p.x() - SK_ScalarNearlyZero,
|
||||
p.y() - SK_ScalarNearlyZero,
|
||||
p.x() + SK_ScalarNearlyZero,
|
||||
p.y() + SK_ScalarNearlyZero));
|
||||
}
|
||||
|
||||
SkRect RRect::onRevalidate(InvalidationController*, const SkMatrix&) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -39,6 +39,13 @@ SkRect ImageFilterEffect::onRevalidate(InvalidationController* ic, const SkMatri
|
||||
return filter->computeFastBounds(this->INHERITED::onRevalidate(ic, ctm));
|
||||
}
|
||||
|
||||
const RenderNode* ImageFilterEffect::onNodeAt(const SkPoint& p) const {
|
||||
// TODO: map p through the filter DAG and dispatch to descendants?
|
||||
// For now, image filters occlude hit-testing.
|
||||
SkASSERT(this->bounds().contains(p.x(), p.y()));
|
||||
return this;
|
||||
}
|
||||
|
||||
void ImageFilterEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
// TODO: hoist these checks to RenderNode?
|
||||
if (this->bounds().isEmpty())
|
||||
|
@ -20,6 +20,10 @@ void RenderNode::render(SkCanvas* canvas, const RenderContext* ctx) const {
|
||||
this->onRender(canvas, ctx);
|
||||
}
|
||||
|
||||
const RenderNode* RenderNode::nodeAt(const SkPoint& p) const {
|
||||
return this->bounds().contains(p.x(), p.y()) ? this->onNodeAt(p) : nullptr;
|
||||
}
|
||||
|
||||
bool RenderNode::RenderContext::modulatePaint(SkPaint* paint) const {
|
||||
const auto initial_alpha = paint->getAlpha(),
|
||||
alpha = SkToU8(sk_float_round2int(initial_alpha * fOpacity));
|
||||
|
@ -32,6 +32,10 @@ void RoundEffect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawPath(fRoundedPath, paint);
|
||||
}
|
||||
|
||||
bool RoundEffect::onContains(const SkPoint& p) const {
|
||||
return fRoundedPath.contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkPath RoundEffect::onAsPath() const {
|
||||
return fRoundedPath;
|
||||
}
|
||||
|
@ -69,4 +69,8 @@ void Scene::animate(float t) {
|
||||
}
|
||||
}
|
||||
|
||||
const RenderNode* Scene::nodeAt(const SkPoint& p) const {
|
||||
return fRoot->nodeAt(p);
|
||||
}
|
||||
|
||||
} // namespace sksg
|
||||
|
@ -73,6 +73,10 @@ void Text::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawTextBlob(fBlob, aligned_pos.x(), aligned_pos.y(), paint);
|
||||
}
|
||||
|
||||
bool Text::onContains(const SkPoint& p) const {
|
||||
return this->asPath().contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkPath Text::onAsPath() const {
|
||||
// TODO
|
||||
return SkPath();
|
||||
@ -99,6 +103,10 @@ void TextBlob::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawTextBlob(fBlob, fPosition.x(), fPosition.y(), paint);
|
||||
}
|
||||
|
||||
bool TextBlob::onContains(const SkPoint& p) const {
|
||||
return this->asPath().contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkPath TextBlob::onAsPath() const {
|
||||
// TODO
|
||||
return SkPath();
|
||||
|
@ -96,6 +96,15 @@ void TransformEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const
|
||||
this->INHERITED::onRender(canvas, ctx);
|
||||
}
|
||||
|
||||
const RenderNode* TransformEffect::onNodeAt(const SkPoint& p) const {
|
||||
const auto m = TransformPriv::As<SkMatrix>(fTransform);
|
||||
|
||||
SkPoint mapped_p;
|
||||
m.mapPoints(&mapped_p, &p, 1);
|
||||
|
||||
return this->INHERITED::onNodeAt(mapped_p);
|
||||
}
|
||||
|
||||
SkRect TransformEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
|
@ -32,6 +32,10 @@ void TrimEffect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
|
||||
canvas->drawPath(fTrimmedPath, paint);
|
||||
}
|
||||
|
||||
bool TrimEffect::onContains(const SkPoint& p) const {
|
||||
return fTrimmedPath.contains(p.x(), p.y());
|
||||
}
|
||||
|
||||
SkPath TrimEffect::onAsPath() const {
|
||||
return fTrimmedPath;
|
||||
}
|
||||
|
@ -56,6 +56,23 @@ static void check_inval(skiatest::Reporter* reporter, const sk_sp<sksg::Node>& r
|
||||
}
|
||||
}
|
||||
|
||||
struct HitTest {
|
||||
const SkPoint pt;
|
||||
sk_sp<sksg::RenderNode> node;
|
||||
};
|
||||
|
||||
static void check_hittest(skiatest::Reporter* reporter, const sk_sp<sksg::RenderNode>& root,
|
||||
const std::vector<HitTest>& tests) {
|
||||
for (const auto& tst : tests) {
|
||||
const auto* node = root->nodeAt(tst.pt);
|
||||
if (node != tst.node.get()) {
|
||||
SkDebugf("*** nodeAt(%f, %f) - expected %p, got %p\n",
|
||||
tst.pt.x(), tst.pt.y(), tst.node.get(), node);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, tst.node.get() == node);
|
||||
}
|
||||
}
|
||||
|
||||
static void inval_test1(skiatest::Reporter* reporter) {
|
||||
auto color = sksg::Color::Make(0xff000000);
|
||||
auto r1 = sksg::Rect::Make(SkRect::MakeWH(100, 100)),
|
||||
@ -63,9 +80,11 @@ static void inval_test1(skiatest::Reporter* reporter) {
|
||||
auto grp = sksg::Group::Make();
|
||||
auto matrix = sksg::Matrix<SkMatrix>::Make(SkMatrix::I());
|
||||
auto root = sksg::TransformEffect::Make(grp, matrix);
|
||||
auto d1 = sksg::Draw::Make(r1, color),
|
||||
d2 = sksg::Draw::Make(r2, color);
|
||||
|
||||
grp->addChild(sksg::Draw::Make(r1, color));
|
||||
grp->addChild(sksg::Draw::Make(r2, color));
|
||||
grp->addChild(d1);
|
||||
grp->addChild(d2);
|
||||
|
||||
{
|
||||
// Initial revalidation.
|
||||
@ -73,6 +92,15 @@ static void inval_test1(skiatest::Reporter* reporter) {
|
||||
SkRect::MakeWH(100, 100),
|
||||
SkRectPriv::MakeLargeS32(),
|
||||
nullptr);
|
||||
|
||||
check_hittest(reporter, root, {
|
||||
{{ -1, 0 }, nullptr },
|
||||
{{ 0, -1 }, nullptr },
|
||||
{{ 100, 0 }, nullptr },
|
||||
{{ 0, 100 }, nullptr },
|
||||
{{ 0, 0 }, d1 },
|
||||
{{ 99, 99 }, d1 },
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@ -83,6 +111,22 @@ static void inval_test1(skiatest::Reporter* reporter) {
|
||||
SkRect::MakeWH(300, 200),
|
||||
SkRect::MakeWH(300, 200),
|
||||
&damage);
|
||||
|
||||
check_hittest(reporter, root, {
|
||||
{{ -1, 0 }, nullptr },
|
||||
{{ 0, -1 }, nullptr },
|
||||
{{ 100, 0 }, nullptr },
|
||||
{{ 0, 100 }, nullptr },
|
||||
{{ 0, 0 }, d1 },
|
||||
{{ 99, 99 }, d1 },
|
||||
|
||||
{{ 199, 100 }, nullptr },
|
||||
{{ 200, 99 }, nullptr },
|
||||
{{ 300, 100 }, nullptr },
|
||||
{{ 200, 200 }, nullptr },
|
||||
{{ 200, 100 }, d2 },
|
||||
{{ 299, 199 }, d2 },
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@ -103,6 +147,22 @@ static void inval_test1(skiatest::Reporter* reporter) {
|
||||
SkRect::MakeWH(300, 200),
|
||||
SkRect::MakeWH(100, 100),
|
||||
&damage);
|
||||
|
||||
check_hittest(reporter, root, {
|
||||
{{ -1, 0 }, nullptr },
|
||||
{{ 0, -1 }, nullptr },
|
||||
{{ 50, 0 }, nullptr },
|
||||
{{ 0, 100 }, nullptr },
|
||||
{{ 0, 0 }, d1 },
|
||||
{{ 49, 99 }, d1 },
|
||||
|
||||
{{ 199, 100 }, nullptr },
|
||||
{{ 200, 99 }, nullptr },
|
||||
{{ 300, 100 }, nullptr },
|
||||
{{ 200, 200 }, nullptr },
|
||||
{{ 200, 100 }, d2 },
|
||||
{{ 299, 199 }, d2 },
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@ -113,6 +173,22 @@ static void inval_test1(skiatest::Reporter* reporter) {
|
||||
SkRect::MakeWH(600, 400),
|
||||
SkRect::MakeWH(600, 400),
|
||||
&damage);
|
||||
|
||||
check_hittest(reporter, root, {
|
||||
{{ -1, 0 }, nullptr },
|
||||
{{ 0, -1 }, nullptr },
|
||||
{{ 25, 0 }, nullptr },
|
||||
{{ 0, 50 }, nullptr },
|
||||
{{ 0, 0 }, d1 },
|
||||
{{ 24, 49 }, d1 },
|
||||
|
||||
{{ 99, 50 }, nullptr },
|
||||
{{ 100, 49 }, nullptr },
|
||||
{{ 150, 50 }, nullptr },
|
||||
{{ 100, 100 }, nullptr },
|
||||
{{ 100, 50 }, d2 },
|
||||
{{ 149, 99 }, d2 },
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@ -123,6 +199,22 @@ static void inval_test1(skiatest::Reporter* reporter) {
|
||||
SkRect::MakeWH(500, 400),
|
||||
SkRect::MakeLTRB(400, 200, 600, 400),
|
||||
&damage);
|
||||
|
||||
check_hittest(reporter, root, {
|
||||
{{ -1, 0 }, nullptr },
|
||||
{{ 0, -1 }, nullptr },
|
||||
{{ 25, 0 }, nullptr },
|
||||
{{ 0, 50 }, nullptr },
|
||||
{{ 0, 0 }, d1 },
|
||||
{{ 24, 49 }, d1 },
|
||||
|
||||
{{ 99, 50 }, nullptr },
|
||||
{{ 100, 49 }, nullptr },
|
||||
{{ 125, 50 }, nullptr },
|
||||
{{ 100, 100 }, nullptr },
|
||||
{{ 100, 50 }, d2 },
|
||||
{{ 124, 99 }, d2 },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,8 @@ protected:
|
||||
fSlide->draw(canvas);
|
||||
}
|
||||
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
|
||||
|
||||
private:
|
||||
void tick(SkMSec t) {
|
||||
fSlide->animate(SkAnimTimer(t * 1e6));
|
||||
|
Loading…
Reference in New Issue
Block a user