blob: 54e9b3302f655ffa4496833940d6b5bb5f368e1f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tests/common/svg/svg_scene.h"
#include <map>
#include <set>
#include "tests/common/path_sink.h"
#include "tests/common/svg/svg_path_sink.h"
#include "tests/common/svg/svg_utils.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#else
#define LOG(...) ((void)0)
#endif
namespace {
// Implements a vector of items of type T that cannot contain duplicates.
// Uses an std::map<> to achieve this, using KEY_COMPARE as the comparison
// function.
template <typename T, typename KEY_COMPARE = std::less<T>>
class UniqueVector {
public:
UniqueVector() = default;
// Try to find |key|, and return its index if present. Otherwise, append it
// to the vector and return its index.
uint32_t
findOrCreate(const T & key)
{
uint32_t index;
auto it = map_.find(key);
if (it == map_.end())
{
items_.push_back(key);
index = static_cast<uint32_t>(items_.size() - 1u);
map_.try_emplace(key, index);
}
else
{
index = it->second;
}
return index;
}
// Find item |key| in vector and return its index, or UINT32_MAX if not found.
uint32_t
find(const T & key) const
{
auto it = map_.find(key);
if (it == map_.end())
return UINT32_MAX;
return it->second;
}
// Return const reference to the items in the vector.
const std::vector<T> &
vector() const
{
return items_;
}
protected:
std::vector<T> items_;
std::map<T, uint32_t, KEY_COMPARE> map_;
};
// Helper struct used in SvgScene::Path and SvgScene::Raster containers.
struct PathCompare
{
bool
operator()(const SvgScene::Path & a, const SvgScene::Path & b) const noexcept
{
if (a.svg_index < b.svg_index)
return true;
if (a.svg_index > b.svg_index)
return false;
if (a.path_id < b.path_id)
return true;
if (a.path_id > b.path_id)
return false;
return false;
}
};
struct RasterCompare
{
bool
operator()(const SvgScene::Raster & a, const SvgScene::Raster & b) const noexcept
{
if (a.svg_index < b.svg_index)
return true;
if (a.svg_index > b.svg_index)
return false;
if (a.path_index < b.path_index)
return true;
if (a.path_index > b.path_index)
return false;
return affine_transform_less(&a.transform, &b.transform);
}
};
} // namespace
class SvgScene::Impl {
public:
explicit Impl(const std::vector<SvgScene::Item> & items)
{
// Maps (item index, raster_id) -> raster_index.
std::map<std::tuple<uint32_t, uint32_t>, uint32_t> raster_id_to_index;
// First, decode all paths and rasters into unique sets.
uint32_t item_index = 0;
LOG("---- svgscene: decode paths and rasters\n");
for (const auto & item : items)
{
uint32_t svg_index = svgs_.findOrCreate(item.svg);
svg_decode_rasters(item.svg, &item.transform, [&](const SvgDecodedRaster & r) {
SvgScene::Path path_key = {
.svg_index = svg_index,
.path_id = r.path_id,
};
uint32_t path_index = paths_.findOrCreate(path_key);
// NOTE: Due to RasterCompare, raster_id will be ignored except when
// inserting new items in |rasters_|. A way to map that ID to the relevant
// |rasters_| index later is needed, hence the use of |raster_ids_to_index|.
SvgScene::Raster raster_key = {
.svg_index = svg_index,
.raster_id = r.raster_id,
.path_index = path_index,
.transform = r.transform,
};
uint32_t raster_index = rasters_.findOrCreate(raster_key);
raster_id_to_index.try_emplace(std::make_pair(item_index, r.raster_id), raster_index);
LOG("item_index:%u svg_index:%u r.path_id:%u path_index:%u r.raster_id:%u "
"raster_index:%u\n",
item_index,
svg_index,
r.path_id,
path_index,
r.raster_id,
raster_index);
return true;
});
item_index++;
}
// Second, decode layers.
{
LOG("---- svgscene: decode layers\n");
uint32_t layer_base = 0;
uint32_t item_index = 0;
for (const auto & item : items)
{
uint32_t svg_index = svgs_.find(item.svg);
svg_decode_layers(item.svg, [&](const SvgDecodedLayer & l) -> bool {
LOG("item_index:%u svg_index:%u l.layer_id:%u l.fill_color:%08x l.fill_opacity:%g "
"l.opacity:%g l.fill_even_odd:%s\n",
item_index,
svg_index,
l.layer_id,
l.fill_color,
l.fill_opacity,
l.opacity,
l.fill_even_odd ? "true" : "false");
Layer layer = {
.svg_index = svg_index,
.layer_id = layer_base + l.layer_id,
.fill_color = l.fill_color,
.fill_opacity = l.fill_opacity * l.opacity,
.fill_even_odd = l.fill_even_odd,
};
for (const SvgDecodedLayer::Print & print : l.prints)
{
// IMPORTANT: print.raster_id might reference a raster that was never
// decoded, because it corresponds to SVG PathStroke commands that are
// not implemented, ignore these.
auto it = raster_id_to_index.find(std::make_pair(item_index, print.raster_id));
if (it == raster_id_to_index.end())
continue;
uint32_t raster_index = it->second;
LOG(" raster_id:%u raster_index:%u tx:%d tx:%d\n",
print.raster_id,
raster_index,
print.tx,
print.ty);
layer.prints.push_back(Print{
.raster_index = raster_index,
.tx = print.tx,
.ty = print.ty,
});
}
layers_.push_back(std::move(layer));
return true;
});
layer_base += svg_layer_count(item.svg);
item_index++;
}
}
}
const std::vector<const svg *> &
unique_svgs() const
{
return svgs_.vector();
}
const std::vector<Path> &
unique_paths() const
{
return paths_.vector();
}
const std::vector<Raster> &
unique_rasters() const
{
return rasters_.vector();
}
const std::vector<Layer> &
layers() const
{
return layers_;
}
protected:
// Unique svgs map.
UniqueVector<const svg *> svgs_;
UniqueVector<SvgScene::Path, PathCompare> paths_;
UniqueVector<SvgScene::Raster, RasterCompare> rasters_;
// svg_index -> raster_id -> index in rasters_.
std::map<uint32_t, std::map<uint32_t, uint32_t>> rasters_to_unique_index_;
std::vector<SvgScene::Layer> layers_;
};
SvgScene::SvgScene() = default;
SvgScene::~SvgScene()
{
invalidate();
}
void
SvgScene::reset()
{
invalidate();
items_.clear();
}
void
SvgScene::addSvgDocument(const struct svg * svg)
{
addSvgDocument(svg, affine_transform_identity);
}
void
SvgScene::addSvgDocument(const struct svg * svg, double tx, double ty)
{
addSvgDocument(svg, affine_transform_make_translation(tx, ty));
}
void
SvgScene::addSvgDocument(const struct svg * svg, affine_transform_t transform)
{
invalidate();
items_.push_back(Item{ svg, transform });
}
const std::vector<const svg *>
SvgScene::unique_svgs() const
{
ensureUpdated();
return impl_->unique_svgs();
}
const std::vector<SvgScene::Path> &
SvgScene::unique_paths() const
{
ensureUpdated();
return impl_->unique_paths();
}
const std::vector<SvgScene::Raster> &
SvgScene::unique_rasters() const
{
ensureUpdated();
return impl_->unique_rasters();
}
const std::vector<SvgScene::Layer> &
SvgScene::layers() const
{
ensureUpdated();
return impl_->layers();
}
void
SvgScene::invalidate()
{
delete impl_;
impl_ = nullptr;
update_needed_ = true;
}
void
SvgScene::update() const
{
if (update_needed_)
{
impl_ = new SvgScene::Impl(items_);
update_needed_ = false;
}
}
void
SvgScene::getBounds(double * xmin, double * ymin, double * xmax, double * ymax)
{
ensureUpdated();
BoundingPathSink sink;
const auto & svgs = impl_->unique_svgs();
const auto & paths = impl_->unique_paths();
for (const Raster & r : impl_->unique_rasters())
svg_decode_path(svgs[r.svg_index], paths[r.path_index].path_id, &r.transform, &sink);
*xmin = sink.bounds().xmin;
*ymin = sink.bounds().ymin;
*xmax = sink.bounds().xmax;
*ymax = sink.bounds().ymax;
}