[svg] Implement <image> element
https://www.w3.org/TR/SVG11/struct.html#ImageElement
Supported reference types are data and non-local URIs. Resource loading
requires a ResourceProvider to have been set on the render context.
Not handled in this CL:
- preserveAspectRatio support
- SVG reference types (i.e. '<image xlink:href="file.svg" ...')
Bug: skia:10842
Change-Id: Ieec7569f60516b01fc847f4160d0733b1e3a1cf5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/364576
Commit-Queue: Tyler Denniston <tdenniston@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/modules/svg/src/SkSVGImage.cpp b/modules/svg/src/SkSVGImage.cpp
new file mode 100644
index 0000000..d872378
--- /dev/null
+++ b/modules/svg/src/SkSVGImage.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkImage.h"
+#include "modules/svg/include/SkSVGAttributeParser.h"
+#include "modules/svg/include/SkSVGImage.h"
+#include "modules/svg/include/SkSVGRenderContext.h"
+#include "modules/svg/include/SkSVGValue.h"
+#include "src/utils/SkOSPath.h"
+
+bool SkSVGImage::parseAndSetAttribute(const char* n, const char* v) {
+ return INHERITED::parseAndSetAttribute(n, v) ||
+ this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", n, v)) ||
+ this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", n, v)) ||
+ this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", n, v)) ||
+ this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", n, v)) ||
+ this->setHref(SkSVGAttributeParser::parse<SkSVGIRI>("xlink:href", n, v));
+}
+
+bool SkSVGImage::onPrepareToRender(SkSVGRenderContext* ctx) const {
+ // Width or height of 0 disables rendering per spec:
+ // https://www.w3.org/TR/SVG11/struct.html#ImageElement
+ return !fHref.iri().isEmpty() && fWidth.value() > 0 && fHeight.value() > 0 &&
+ INHERITED::onPrepareToRender(ctx);
+}
+
+static sk_sp<SkImage> LoadImage(const sk_sp<skresources::ResourceProvider>& rp,
+ const SkSVGIRI& href) {
+ // TODO: It may be better to use the SVG 'id' attribute as the asset id, to allow
+ // clients to perform asset substitution based on element id.
+ sk_sp<skresources::ImageAsset> imageAsset;
+ switch (href.type()) {
+ case SkSVGIRI::Type::kDataURI:
+ imageAsset = rp->loadImageAsset("", href.iri().c_str(), "");
+ break;
+ case SkSVGIRI::Type::kNonlocal: {
+ const auto path = SkOSPath::Dirname(href.iri().c_str());
+ const auto name = SkOSPath::Basename(href.iri().c_str());
+ imageAsset = rp->loadImageAsset(path.c_str(), name.c_str(), /* id */ name.c_str());
+ break;
+ }
+ default:
+ SkDebugf("error loading image: unhandled iri type %d\n", href.type());
+ return nullptr;
+ }
+
+ return imageAsset ? imageAsset->getFrameData(0).image : nullptr;
+}
+
+void SkSVGImage::onRender(const SkSVGRenderContext& ctx) const {
+ const auto& rp = ctx.resourceProvider();
+ SkASSERT(rp);
+
+ // TODO: svg sources
+ sk_sp<SkImage> image = LoadImage(rp, fHref);
+ if (!image) {
+ SkDebugf("can't render image: load image failed\n");
+ return;
+ }
+
+ // TODO: preserveAspectRatio support
+ const SkSVGLengthContext& lctx = ctx.lengthContext();
+ const SkRect rect = lctx.resolveRect(fX, fY, fWidth, fHeight);
+ ctx.canvas()->drawImageRect(image, rect, SkSamplingOptions(SkFilterMode::kLinear));
+}
+
+SkPath SkSVGImage::onAsPath(const SkSVGRenderContext&) const { return {}; }
+
+SkRect SkSVGImage::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
+ const SkSVGLengthContext& lctx = ctx.lengthContext();
+ return lctx.resolveRect(fX, fY, fWidth, fHeight);
+}