blob: c7d6572caa9f03cf21aed9105acbce89e416a62c [file] [log] [blame]
/*
* 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;
}