xref: /aosp_15_r20/external/skia/modules/svg/src/SkSVGUse.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2017 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 "modules/svg/include/SkSVGUse.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkScalar.h"
12 #include "include/private/base/SkDebug.h"
13 #include "modules/svg/include/SkSVGAttributeParser.h"
14 #include "modules/svg/include/SkSVGRenderContext.h"
15 
SkSVGUse()16 SkSVGUse::SkSVGUse() : INHERITED(SkSVGTag::kUse) {}
17 
appendChild(sk_sp<SkSVGNode>)18 void SkSVGUse::appendChild(sk_sp<SkSVGNode>) {
19     SkDEBUGF("cannot append child nodes to this element.\n");
20 }
21 
parseAndSetAttribute(const char * n,const char * v)22 bool SkSVGUse::parseAndSetAttribute(const char* n, const char* v) {
23     return INHERITED::parseAndSetAttribute(n, v) ||
24            this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", n, v)) ||
25            this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", n, v)) ||
26            this->setHref(SkSVGAttributeParser::parse<SkSVGIRI>("xlink:href", n, v));
27 }
28 
onPrepareToRender(SkSVGRenderContext * ctx) const29 bool SkSVGUse::onPrepareToRender(SkSVGRenderContext* ctx) const {
30     if (fHref.iri().isEmpty() || !INHERITED::onPrepareToRender(ctx)) {
31         return false;
32     }
33 
34     if (fX.value() || fY.value()) {
35         // Restored when the local SkSVGRenderContext leaves scope.
36         ctx->saveOnce();
37         ctx->canvas()->translate(fX.value(), fY.value());
38     }
39 
40     // TODO: width/height override for <svg> targets.
41 
42     return true;
43 }
44 
onRender(const SkSVGRenderContext & ctx) const45 void SkSVGUse::onRender(const SkSVGRenderContext& ctx) const {
46     const auto ref = ctx.findNodeById(fHref);
47     if (!ref) {
48         return;
49     }
50 
51     ref->render(ctx);
52 }
53 
onAsPath(const SkSVGRenderContext & ctx) const54 SkPath SkSVGUse::onAsPath(const SkSVGRenderContext& ctx) const {
55     const auto ref = ctx.findNodeById(fHref);
56     if (!ref) {
57         return SkPath();
58     }
59 
60     return ref->asPath(ctx);
61 }
62 
onObjectBoundingBox(const SkSVGRenderContext & ctx) const63 SkRect SkSVGUse::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
64     const auto ref = ctx.findNodeById(fHref);
65     if (!ref) {
66         return SkRect::MakeEmpty();
67     }
68 
69     const SkSVGLengthContext& lctx = ctx.lengthContext();
70     const SkScalar x = lctx.resolve(fX, SkSVGLengthContext::LengthType::kHorizontal);
71     const SkScalar y = lctx.resolve(fY, SkSVGLengthContext::LengthType::kVertical);
72 
73     SkRect bounds = ref->objectBoundingBox(ctx);
74     bounds.offset(x, y);
75 
76     return bounds;
77 }
78