| /* |
| * Copyright 2022 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/SkColor.h" |
| #include "include/core/SkOpenTypeSVGDecoder.h" |
| #include "include/core/SkSpan.h" |
| #include "include/core/SkStream.h" |
| #include "include/core/SkTypes.h" |
| #include "include/utils/SkBase64.h" |
| #include "modules/skresources/include/SkResources.h" |
| #include "modules/svg/include/SkSVGDOM.h" |
| #include "modules/svg/include/SkSVGNode.h" |
| #include "modules/svg/include/SkSVGOpenTypeSVGDecoder.h" |
| #include "modules/svg/include/SkSVGRenderContext.h" |
| #include "modules/svg/include/SkSVGSVG.h" |
| #include "modules/svg/include/SkSVGUse.h" |
| #include "src/core/SkEnumerate.h" |
| |
| #include <memory> |
| |
| namespace { |
| class DataResourceProvider final : public skresources::ResourceProvider { |
| public: |
| static sk_sp<skresources::ResourceProvider> Make() { |
| return sk_sp<skresources::ResourceProvider>(new DataResourceProvider()); |
| } |
| |
| sk_sp<skresources::ImageAsset> loadImageAsset(const char rpath[], |
| const char rname[], |
| const char rid[]) const override { |
| if (auto data = decode_datauri("data:image/", rname)) { |
| return skresources::MultiFrameImageAsset::Make(std::move(data)); |
| } |
| return nullptr; |
| } |
| |
| private: |
| DataResourceProvider() = default; |
| |
| static sk_sp<SkData> decode_datauri(const char prefix[], const char uri[]) { |
| // We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data> |
| // (https://en.wikipedia.org/wiki/Data_URI_scheme) |
| static constexpr char kDataURIEncodingStr[] = ";base64,"; |
| |
| const size_t prefixLen = strlen(prefix); |
| if (strncmp(uri, prefix, prefixLen) != 0) { |
| return nullptr; |
| } |
| |
| const char* encoding = strstr(uri + prefixLen, kDataURIEncodingStr); |
| if (!encoding) { |
| return nullptr; |
| } |
| |
| const char* b64Data = encoding + SK_ARRAY_COUNT(kDataURIEncodingStr) - 1; |
| size_t b64DataLen = strlen(b64Data); |
| size_t dataLen; |
| if (SkBase64::Decode(b64Data, b64DataLen, nullptr, &dataLen) != SkBase64::kNoError) { |
| return nullptr; |
| } |
| |
| sk_sp<SkData> data = SkData::MakeUninitialized(dataLen); |
| void* rawData = data->writable_data(); |
| if (SkBase64::Decode(b64Data, b64DataLen, rawData, &dataLen) != SkBase64::kNoError) { |
| return nullptr; |
| } |
| |
| return data; |
| } |
| |
| using INHERITED = ResourceProvider; |
| }; |
| } // namespace |
| |
| SkSVGOpenTypeSVGDecoder::SkSVGOpenTypeSVGDecoder(sk_sp<SkSVGDOM> skSvg, size_t approximateSize) |
| : fSkSvg(std::move(skSvg)) |
| , fApproximateSize(approximateSize) |
| {} |
| |
| SkSVGOpenTypeSVGDecoder::~SkSVGOpenTypeSVGDecoder() = default; |
| |
| std::unique_ptr<SkOpenTypeSVGDecoder> SkSVGOpenTypeSVGDecoder::Make(const uint8_t* svg, |
| size_t svgLength) { |
| std::unique_ptr<SkStreamAsset> stream = SkMemoryStream::MakeDirect(svg, svgLength); |
| if (!stream) { |
| return nullptr; |
| } |
| SkSVGDOM::Builder builder; |
| builder.setResourceProvider(DataResourceProvider::Make()); |
| sk_sp<SkSVGDOM> skSvg = builder.make(*stream); |
| if (!skSvg) { |
| return nullptr; |
| } |
| return std::unique_ptr<SkOpenTypeSVGDecoder>( |
| new SkSVGOpenTypeSVGDecoder(std::move(skSvg), svgLength)); |
| } |
| |
| size_t SkSVGOpenTypeSVGDecoder::approximateSize() { |
| // TODO |
| return fApproximateSize; |
| } |
| |
| bool SkSVGOpenTypeSVGDecoder::render(SkCanvas& canvas, int upem, SkGlyphID glyphId, |
| SkColor foregroundColor, SkSpan<SkColor> palette) { |
| SkSize emSize = SkSize::Make(SkScalar(upem), SkScalar(upem)); |
| fSkSvg->setContainerSize(emSize); |
| |
| SkSVGPresentationContext pctx; |
| pctx.fInherited.fColor.set(foregroundColor); |
| |
| SkTHashMap<SkString, SkSVGColorType> namedColors; |
| if (palette.size()) { |
| for (auto&& [i, color] : SkMakeEnumerate(palette)) { |
| constexpr const size_t colorStringLen = sizeof("color") - 1; |
| char colorIdString[colorStringLen + kSkStrAppendU32_MaxSize + 1] = "color"; |
| *SkStrAppendU32(colorIdString + colorStringLen, i) = 0; |
| |
| namedColors.set(SkString(colorIdString), color); |
| } |
| pctx.fNamedColors = &namedColors; |
| } |
| |
| constexpr const size_t glyphStringLen = sizeof("glyph") - 1; |
| char glyphIdString[glyphStringLen + kSkStrAppendU32_MaxSize + 1] = "glyph"; |
| *SkStrAppendU32(glyphIdString + glyphStringLen, glyphId) = 0; |
| |
| fSkSvg->renderNode(&canvas, pctx, glyphIdString); |
| return true; |
| } |