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