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