/* * 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("x", n, v)) || this->setY(SkSVGAttributeParser::parse("y", n, v)) || this->setWidth(SkSVGAttributeParser::parse("width", n, v)) || this->setHeight(SkSVGAttributeParser::parse("height", n, v)) || this->setHref(SkSVGAttributeParser::parse("xlink:href", n, v)) || this->setPreserveAspectRatio(SkSVGAttributeParser::parse( "preserveAspectRatio", 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 LoadImage(const sk_sp& 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 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", (int)href.type()); return nullptr; } return imageAsset ? imageAsset->getFrameData(0).image : nullptr; } SkSVGImage::ImageInfo SkSVGImage::LoadImage(const sk_sp& rp, const SkSVGIRI& iri, const SkRect& viewPort, SkSVGPreserveAspectRatio par) { SkASSERT(rp); // TODO: svg sources sk_sp image = ::LoadImage(rp, iri); if (!image) { return {}; } // Per spec: raster content has implicit viewbox of '0 0 width height'. const SkRect viewBox = SkRect::Make(image->bounds()); // Map and place at x, y specified by viewport const SkMatrix m = ComputeViewboxMatrix(viewBox, viewPort, par); const SkRect dst = m.mapRect(viewBox).makeOffset(viewPort.fLeft, viewPort.fTop); return {std::move(image), dst}; } void SkSVGImage::onRender(const SkSVGRenderContext& ctx) const { // Per spec: x, w, width, height attributes establish the new viewport. const SkSVGLengthContext& lctx = ctx.lengthContext(); const SkRect viewPort = lctx.resolveRect(fX, fY, fWidth, fHeight); const auto imgInfo = LoadImage(ctx.resourceProvider(), fHref, viewPort, fPreserveAspectRatio); if (!imgInfo.fImage) { SkDebugf("can't render image: load image failed\n"); return; } // TODO: image-rendering property ctx.canvas()->drawImageRect( imgInfo.fImage, imgInfo.fDst, 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); }