Use ThorVG instead of NanoSVG for importing SVGs
ThorVG is a platform-independent portable library for drawing vector-based scene and animation. Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
This commit is contained in:
parent
9b3535a33a
commit
8d02759c72
|
@ -362,10 +362,6 @@ Comment: Multi-channel signed distance field generator
|
|||
Copyright: 2016, Viktor Chlumsky
|
||||
License: MIT
|
||||
|
||||
Files: ./thirdparty/nanosvg/
|
||||
Comment: NanoSVG
|
||||
Copyright: 2013-2014, Mikko Mononen
|
||||
License: Zlib
|
||||
|
||||
Files: ./thirdparty/oidn/
|
||||
Comment: Intel Open Image Denoise
|
||||
|
@ -403,6 +399,11 @@ Comment: libSquish
|
|||
Copyright: 2006, Simon Brown
|
||||
License: Expat
|
||||
|
||||
Files: ./thirdparty/thorvg/
|
||||
Comment: ThorVG
|
||||
Copyright: 2020-2021, Samsung Electronics Co., Ltd.
|
||||
License: Expat
|
||||
|
||||
Files: ./thirdparty/tinyexr/
|
||||
Comment: TinyEXR
|
||||
Copyright: 2014-2021, Syoyo Fujita
|
||||
|
|
|
@ -30,7 +30,9 @@
|
|||
|
||||
#include "editor_themes.h"
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/variant/dictionary.h"
|
||||
#include "editor_fonts.h"
|
||||
#include "editor_icons.gen.h"
|
||||
#include "editor_scale.h"
|
||||
|
@ -95,6 +97,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false,
|
|||
|
||||
Ref<ImageTexture> texture(memnew(ImageTexture));
|
||||
Ref<Image> img = p_texture->get_image();
|
||||
ERR_FAIL_NULL_V(img, Ref<Texture2D>());
|
||||
img = img->duplicate();
|
||||
|
||||
if (p_flip_y) {
|
||||
|
@ -109,7 +112,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false,
|
|||
}
|
||||
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, float p_saturation = 1.0) {
|
||||
static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, float p_saturation = 1.0, Dictionary p_convert_colors = Dictionary()) {
|
||||
Ref<ImageTexture> icon = memnew(ImageTexture);
|
||||
Ref<Image> img = memnew(Image);
|
||||
|
||||
|
@ -117,8 +120,9 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color,
|
|||
// Generating upsampled icons is slower, and the benefit is hardly visible
|
||||
// with integer editor scales.
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale);
|
||||
ImageLoaderSVG::create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.set_replace_colors(p_convert_colors);
|
||||
img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color);
|
||||
if (p_saturation != 1.0) {
|
||||
img->adjust_bcs(1.0, 1.0, p_saturation);
|
||||
}
|
||||
|
@ -135,8 +139,10 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color,
|
|||
void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = true, int p_thumb_size = 32, bool p_only_thumbs = false, float p_icon_saturation = 1.0) {
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
// The default icon theme is designed to be used for a dark theme.
|
||||
// This dictionary stores color codes to convert to other colors
|
||||
// This dictionary stores Color values to convert to other colors
|
||||
// for better readability on a light theme.
|
||||
// Godot Color values are used to avoid the ambiguity of strings
|
||||
// (where "#ffffff", "fff", and "white" are all equivalent).
|
||||
Dictionary dark_icon_color_dictionary;
|
||||
|
||||
// The names of the icons to never convert, even if one of their colors
|
||||
|
@ -243,8 +249,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
|
|||
dark_icon_color_dictionary[Color::html("#45ff8b")] = success_color;
|
||||
dark_icon_color_dictionary[Color::html("#dbab09")] = warning_color;
|
||||
|
||||
ImageLoaderSVG::set_convert_colors(&dark_icon_color_dictionary);
|
||||
|
||||
// Generate icons.
|
||||
if (!p_only_thumbs) {
|
||||
for (int i = 0; i < editor_icons_count; i++) {
|
||||
|
@ -255,7 +259,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
|
|||
}
|
||||
|
||||
const int is_exception = exceptions.has(editor_icons_names[i]);
|
||||
const Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation);
|
||||
const Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation, dark_icon_color_dictionary);
|
||||
|
||||
p_theme->set_icon(editor_icons_names[i], "EditorIcons", icon);
|
||||
}
|
||||
|
@ -269,7 +273,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
|
|||
for (int i = 0; i < editor_bg_thumbs_count; i++) {
|
||||
const int index = editor_bg_thumbs_indices[i];
|
||||
const int is_exception = exceptions.has(editor_icons_names[index]);
|
||||
const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter);
|
||||
const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary);
|
||||
|
||||
p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon);
|
||||
}
|
||||
|
@ -278,13 +282,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
|
|||
for (int i = 0; i < editor_md_thumbs_count; i++) {
|
||||
const int index = editor_md_thumbs_indices[i];
|
||||
const bool is_exception = exceptions.has(editor_icons_names[index]);
|
||||
const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter);
|
||||
const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary);
|
||||
|
||||
p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon);
|
||||
}
|
||||
}
|
||||
|
||||
ImageLoaderSVG::set_convert_colors(nullptr);
|
||||
#else
|
||||
WARN_PRINT("SVG support disabled, editor icons won't be rendered.");
|
||||
#endif
|
||||
|
|
|
@ -9,16 +9,66 @@ env_svg = env_modules.Clone()
|
|||
|
||||
thirdparty_obj = []
|
||||
|
||||
thirdparty_dir = "#thirdparty/nanosvg/"
|
||||
thirdparty_dir = "#thirdparty/thorvg/"
|
||||
thirdparty_sources = [
|
||||
"nanosvg.cc",
|
||||
"src/lib/tvgBezier.cpp",
|
||||
"src/lib/tvgCanvas.cpp",
|
||||
"src/lib/tvgFill.cpp",
|
||||
"src/lib/tvgGlCanvas.cpp",
|
||||
"src/lib/tvgInitializer.cpp",
|
||||
"src/lib/tvgLinearGradient.cpp",
|
||||
"src/lib/tvgLoader.cpp",
|
||||
"src/lib/tvgLzw.cpp",
|
||||
"src/lib/tvgPaint.cpp",
|
||||
"src/lib/tvgPicture.cpp",
|
||||
"src/lib/tvgRadialGradient.cpp",
|
||||
"src/lib/tvgRender.cpp",
|
||||
"src/lib/tvgSaver.cpp",
|
||||
"src/lib/tvgScene.cpp",
|
||||
"src/lib/tvgShape.cpp",
|
||||
"src/lib/tvgSwCanvas.cpp",
|
||||
"src/lib/tvgTaskScheduler.cpp",
|
||||
"src/loaders/raw/tvgRawLoader.cpp",
|
||||
"src/loaders/svg/tvgXmlParser.cpp",
|
||||
"src/loaders/svg/tvgSvgUtil.cpp",
|
||||
"src/loaders/svg/tvgSvgSceneBuilder.cpp",
|
||||
"src/loaders/svg/tvgSvgPath.cpp",
|
||||
"src/loaders/svg/tvgSvgLoader.cpp",
|
||||
"src/loaders/tvg/tvgTvgBinInterpreter.cpp",
|
||||
"src/loaders/tvg/tvgTvgLoader.cpp",
|
||||
"src/loaders/jpg/tvgJpgLoader.cpp",
|
||||
"src/loaders/jpg/tvgJpgd.cpp",
|
||||
"src/loaders/external_png/tvgPngLoader.cpp",
|
||||
"src/lib/sw_engine/tvgSwFill.cpp",
|
||||
"src/lib/sw_engine/tvgSwImage.cpp",
|
||||
"src/lib/sw_engine/tvgSwMath.cpp",
|
||||
"src/lib/sw_engine/tvgSwMemPool.cpp",
|
||||
"src/lib/sw_engine/tvgSwRaster.cpp",
|
||||
"src/lib/sw_engine/tvgSwRenderer.cpp",
|
||||
"src/lib/sw_engine/tvgSwRle.cpp",
|
||||
"src/lib/sw_engine/tvgSwShape.cpp",
|
||||
"src/lib/sw_engine/tvgSwStroke.cpp",
|
||||
"src/savers/tvg/tvgTvgSaver.cpp",
|
||||
]
|
||||
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_svg.Prepend(CPPPATH=[thirdparty_dir])
|
||||
env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"])
|
||||
|
||||
env_thirdparty = env_svg.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.Prepend(
|
||||
CPPPATH=[
|
||||
thirdparty_dir + "src/lib",
|
||||
thirdparty_dir + "src/lib/sw_engine",
|
||||
thirdparty_dir + "src/loaders/raw",
|
||||
thirdparty_dir + "src/loaders/svg",
|
||||
thirdparty_dir + "src/loaders/jpg",
|
||||
thirdparty_dir + "src/loaders/png",
|
||||
thirdparty_dir + "src/loaders/tvg",
|
||||
thirdparty_dir + "src/savers/tvg",
|
||||
]
|
||||
)
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env.modules_sources += thirdparty_obj
|
||||
|
||||
|
|
|
@ -30,132 +30,118 @@
|
|||
|
||||
#include "image_loader_svg.h"
|
||||
|
||||
#include <nanosvg.h>
|
||||
#include <nanosvgrast.h>
|
||||
#include "core/os/memory.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
void SVGRasterizer::rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride) {
|
||||
nsvgRasterize(rasterizer, p_image, p_tx, p_ty, p_scale, p_dst, p_w, p_h, p_stride);
|
||||
}
|
||||
#include <thorvg.h>
|
||||
|
||||
SVGRasterizer::SVGRasterizer() {
|
||||
rasterizer = nsvgCreateRasterizer();
|
||||
}
|
||||
void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_string) {
|
||||
// Replace colors in the SVG based on what is configured in `replace_colors`.
|
||||
// Used to change the colors of editor icons based on the used theme.
|
||||
// The strings being replaced are typically of the form:
|
||||
// fill="#5abbef"
|
||||
// But can also be 3-letter codes, include alpha, be "none" or a named color
|
||||
// string ("blue"). So we convert to Godot Color to compare with `replace_colors`.
|
||||
|
||||
SVGRasterizer::~SVGRasterizer() {
|
||||
nsvgDeleteRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
SVGRasterizer ImageLoaderSVG::rasterizer;
|
||||
|
||||
inline void change_nsvg_paint_color(NSVGpaint *p_paint, const uint32_t p_old, const uint32_t p_new) {
|
||||
if (p_paint->type == NSVG_PAINT_COLOR) {
|
||||
if (p_paint->color << 8 == p_old << 8) {
|
||||
p_paint->color = (p_paint->color & 0xFF000000) | (p_new & 0x00FFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_paint->type == NSVG_PAINT_LINEAR_GRADIENT || p_paint->type == NSVG_PAINT_RADIAL_GRADIENT) {
|
||||
for (int stop_index = 0; stop_index < p_paint->gradient->nstops; stop_index++) {
|
||||
if (p_paint->gradient->stops[stop_index].color << 8 == p_old << 8) {
|
||||
p_paint->gradient->stops[stop_index].color = p_new;
|
||||
const int prefix_len = p_prefix.length();
|
||||
int pos = r_string.find(p_prefix);
|
||||
while (pos != -1) {
|
||||
pos += prefix_len; // Skip prefix.
|
||||
int end_pos = r_string.find("\"", pos);
|
||||
ERR_FAIL_COND_MSG(end_pos == -1, vformat("Malformed SVG string after property \"%s\".", p_prefix));
|
||||
const String color_code = r_string.substr(pos, end_pos - pos);
|
||||
if (color_code != "none" && !color_code.begins_with("url(")) {
|
||||
const Color color = Color(color_code); // Handles both HTML codes and named colors.
|
||||
if (replace_colors.has(color)) {
|
||||
r_string = r_string.left(pos) + "#" + replace_colors[color].operator Color().to_html(false) + r_string.substr(end_pos);
|
||||
}
|
||||
}
|
||||
// Search for other occurrences.
|
||||
pos = r_string.find(p_prefix, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageLoaderSVG::_convert_colors(NSVGimage *p_svg_image) {
|
||||
for (NSVGshape *shape = p_svg_image->shapes; shape != nullptr; shape = shape->next) {
|
||||
for (int i = 0; i < replace_colors.old_colors.size(); i++) {
|
||||
change_nsvg_paint_color(&(shape->stroke), replace_colors.old_colors[i], replace_colors.new_colors[i]);
|
||||
change_nsvg_paint_color(&(shape->fill), replace_colors.old_colors[i], replace_colors.new_colors[i]);
|
||||
void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color) {
|
||||
ERR_FAIL_COND(Math::is_zero_approx(p_scale));
|
||||
|
||||
if (p_convert_color) {
|
||||
_replace_color_property("stop-color=\"", p_string);
|
||||
_replace_color_property("fill=\"", p_string);
|
||||
_replace_color_property("stroke=\"", p_string);
|
||||
}
|
||||
|
||||
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
|
||||
PackedByteArray bytes = p_string.to_utf8_buffer();
|
||||
|
||||
tvg::Result result = picture->load((const char *)bytes.ptr(), bytes.size(), "svg", true);
|
||||
if (result != tvg::Result::Success) {
|
||||
return;
|
||||
}
|
||||
float fw, fh;
|
||||
picture->viewbox(nullptr, nullptr, &fw, &fh);
|
||||
|
||||
uint32_t width = MIN(fw * p_scale, 16 * 1024);
|
||||
uint32_t height = MIN(fh * p_scale, 16 * 1024);
|
||||
picture->size(width, height);
|
||||
|
||||
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
|
||||
// Note: memalloc here, be sure to memfree before any return.
|
||||
uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height);
|
||||
|
||||
tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT);
|
||||
if (res != tvg::Result::Success) {
|
||||
memfree(buffer);
|
||||
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
|
||||
}
|
||||
|
||||
res = sw_canvas->push(move(picture));
|
||||
if (res != tvg::Result::Success) {
|
||||
memfree(buffer);
|
||||
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
|
||||
}
|
||||
|
||||
res = sw_canvas->draw();
|
||||
if (res != tvg::Result::Success) {
|
||||
memfree(buffer);
|
||||
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
|
||||
}
|
||||
|
||||
res = sw_canvas->sync();
|
||||
if (res != tvg::Result::Success) {
|
||||
memfree(buffer);
|
||||
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
|
||||
}
|
||||
|
||||
Vector<uint8_t> image;
|
||||
image.resize(width * height * sizeof(uint32_t));
|
||||
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
for (uint32_t x = 0; x < width; x++) {
|
||||
uint32_t n = buffer[y * width + x];
|
||||
const size_t offset = sizeof(uint32_t) * width * y + sizeof(uint32_t) * x;
|
||||
image.write[offset + 0] = (n >> 16) & 0xff;
|
||||
image.write[offset + 1] = (n >> 8) & 0xff;
|
||||
image.write[offset + 2] = n & 0xff;
|
||||
image.write[offset + 3] = (n >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageLoaderSVG::set_convert_colors(Dictionary *p_replace_color) {
|
||||
if (p_replace_color) {
|
||||
Dictionary replace_color = *p_replace_color;
|
||||
for (int i = 0; i < replace_color.keys().size(); i++) {
|
||||
Variant o_c = replace_color.keys()[i];
|
||||
Variant n_c = replace_color[replace_color.keys()[i]];
|
||||
if (o_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) {
|
||||
Color old_color = o_c;
|
||||
Color new_color = n_c;
|
||||
replace_colors.old_colors.push_back(old_color.to_abgr32());
|
||||
replace_colors.new_colors.push_back(new_color.to_abgr32());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
replace_colors.old_colors.clear();
|
||||
replace_colors.new_colors.clear();
|
||||
}
|
||||
}
|
||||
res = sw_canvas->clear(true);
|
||||
memfree(buffer);
|
||||
|
||||
Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const Vector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors) {
|
||||
NSVGimage *svg_image;
|
||||
const uint8_t *src_r = p_data->ptr();
|
||||
svg_image = nsvgParse((char *)src_r, "px", 96);
|
||||
if (svg_image == nullptr) {
|
||||
ERR_PRINT("SVG Corrupted");
|
||||
return ERR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
if (convert_colors) {
|
||||
_convert_colors(svg_image);
|
||||
}
|
||||
|
||||
const float upscale = upsample ? 2.0 : 1.0;
|
||||
|
||||
const int w = (int)(svg_image->width * p_scale * upscale);
|
||||
ERR_FAIL_COND_V_MSG(w > Image::MAX_WIDTH, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max width.", rtos(p_scale)));
|
||||
|
||||
const int h = (int)(svg_image->height * p_scale * upscale);
|
||||
ERR_FAIL_COND_V_MSG(h > Image::MAX_HEIGHT, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max height.", rtos(p_scale)));
|
||||
|
||||
Vector<uint8_t> dst_image;
|
||||
dst_image.resize(w * h * 4);
|
||||
|
||||
uint8_t *dw = dst_image.ptrw();
|
||||
|
||||
rasterizer.rasterize(svg_image, 0, 0, p_scale * upscale, (unsigned char *)dw, w, h, w * 4);
|
||||
|
||||
p_image->create(w, h, false, Image::FORMAT_RGBA8, dst_image);
|
||||
if (upsample) {
|
||||
p_image->shrink_x2();
|
||||
}
|
||||
|
||||
nsvgDelete(svg_image);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors) {
|
||||
size_t str_len = strlen(p_svg_str);
|
||||
Vector<uint8_t> src_data;
|
||||
src_data.resize(str_len + 1);
|
||||
uint8_t *src_w = src_data.ptrw();
|
||||
memcpy(src_w, p_svg_str, str_len + 1);
|
||||
|
||||
return _create_image(p_image, &src_data, p_scale, upsample, convert_colors);
|
||||
}
|
||||
|
||||
Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
|
||||
uint64_t size = f->get_length();
|
||||
Vector<uint8_t> src_image;
|
||||
src_image.resize(size + 1);
|
||||
uint8_t *src_w = src_image.ptrw();
|
||||
f->get_buffer(src_w, size);
|
||||
src_w[size] = '\0';
|
||||
|
||||
return _create_image(p_image, &src_image, p_scale, 1.0);
|
||||
p_image->create(width, height, false, Image::FORMAT_RGBA8, image);
|
||||
}
|
||||
|
||||
void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
p_extensions->push_back("svg");
|
||||
p_extensions->push_back("svgz");
|
||||
}
|
||||
|
||||
ImageLoaderSVG::ImageLoaderSVG() {
|
||||
Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) {
|
||||
String svg = p_fileaccess->get_as_utf8_string();
|
||||
create_image_from_string(p_image, svg, p_scale, false, false);
|
||||
ERR_FAIL_COND_V(p_image->is_empty(), FAILED);
|
||||
if (p_force_linear) {
|
||||
p_image->srgb_to_linear();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ImageLoaderSVG::ReplaceColors ImageLoaderSVG::replace_colors;
|
||||
|
|
|
@ -32,38 +32,18 @@
|
|||
#define IMAGE_LOADER_SVG_H
|
||||
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
// Forward declare and include thirdparty headers in .cpp.
|
||||
struct NSVGrasterizer;
|
||||
struct NSVGimage;
|
||||
|
||||
class SVGRasterizer {
|
||||
NSVGrasterizer *rasterizer;
|
||||
|
||||
public:
|
||||
void rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride);
|
||||
|
||||
SVGRasterizer();
|
||||
~SVGRasterizer();
|
||||
};
|
||||
|
||||
class ImageLoaderSVG : public ImageFormatLoader {
|
||||
static struct ReplaceColors {
|
||||
List<uint32_t> old_colors;
|
||||
List<uint32_t> new_colors;
|
||||
} replace_colors;
|
||||
static SVGRasterizer rasterizer;
|
||||
static void _convert_colors(NSVGimage *p_svg_image);
|
||||
static Error _create_image(Ref<Image> p_image, const Vector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors = false);
|
||||
Dictionary replace_colors;
|
||||
void _replace_color_property(const String &p_prefix, String &r_string);
|
||||
|
||||
public:
|
||||
static void set_convert_colors(Dictionary *p_replace_color = nullptr);
|
||||
static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors = false);
|
||||
// Called by the editor to handle theme icon colors.
|
||||
void set_replace_colors(Dictionary p_replace_colors) { replace_colors = p_replace_colors; }
|
||||
void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color);
|
||||
|
||||
virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale);
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||
ImageLoaderSVG();
|
||||
virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) override;
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
|
||||
};
|
||||
|
||||
#endif // IMAGE_LOADER_SVG_H
|
||||
|
|
|
@ -32,13 +32,23 @@
|
|||
|
||||
#include "image_loader_svg.h"
|
||||
|
||||
#include <thorvg.h>
|
||||
|
||||
static ImageLoaderSVG *image_loader_svg = nullptr;
|
||||
|
||||
void register_svg_types() {
|
||||
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
|
||||
if (tvg::Initializer::init(tvgEngine, 0) != tvg::Result::Success) {
|
||||
return;
|
||||
}
|
||||
image_loader_svg = memnew(ImageLoaderSVG);
|
||||
ImageLoader::add_image_format_loader(image_loader_svg);
|
||||
}
|
||||
|
||||
void unregister_svg_types() {
|
||||
if (!image_loader_svg) {
|
||||
return;
|
||||
}
|
||||
memdelete(image_loader_svg);
|
||||
tvg::Initializer::term(tvg::CanvasEngine::Sw);
|
||||
}
|
||||
|
|
|
@ -477,19 +477,6 @@ Files extracted from the upstream source:
|
|||
- Files in `core/` folder.
|
||||
- `LICENSE.txt` and `CHANGELOG.md`
|
||||
|
||||
|
||||
## nanosvg
|
||||
|
||||
- Upstream: https://github.com/memononen/nanosvg
|
||||
- Version: git (ccdb1995134d340a93fb20e3a3d323ccb3838dd0, 2021)
|
||||
- License: zlib
|
||||
|
||||
Files extracted from the upstream source:
|
||||
|
||||
- All .h files in `src/`
|
||||
- LICENSE.txt
|
||||
|
||||
|
||||
## oidn
|
||||
|
||||
- Upstream: https://github.com/OpenImageDenoise/oidn
|
||||
|
@ -628,6 +615,18 @@ The `tinyexr.cc` file was modified to include `zlib.h` which we provide,
|
|||
instead of `miniz.h` as an external dependency.
|
||||
|
||||
|
||||
## thorvg
|
||||
|
||||
- Upstream: https://github.com/Samsung/thorvg
|
||||
- Version: 0.7.0 (e527f565b770f0a41df821e6618ccaeea94f465e, 2021)
|
||||
- License: MIT
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
||||
See `thorvg/update-thorvg.sh` for extraction instructions. Set the version
|
||||
number and run the script.
|
||||
|
||||
|
||||
## vhacd
|
||||
|
||||
- Upstream: https://github.com/kmammou/v-hacd
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
Copyright (c) 2013-14 Mikko Mononen memon@inside.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "math.h"
|
||||
#define NANOSVG_ALL_COLOR_KEYWORDS
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
#include "nanosvg.h"
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvgrast.h"
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
|||
Hermet Park <chuneon.park@samsung.com>
|
||||
Prudhvi Raj Vasireddi <prudhvi.raj@samsung.com>
|
||||
Junsu Choi <jsuya.choi@samsung.com>
|
||||
Pranay Samanta <pranay.ks@samsung.com>
|
||||
Mateusz Palkowski <m.palkowski@samsung.com>
|
||||
Subhransu Mohanty <sub.mohanty@samsung.com>
|
||||
Mira Grudzinska <m.grudzinska@samsung.com>
|
||||
Michal Szczecinski <m.szczecinsk@partner.samsung.com>
|
||||
Shinwoo Kim <cinoo.kim@samsung.com>
|
||||
Piotr Kalota <p.kalota@samsung.com>
|
||||
Vincent Torri <vincent.torri@gmail.com>
|
||||
Pankaj Kumar <pankaj.m1@samsung.com>
|
||||
Patryk Kaczmarek <patryk.k@partner.samsung.com>
|
||||
Michal Maciola <m.maciola@samsung.com>
|
||||
Peter Vullings <peter@projectitis.com>
|
|
@ -0,0 +1,7 @@
|
|||
Copyright (c) 2020 - 2021 notice for the ThorVG Project (see AUTHORS)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef THORVG_CONFIG_H
|
||||
#define THORVG_CONFIG_H
|
||||
|
||||
#define THORVG_SW_RASTER_SUPPORT 1
|
||||
|
||||
#define THORVG_SVG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_PNG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_TVG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_TVG_SAVER_SUPPORT 1
|
||||
|
||||
#define THORVG_JPG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_VERSION_STRING "0.7.0"
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SW_COMMON_H_
|
||||
#define _TVG_SW_COMMON_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
#if 0
|
||||
#include <sys/time.h>
|
||||
static double timeStamp()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (tv.tv_sec + tv.tv_usec / 1000000.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SW_CURVE_TYPE_POINT 0
|
||||
#define SW_CURVE_TYPE_CUBIC 1
|
||||
#define SW_ANGLE_PI (180L << 16)
|
||||
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
|
||||
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
|
||||
#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
|
||||
|
||||
using SwCoord = signed long;
|
||||
using SwFixed = signed long long;
|
||||
|
||||
struct SwPoint
|
||||
{
|
||||
SwCoord x, y;
|
||||
|
||||
SwPoint& operator+=(const SwPoint& rhs)
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SwPoint operator+(const SwPoint& rhs) const
|
||||
{
|
||||
return {x + rhs.x, y + rhs.y};
|
||||
}
|
||||
|
||||
SwPoint operator-(const SwPoint& rhs) const
|
||||
{
|
||||
return {x - rhs.x, y - rhs.y};
|
||||
}
|
||||
|
||||
bool operator==(const SwPoint& rhs) const
|
||||
{
|
||||
return (x == rhs.x && y == rhs.y);
|
||||
}
|
||||
|
||||
bool operator!=(const SwPoint& rhs) const
|
||||
{
|
||||
return (x != rhs.x || y != rhs.y);
|
||||
}
|
||||
|
||||
bool zero() const
|
||||
{
|
||||
if (x == 0 && y == 0) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool small() const
|
||||
{
|
||||
//2 is epsilon...
|
||||
if (abs(x) < 2 && abs(y) < 2) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct SwSize
|
||||
{
|
||||
SwCoord w, h;
|
||||
};
|
||||
|
||||
struct SwOutline
|
||||
{
|
||||
SwPoint* pts; //the outline's points
|
||||
uint16_t ptsCnt; //number of points in the glyph
|
||||
uint16_t reservedPtsCnt;
|
||||
uint16_t* cntrs; //the contour end points
|
||||
uint16_t cntrsCnt; //number of contours in glyph
|
||||
uint16_t reservedCntrsCnt;
|
||||
uint8_t* types; //curve type
|
||||
bool* closed; //opened or closed path?
|
||||
FillRule fillRule;
|
||||
};
|
||||
|
||||
struct SwSpan
|
||||
{
|
||||
uint16_t x, y;
|
||||
uint16_t len;
|
||||
uint8_t coverage;
|
||||
};
|
||||
|
||||
struct SwRleData
|
||||
{
|
||||
SwSpan *spans;
|
||||
uint32_t alloc;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct SwBBox
|
||||
{
|
||||
SwPoint min, max;
|
||||
|
||||
void reset()
|
||||
{
|
||||
min.x = min.y = max.x = max.y = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct SwFill
|
||||
{
|
||||
struct SwLinear {
|
||||
float dx, dy;
|
||||
float len;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SwRadial {
|
||||
float a11, a12, shiftX;
|
||||
float a21, a22, shiftY;
|
||||
float detSecDeriv;
|
||||
float a;
|
||||
};
|
||||
|
||||
union {
|
||||
SwLinear linear;
|
||||
SwRadial radial;
|
||||
};
|
||||
|
||||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
|
||||
bool translucent;
|
||||
};
|
||||
|
||||
struct SwStrokeBorder
|
||||
{
|
||||
uint32_t ptsCnt;
|
||||
uint32_t maxPts;
|
||||
SwPoint* pts;
|
||||
uint8_t* tags;
|
||||
int32_t start; //index of current sub-path start point
|
||||
bool movable; //true: for ends of lineto borders
|
||||
};
|
||||
|
||||
struct SwStroke
|
||||
{
|
||||
SwFixed angleIn;
|
||||
SwFixed angleOut;
|
||||
SwPoint center;
|
||||
SwFixed lineLength;
|
||||
SwFixed subPathAngle;
|
||||
SwPoint ptStartSubPath;
|
||||
SwFixed subPathLineLength;
|
||||
SwFixed width;
|
||||
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
StrokeJoin joinSaved;
|
||||
SwFill* fill = nullptr;
|
||||
|
||||
SwStrokeBorder borders[2];
|
||||
|
||||
float sx, sy;
|
||||
|
||||
bool firstPt;
|
||||
bool closedSubPath;
|
||||
bool handleWideStrokes;
|
||||
};
|
||||
|
||||
struct SwDashStroke
|
||||
{
|
||||
SwOutline* outline;
|
||||
float curLen;
|
||||
int32_t curIdx;
|
||||
Point ptStart;
|
||||
Point ptCur;
|
||||
float* pattern;
|
||||
uint32_t cnt;
|
||||
bool curOpGap;
|
||||
};
|
||||
|
||||
struct SwShape
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
SwStroke* stroke = nullptr;
|
||||
SwFill* fill = nullptr;
|
||||
SwRleData* rle = nullptr;
|
||||
SwRleData* strokeRle = nullptr;
|
||||
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
|
||||
|
||||
bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
|
||||
};
|
||||
|
||||
struct SwImage
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
SwRleData* rle = nullptr;
|
||||
uint32_t* data = nullptr;
|
||||
uint32_t w, h, stride;
|
||||
int32_t ox = 0; //offset x
|
||||
int32_t oy = 0; //offset y
|
||||
float scale;
|
||||
|
||||
bool direct = false; //draw image directly (with offset)
|
||||
bool scaled = false; //draw scaled image
|
||||
};
|
||||
|
||||
struct SwBlender
|
||||
{
|
||||
uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
};
|
||||
|
||||
struct SwCompositor;
|
||||
|
||||
struct SwSurface : Surface
|
||||
{
|
||||
SwBlender blender; //mandatory
|
||||
SwCompositor* compositor = nullptr; //compositor (optional)
|
||||
};
|
||||
|
||||
struct SwCompositor : Compositor
|
||||
{
|
||||
SwSurface* recoverSfc; //Recover surface when composition is started
|
||||
SwCompositor* recoverCmp; //Recover compositor when composition is done
|
||||
SwImage image;
|
||||
SwBBox bbox;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct SwMpool
|
||||
{
|
||||
SwOutline* outline;
|
||||
SwOutline* strokeOutline;
|
||||
unsigned allocSize;
|
||||
};
|
||||
|
||||
static inline SwCoord TO_SWCOORD(float val)
|
||||
{
|
||||
return SwCoord(val * 64.0f);
|
||||
}
|
||||
|
||||
static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
|
||||
{
|
||||
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
|
||||
((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
|
||||
}
|
||||
|
||||
static inline uint32_t INTERPOLATE(uint32_t a, uint32_t c0, uint32_t c1)
|
||||
{
|
||||
return (((((((c0 >> 8) & 0xff00ff) - ((c1 >> 8) & 0xff00ff)) * a) + (c1 & 0xff00ff00)) & 0xff00ff00) + ((((((c0 & 0xff00ff) - (c1 & 0xff00ff)) * a) >> 8) + (c1 & 0xff00ff)) & 0xff00ff));
|
||||
}
|
||||
|
||||
static inline SwCoord HALF_STROKE(float width)
|
||||
{
|
||||
return TO_SWCOORD(width * 0.5f);
|
||||
}
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b);
|
||||
int64_t mathDivide(int64_t a, int64_t b);
|
||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
|
||||
void mathRotate(SwPoint& pt, SwFixed angle);
|
||||
SwFixed mathTan(SwFixed angle);
|
||||
SwFixed mathAtan(const SwPoint& pt);
|
||||
SwFixed mathCos(SwFixed angle);
|
||||
SwFixed mathSin(SwFixed angle);
|
||||
void mathSplitCubic(SwPoint* base);
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
|
||||
SwFixed mathLength(const SwPoint& pt);
|
||||
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
||||
SwPoint mathTransform(const Point* to, const Matrix* transform);
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
|
||||
|
||||
void shapeReset(SwShape* shape);
|
||||
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepared(const SwShape* shape);
|
||||
bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias);
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
void shapeFree(SwShape* shape);
|
||||
void shapeDelStroke(SwShape* shape);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||
void shapeResetFill(SwShape* shape);
|
||||
void shapeResetStrokeFill(SwShape* shape);
|
||||
void shapeDelFill(SwShape* shape);
|
||||
void shapeDelStrokeFill(SwShape* shape);
|
||||
|
||||
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
|
||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
||||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
|
||||
void strokeFree(SwStroke* stroke);
|
||||
|
||||
bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
|
||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
||||
void imageReset(SwImage* image);
|
||||
void imageFree(SwImage* image);
|
||||
|
||||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||
void fillReset(SwFill* fill);
|
||||
void fillFree(SwFill* fill);
|
||||
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
|
||||
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
|
||||
|
||||
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
|
||||
void rleFree(SwRleData* rle);
|
||||
void rleReset(SwRleData* rle);
|
||||
void rleClipPath(SwRleData *rle, const SwRleData *clip);
|
||||
void rleClipRect(SwRleData *rle, const SwBBox* clip);
|
||||
|
||||
SwMpool* mpoolInit(uint32_t threads);
|
||||
bool mpoolTerm(SwMpool* mpool);
|
||||
bool mpoolClear(SwMpool* mpool);
|
||||
SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx);
|
||||
void mpoolRetOutline(SwMpool* mpool, unsigned idx);
|
||||
SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||
|
||||
bool rasterCompositor(SwSurface* surface);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
|
||||
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
|
||||
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
|
||||
bool rasterClear(SwSurface* surface);
|
||||
void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
|
||||
void rasterUnpremultiply(SwSurface* surface);
|
||||
|
||||
#endif /* _TVG_SW_COMMON_H_ */
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#define GRADIENT_STOP_SIZE 1024
|
||||
#define FIXPT_BITS 8
|
||||
#define FIXPT_SIZE (1<<FIXPT_BITS)
|
||||
|
||||
|
||||
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint32_t opacity)
|
||||
{
|
||||
if (!fill->ctable) {
|
||||
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
|
||||
if (!fill->ctable) return false;
|
||||
}
|
||||
|
||||
const Fill::ColorStop* colors;
|
||||
auto cnt = fdata->colorStops(&colors);
|
||||
if (cnt == 0 || !colors) return false;
|
||||
|
||||
auto pColors = colors;
|
||||
|
||||
auto a = (pColors->a * opacity) / 255;
|
||||
if (a < 255) fill->translucent = true;
|
||||
|
||||
auto r = pColors->r;
|
||||
auto g = pColors->g;
|
||||
auto b = pColors->b;
|
||||
auto rgba = surface->blender.join(r, g, b, a);
|
||||
|
||||
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
|
||||
auto pos = 1.5f * inc;
|
||||
uint32_t i = 0;
|
||||
|
||||
fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a);
|
||||
|
||||
while (pos <= pColors->offset) {
|
||||
fill->ctable[i] = fill->ctable[i - 1];
|
||||
++i;
|
||||
pos += inc;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < cnt - 1; ++j) {
|
||||
auto curr = colors + j;
|
||||
auto next = curr + 1;
|
||||
auto delta = 1.0f / (next->offset - curr->offset);
|
||||
auto a2 = (next->a * opacity) / 255;
|
||||
if (!fill->translucent && a2 < 255) fill->translucent = true;
|
||||
|
||||
auto rgba2 = surface->blender.join(next->r, next->g, next->b, a2);
|
||||
|
||||
while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
|
||||
auto t = (pos - curr->offset) * delta;
|
||||
auto dist = static_cast<int32_t>(255 * t);
|
||||
auto dist2 = 255 - dist;
|
||||
|
||||
auto color = INTERPOLATE(dist2, rgba, rgba2);
|
||||
fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
|
||||
|
||||
++i;
|
||||
pos += inc;
|
||||
}
|
||||
rgba = rgba2;
|
||||
a = a2;
|
||||
}
|
||||
rgba = ALPHA_BLEND((rgba | 0xff000000), a);
|
||||
|
||||
for (; i < GRADIENT_STOP_SIZE; ++i)
|
||||
fill->ctable[i] = rgba;
|
||||
|
||||
//Make sure the last color stop is represented at the end of the table
|
||||
fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform)
|
||||
{
|
||||
float x1, x2, y1, y2;
|
||||
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
|
||||
|
||||
fill->linear.dx = x2 - x1;
|
||||
fill->linear.dy = y2 - y1;
|
||||
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
|
||||
if (fill->linear.len < FLT_EPSILON) return true;
|
||||
|
||||
fill->linear.dx /= fill->linear.len;
|
||||
fill->linear.dy /= fill->linear.len;
|
||||
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
|
||||
|
||||
auto gradTransform = linear->transform();
|
||||
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
|
||||
|
||||
if (isTransformation) {
|
||||
if (transform) gradTransform = mathMultiply(transform, &gradTransform);
|
||||
} else if (transform) {
|
||||
gradTransform = *transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
|
||||
if (isTransformation) {
|
||||
Matrix invTransform;
|
||||
if (!mathInverse(&gradTransform, &invTransform)) return false;
|
||||
|
||||
fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
|
||||
|
||||
auto dx = fill->linear.dx;
|
||||
fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
|
||||
fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
|
||||
|
||||
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
if (fill->linear.len < FLT_EPSILON) return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
|
||||
{
|
||||
float radius, cx, cy;
|
||||
if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
|
||||
if (radius < FLT_EPSILON) return true;
|
||||
|
||||
float invR = 1.0f / radius;
|
||||
fill->radial.shiftX = -cx;
|
||||
fill->radial.shiftY = -cy;
|
||||
fill->radial.a = radius;
|
||||
|
||||
auto gradTransform = radial->transform();
|
||||
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
|
||||
|
||||
if (isTransformation) {
|
||||
if (transform) gradTransform = mathMultiply(transform, &gradTransform);
|
||||
} else if (transform) {
|
||||
gradTransform = *transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
|
||||
if (isTransformation) {
|
||||
Matrix invTransform;
|
||||
if (!mathInverse(&gradTransform, &invTransform)) return false;
|
||||
|
||||
fill->radial.a11 = invTransform.e11 * invR;
|
||||
fill->radial.a12 = invTransform.e12 * invR;
|
||||
fill->radial.shiftX += invTransform.e13;
|
||||
fill->radial.a21 = invTransform.e21 * invR;
|
||||
fill->radial.a22 = invTransform.e22 * invR;
|
||||
fill->radial.shiftY += invTransform.e23;
|
||||
fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
|
||||
|
||||
fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
|
||||
} else {
|
||||
fill->radial.a11 = fill->radial.a22 = invR;
|
||||
fill->radial.a12 = fill->radial.a21 = 0.0f;
|
||||
fill->radial.detSecDeriv = 2.0f * invR * invR;
|
||||
}
|
||||
fill->radial.shiftX *= invR;
|
||||
fill->radial.shiftY *= invR;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _clamp(const SwFill* fill, int32_t pos)
|
||||
{
|
||||
switch (fill->spread) {
|
||||
case FillSpread::Pad: {
|
||||
if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
|
||||
else if (pos < 0) pos = 0;
|
||||
break;
|
||||
}
|
||||
case FillSpread::Repeat: {
|
||||
pos = pos % GRADIENT_STOP_SIZE;
|
||||
if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
|
||||
break;
|
||||
}
|
||||
case FillSpread::Reflect: {
|
||||
auto limit = GRADIENT_STOP_SIZE * 2;
|
||||
pos = pos % limit;
|
||||
if (pos < 0) pos = limit + pos;
|
||||
if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
|
||||
{
|
||||
int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
|
||||
return fill->ctable[_clamp(fill, i)];
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _pixel(const SwFill* fill, float pos)
|
||||
{
|
||||
auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
|
||||
return fill->ctable[_clamp(fill, i)];
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
|
||||
{
|
||||
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
|
||||
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
|
||||
|
||||
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
|
||||
auto detSecondDerivative = fill->radial.detSecDeriv;
|
||||
// detFirstDerivative = d(det)/dx
|
||||
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
|
||||
auto det = rx * rx + ry * ry;
|
||||
|
||||
for (uint32_t i = 0 ; i < len ; ++i) {
|
||||
*dst = _pixel(fill, sqrtf(det));
|
||||
++dst;
|
||||
det += detFirstDerivative;
|
||||
detFirstDerivative += detSecondDerivative;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (mathZero(inc)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
||||
rasterRGBA32(dst, color, 0, len);
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
|
||||
auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
|
||||
for (uint32_t j = 0; j < len; ++j) {
|
||||
*dst = _fixedPixel(fill, t2);
|
||||
++dst;
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
*dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
|
||||
++dst;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
|
||||
{
|
||||
if (!fill) return false;
|
||||
|
||||
fill->spread = fdata->spread();
|
||||
|
||||
if (ctable) {
|
||||
if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
|
||||
}
|
||||
|
||||
if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
|
||||
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
|
||||
} else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
|
||||
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
|
||||
}
|
||||
|
||||
//LOG: What type of gradient?!
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void fillReset(SwFill* fill)
|
||||
{
|
||||
if (fill->ctable) {
|
||||
free(fill->ctable);
|
||||
fill->ctable = nullptr;
|
||||
}
|
||||
fill->translucent = false;
|
||||
}
|
||||
|
||||
|
||||
void fillFree(SwFill* fill)
|
||||
{
|
||||
if (!fill) return;
|
||||
|
||||
if (fill->ctable) free(fill->ctable);
|
||||
|
||||
free(fill);
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline bool _onlyShifted(const Matrix* m)
|
||||
{
|
||||
if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->outline = mpoolReqOutline(mpool, tid);
|
||||
auto outline = image->outline;
|
||||
|
||||
if (outline->reservedPtsCnt < 5) {
|
||||
outline->reservedPtsCnt = 5;
|
||||
outline->pts = static_cast<SwPoint*>(realloc(outline->pts, outline->reservedPtsCnt * sizeof(SwPoint)));
|
||||
outline->types = static_cast<uint8_t*>(realloc(outline->types, outline->reservedPtsCnt * sizeof(uint8_t)));
|
||||
}
|
||||
|
||||
if (outline->reservedCntrsCnt < 1) {
|
||||
outline->reservedCntrsCnt = 1;
|
||||
outline->cntrs = static_cast<uint16_t*>(realloc(outline->cntrs, outline->reservedCntrsCnt * sizeof(uint16_t)));
|
||||
outline->closed = static_cast<bool*>(realloc(outline->closed, outline->reservedCntrsCnt * sizeof(bool)));
|
||||
outline->closed[0] = true;
|
||||
}
|
||||
|
||||
auto w = static_cast<float>(image->w);
|
||||
auto h = static_cast<float>(image->h);
|
||||
|
||||
Point to[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform);
|
||||
outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
++outline->ptsCnt;
|
||||
}
|
||||
|
||||
outline->pts[outline->ptsCnt] = outline->pts[0];
|
||||
outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
++outline->ptsCnt;
|
||||
|
||||
outline->cntrs[outline->cntrsCnt] = outline->ptsCnt - 1;
|
||||
++outline->cntrsCnt;
|
||||
|
||||
image->outline = outline;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->direct = _onlyShifted(transform);
|
||||
|
||||
//Fast track: Non-transformed image but just shifted.
|
||||
if (image->direct) {
|
||||
image->ox = -static_cast<uint32_t>(round(transform->e13));
|
||||
image->oy = -static_cast<uint32_t>(round(transform->e23));
|
||||
//Figure out the scale factor by transform matrix
|
||||
} else {
|
||||
auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
|
||||
auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
|
||||
image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
|
||||
|
||||
if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true;
|
||||
else image->scaled = false;
|
||||
}
|
||||
|
||||
if (!_genOutline(image, transform, mpool, tid)) return false;
|
||||
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
|
||||
}
|
||||
|
||||
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
|
||||
{
|
||||
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid)
|
||||
{
|
||||
mpoolRetOutline(mpool, tid);
|
||||
image->outline = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void imageReset(SwImage* image)
|
||||
{
|
||||
rleReset(image->rle);
|
||||
}
|
||||
|
||||
|
||||
void imageFree(SwImage* image)
|
||||
{
|
||||
rleFree(image->rle);
|
||||
}
|
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <math.h>
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
//clz: count leading zero’s
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include <intrin.h>
|
||||
static uint32_t __inline _clz(uint32_t value)
|
||||
{
|
||||
unsigned long leadingZero = 0;
|
||||
if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
|
||||
else return 32;
|
||||
}
|
||||
#else
|
||||
#define _clz(x) __builtin_clz((x))
|
||||
#endif
|
||||
|
||||
|
||||
constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
|
||||
|
||||
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
|
||||
constexpr static auto ATAN_MAX = 23;
|
||||
constexpr static SwFixed ATAN_TBL[] = {
|
||||
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
|
||||
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
|
||||
57L, 29L, 14L, 7L, 4L, 2L, 1L};
|
||||
|
||||
static inline SwCoord SATURATE(const SwCoord x)
|
||||
{
|
||||
return (x >> (sizeof(SwCoord) * 8 - 1));
|
||||
}
|
||||
|
||||
|
||||
static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
|
||||
{
|
||||
return (((x) + ((n)/2)) & ~((n)-1));
|
||||
}
|
||||
|
||||
|
||||
static SwCoord _downscale(SwFixed x)
|
||||
{
|
||||
//multiply a give value by the CORDIC shrink factor
|
||||
auto s = abs(x);
|
||||
int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
|
||||
s = static_cast<SwFixed>(t >> 32);
|
||||
if (x < 0) s = -s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static int32_t _normalize(SwPoint& pt)
|
||||
{
|
||||
/* the highest bit in overflow-safe vector components
|
||||
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
|
||||
constexpr auto SAFE_MSB = 29;
|
||||
|
||||
auto v = pt;
|
||||
|
||||
//High order bit(MSB)
|
||||
int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
|
||||
|
||||
if (shift <= SAFE_MSB) {
|
||||
shift = SAFE_MSB - shift;
|
||||
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
} else {
|
||||
shift -= SAFE_MSB;
|
||||
pt.x = v.x >> shift;
|
||||
pt.y = v.y >> shift;
|
||||
shift = -shift;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
|
||||
static void _polarize(SwPoint& pt)
|
||||
{
|
||||
auto v = pt;
|
||||
SwFixed theta;
|
||||
|
||||
//Get the vector into [-PI/4, PI/4] sector
|
||||
if (v.y > v.x) {
|
||||
if (v.y > -v.x) {
|
||||
auto tmp = v.y;
|
||||
v.y = -v.x;
|
||||
v.x = tmp;
|
||||
theta = SW_ANGLE_PI2;
|
||||
} else {
|
||||
theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
|
||||
v.x = -v.x;
|
||||
v.y = -v.y;
|
||||
}
|
||||
} else {
|
||||
if (v.y < -v.x) {
|
||||
theta = -SW_ANGLE_PI2;
|
||||
auto tmp = -v.y;
|
||||
v.y = v.x;
|
||||
v.x = tmp;
|
||||
} else {
|
||||
theta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
//Pseudorotations. with right shifts
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (v.y > 0) {
|
||||
auto tmp = v.x + ((v.y + j) >> i);
|
||||
v.y = v.y - ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta += *atan++;
|
||||
} else {
|
||||
auto tmp = v.x - ((v.y + j) >> i);
|
||||
v.y = v.y + ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
//round theta
|
||||
if (theta >= 0) theta = PAD_ROUND(theta, 32);
|
||||
else theta = -PAD_ROUND(-theta, 32);
|
||||
|
||||
pt.x = v.x;
|
||||
pt.y = theta;
|
||||
}
|
||||
|
||||
|
||||
static void _rotate(SwPoint& pt, SwFixed theta)
|
||||
{
|
||||
SwFixed x = pt.x;
|
||||
SwFixed y = pt.y;
|
||||
|
||||
//Rotate inside [-PI/4, PI/4] sector
|
||||
while (theta < -SW_ANGLE_PI4) {
|
||||
auto tmp = y;
|
||||
y = -x;
|
||||
x = tmp;
|
||||
theta += SW_ANGLE_PI2;
|
||||
}
|
||||
|
||||
while (theta > SW_ANGLE_PI4) {
|
||||
auto tmp = -y;
|
||||
y = x;
|
||||
x = tmp;
|
||||
theta -= SW_ANGLE_PI2;
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (theta < 0) {
|
||||
auto tmp = x + ((y + j) >> i);
|
||||
y = y - ((x + j) >> i);
|
||||
x = tmp;
|
||||
theta += *atan++;
|
||||
} else {
|
||||
auto tmp = x - ((y + j) >> i);
|
||||
y = y + ((x + j) >> i);
|
||||
x = tmp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
pt.x = static_cast<SwCoord>(x);
|
||||
pt.y = static_cast<SwCoord>(y);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
return angle1 + mathDiff(angle1, angle2) / 2;
|
||||
}
|
||||
|
||||
|
||||
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
|
||||
{
|
||||
auto d1 = base[2] - base[3];
|
||||
auto d2 = base[1] - base[2];
|
||||
auto d3 = base[0] - base[1];
|
||||
|
||||
if (d1.small()) {
|
||||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
//basically a point.
|
||||
//do nothing to retain original direction
|
||||
} else {
|
||||
angleIn = angleMid = angleOut = mathAtan(d3);
|
||||
}
|
||||
} else {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = mathAtan(d2);
|
||||
} else {
|
||||
angleIn = angleMid = mathAtan(d2);
|
||||
angleOut = mathAtan(d3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = mathAtan(d1);
|
||||
} else {
|
||||
angleIn = mathAtan(d1);
|
||||
angleOut = mathAtan(d3);
|
||||
angleMid = mathMean(angleIn, angleOut);
|
||||
}
|
||||
} else {
|
||||
if (d3.small()) {
|
||||
angleIn = mathAtan(d1);
|
||||
angleMid = angleOut = mathAtan(d2);
|
||||
} else {
|
||||
angleIn = mathAtan(d1);
|
||||
angleMid = mathAtan(d2);
|
||||
angleOut = mathAtan(d3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto theta1 = abs(mathDiff(angleIn, angleMid));
|
||||
auto theta2 = abs(mathDiff(angleMid, angleOut));
|
||||
|
||||
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t c = (a * b + 0x8000L) >> 16;
|
||||
return (s > 0) ? c : -c;
|
||||
}
|
||||
|
||||
|
||||
int64_t mathDivide(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
|
||||
return (s < 0 ? -q : q);
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
if (c < 0) {
|
||||
c = -c;
|
||||
s = -s;
|
||||
}
|
||||
int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
|
||||
|
||||
return (s > 0 ? d : -d);
|
||||
}
|
||||
|
||||
|
||||
void mathRotate(SwPoint& pt, SwFixed angle)
|
||||
{
|
||||
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
|
||||
|
||||
auto v = pt;
|
||||
auto shift = _normalize(v);
|
||||
|
||||
auto theta = angle;
|
||||
_rotate(v, theta);
|
||||
|
||||
v.x = _downscale(v.x);
|
||||
v.y = _downscale(v.y);
|
||||
|
||||
if (shift > 0) {
|
||||
auto half = static_cast<int32_t>(1L << (shift - 1));
|
||||
pt.x = (v.x + half + SATURATE(v.x)) >> shift;
|
||||
pt.y = (v.y + half + SATURATE(v.y)) >> shift;
|
||||
} else {
|
||||
shift = -shift;
|
||||
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
}
|
||||
}
|
||||
|
||||
SwFixed mathTan(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
_rotate(v, angle);
|
||||
return mathDivide(v.y, v.x);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathAtan(const SwPoint& pt)
|
||||
{
|
||||
if (pt.x == 0 && pt.y == 0) return 0;
|
||||
|
||||
auto v = pt;
|
||||
_normalize(v);
|
||||
_polarize(v);
|
||||
|
||||
return v.y;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathSin(SwFixed angle)
|
||||
{
|
||||
return mathCos(SW_ANGLE_PI2 - angle);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathCos(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
_rotate(v, angle);
|
||||
return (v.x + 0x80L) >> 8;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathLength(const SwPoint& pt)
|
||||
{
|
||||
auto v = pt;
|
||||
|
||||
//trivial case
|
||||
if (v.x == 0) return abs(v.y);
|
||||
if (v.y == 0) return abs(v.x);
|
||||
|
||||
//general case
|
||||
auto shift = _normalize(v);
|
||||
_polarize(v);
|
||||
v.x = _downscale(v.x);
|
||||
|
||||
if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
|
||||
return static_cast<SwFixed>((uint32_t)v.x << -shift);
|
||||
}
|
||||
|
||||
|
||||
void mathSplitCubic(SwPoint* base)
|
||||
{
|
||||
SwCoord a, b, c, d;
|
||||
|
||||
base[6].x = base[3].x;
|
||||
c = base[1].x;
|
||||
d = base[2].x;
|
||||
base[1].x = a = (base[0].x + c) / 2;
|
||||
base[5].x = b = (base[3].x + d) / 2;
|
||||
c = (c + d) / 2;
|
||||
base[2].x = a = (a + c) / 2;
|
||||
base[4].x = b = (b + c) / 2;
|
||||
base[3].x = (a + b) / 2;
|
||||
|
||||
base[6].y = base[3].y;
|
||||
c = base[1].y;
|
||||
d = base[2].y;
|
||||
base[1].y = a = (base[0].y + c) / 2;
|
||||
base[5].y = b = (base[3].y + d) / 2;
|
||||
c = (c + d) / 2;
|
||||
base[2].y = a = (a + c) / 2;
|
||||
base[4].y = b = (b + c) / 2;
|
||||
base[3].y = (a + b) / 2;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
auto delta = angle2 - angle1;
|
||||
|
||||
delta %= SW_ANGLE_2PI;
|
||||
if (delta < 0) delta += SW_ANGLE_2PI;
|
||||
if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
|
||||
SwPoint mathTransform(const Point* to, const Matrix* transform)
|
||||
{
|
||||
if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)};
|
||||
|
||||
auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13;
|
||||
auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23;
|
||||
|
||||
return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
|
||||
}
|
||||
|
||||
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
|
||||
{
|
||||
clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x;
|
||||
clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y;
|
||||
clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x;
|
||||
clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y;
|
||||
|
||||
//Check valid region
|
||||
if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false;
|
||||
|
||||
//Check boundary
|
||||
if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y ||
|
||||
clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
|
||||
{
|
||||
if (!outline) return false;
|
||||
|
||||
auto pt = outline->pts;
|
||||
|
||||
if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) {
|
||||
renderRegion.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto xMin = pt->x;
|
||||
auto xMax = pt->x;
|
||||
auto yMin = pt->y;
|
||||
auto yMax = pt->y;
|
||||
|
||||
++pt;
|
||||
|
||||
for (uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
|
||||
if (xMin > pt->x) xMin = pt->x;
|
||||
if (xMax < pt->x) xMax = pt->x;
|
||||
if (yMin > pt->y) yMin = pt->y;
|
||||
if (yMax < pt->y) yMax = pt->y;
|
||||
}
|
||||
//Since no antialiasing is applied in the Fast Track case,
|
||||
//the rasterization region has to be rearranged.
|
||||
//https://github.com/Samsung/thorvg/issues/916
|
||||
if (fastTrack) {
|
||||
renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
|
||||
renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
|
||||
renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
|
||||
renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
|
||||
} else {
|
||||
renderRegion.min.x = xMin >> 6;
|
||||
renderRegion.max.x = (xMax + 63) >> 6;
|
||||
renderRegion.min.y = yMin >> 6;
|
||||
renderRegion.max.y = (yMax + 63) >> 6;
|
||||
}
|
||||
return mathClipBBox(clipRegion, renderRegion);
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
return &mpool->outline[idx];
|
||||
}
|
||||
|
||||
|
||||
void mpoolRetOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
mpool->outline[idx].cntrsCnt = 0;
|
||||
mpool->outline[idx].ptsCnt = 0;
|
||||
}
|
||||
|
||||
|
||||
SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
return &mpool->strokeOutline[idx];
|
||||
}
|
||||
|
||||
|
||||
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
mpool->strokeOutline[idx].cntrsCnt = 0;
|
||||
mpool->strokeOutline[idx].ptsCnt = 0;
|
||||
}
|
||||
|
||||
|
||||
SwMpool* mpoolInit(unsigned threads)
|
||||
{
|
||||
if (threads == 0) threads = 1;
|
||||
|
||||
auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
|
||||
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
|
||||
if (!mpool->outline) goto err;
|
||||
|
||||
mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
|
||||
if (!mpool->strokeOutline) goto err;
|
||||
|
||||
mpool->allocSize = threads;
|
||||
|
||||
return mpool;
|
||||
|
||||
err:
|
||||
if (mpool->outline) {
|
||||
free(mpool->outline);
|
||||
mpool->outline = nullptr;
|
||||
}
|
||||
|
||||
if (mpool->strokeOutline) {
|
||||
free(mpool->strokeOutline);
|
||||
mpool->strokeOutline = nullptr;
|
||||
}
|
||||
free(mpool);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool mpoolClear(SwMpool* mpool)
|
||||
{
|
||||
SwOutline* p;
|
||||
|
||||
for (unsigned i = 0; i < mpool->allocSize; ++i) {
|
||||
|
||||
//Outline
|
||||
p = &mpool->outline[i];
|
||||
|
||||
free(p->cntrs);
|
||||
p->cntrs = nullptr;
|
||||
|
||||
free(p->pts);
|
||||
p->pts = nullptr;
|
||||
|
||||
free(p->types);
|
||||
p->types = nullptr;
|
||||
|
||||
free(p->closed);
|
||||
p->closed = nullptr;
|
||||
|
||||
p->cntrsCnt = p->reservedCntrsCnt = 0;
|
||||
p->ptsCnt = p->reservedPtsCnt = 0;
|
||||
|
||||
//StrokeOutline
|
||||
p = &mpool->strokeOutline[i];
|
||||
|
||||
free(p->cntrs);
|
||||
p->cntrs = nullptr;
|
||||
|
||||
free(p->pts);
|
||||
p->pts = nullptr;
|
||||
|
||||
free(p->types);
|
||||
p->types = nullptr;
|
||||
|
||||
free(p->closed);
|
||||
p->closed = nullptr;
|
||||
|
||||
p->cntrsCnt = p->reservedCntrsCnt = 0;
|
||||
p->ptsCnt = p->reservedPtsCnt = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool mpoolTerm(SwMpool* mpool)
|
||||
{
|
||||
if (!mpool) return false;
|
||||
|
||||
mpoolClear(mpool);
|
||||
|
||||
if (mpool->outline) {
|
||||
free(mpool->outline);
|
||||
mpool->outline = nullptr;
|
||||
}
|
||||
|
||||
if (mpool->strokeOutline) {
|
||||
free(mpool->strokeOutline);
|
||||
mpool->strokeOutline = nullptr;
|
||||
}
|
||||
|
||||
free(mpool);
|
||||
|
||||
return true;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef THORVG_AVX_VECTOR_SUPPORT
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
#define N_32BITS_IN_128REG 4
|
||||
#define N_32BITS_IN_256REG 8
|
||||
|
||||
static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
|
||||
{
|
||||
//1. set the masks for the A/G and R/B channels
|
||||
auto AG = _mm_set1_epi32(0xff00ff00);
|
||||
auto RB = _mm_set1_epi32(0x00ff00ff);
|
||||
|
||||
//2. mask the alpha vector - originally quartet [a, a, a, a]
|
||||
auto aAG = _mm_and_si128(a, AG);
|
||||
auto aRB = _mm_and_si128(a, RB);
|
||||
|
||||
//3. calculate the alpha blending of the 2nd and 4th channel
|
||||
//- mask the color vector
|
||||
//- multiply it by the masked alpha vector
|
||||
//- add the correction to compensate bit shifting used instead of dividing by 255
|
||||
//- shift bits - corresponding to division by 256
|
||||
auto even = _mm_and_si128(c, RB);
|
||||
even = _mm_mullo_epi16(even, aRB);
|
||||
even =_mm_add_epi16(even, RB);
|
||||
even = _mm_srli_epi16(even, 8);
|
||||
|
||||
//4. calculate the alpha blending of the 1st and 3rd channel:
|
||||
//- mask the color vector
|
||||
//- multiply it by the corresponding masked alpha vector and store the high bits of the result
|
||||
//- add the correction to compensate division by 256 instead of by 255 (next step)
|
||||
//- remove the low 8 bits to mimic the division by 256
|
||||
auto odd = _mm_and_si128(c, AG);
|
||||
odd = _mm_mulhi_epu16(odd, aAG);
|
||||
odd = _mm_add_epi16(odd, RB);
|
||||
odd = _mm_and_si128(odd, AG);
|
||||
|
||||
//5. the final result
|
||||
return _mm_or_si128(odd, even);
|
||||
}
|
||||
|
||||
|
||||
static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
//1. calculate how many iterations we need to cover the length
|
||||
uint32_t iterations = len / N_32BITS_IN_256REG;
|
||||
uint32_t avxFilled = iterations * N_32BITS_IN_256REG;
|
||||
|
||||
//2. set the beginning of the array
|
||||
dst += offset;
|
||||
|
||||
//3. fill the octets
|
||||
for (uint32_t i = 0; i < iterations; ++i, dst += N_32BITS_IN_256REG) {
|
||||
_mm256_storeu_si256((__m256i*)dst, _mm256_set1_epi32(val));
|
||||
}
|
||||
|
||||
//4. fill leftovers (in the first step we have to set the pointer to the place where the avx job is done)
|
||||
int32_t leftovers = len - avxFilled;
|
||||
while (leftovers--) *dst++ = val;
|
||||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
|
||||
{
|
||||
auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
|
||||
auto ialpha = 255 - static_cast<uint8_t>(_alpha(color));
|
||||
|
||||
auto avxColor = _mm_set1_epi32(color);
|
||||
auto avxIalpha = _mm_set1_epi8(ialpha);
|
||||
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
||||
//1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
|
||||
auto notAligned = ((uintptr_t)dst & 0xf) / 4;
|
||||
if (notAligned) {
|
||||
notAligned = (N_32BITS_IN_128REG - notAligned > w ? w : N_32BITS_IN_128REG - notAligned);
|
||||
for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
//2. fill the aligned memory - N_32BITS_IN_128REG pixels processed at once
|
||||
uint32_t iterations = (w - notAligned) / N_32BITS_IN_128REG;
|
||||
uint32_t avxFilled = iterations * N_32BITS_IN_128REG;
|
||||
auto avxDst = (__m128i*)dst;
|
||||
for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
|
||||
*avxDst = _mm_add_epi32(avxColor, ALPHA_BLEND(*avxDst, avxIalpha));
|
||||
}
|
||||
|
||||
//3. fill the remaining pixels
|
||||
int32_t leftovers = w - notAligned - avxFilled;
|
||||
dst += avxFilled;
|
||||
while (leftovers--) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
uint32_t src;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
auto dst = &surface->buffer[span->y * surface->stride + span->x];
|
||||
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
auto ialpha = 255 - static_cast<uint8_t>(_alpha(src));
|
||||
|
||||
//1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
|
||||
auto notAligned = ((uintptr_t)dst & 0xf) / 4;
|
||||
if (notAligned) {
|
||||
notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned);
|
||||
for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
//2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
|
||||
//In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all
|
||||
uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
|
||||
uint32_t avxFilled = 0;
|
||||
if (iterations > 0) {
|
||||
auto avxSrc = _mm_set1_epi32(src);
|
||||
auto avxIalpha = _mm_set1_epi8(ialpha);
|
||||
|
||||
avxFilled = iterations * N_32BITS_IN_128REG;
|
||||
auto avxDst = (__m128i*)dst;
|
||||
for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
|
||||
*avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha));
|
||||
}
|
||||
}
|
||||
|
||||
//3. fill the remaining pixels
|
||||
int32_t leftovers = span->len - notAligned - avxFilled;
|
||||
dst += avxFilled;
|
||||
while (leftovers--) {
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
dst++;
|
||||
}
|
||||
|
||||
++span;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
static void inline cRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
dst += offset;
|
||||
while (len--) *dst++ = val;
|
||||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
uint32_t src;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
auto dst = &surface->buffer[span->y * surface->stride + span->x];
|
||||
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
|
||||
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
|
||||
{
|
||||
auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
auto ialpha = _ialpha(color);
|
||||
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef THORVG_NEON_VECTOR_SUPPORT
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
|
||||
{
|
||||
uint16x8_t t = vmull_u8(c, a);
|
||||
return vshrn_n_u16(t, 8);
|
||||
}
|
||||
|
||||
|
||||
static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
uint32_t iterations = len / 4;
|
||||
uint32_t neonFilled = iterations * 4;
|
||||
|
||||
dst += offset;
|
||||
uint32x4_t vectorVal = {val, val, val, val};
|
||||
|
||||
for (uint32_t i = 0; i < iterations; ++i) {
|
||||
vst1q_u32(dst, vectorVal);
|
||||
dst += 4;
|
||||
}
|
||||
|
||||
int32_t leftovers = len - neonFilled;
|
||||
while (leftovers--) *dst++ = val;
|
||||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
uint32_t src;
|
||||
uint8x8_t *vDst = nullptr;
|
||||
uint16_t align;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
auto dst = &surface->buffer[span->y * surface->stride + span->x];
|
||||
auto ialpha = 255 - _alpha(src);
|
||||
|
||||
if ((((uint32_t) dst) & 0x7) != 0) {
|
||||
//fill not aligned byte
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
vDst = (uint8x8_t*)(dst + 1);
|
||||
align = 1;
|
||||
} else {
|
||||
vDst = (uint8x8_t*) dst;
|
||||
align = 0;
|
||||
}
|
||||
|
||||
uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src);
|
||||
uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha);
|
||||
|
||||
for (uint32_t x = 0; x < (span->len - align) / 2; ++x)
|
||||
vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha));
|
||||
|
||||
auto leftovers = (span->len - align) % 2;
|
||||
if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha);
|
||||
|
||||
++span;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
|
||||
{
|
||||
auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
auto ialpha = 255 - _alpha(color);
|
||||
|
||||
auto vColor = vdup_n_u32(color);
|
||||
auto vIalpha = vdup_n_u8((uint8_t) ialpha);
|
||||
|
||||
uint8x8_t* vDst = nullptr;
|
||||
uint32_t align;
|
||||
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
||||
if ((((uint32_t) dst) & 0x7) != 0) {
|
||||
//fill not aligned byte
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
vDst = (uint8x8_t*) (dst + 1);
|
||||
align = 1;
|
||||
} else {
|
||||
vDst = (uint8x8_t*) dst;
|
||||
align = 0;
|
||||
}
|
||||
|
||||
for (uint32_t x = 0; x < (w - align) / 2; ++x)
|
||||
vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha));
|
||||
|
||||
auto leftovers = (w - align) % 2;
|
||||
if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,602 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
Point pt;
|
||||
Point uv;
|
||||
};
|
||||
|
||||
struct Polygon
|
||||
{
|
||||
Vertex vertex[3];
|
||||
};
|
||||
|
||||
struct AALine
|
||||
{
|
||||
int32_t x[2];
|
||||
int32_t coverage[2];
|
||||
int32_t length[2];
|
||||
};
|
||||
|
||||
struct AASpans
|
||||
{
|
||||
AALine *lines;
|
||||
int32_t yStart;
|
||||
int32_t yEnd;
|
||||
};
|
||||
|
||||
static inline void _swap(float& a, float& b, float& tmp)
|
||||
{
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
//Careful! Shared resource, No support threading
|
||||
static float dudx, dvdx;
|
||||
static float dxdya, dxdyb, dudya, dvdya;
|
||||
static float xa, xb, ua, va;
|
||||
|
||||
|
||||
//Y Range exception handling
|
||||
static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
|
||||
{
|
||||
int32_t regionTop, regionBottom;
|
||||
|
||||
if (region) {
|
||||
regionTop = region->min.y;
|
||||
regionBottom = region->max.y;
|
||||
} else {
|
||||
regionTop = image->rle->spans->y;
|
||||
regionBottom = image->rle->spans[image->rle->size - 1].y;
|
||||
}
|
||||
|
||||
if (yStart >= regionBottom) return false;
|
||||
|
||||
if (yStart < regionTop) yStart = regionTop;
|
||||
if (yEnd > regionBottom) yEnd = regionBottom;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
|
||||
{
|
||||
#define TEXMAP_TRANSLUCENT
|
||||
#define TEXMAP_MASKING
|
||||
#include "tvgSwRasterTexmapInternal.h"
|
||||
#undef TEXMAP_MASKING
|
||||
#undef TEXMAP_TRANSLUCENT
|
||||
}
|
||||
|
||||
|
||||
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
|
||||
{
|
||||
#define TEXMAP_MASKING
|
||||
#include "tvgSwRasterTexmapInternal.h"
|
||||
#undef TEXMAP_MASKING
|
||||
}
|
||||
|
||||
|
||||
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans)
|
||||
{
|
||||
#define TEXMAP_TRANSLUCENT
|
||||
#include "tvgSwRasterTexmapInternal.h"
|
||||
#undef TEXMAP_TRANSLUCENT
|
||||
}
|
||||
|
||||
|
||||
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans)
|
||||
{
|
||||
#include "tvgSwRasterTexmapInternal.h"
|
||||
}
|
||||
|
||||
|
||||
/* This mapping algorithm is based on Mikael Kalms's. */
|
||||
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
|
||||
{
|
||||
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
|
||||
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
|
||||
float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x};
|
||||
float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y};
|
||||
|
||||
float off_y;
|
||||
float dxdy[3] = {0.0f, 0.0f, 0.0f};
|
||||
float tmp;
|
||||
|
||||
auto upper = false;
|
||||
|
||||
//Sort the vertices in ascending Y order
|
||||
if (y[0] > y[1]) {
|
||||
_swap(x[0], x[1], tmp);
|
||||
_swap(y[0], y[1], tmp);
|
||||
_swap(u[0], u[1], tmp);
|
||||
_swap(v[0], v[1], tmp);
|
||||
}
|
||||
if (y[0] > y[2]) {
|
||||
_swap(x[0], x[2], tmp);
|
||||
_swap(y[0], y[2], tmp);
|
||||
_swap(u[0], u[2], tmp);
|
||||
_swap(v[0], v[2], tmp);
|
||||
}
|
||||
if (y[1] > y[2]) {
|
||||
_swap(x[1], x[2], tmp);
|
||||
_swap(y[1], y[2], tmp);
|
||||
_swap(u[1], u[2], tmp);
|
||||
_swap(v[1], v[2], tmp);
|
||||
}
|
||||
|
||||
//Y indexes
|
||||
int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
|
||||
|
||||
//Skip drawing if it's too thin to cover any pixels at all.
|
||||
if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return;
|
||||
|
||||
//Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0)
|
||||
auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
|
||||
|
||||
//Skip poly if it's an infinitely thin line
|
||||
if (mathZero(denom)) return;
|
||||
|
||||
denom = 1 / denom; //Reciprocal for speeding up
|
||||
dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
|
||||
dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom;
|
||||
auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom;
|
||||
auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom;
|
||||
|
||||
//Calculate X-slopes along the edges
|
||||
if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]);
|
||||
if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]);
|
||||
if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]);
|
||||
|
||||
//Determine which side of the polygon the longer edge is on
|
||||
auto side = (dxdy[1] > dxdy[0]) ? true : false;
|
||||
|
||||
if (mathEqual(y[0], y[1])) side = x[0] > x[1];
|
||||
if (mathEqual(y[1], y[2])) side = x[2] > x[1];
|
||||
|
||||
auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
|
||||
|
||||
//Longer edge is on the left side
|
||||
if (!side) {
|
||||
//Calculate slopes along left edge
|
||||
dxdya = dxdy[1];
|
||||
dudya = dxdya * dudx + dudy;
|
||||
dvdya = dxdya * dvdx + dvdy;
|
||||
|
||||
//Perform subpixel pre-stepping along left edge
|
||||
auto dy = 1.0f - (y[0] - yi[0]);
|
||||
xa = x[0] + dy * dxdya;
|
||||
ua = u[0] + dy * dudya;
|
||||
va = v[0] + dy * dvdya;
|
||||
|
||||
//Draw upper segment if possibly visible
|
||||
if (yi[0] < yi[1]) {
|
||||
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
|
||||
xa += (off_y * dxdya);
|
||||
ua += (off_y * dudya);
|
||||
va += (off_y * dvdya);
|
||||
|
||||
// Set right edge X-slope and perform subpixel pre-stepping
|
||||
dxdyb = dxdy[0];
|
||||
xb = x[0] + dy * dxdyb + (off_y * dxdyb);
|
||||
|
||||
if (blendMethod) {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
|
||||
} else {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
|
||||
}
|
||||
|
||||
upper = true;
|
||||
}
|
||||
//Draw lower segment if possibly visible
|
||||
if (yi[1] < yi[2]) {
|
||||
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
|
||||
if (!upper) {
|
||||
xa += (off_y * dxdya);
|
||||
ua += (off_y * dudya);
|
||||
va += (off_y * dvdya);
|
||||
}
|
||||
// Set right edge X-slope and perform subpixel pre-stepping
|
||||
dxdyb = dxdy[2];
|
||||
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
|
||||
if (blendMethod) {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
|
||||
} else {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
|
||||
}
|
||||
}
|
||||
//Longer edge is on the right side
|
||||
} else {
|
||||
//Set right edge X-slope and perform subpixel pre-stepping
|
||||
dxdyb = dxdy[1];
|
||||
auto dy = 1.0f - (y[0] - yi[0]);
|
||||
xb = x[0] + dy * dxdyb;
|
||||
|
||||
//Draw upper segment if possibly visible
|
||||
if (yi[0] < yi[1]) {
|
||||
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
|
||||
xb += (off_y *dxdyb);
|
||||
|
||||
// Set slopes along left edge and perform subpixel pre-stepping
|
||||
dxdya = dxdy[0];
|
||||
dudya = dxdya * dudx + dudy;
|
||||
dvdya = dxdya * dvdx + dvdy;
|
||||
|
||||
xa = x[0] + dy * dxdya + (off_y * dxdya);
|
||||
ua = u[0] + dy * dudya + (off_y * dudya);
|
||||
va = v[0] + dy * dvdya + (off_y * dvdya);
|
||||
|
||||
if (blendMethod) {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
|
||||
} else {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
|
||||
}
|
||||
|
||||
upper = true;
|
||||
}
|
||||
//Draw lower segment if possibly visible
|
||||
if (yi[1] < yi[2]) {
|
||||
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
|
||||
if (!upper) xb += (off_y *dxdyb);
|
||||
|
||||
// Set slopes along left edge and perform subpixel pre-stepping
|
||||
dxdya = dxdy[2];
|
||||
dudya = dxdya * dudx + dudy;
|
||||
dvdya = dxdya * dvdx + dvdy;
|
||||
dy = 1 - (y[1] - yi[1]);
|
||||
xa = x[1] + dy * dxdya + (off_y * dxdya);
|
||||
ua = u[1] + dy * dudya + (off_y * dudya);
|
||||
va = v[1] + dy * dvdya + (off_y * dvdya);
|
||||
|
||||
if (blendMethod) {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
|
||||
} else {
|
||||
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
|
||||
else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region)
|
||||
{
|
||||
//Initialize Y range
|
||||
float ys = FLT_MAX, ye = -1.0f;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
|
||||
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
|
||||
}
|
||||
|
||||
auto yStart = static_cast<int32_t>(ys);
|
||||
auto yEnd = static_cast<int32_t>(ye);
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return nullptr;
|
||||
|
||||
auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
|
||||
aaSpans->yStart = yStart;
|
||||
aaSpans->yEnd = yEnd;
|
||||
|
||||
//Initialize X range
|
||||
auto height = yEnd - yStart;
|
||||
|
||||
aaSpans->lines = static_cast<AALine*>(calloc(height, sizeof(AALine)));
|
||||
|
||||
for (int32_t i = 0; i < height; i++) {
|
||||
aaSpans->lines[i].x[0] = INT32_MAX;
|
||||
aaSpans->lines[i].x[1] = INT32_MIN;
|
||||
}
|
||||
return aaSpans;
|
||||
}
|
||||
|
||||
|
||||
static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
|
||||
{
|
||||
if (eidx == 1) reverse = !reverse;
|
||||
int32_t coverage = (255 / (diagonal + 2));
|
||||
int32_t tmp;
|
||||
for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
|
||||
tmp = y - ry - edgeDist;
|
||||
if (tmp < 0) return;
|
||||
lines[tmp].length[eidx] = 1;
|
||||
if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
|
||||
else lines[tmp].coverage[eidx] = (coverage * ry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
|
||||
{
|
||||
if (eidx == 1) reverse = !reverse;
|
||||
int32_t coverage = (255 / (rewind + 1));
|
||||
int32_t tmp;
|
||||
for (int ry = 1; ry < (rewind + 1); ry++) {
|
||||
tmp = y - ry;
|
||||
if (tmp < 0) return;
|
||||
lines[tmp].length[eidx] = 1;
|
||||
if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
|
||||
else lines[tmp].coverage[eidx] = (coverage * ry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
|
||||
{
|
||||
if (lines[y].length[eidx] < abs(x - x2)) {
|
||||
lines[y].length[eidx] = abs(x - x2);
|
||||
lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This Anti-Aliasing mechanism is originated from Hermet Park's idea.
|
||||
* To understand this AA logic, you can refer this page:
|
||||
* www.hermet.pe.kr/122 (hermetpark@gmail.com)
|
||||
*/
|
||||
static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
|
||||
{
|
||||
//Previous edge direction:
|
||||
#define DirOutHor 0x0011
|
||||
#define DirOutVer 0x0001
|
||||
#define DirInHor 0x0010
|
||||
#define DirInVer 0x0000
|
||||
#define DirNone 0x1000
|
||||
|
||||
#define PUSH_VERTEX() \
|
||||
do { \
|
||||
pEdge.x = lines[y].x[eidx]; \
|
||||
pEdge.y = y; \
|
||||
ptx[0] = tx[0]; \
|
||||
ptx[1] = tx[1]; \
|
||||
} while (0)
|
||||
|
||||
int32_t y = 0;
|
||||
SwPoint pEdge = {-1, -1}; //previous edge point
|
||||
SwPoint edgeDiff = {0, 0}; //temporary used for point distance
|
||||
|
||||
/* store bigger to tx[0] between prev and current edge's x positions. */
|
||||
int32_t tx[2] = {0, 0};
|
||||
/* back up prev tx values */
|
||||
int32_t ptx[2] = {0, 0};
|
||||
int32_t diagonal = 0; //straight diagonal pixels count
|
||||
|
||||
auto yStart = aaSpans->yStart;
|
||||
auto yEnd = aaSpans->yEnd;
|
||||
auto lines = aaSpans->lines;
|
||||
|
||||
int32_t prevDir = DirNone;
|
||||
int32_t curDir = DirNone;
|
||||
|
||||
yEnd -= yStart;
|
||||
|
||||
//Start Edge
|
||||
if (y < yEnd) {
|
||||
pEdge.x = lines[y].x[eidx];
|
||||
pEdge.y = y;
|
||||
}
|
||||
|
||||
//Calculates AA Edges
|
||||
for (y++; y < yEnd; y++) {
|
||||
//Ready tx
|
||||
if (eidx == 0) {
|
||||
tx[0] = pEdge.x;
|
||||
tx[1] = lines[y].x[0];
|
||||
} else {
|
||||
tx[0] = lines[y].x[1];
|
||||
tx[1] = pEdge.x;
|
||||
}
|
||||
edgeDiff.x = (tx[0] - tx[1]);
|
||||
edgeDiff.y = (y - pEdge.y);
|
||||
|
||||
//Confirm current edge direction
|
||||
if (edgeDiff.x > 0) {
|
||||
if (edgeDiff.y == 1) curDir = DirOutHor;
|
||||
else curDir = DirOutVer;
|
||||
} else if (edgeDiff.x < 0) {
|
||||
if (edgeDiff.y == 1) curDir = DirInHor;
|
||||
else curDir = DirInVer;
|
||||
} else curDir = DirNone;
|
||||
|
||||
//straight diagonal increase
|
||||
if ((curDir == prevDir) && (y < yEnd)) {
|
||||
if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
|
||||
++diagonal;
|
||||
PUSH_VERTEX();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (curDir) {
|
||||
case DirOutHor: {
|
||||
_calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
|
||||
if (diagonal > 0) {
|
||||
_calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
|
||||
diagonal = 0;
|
||||
}
|
||||
/* Increment direction is changed: Outside Vertical -> Outside Horizontal */
|
||||
if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
||||
|
||||
//Trick, but fine-tunning!
|
||||
if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
|
||||
PUSH_VERTEX();
|
||||
}
|
||||
break;
|
||||
case DirOutVer: {
|
||||
_calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
|
||||
if (diagonal > 0) {
|
||||
_calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
|
||||
diagonal = 0;
|
||||
}
|
||||
/* Increment direction is changed: Outside Horizontal -> Outside Vertical */
|
||||
if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
||||
PUSH_VERTEX();
|
||||
}
|
||||
break;
|
||||
case DirInHor: {
|
||||
_calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
|
||||
if (diagonal > 0) {
|
||||
_calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
|
||||
diagonal = 0;
|
||||
}
|
||||
/* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
|
||||
if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
||||
PUSH_VERTEX();
|
||||
}
|
||||
break;
|
||||
case DirInVer: {
|
||||
_calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
|
||||
if (prevDir == DirOutHor) edgeDiff.y -= 1; //Weird, fine tuning?????????????????????
|
||||
if (diagonal > 0) {
|
||||
_calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
|
||||
diagonal = 0;
|
||||
}
|
||||
/* Increment direction is changed: Outside Horizontal -> Inside Vertical */
|
||||
if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
|
||||
PUSH_VERTEX();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (curDir != DirNone) prevDir = curDir;
|
||||
}
|
||||
|
||||
//leftovers...?
|
||||
if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) {
|
||||
if (y >= yEnd) y = (yEnd - 1);
|
||||
_calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]);
|
||||
_calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
|
||||
} else {
|
||||
++y;
|
||||
if (y > yEnd) y = yEnd;
|
||||
_calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
||||
{
|
||||
auto y = aaSpans->yStart;
|
||||
uint32_t pixel;
|
||||
uint32_t* dst;
|
||||
int32_t pos;
|
||||
|
||||
//left side
|
||||
_calcAAEdge(aaSpans, 0);
|
||||
//right side
|
||||
_calcAAEdge(aaSpans, 1);
|
||||
|
||||
while (y < aaSpans->yEnd) {
|
||||
auto line = &aaSpans->lines[y - aaSpans->yStart];
|
||||
auto width = line->x[1] - line->x[0];
|
||||
if (width > 0) {
|
||||
auto offset = y * surface->stride;
|
||||
|
||||
//Left edge
|
||||
dst = surface->buffer + (offset + line->x[0]);
|
||||
if (line->x[0] > 1) pixel = *(dst - 1);
|
||||
else pixel = *dst;
|
||||
|
||||
pos = 1;
|
||||
while (pos <= line->length[0]) {
|
||||
*dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel);
|
||||
++dst;
|
||||
++pos;
|
||||
}
|
||||
|
||||
//Right edge
|
||||
dst = surface->buffer + (offset + line->x[1] - 1);
|
||||
if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
|
||||
else pixel = *dst;
|
||||
|
||||
pos = width;
|
||||
while ((int32_t)(width - line->length[1]) < pos) {
|
||||
*dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
|
||||
--dst;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
y++;
|
||||
}
|
||||
|
||||
free(aaSpans->lines);
|
||||
free(aaSpans);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
2 triangles constructs 1 mesh.
|
||||
below figure illustrates vert[4] index info.
|
||||
If you need better quality, please divide a mesh by more number of triangles.
|
||||
|
||||
0 -- 1
|
||||
| / |
|
||||
| / |
|
||||
3 -- 2
|
||||
*/
|
||||
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
|
||||
{
|
||||
//Exceptions: No dedicated drawing area?
|
||||
if (!region && image->rle->size == 0) return false;
|
||||
|
||||
/* Prepare vertices.
|
||||
shift XY coordinates to match the sub-pixeling technique. */
|
||||
Vertex vertices[4];
|
||||
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
|
||||
vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
|
||||
vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
|
||||
vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
|
||||
|
||||
for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform);
|
||||
|
||||
auto aaSpans = _AASpans(vertices, image, region);
|
||||
if (!aaSpans) return true;
|
||||
|
||||
Polygon polygon;
|
||||
|
||||
//Draw the first polygon
|
||||
polygon.vertex[0] = vertices[0];
|
||||
polygon.vertex[1] = vertices[1];
|
||||
polygon.vertex[2] = vertices[3];
|
||||
|
||||
_rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
|
||||
|
||||
//Draw the second polygon
|
||||
polygon.vertex[0] = vertices[1];
|
||||
polygon.vertex[1] = vertices[2];
|
||||
polygon.vertex[2] = vertices[3];
|
||||
|
||||
_rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
|
||||
|
||||
return _apply(surface, aaSpans);
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
{
|
||||
float _dudx = dudx, _dvdx = dvdx;
|
||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->data;
|
||||
auto dbuf = surface->buffer;
|
||||
int32_t sw = static_cast<int32_t>(image->stride);
|
||||
int32_t sh = image->h;
|
||||
int32_t dw = surface->stride;
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx, maxx;
|
||||
float dx, u, v, iptr;
|
||||
uint32_t* buf;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
#ifdef TEXMAP_MASKING
|
||||
uint32_t* cmp;
|
||||
#endif
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (region) {
|
||||
minx = region->min.x;
|
||||
maxx = region->max.x;
|
||||
} else {
|
||||
span = image->rle->spans;
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
|
||||
y = yStart;
|
||||
|
||||
while (y < yEnd) {
|
||||
x1 = _xa;
|
||||
x2 = _xb;
|
||||
|
||||
if (!region) {
|
||||
minx = INT32_MAX;
|
||||
maxx = INT32_MIN;
|
||||
//one single row, could be consisted of multiple spans.
|
||||
while (span->y == y && spanIdx < image->rle->size) {
|
||||
if (minx > span->x) minx = span->x;
|
||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
if (x1 < minx) x1 = minx;
|
||||
if (x2 > maxx) x2 = maxx;
|
||||
|
||||
//Anti-Aliasing frames
|
||||
ay = y - aaSpans->yStart;
|
||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
||||
|
||||
//Range exception
|
||||
if ((x2 - x1) < 1 || (x1 >= maxx) || (x2 <= minx)) goto next;
|
||||
|
||||
//Perform subtexel pre-stepping on UV
|
||||
dx = 1 - (_xa - x1);
|
||||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
|
||||
buf = dbuf + ((y * dw) + x1);
|
||||
|
||||
x = x1;
|
||||
|
||||
#ifdef TEXMAP_MASKING
|
||||
cmp = &surface->compositor->image.data[y * surface->compositor->image.stride + x1];
|
||||
#endif
|
||||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
vv = (int) v;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
px = INTERPOLATE(ar, px, px2);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
px2 = INTERPOLATE(ar, px2, px3);
|
||||
}
|
||||
px = INTERPOLATE(ab, px, px2);
|
||||
}
|
||||
#if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT)
|
||||
auto src = ALPHA_BLEND(px, _multiplyAlpha(opacity, blendMethod(*cmp)));
|
||||
#elif defined(TEXMAP_MASKING)
|
||||
auto src = ALPHA_BLEND(px, blendMethod(*cmp));
|
||||
#elif defined(TEXMAP_TRANSLUCENT)
|
||||
auto src = ALPHA_BLEND(px, opacity);
|
||||
#else
|
||||
auto src = px;
|
||||
#endif
|
||||
*buf = src + ALPHA_BLEND(*buf, _ialpha(src));
|
||||
++buf;
|
||||
#ifdef TEXMAP_MASKING
|
||||
++cmp;
|
||||
#endif
|
||||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
next:
|
||||
//Step along both edges
|
||||
_xa += _dxdya;
|
||||
_xb += _dxdyb;
|
||||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
|
||||
if (!region && spanIdx >= image->rle->size) break;
|
||||
|
||||
++y;
|
||||
}
|
||||
xa = _xa;
|
||||
xb = _xb;
|
||||
ua = _ua;
|
||||
va = _va;
|
||||
}
|
|
@ -0,0 +1,685 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <math.h>
|
||||
#include "tvgSwCommon.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgSwRenderer.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
static int32_t initEngineCnt = false;
|
||||
static int32_t rendererCnt = 0;
|
||||
static SwMpool* globalMpool = nullptr;
|
||||
static uint32_t threadsCnt = 0;
|
||||
|
||||
struct SwTask : Task
|
||||
{
|
||||
Matrix* transform = nullptr;
|
||||
SwSurface* surface = nullptr;
|
||||
SwMpool* mpool = nullptr;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
Array<RenderData> clips;
|
||||
uint32_t opacity;
|
||||
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
|
||||
bool pushed = false; //Pushed into task list?
|
||||
bool disposed = false; //Disposed task?
|
||||
|
||||
RenderRegion bounds() const
|
||||
{
|
||||
RenderRegion region;
|
||||
|
||||
//Range over?
|
||||
region.x = bbox.min.x > 0 ? bbox.min.x : 0;
|
||||
region.y = bbox.min.y > 0 ? bbox.min.y : 0;
|
||||
region.w = bbox.max.x - region.x;
|
||||
region.h = bbox.max.y - region.y;
|
||||
if (region.w < 0) region.w = 0;
|
||||
if (region.h < 0) region.h = 0;
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
virtual bool dispose() = 0;
|
||||
|
||||
virtual ~SwTask()
|
||||
{
|
||||
free(transform);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SwShapeTask : SwTask
|
||||
{
|
||||
SwShape shape;
|
||||
const Shape* sdata = nullptr;
|
||||
bool cmpStroking = false;
|
||||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
if (opacity == 0) return; //Invisible
|
||||
|
||||
uint8_t strokeAlpha = 0;
|
||||
auto visibleStroke = false;
|
||||
bool visibleFill = false;
|
||||
auto clipRegion = bbox;
|
||||
|
||||
if (HALF_STROKE(sdata->strokeWidth()) > 0) {
|
||||
sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
|
||||
visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
|
||||
}
|
||||
|
||||
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
||||
auto prepareShape = false;
|
||||
if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
|
||||
|
||||
//Shape
|
||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
|
||||
uint8_t alpha = 0;
|
||||
sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
|
||||
visibleFill = (alpha > 0 || sdata->fill());
|
||||
if (visibleFill || visibleStroke) {
|
||||
shapeReset(&shape);
|
||||
if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
|
||||
}
|
||||
}
|
||||
|
||||
//Decide Stroking Composition
|
||||
if (visibleStroke && visibleFill && opacity < 255) cmpStroking = true;
|
||||
else cmpStroking = false;
|
||||
|
||||
//Fill
|
||||
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
|
||||
if (visibleFill) {
|
||||
/* We assume that if stroke width is bigger than 2,
|
||||
shape outline below stroke could be full covered by stroke drawing.
|
||||
Thus it turns off antialising in that condition.
|
||||
Also, it shouldn't be dash style. */
|
||||
auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
|
||||
|
||||
if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
|
||||
}
|
||||
if (auto fill = sdata->fill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
|
||||
if (ctable) shapeResetFill(&shape);
|
||||
if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
|
||||
} else {
|
||||
shapeDelFill(&shape);
|
||||
}
|
||||
}
|
||||
|
||||
//Stroke
|
||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||
if (visibleStroke) {
|
||||
shapeResetStroke(&shape, sdata, transform);
|
||||
if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
|
||||
|
||||
if (auto fill = sdata->strokeFill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
|
||||
} else {
|
||||
shapeDelStrokeFill(&shape);
|
||||
}
|
||||
} else {
|
||||
shapeDelStroke(&shape);
|
||||
}
|
||||
}
|
||||
|
||||
//Clip Path
|
||||
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
|
||||
auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
|
||||
//Clip shape rle
|
||||
if (shape.rle) {
|
||||
if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox);
|
||||
else if (clipper->rle) rleClipPath(shape.rle, clipper->rle);
|
||||
else goto err;
|
||||
}
|
||||
//Clip stroke rle
|
||||
if (shape.strokeRle) {
|
||||
if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox);
|
||||
else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle);
|
||||
else goto err;
|
||||
}
|
||||
}
|
||||
goto end;
|
||||
|
||||
err:
|
||||
shapeReset(&shape);
|
||||
end:
|
||||
shapeDelOutline(&shape, mpool, tid);
|
||||
}
|
||||
|
||||
bool dispose() override
|
||||
{
|
||||
shapeFree(&shape);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SwImageTask : SwTask
|
||||
{
|
||||
SwImage image;
|
||||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
auto clipRegion = bbox;
|
||||
|
||||
//Invisible shape turned to visible by alpha.
|
||||
if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
|
||||
imageReset(&image);
|
||||
if (!image.data || image.w == 0 || image.h == 0) goto end;
|
||||
|
||||
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
|
||||
|
||||
if (clips.count > 0) {
|
||||
if (!imageGenRle(&image, bbox, false)) goto end;
|
||||
if (image.rle) {
|
||||
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
|
||||
auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
|
||||
if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox);
|
||||
else if (clipper->rle) rleClipPath(image.rle, clipper->rle);
|
||||
else goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
goto end;
|
||||
|
||||
err:
|
||||
rleReset(image.rle);
|
||||
end:
|
||||
imageDelOutline(&image, mpool, tid);
|
||||
}
|
||||
|
||||
bool dispose() override
|
||||
{
|
||||
imageFree(&image);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void _termEngine()
|
||||
{
|
||||
if (rendererCnt > 0) return;
|
||||
|
||||
mpoolTerm(globalMpool);
|
||||
globalMpool = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwRenderer::~SwRenderer()
|
||||
{
|
||||
clearCompositors();
|
||||
|
||||
if (surface) delete(surface);
|
||||
|
||||
if (!sharedMpool) mpoolTerm(mpool);
|
||||
|
||||
--rendererCnt;
|
||||
|
||||
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::clear()
|
||||
{
|
||||
for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
|
||||
if ((*task)->disposed) {
|
||||
delete(*task);
|
||||
} else {
|
||||
(*task)->done();
|
||||
(*task)->pushed = false;
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
|
||||
if (!sharedMpool) mpoolClear(mpool);
|
||||
|
||||
if (surface) {
|
||||
vport.x = vport.y = 0;
|
||||
vport.w = surface->w;
|
||||
vport.h = surface->h;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::sync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RenderRegion SwRenderer::viewport()
|
||||
{
|
||||
return vport;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::viewport(const RenderRegion& vp)
|
||||
{
|
||||
vport = vp;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
|
||||
{
|
||||
if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
|
||||
|
||||
if (!surface) surface = new SwSurface;
|
||||
|
||||
surface->buffer = buffer;
|
||||
surface->stride = stride;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = cs;
|
||||
|
||||
vport.x = vport.y = 0;
|
||||
vport.w = surface->w;
|
||||
vport.h = surface->h;
|
||||
|
||||
return rasterCompositor(surface);
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::preRender()
|
||||
{
|
||||
return rasterClear(surface);
|
||||
}
|
||||
|
||||
void SwRenderer::clearCompositors()
|
||||
{
|
||||
//Free Composite Caches
|
||||
for (auto comp = compositors.data; comp < (compositors.data + compositors.count); ++comp) {
|
||||
free((*comp)->compositor->image.data);
|
||||
delete((*comp)->compositor);
|
||||
delete(*comp);
|
||||
}
|
||||
compositors.reset();
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::postRender()
|
||||
{
|
||||
//Unmultiply alpha if needed
|
||||
if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
|
||||
rasterUnpremultiply(surface);
|
||||
}
|
||||
|
||||
for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
|
||||
(*task)->pushed = false;
|
||||
}
|
||||
tasks.clear();
|
||||
|
||||
clearCompositors();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::renderImage(RenderData data)
|
||||
{
|
||||
auto task = static_cast<SwImageTask*>(data);
|
||||
task->done();
|
||||
|
||||
if (task->opacity == 0) return true;
|
||||
|
||||
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::renderShape(RenderData data)
|
||||
{
|
||||
auto task = static_cast<SwShapeTask*>(data);
|
||||
if (!task) return false;
|
||||
|
||||
task->done();
|
||||
|
||||
if (task->opacity == 0) return true;
|
||||
|
||||
uint32_t opacity;
|
||||
Compositor* cmp = nullptr;
|
||||
|
||||
//Do Stroking Composition
|
||||
if (task->cmpStroking) {
|
||||
opacity = 255;
|
||||
cmp = target(task->bounds());
|
||||
beginComposite(cmp, CompositeMethod::None, task->opacity);
|
||||
//No Stroking Composition
|
||||
} else {
|
||||
opacity = task->opacity;
|
||||
}
|
||||
|
||||
//Main raster stage
|
||||
uint8_t r, g, b, a;
|
||||
|
||||
if (auto fill = task->sdata->fill()) {
|
||||
rasterGradientShape(surface, &task->shape, fill->identifier());
|
||||
} else {
|
||||
task->sdata->fillColor(&r, &g, &b, &a);
|
||||
a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
|
||||
if (auto strokeFill = task->sdata->strokeFill()) {
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
|
||||
} else {
|
||||
if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
|
||||
a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
if (task->cmpStroking) endComposite(cmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RenderRegion SwRenderer::region(RenderData data)
|
||||
{
|
||||
return static_cast<SwTask*>(data)->bounds();
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity)
|
||||
{
|
||||
if (!cmp) return false;
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
|
||||
p->method = method;
|
||||
p->opacity = opacity;
|
||||
|
||||
//Current Context?
|
||||
if (p->method != CompositeMethod::None) {
|
||||
surface = p->recoverSfc;
|
||||
surface->compositor = p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::mempool(bool shared)
|
||||
{
|
||||
if (shared == sharedMpool) return true;
|
||||
|
||||
if (shared) {
|
||||
if (!sharedMpool) {
|
||||
if (!mpoolTerm(mpool)) return false;
|
||||
mpool = globalMpool;
|
||||
}
|
||||
} else {
|
||||
if (sharedMpool) mpool = mpoolInit(threadsCnt);
|
||||
}
|
||||
|
||||
sharedMpool = shared;
|
||||
|
||||
if (mpool) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Compositor* SwRenderer::target(const RenderRegion& region)
|
||||
{
|
||||
auto x = region.x;
|
||||
auto y = region.y;
|
||||
auto w = region.w;
|
||||
auto h = region.h;
|
||||
auto sw = static_cast<int32_t>(surface->w);
|
||||
auto sh = static_cast<int32_t>(surface->h);
|
||||
|
||||
//Out of boundary
|
||||
if (x > sw || y > sh) return nullptr;
|
||||
|
||||
SwSurface* cmp = nullptr;
|
||||
|
||||
//Use cached data
|
||||
for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) {
|
||||
if ((*p)->compositor->valid) {
|
||||
cmp = *p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//New Composition
|
||||
if (!cmp) {
|
||||
cmp = new SwSurface;
|
||||
if (!cmp) goto err;
|
||||
|
||||
//Inherits attributes from main surface
|
||||
*cmp = *surface;
|
||||
|
||||
cmp->compositor = new SwCompositor;
|
||||
if (!cmp->compositor) goto err;
|
||||
|
||||
//SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
|
||||
cmp->compositor->image.data = (uint32_t*) malloc(sizeof(uint32_t) * surface->stride * surface->h);
|
||||
if (!cmp->compositor->image.data) goto err;
|
||||
compositors.push(cmp);
|
||||
}
|
||||
|
||||
//Boundary Check
|
||||
if (x + w > sw) w = (sw - x);
|
||||
if (y + h > sh) h = (sh - y);
|
||||
|
||||
TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
|
||||
|
||||
cmp->compositor->recoverSfc = surface;
|
||||
cmp->compositor->recoverCmp = surface->compositor;
|
||||
cmp->compositor->valid = false;
|
||||
cmp->compositor->bbox.min.x = x;
|
||||
cmp->compositor->bbox.min.y = y;
|
||||
cmp->compositor->bbox.max.x = x + w;
|
||||
cmp->compositor->bbox.max.y = y + h;
|
||||
cmp->compositor->image.stride = surface->stride;
|
||||
cmp->compositor->image.w = surface->w;
|
||||
cmp->compositor->image.h = surface->h;
|
||||
cmp->compositor->image.direct = true;
|
||||
|
||||
//We know partial clear region
|
||||
cmp->buffer = cmp->compositor->image.data + (cmp->stride * y + x);
|
||||
cmp->w = w;
|
||||
cmp->h = h;
|
||||
|
||||
rasterClear(cmp);
|
||||
|
||||
//Recover context
|
||||
cmp->buffer = cmp->compositor->image.data;
|
||||
cmp->w = cmp->compositor->image.w;
|
||||
cmp->h = cmp->compositor->image.h;
|
||||
|
||||
//Switch render target
|
||||
surface = cmp;
|
||||
|
||||
return cmp->compositor;
|
||||
|
||||
err:
|
||||
if (cmp) {
|
||||
if (cmp->compositor) delete(cmp->compositor);
|
||||
delete(cmp);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::endComposite(Compositor* cmp)
|
||||
{
|
||||
if (!cmp) return false;
|
||||
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
p->valid = true;
|
||||
|
||||
//Recover Context
|
||||
surface = p->recoverSfc;
|
||||
surface->compositor = p->recoverCmp;
|
||||
|
||||
//Default is alpha blending
|
||||
if (p->method == CompositeMethod::None) {
|
||||
return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::dispose(RenderData data)
|
||||
{
|
||||
auto task = static_cast<SwTask*>(data);
|
||||
if (!task) return true;
|
||||
task->done();
|
||||
task->dispose();
|
||||
|
||||
if (task->pushed) task->disposed = true;
|
||||
else delete(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags)
|
||||
{
|
||||
if (!surface) return task;
|
||||
if (flags == RenderUpdateFlag::None) return task;
|
||||
|
||||
//Finish previous task if it has duplicated request.
|
||||
task->done();
|
||||
|
||||
if (clips.count > 0) {
|
||||
//Guarantee composition targets get ready.
|
||||
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
|
||||
static_cast<SwShapeTask*>(*clip)->done();
|
||||
}
|
||||
task->clips = clips;
|
||||
}
|
||||
|
||||
if (transform) {
|
||||
if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
*task->transform = transform->m;
|
||||
} else {
|
||||
if (task->transform) free(task->transform);
|
||||
task->transform = nullptr;
|
||||
}
|
||||
|
||||
task->opacity = opacity;
|
||||
task->surface = surface;
|
||||
task->mpool = mpool;
|
||||
task->flags = flags;
|
||||
task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
|
||||
task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
|
||||
task->bbox.max.x = min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
|
||||
task->bbox.max.y = min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
|
||||
|
||||
if (!task->pushed) {
|
||||
task->pushed = true;
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
TaskScheduler::request(task);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwImageTask*>(data);
|
||||
if (!task) {
|
||||
task = new SwImageTask;
|
||||
if (flags & RenderUpdateFlag::Image) {
|
||||
task->image.data = image->buffer;
|
||||
task->image.w = image->w;
|
||||
task->image.h = image->h;
|
||||
task->image.stride = image->stride;
|
||||
}
|
||||
}
|
||||
return prepareCommon(task, transform, opacity, clips, flags);
|
||||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwShapeTask*>(data);
|
||||
if (!task) {
|
||||
task = new SwShapeTask;
|
||||
task->sdata = &sdata;
|
||||
}
|
||||
return prepareCommon(task, transform, opacity, clips, flags);
|
||||
}
|
||||
|
||||
|
||||
SwRenderer::SwRenderer():mpool(globalMpool)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::init(uint32_t threads)
|
||||
{
|
||||
if ((initEngineCnt++) > 0) return true;
|
||||
|
||||
threadsCnt = threads;
|
||||
|
||||
//Share the memory pool among the renderer
|
||||
globalMpool = mpoolInit(threads);
|
||||
if (!globalMpool) {
|
||||
--initEngineCnt;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int32_t SwRenderer::init()
|
||||
{
|
||||
return initEngineCnt;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::term()
|
||||
{
|
||||
if ((--initEngineCnt) > 0) return true;
|
||||
|
||||
initEngineCnt = 0;
|
||||
|
||||
_termEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SwRenderer* SwRenderer::gen()
|
||||
{
|
||||
++rendererCnt;
|
||||
return new SwRenderer();
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SW_RENDERER_H_
|
||||
#define _TVG_SW_RENDERER_H_
|
||||
|
||||
#include "tvgRender.h"
|
||||
|
||||
struct SwSurface;
|
||||
struct SwTask;
|
||||
struct SwCompositor;
|
||||
struct SwMpool;
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
public:
|
||||
RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
|
||||
RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
|
||||
bool preRender() override;
|
||||
bool renderShape(RenderData data) override;
|
||||
bool renderImage(RenderData data) override;
|
||||
bool postRender() override;
|
||||
bool dispose(RenderData data) override;
|
||||
RenderRegion region(RenderData data) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
|
||||
bool clear() override;
|
||||
bool sync() override;
|
||||
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
|
||||
bool mempool(bool shared);
|
||||
|
||||
Compositor* target(const RenderRegion& region) override;
|
||||
bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
|
||||
bool endComposite(Compositor* cmp) override;
|
||||
void clearCompositors();
|
||||
|
||||
static SwRenderer* gen();
|
||||
static bool init(uint32_t threads);
|
||||
static int32_t init();
|
||||
static bool term();
|
||||
|
||||
private:
|
||||
SwSurface* surface = nullptr; //active surface
|
||||
Array<SwTask*> tasks; //async task list
|
||||
Array<SwSurface*> compositors; //render targets cache list
|
||||
SwMpool* mpool; //private memory pool
|
||||
RenderRegion vport; //viewport
|
||||
|
||||
bool sharedMpool = true; //memory-pool behavior policy
|
||||
|
||||
SwRenderer();
|
||||
~SwRenderer();
|
||||
|
||||
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* _TVG_SW_RENDERER_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgSwCommon.h"
|
||||
#include "tvgBezier.h"
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct Line
|
||||
{
|
||||
Point pt1;
|
||||
Point pt2;
|
||||
};
|
||||
|
||||
|
||||
static float _lineLength(const Point& pt1, const Point& pt2)
|
||||
{
|
||||
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
|
||||
With alpha = 1, beta = 3/8, giving results with the largest error less
|
||||
than 7% compared to the exact value. */
|
||||
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
|
||||
if (diff.x < 0) diff.x = -diff.x;
|
||||
if (diff.y < 0) diff.y = -diff.y;
|
||||
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
|
||||
}
|
||||
|
||||
|
||||
static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
|
||||
{
|
||||
auto len = _lineLength(cur.pt1, cur.pt2);
|
||||
auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
|
||||
auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
|
||||
left.pt1 = cur.pt1;
|
||||
left.pt2.x = left.pt1.x + dx;
|
||||
left.pt2.y = left.pt1.y + dy;
|
||||
right.pt1 = left.pt2;
|
||||
right.pt2 = cur.pt2;
|
||||
}
|
||||
|
||||
|
||||
static bool _growOutlineContour(SwOutline& outline, uint32_t n)
|
||||
{
|
||||
if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return false;
|
||||
outline.reservedCntrsCnt = outline.cntrsCnt + n;
|
||||
outline.cntrs = static_cast<uint16_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint16_t)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _reserveOutlineClose(SwOutline& outline)
|
||||
{
|
||||
//Dash outlines are always opened.
|
||||
//Only normal outlines use this information, it sholud be same to their contour counts.
|
||||
if (outline.closed) free(outline.closed);
|
||||
outline.closed = static_cast<bool*>(calloc(outline.reservedCntrsCnt, sizeof(bool)));
|
||||
}
|
||||
|
||||
|
||||
static void _resetOutlineClose(SwOutline& outline)
|
||||
{
|
||||
memset(outline.closed, 0x0, outline.reservedCntrsCnt * sizeof(bool));
|
||||
}
|
||||
|
||||
|
||||
static void _growOutlinePoint(SwOutline& outline, uint32_t n)
|
||||
{
|
||||
if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
|
||||
outline.reservedPtsCnt = outline.ptsCnt + n;
|
||||
outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
|
||||
outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
|
||||
}
|
||||
|
||||
|
||||
static void _outlineEnd(SwOutline& outline)
|
||||
{
|
||||
if (outline.ptsCnt == 0) return;
|
||||
|
||||
_growOutlineContour(outline, 1);
|
||||
outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
|
||||
++outline.cntrsCnt;
|
||||
}
|
||||
|
||||
|
||||
static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
|
||||
{
|
||||
_growOutlinePoint(outline, 1);
|
||||
|
||||
outline.pts[outline.ptsCnt] = mathTransform(to, transform);
|
||||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
|
||||
if (outline.ptsCnt > 0) {
|
||||
_growOutlineContour(outline, 1);
|
||||
outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
|
||||
++outline.cntrsCnt;
|
||||
}
|
||||
|
||||
++outline.ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
|
||||
{
|
||||
_growOutlinePoint(outline, 1);
|
||||
|
||||
outline.pts[outline.ptsCnt] = mathTransform(to, transform);
|
||||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
++outline.ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
|
||||
{
|
||||
_growOutlinePoint(outline, 3);
|
||||
|
||||
outline.pts[outline.ptsCnt] = mathTransform(ctrl1, transform);
|
||||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
|
||||
++outline.ptsCnt;
|
||||
|
||||
outline.pts[outline.ptsCnt] = mathTransform(ctrl2, transform);
|
||||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
|
||||
++outline.ptsCnt;
|
||||
|
||||
outline.pts[outline.ptsCnt] = mathTransform(to, transform);
|
||||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
++outline.ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
static void _outlineClose(SwOutline& outline)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
|
||||
if (outline.cntrsCnt > 0) {
|
||||
i = outline.cntrs[outline.cntrsCnt - 1] + 1;
|
||||
} else {
|
||||
i = 0; //First Path
|
||||
}
|
||||
|
||||
//Make sure there is at least one point in the current path
|
||||
if (outline.ptsCnt == i) return;
|
||||
|
||||
//Close the path
|
||||
_growOutlinePoint(outline, 1);
|
||||
|
||||
outline.pts[outline.ptsCnt] = outline.pts[i];
|
||||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
++outline.ptsCnt;
|
||||
outline.closed[outline.cntrsCnt] = true;
|
||||
}
|
||||
|
||||
|
||||
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
|
||||
{
|
||||
_growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
|
||||
_growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
|
||||
|
||||
Line cur = {dash.ptCur, *to};
|
||||
auto len = _lineLength(cur.pt1, cur.pt2);
|
||||
|
||||
if (len < dash.curLen) {
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
_outlineLineTo(*dash.outline, to, transform);
|
||||
}
|
||||
} else {
|
||||
while (len > dash.curLen) {
|
||||
len -= dash.curLen;
|
||||
Line left, right;
|
||||
_lineSplitAt(cur, dash.curLen, left, right);;
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
||||
_outlineLineTo(*dash.outline, &left.pt2, transform);
|
||||
}
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
cur = right;
|
||||
dash.ptCur = cur.pt1;
|
||||
}
|
||||
//leftovers
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &cur.pt1, transform);
|
||||
_outlineLineTo(*dash.outline, &cur.pt2, transform);
|
||||
}
|
||||
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
|
||||
//move to next dash
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
}
|
||||
}
|
||||
dash.ptCur = *to;
|
||||
}
|
||||
|
||||
|
||||
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
|
||||
{
|
||||
_growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
|
||||
_growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
|
||||
|
||||
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
|
||||
auto len = bezLength(cur);
|
||||
|
||||
if (len < dash.curLen) {
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
|
||||
}
|
||||
} else {
|
||||
while (len > dash.curLen) {
|
||||
Bezier left, right;
|
||||
len -= dash.curLen;
|
||||
bezSplitAt(cur, dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
// leftovers from a previous command don't require moveTo
|
||||
if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.start, transform);
|
||||
}
|
||||
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
|
||||
}
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
cur = right;
|
||||
dash.ptCur = right.start;
|
||||
}
|
||||
//leftovers
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &cur.start, transform);
|
||||
_outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
|
||||
}
|
||||
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
|
||||
//move to next dash
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
}
|
||||
}
|
||||
dash.ptCur = *to;
|
||||
}
|
||||
|
||||
|
||||
static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
|
||||
{
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = sdata->pathCommands(&cmds);
|
||||
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = sdata->pathCoords(&pts);
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
||||
|
||||
SwDashStroke dash;
|
||||
dash.curIdx = 0;
|
||||
dash.curLen = 0;
|
||||
dash.ptStart = {0, 0};
|
||||
dash.ptCur = {0, 0};
|
||||
dash.curOpGap = false;
|
||||
|
||||
const float* pattern;
|
||||
dash.cnt = sdata->strokeDash(&pattern);
|
||||
if (dash.cnt == 0) return nullptr;
|
||||
|
||||
//OPTMIZE ME: Use mempool???
|
||||
dash.pattern = const_cast<float*>(pattern);
|
||||
dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
|
||||
|
||||
//smart reservation
|
||||
auto outlinePtsCnt = 0;
|
||||
auto outlineCntrsCnt = 0;
|
||||
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
switch (*(cmds + i)) {
|
||||
case PathCommand::Close: {
|
||||
++outlinePtsCnt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
++outlineCntrsCnt;
|
||||
++outlinePtsCnt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
++outlinePtsCnt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
outlinePtsCnt += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++outlinePtsCnt; //for close
|
||||
++outlineCntrsCnt; //for end
|
||||
|
||||
//No idea exact count.... Reserve Approximitely 20x...
|
||||
_growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
|
||||
_growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
|
||||
|
||||
while (cmdCnt-- > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
_dashLineTo(dash, &dash.ptStart, transform);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
//reset the dash
|
||||
dash.curIdx = 0;
|
||||
dash.curLen = *dash.pattern;
|
||||
dash.curOpGap = false;
|
||||
dash.ptStart = dash.ptCur = *pts;
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
_dashLineTo(dash, pts, transform);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++cmds;
|
||||
}
|
||||
|
||||
_outlineEnd(*dash.outline);
|
||||
|
||||
return dash.outline;
|
||||
}
|
||||
|
||||
|
||||
static bool _axisAlignedRect(const SwOutline* outline)
|
||||
{
|
||||
//Fast Track: axis-aligned rectangle?
|
||||
if (outline->ptsCnt != 5) return false;
|
||||
|
||||
auto pt1 = outline->pts + 0;
|
||||
auto pt2 = outline->pts + 1;
|
||||
auto pt3 = outline->pts + 2;
|
||||
auto pt4 = outline->pts + 3;
|
||||
|
||||
auto a = SwPoint{pt1->x, pt3->y};
|
||||
auto b = SwPoint{pt3->x, pt1->y};
|
||||
|
||||
if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = sdata->pathCommands(&cmds);
|
||||
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = sdata->pathCoords(&pts);
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return false;
|
||||
|
||||
//smart reservation
|
||||
auto outlinePtsCnt = 0;
|
||||
auto outlineCntrsCnt = 0;
|
||||
auto closeCnt = 0;
|
||||
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
switch (*(cmds + i)) {
|
||||
case PathCommand::Close: {
|
||||
++outlinePtsCnt;
|
||||
++closeCnt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
++outlineCntrsCnt;
|
||||
++outlinePtsCnt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
++outlinePtsCnt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
outlinePtsCnt += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
|
||||
TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
|
||||
return false;
|
||||
}
|
||||
|
||||
++outlinePtsCnt; //for close
|
||||
++outlineCntrsCnt; //for end
|
||||
|
||||
shape->outline = mpoolReqOutline(mpool, tid);
|
||||
auto outline = shape->outline;
|
||||
|
||||
_growOutlinePoint(*outline, outlinePtsCnt);
|
||||
|
||||
if (_growOutlineContour(*outline, outlineCntrsCnt)) {
|
||||
_reserveOutlineClose(*outline);
|
||||
} else {
|
||||
_resetOutlineClose(*outline);
|
||||
}
|
||||
|
||||
//Generate Outlines
|
||||
while (cmdCnt-- > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
_outlineClose(*outline);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
_outlineMoveTo(*outline, pts, transform);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
_outlineLineTo(*outline, pts, transform);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
_outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++cmds;
|
||||
}
|
||||
|
||||
_outlineEnd(*outline);
|
||||
|
||||
outline->fillRule = sdata->fillRule();
|
||||
shape->outline = outline;
|
||||
|
||||
shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
|
||||
|
||||
//Keep it for Rasterization Region
|
||||
shape->bbox = renderRegion;
|
||||
|
||||
//Check valid region
|
||||
if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
|
||||
|
||||
//Check boundary
|
||||
if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
|
||||
renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool shapePrepared(const SwShape* shape)
|
||||
{
|
||||
return shape->rle ? true : false;
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias)
|
||||
{
|
||||
//FIXME: Should we draw it?
|
||||
//Case: Stroke Line
|
||||
//if (shape.outline->opened) return true;
|
||||
|
||||
//Case A: Fast Track Rectangle Drawing
|
||||
if (shape->fastTrack) return true;
|
||||
|
||||
//Case B: Normal Shape RLE Drawing
|
||||
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid)
|
||||
{
|
||||
mpoolRetOutline(mpool, tid);
|
||||
shape->outline = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeReset(SwShape* shape)
|
||||
{
|
||||
rleReset(shape->rle);
|
||||
rleReset(shape->strokeRle);
|
||||
shape->fastTrack = false;
|
||||
shape->bbox.reset();
|
||||
}
|
||||
|
||||
|
||||
void shapeFree(SwShape* shape)
|
||||
{
|
||||
rleFree(shape->rle);
|
||||
shapeDelFill(shape);
|
||||
|
||||
if (shape->stroke) {
|
||||
rleFree(shape->strokeRle);
|
||||
strokeFree(shape->stroke);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void shapeDelStroke(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke) return;
|
||||
rleFree(shape->strokeRle);
|
||||
shape->strokeRle = nullptr;
|
||||
strokeFree(shape->stroke);
|
||||
shape->stroke = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform)
|
||||
{
|
||||
if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
|
||||
auto stroke = shape->stroke;
|
||||
if (!stroke) return;
|
||||
|
||||
strokeReset(stroke, sdata, transform);
|
||||
rleReset(shape->strokeRle);
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
SwOutline* shapeOutline = nullptr;
|
||||
SwOutline* strokeOutline = nullptr;
|
||||
bool freeOutline = false;
|
||||
bool ret = true;
|
||||
|
||||
//Dash Style Stroke
|
||||
if (sdata->strokeDash(nullptr) > 0) {
|
||||
shapeOutline = _genDashOutline(sdata, transform);
|
||||
if (!shapeOutline) return false;
|
||||
freeOutline = true;
|
||||
//Normal Style stroke
|
||||
} else {
|
||||
if (!shape->outline) {
|
||||
if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false;
|
||||
}
|
||||
shapeOutline = shape->outline;
|
||||
}
|
||||
|
||||
if (!strokeParseOutline(shape->stroke, *shapeOutline)) {
|
||||
ret = false;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
||||
|
||||
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
|
||||
ret = false;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
|
||||
|
||||
fail:
|
||||
if (freeOutline) {
|
||||
if (shapeOutline->cntrs) free(shapeOutline->cntrs);
|
||||
if (shapeOutline->pts) free(shapeOutline->pts);
|
||||
if (shapeOutline->types) free(shapeOutline->types);
|
||||
if (shapeOutline->closed) free(shapeOutline->closed);
|
||||
free(shapeOutline);
|
||||
}
|
||||
mpoolRetStrokeOutline(mpool, tid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
|
||||
{
|
||||
return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
|
||||
{
|
||||
return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
|
||||
}
|
||||
|
||||
|
||||
void shapeResetFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->fill) {
|
||||
shape->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
|
||||
if (!shape->fill) return;
|
||||
}
|
||||
fillReset(shape->fill);
|
||||
}
|
||||
|
||||
|
||||
void shapeResetStrokeFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke->fill) {
|
||||
shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
|
||||
if (!shape->stroke->fill) return;
|
||||
}
|
||||
fillReset(shape->stroke->fill);
|
||||
}
|
||||
|
||||
|
||||
void shapeDelFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->fill) return;
|
||||
fillFree(shape->fill);
|
||||
shape->fill = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeDelStrokeFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke->fill) return;
|
||||
fillFree(shape->stroke->fill);
|
||||
shape->stroke->fill = nullptr;
|
||||
}
|
|
@ -0,0 +1,932 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static constexpr auto SW_STROKE_TAG_POINT = 1;
|
||||
static constexpr auto SW_STROKE_TAG_CUBIC = 2;
|
||||
static constexpr auto SW_STROKE_TAG_BEGIN = 4;
|
||||
static constexpr auto SW_STROKE_TAG_END = 8;
|
||||
|
||||
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
|
||||
{
|
||||
return (SW_ANGLE_PI2 - static_cast<SwFixed>(s) * SW_ANGLE_PI);
|
||||
}
|
||||
|
||||
|
||||
static inline void SCALE(const SwStroke& stroke, SwPoint& pt)
|
||||
{
|
||||
pt.x = static_cast<SwCoord>(pt.x * stroke.sx);
|
||||
pt.y = static_cast<SwCoord>(pt.y * stroke.sy);
|
||||
}
|
||||
|
||||
|
||||
static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
|
||||
{
|
||||
auto maxOld = border->maxPts;
|
||||
auto maxNew = border->ptsCnt + newPts;
|
||||
|
||||
if (maxNew <= maxOld) return;
|
||||
|
||||
auto maxCur = maxOld;
|
||||
|
||||
while (maxCur < maxNew)
|
||||
maxCur += (maxCur >> 1) + 16;
|
||||
//OPTIMIZE: use mempool!
|
||||
border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
|
||||
border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
|
||||
border->maxPts = maxCur;
|
||||
}
|
||||
|
||||
|
||||
static void _borderClose(SwStrokeBorder* border, bool reverse)
|
||||
{
|
||||
auto start = border->start;
|
||||
auto count = border->ptsCnt;
|
||||
|
||||
//Don't record empty paths!
|
||||
if (count <= start + 1U) {
|
||||
border->ptsCnt = start;
|
||||
} else {
|
||||
/* Copy the last point to the start of this sub-path,
|
||||
since it contains the adjusted starting coordinates */
|
||||
border->ptsCnt = --count;
|
||||
border->pts[start] = border->pts[count];
|
||||
|
||||
if (reverse) {
|
||||
//reverse the points
|
||||
auto pt1 = border->pts + start + 1;
|
||||
auto pt2 = border->pts + count - 1;
|
||||
|
||||
while (pt1 < pt2) {
|
||||
auto tmp = *pt1;
|
||||
*pt1 = *pt2;
|
||||
*pt2 = tmp;
|
||||
++pt1;
|
||||
--pt2;
|
||||
}
|
||||
|
||||
//reverse the tags
|
||||
auto tag1 = border->tags + start + 1;
|
||||
auto tag2 = border->tags + count - 1;
|
||||
|
||||
while (tag1 < tag2) {
|
||||
auto tmp = *tag1;
|
||||
*tag1 = *tag2;
|
||||
*tag2 = tmp;
|
||||
++tag1;
|
||||
--tag2;
|
||||
}
|
||||
}
|
||||
|
||||
border->tags[start] |= SW_STROKE_TAG_BEGIN;
|
||||
border->tags[count - 1] |= SW_STROKE_TAG_END;
|
||||
}
|
||||
|
||||
border->start = -1;
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
_growBorder(border, 3);
|
||||
|
||||
auto pt = border->pts + border->ptsCnt;
|
||||
auto tag = border->tags + border->ptsCnt;
|
||||
|
||||
pt[0] = ctrl1;
|
||||
pt[1] = ctrl2;
|
||||
pt[2] = to;
|
||||
|
||||
tag[0] = SW_STROKE_TAG_CUBIC;
|
||||
tag[1] = SW_STROKE_TAG_CUBIC;
|
||||
tag[2] = SW_STROKE_TAG_POINT;
|
||||
|
||||
border->ptsCnt += 3;
|
||||
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _borderArcTo(SwStrokeBorder* border, const SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
|
||||
{
|
||||
constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
|
||||
SwPoint a = {static_cast<SwCoord>(radius), 0};
|
||||
mathRotate(a, angleStart);
|
||||
SCALE(stroke, a);
|
||||
a += center;
|
||||
|
||||
auto total = angleDiff;
|
||||
auto angle = angleStart;
|
||||
auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
|
||||
|
||||
while (total != 0) {
|
||||
auto step = total;
|
||||
if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
|
||||
else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
|
||||
|
||||
auto next = angle + step;
|
||||
auto theta = step;
|
||||
if (theta < 0) theta = -theta;
|
||||
|
||||
theta >>= 1;
|
||||
|
||||
//compute end point
|
||||
SwPoint b = {static_cast<SwCoord>(radius), 0};
|
||||
mathRotate(b, next);
|
||||
SCALE(stroke, b);
|
||||
b += center;
|
||||
|
||||
//compute first and second control points
|
||||
auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
|
||||
|
||||
SwPoint a2 = {static_cast<SwCoord>(length), 0};
|
||||
mathRotate(a2, angle + rotate);
|
||||
SCALE(stroke, a2);
|
||||
a2 += a;
|
||||
|
||||
SwPoint b2 = {static_cast<SwCoord>(length), 0};
|
||||
mathRotate(b2, next - rotate);
|
||||
SCALE(stroke, b2);
|
||||
b2 += b;
|
||||
|
||||
//add cubic arc
|
||||
_borderCubicTo(border, a2, b2, b);
|
||||
|
||||
//process the rest of the arc?
|
||||
a = b;
|
||||
total -= step;
|
||||
angle = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movable)
|
||||
{
|
||||
if (border->movable) {
|
||||
//move last point
|
||||
border->pts[border->ptsCnt - 1] = to;
|
||||
} else {
|
||||
|
||||
//don't add zero-length line_to
|
||||
if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
|
||||
|
||||
_growBorder(border, 1);
|
||||
border->pts[border->ptsCnt] = to;
|
||||
border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
|
||||
border->ptsCnt += 1;
|
||||
}
|
||||
|
||||
border->movable = movable;
|
||||
}
|
||||
|
||||
|
||||
static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
|
||||
{
|
||||
//close current open path if any?
|
||||
if (border->start >= 0) _borderClose(border, false);
|
||||
|
||||
border->start = border->ptsCnt;
|
||||
border->movable = false;
|
||||
|
||||
_borderLineTo(border, to, false);
|
||||
}
|
||||
|
||||
|
||||
static void _arcTo(SwStroke& stroke, int32_t side)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto total = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
if (total == SW_ANGLE_PI) total = -rotate * 2;
|
||||
|
||||
_borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
|
||||
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
if (stroke.join == StrokeJoin::Round) {
|
||||
_arcTo(stroke, side);
|
||||
} else {
|
||||
//this is a mitered (pointed) or beveled (truncated) corner
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false;
|
||||
SwFixed phi = 0;
|
||||
SwFixed thcos = 0;
|
||||
|
||||
if (!bevel) {
|
||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
if (theta == SW_ANGLE_PI) {
|
||||
theta = rotate;
|
||||
phi = stroke.angleIn;
|
||||
} else {
|
||||
theta /= 2;
|
||||
phi = stroke.angleIn + theta + rotate;
|
||||
}
|
||||
|
||||
thcos = mathCos(theta);
|
||||
auto sigma = mathMultiply(MITER_LIMIT, thcos);
|
||||
|
||||
//is miter limit exceeded?
|
||||
if (sigma < 0x10000L) bevel = true;
|
||||
}
|
||||
|
||||
//this is a bevel (broken angle)
|
||||
if (bevel) {
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
_borderLineTo(border, delta, false);
|
||||
//this is a miter (intersection)
|
||||
} else {
|
||||
auto length = mathDivide(stroke.width, thcos);
|
||||
SwPoint delta = {static_cast<SwCoord>(length), 0};
|
||||
mathRotate(delta, phi);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
/* Now add and end point
|
||||
Only needed if not lineto (lineLength is zero for curves) */
|
||||
if (lineLength == 0) {
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
|
||||
SwPoint delta;
|
||||
bool intersect = false;
|
||||
|
||||
/* Only intersect borders if between two line_to's and both
|
||||
lines are long enough (line length is zero fur curves). */
|
||||
if (border->movable && lineLength > 0) {
|
||||
//compute minimum required length of lines
|
||||
SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
|
||||
if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
|
||||
}
|
||||
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
if (!intersect) {
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
} else {
|
||||
//compute median angle
|
||||
auto phi = stroke.angleIn + theta;
|
||||
auto thcos = mathCos(theta);
|
||||
delta = {static_cast<SwCoord>(mathDivide(stroke.width, thcos)), 0};
|
||||
mathRotate(delta, phi + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
}
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
|
||||
|
||||
void _processCorner(SwStroke& stroke, SwFixed lineLength)
|
||||
{
|
||||
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
|
||||
//no specific corner processing is required if the turn is 0
|
||||
if (turn == 0) return;
|
||||
|
||||
//when we turn to the right, the inside side is 0
|
||||
int32_t inside = 0;
|
||||
|
||||
//otherwise, the inside is 1
|
||||
if (turn < 0) inside = 1;
|
||||
|
||||
//process the inside
|
||||
_inside(stroke, inside, lineLength);
|
||||
|
||||
//process the outside
|
||||
_outside(stroke, 1 - inside, lineLength);
|
||||
}
|
||||
|
||||
|
||||
void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
|
||||
{
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, startAngle + SW_ANGLE_PI2);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
auto pt = stroke.center + delta;
|
||||
auto border = stroke.borders;
|
||||
_borderMoveTo(border, pt);
|
||||
|
||||
pt = stroke.center - delta;
|
||||
++border;
|
||||
_borderMoveTo(border, pt);
|
||||
|
||||
/* Save angle, position and line length for last join
|
||||
lineLength is zero for curves */
|
||||
stroke.subPathAngle = startAngle;
|
||||
stroke.firstPt = false;
|
||||
stroke.subPathLineLength = lineLength;
|
||||
}
|
||||
|
||||
|
||||
static void _lineTo(SwStroke& stroke, const SwPoint& to)
|
||||
{
|
||||
auto delta = to - stroke.center;
|
||||
|
||||
//a zero-length lineto is a no-op; avoid creating a spurious corner
|
||||
if (delta.zero()) return;
|
||||
|
||||
//compute length of line
|
||||
auto lineLength = mathLength(delta);
|
||||
auto angle = mathAtan(delta);
|
||||
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle + SW_ANGLE_PI2);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
//process corner if necessary
|
||||
if (stroke.firstPt) {
|
||||
/* This is the first segment of a subpath. We need to add a point to each border
|
||||
at their respective starting point locations. */
|
||||
_firstSubPath(stroke, angle, lineLength);
|
||||
} else {
|
||||
//process the current corner
|
||||
stroke.angleOut = angle;
|
||||
_processCorner(stroke, lineLength);
|
||||
}
|
||||
|
||||
//now add a line segment to both the inside and outside paths
|
||||
auto border = stroke.borders;
|
||||
auto side = 1;
|
||||
|
||||
while (side >= 0) {
|
||||
auto pt = to + delta;
|
||||
|
||||
//the ends of lineto borders are movable
|
||||
_borderLineTo(border, pt, true);
|
||||
|
||||
delta.x = -delta.x;
|
||||
delta.y = -delta.y;
|
||||
|
||||
--side;
|
||||
++border;
|
||||
}
|
||||
|
||||
stroke.angleIn = angle;
|
||||
stroke.center = to;
|
||||
stroke.lineLength = lineLength;
|
||||
}
|
||||
|
||||
|
||||
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
/* if all control points are coincident, this is a no-op;
|
||||
avoid creating a spurious corner */
|
||||
if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
|
||||
stroke.center = to;
|
||||
return;
|
||||
}
|
||||
|
||||
SwPoint bezStack[37]; //TODO: static?
|
||||
auto limit = bezStack + 32;
|
||||
auto arc = bezStack;
|
||||
auto firstArc = true;
|
||||
arc[0] = to;
|
||||
arc[1] = ctrl2;
|
||||
arc[2] = ctrl1;
|
||||
arc[3] = stroke.center;
|
||||
|
||||
while (arc >= bezStack) {
|
||||
SwFixed angleIn, angleOut, angleMid;
|
||||
|
||||
//initialize with current direction
|
||||
angleIn = angleOut = angleMid = stroke.angleIn;
|
||||
|
||||
if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
|
||||
if (stroke.firstPt) stroke.angleIn = angleIn;
|
||||
mathSplitCubic(arc);
|
||||
arc += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstArc) {
|
||||
firstArc = false;
|
||||
//process corner if necessary
|
||||
if (stroke.firstPt) {
|
||||
_firstSubPath(stroke, angleIn, 0);
|
||||
} else {
|
||||
stroke.angleOut = angleIn;
|
||||
_processCorner(stroke, 0);
|
||||
}
|
||||
} else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) {
|
||||
//if the deviation from one arc to the next is too great add a round corner
|
||||
stroke.center = arc[3];
|
||||
stroke.angleOut = angleIn;
|
||||
stroke.join = StrokeJoin::Round;
|
||||
|
||||
_processCorner(stroke, 0);
|
||||
|
||||
//reinstate line join style
|
||||
stroke.join = stroke.joinSaved;
|
||||
}
|
||||
|
||||
//the arc's angle is small enough; we can add it directly to each border
|
||||
auto theta1 = mathDiff(angleIn, angleMid) / 2;
|
||||
auto theta2 = mathDiff(angleMid, angleOut) / 2;
|
||||
auto phi1 = mathMean(angleIn, angleMid);
|
||||
auto phi2 = mathMean(angleMid, angleOut);
|
||||
auto length1 = mathDivide(stroke.width, mathCos(theta1));
|
||||
auto length2 = mathDivide(stroke.width, mathCos(theta2));
|
||||
SwFixed alpha0 = 0;
|
||||
|
||||
//compute direction of original arc
|
||||
if (stroke.handleWideStrokes) {
|
||||
alpha0 = mathAtan(arc[0] - arc[3]);
|
||||
}
|
||||
|
||||
auto border = stroke.borders;
|
||||
int32_t side = 0;
|
||||
|
||||
while (side <= 1)
|
||||
{
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
//compute control points
|
||||
SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
|
||||
mathRotate(_ctrl1, phi1 + rotate);
|
||||
SCALE(stroke, _ctrl1);
|
||||
_ctrl1 += arc[2];
|
||||
|
||||
SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
|
||||
mathRotate(_ctrl2, phi2 + rotate);
|
||||
SCALE(stroke, _ctrl2);
|
||||
_ctrl2 += arc[1];
|
||||
|
||||
//compute end point
|
||||
SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(_end, angleOut + rotate);
|
||||
SCALE(stroke, _end);
|
||||
_end += arc[0];
|
||||
|
||||
if (stroke.handleWideStrokes) {
|
||||
|
||||
/* determine whether the border radius is greater than the radius of
|
||||
curvature of the original arc */
|
||||
auto _start = border->pts[border->ptsCnt - 1];
|
||||
auto alpha1 = mathAtan(_end - _start);
|
||||
|
||||
//is the direction of the border arc opposite to that of the original arc?
|
||||
if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
|
||||
|
||||
//use the sine rule to find the intersection point
|
||||
auto beta = mathAtan(arc[3] - _start);
|
||||
auto gamma = mathAtan(arc[0] - _end);
|
||||
auto bvec = _end - _start;
|
||||
auto blen = mathLength(bvec);
|
||||
auto sinA = abs(mathSin(alpha1 - gamma));
|
||||
auto sinB = abs(mathSin(beta - gamma));
|
||||
auto alen = mathMulDiv(blen, sinA, sinB);
|
||||
|
||||
SwPoint delta = {static_cast<SwCoord>(alen), 0};
|
||||
mathRotate(delta, beta);
|
||||
delta += _start;
|
||||
|
||||
//circumnavigate the negative sector backwards
|
||||
border->movable = false;
|
||||
_borderLineTo(border, delta, false);
|
||||
_borderLineTo(border, _end, false);
|
||||
_borderCubicTo(border, _ctrl2, _ctrl1, _start);
|
||||
|
||||
//and then move to the endpoint
|
||||
_borderLineTo(border, _end, false);
|
||||
|
||||
++side;
|
||||
++border;
|
||||
continue;
|
||||
}
|
||||
|
||||
//else fall through
|
||||
}
|
||||
_borderCubicTo(border, _ctrl1, _ctrl2, _end);
|
||||
++side;
|
||||
++border;
|
||||
}
|
||||
arc -= 3;
|
||||
stroke.angleIn = angleOut;
|
||||
}
|
||||
stroke.center = to;
|
||||
}
|
||||
|
||||
|
||||
static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
|
||||
{
|
||||
if (stroke.cap == StrokeCap::Square) {
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta2, angle + rotate);
|
||||
SCALE(stroke, delta2);
|
||||
delta += stroke.center + delta2;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
delta2 = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta2, angle - rotate);
|
||||
SCALE(stroke, delta2);
|
||||
delta += delta2 + stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
} else if (stroke.cap == StrokeCap::Round) {
|
||||
|
||||
stroke.angleIn = angle;
|
||||
stroke.angleOut = angle + SW_ANGLE_PI;
|
||||
_arcTo(stroke, side);
|
||||
return;
|
||||
|
||||
} else { //Butt
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle - rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _addReverseLeft(SwStroke& stroke, bool opened)
|
||||
{
|
||||
auto right = stroke.borders + 0;
|
||||
auto left = stroke.borders + 1;
|
||||
auto newPts = left->ptsCnt - left->start;
|
||||
|
||||
if (newPts <= 0) return;
|
||||
|
||||
_growBorder(right, newPts);
|
||||
|
||||
auto dstPt = right->pts + right->ptsCnt;
|
||||
auto dstTag = right->tags + right->ptsCnt;
|
||||
auto srcPt = left->pts + left->ptsCnt - 1;
|
||||
auto srcTag = left->tags + left->ptsCnt - 1;
|
||||
|
||||
while (srcPt >= left->pts + left->start) {
|
||||
*dstPt = *srcPt;
|
||||
*dstTag = *srcTag;
|
||||
|
||||
if (opened) {
|
||||
dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
|
||||
} else {
|
||||
//switch begin/end tags if necessary
|
||||
auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
|
||||
if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
|
||||
dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
|
||||
}
|
||||
|
||||
--srcPt;
|
||||
--srcTag;
|
||||
++dstPt;
|
||||
++dstTag;
|
||||
}
|
||||
|
||||
left->ptsCnt = left->start;
|
||||
right->ptsCnt += newPts;
|
||||
right->movable = false;
|
||||
left->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
|
||||
{
|
||||
/* We cannot process the first point because there is not enough
|
||||
information regarding its corner/cap. Later, it will be processed
|
||||
in the _endSubPath() */
|
||||
|
||||
stroke.firstPt = true;
|
||||
stroke.center = to;
|
||||
stroke.closedSubPath = closed;
|
||||
|
||||
/* Determine if we need to check whether the border radius is greater
|
||||
than the radius of curvature of a curve, to handle this case specially.
|
||||
This is only required if bevel joins or butt caps may be created because
|
||||
round & miter joins and round & square caps cover the nagative sector
|
||||
created with wide strokes. */
|
||||
if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
|
||||
stroke.handleWideStrokes = true;
|
||||
else
|
||||
stroke.handleWideStrokes = false;
|
||||
|
||||
stroke.ptStartSubPath = to;
|
||||
stroke.angleIn = 0;
|
||||
}
|
||||
|
||||
|
||||
static void _endSubPath(SwStroke& stroke)
|
||||
{
|
||||
if (stroke.closedSubPath) {
|
||||
//close the path if needed
|
||||
if (stroke.center != stroke.ptStartSubPath)
|
||||
_lineTo(stroke, stroke.ptStartSubPath);
|
||||
|
||||
//process the corner
|
||||
stroke.angleOut = stroke.subPathAngle;
|
||||
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
|
||||
//No specific corner processing is required if the turn is 0
|
||||
if (turn != 0) {
|
||||
|
||||
//when we turn to the right, the inside is 0
|
||||
int32_t inside = 0;
|
||||
|
||||
//otherwise, the inside is 1
|
||||
if (turn < 0) inside = 1;
|
||||
|
||||
_inside(stroke, inside, stroke.subPathLineLength); //inside
|
||||
_outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
|
||||
}
|
||||
|
||||
_borderClose(stroke.borders + 0, false);
|
||||
_borderClose(stroke.borders + 1, true);
|
||||
} else {
|
||||
auto right = stroke.borders;
|
||||
|
||||
/* all right, this is an opened path, we need to add a cap between
|
||||
right & left, add the reverse of left, then add a final cap
|
||||
between left & right */
|
||||
_addCap(stroke, stroke.angleIn, 0);
|
||||
|
||||
//add reversed points from 'left' to 'right'
|
||||
_addReverseLeft(stroke, true);
|
||||
|
||||
//now add the final cap
|
||||
stroke.center = stroke.ptStartSubPath;
|
||||
_addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
|
||||
|
||||
/* now end the right subpath accordingly. The left one is rewind
|
||||
and deosn't need further processing */
|
||||
_borderClose(right, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
|
||||
{
|
||||
auto count = border->ptsCnt;
|
||||
auto tags = border->tags;
|
||||
uint32_t _ptsCnt = 0;
|
||||
uint32_t _cntrsCnt = 0;
|
||||
bool inCntr = false;
|
||||
|
||||
while (count > 0) {
|
||||
if (tags[0] & SW_STROKE_TAG_BEGIN) {
|
||||
if (inCntr) goto fail;
|
||||
inCntr = true;
|
||||
} else if (!inCntr) goto fail;
|
||||
|
||||
if (tags[0] & SW_STROKE_TAG_END) {
|
||||
inCntr = false;
|
||||
++_cntrsCnt;
|
||||
}
|
||||
--count;
|
||||
++_ptsCnt;
|
||||
++tags;
|
||||
}
|
||||
|
||||
if (inCntr) goto fail;
|
||||
|
||||
ptsCnt = _ptsCnt;
|
||||
cntrsCnt = _cntrsCnt;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
ptsCnt = 0;
|
||||
cntrsCnt = 0;
|
||||
}
|
||||
|
||||
|
||||
static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
if (border->ptsCnt == 0) return; //invalid border
|
||||
|
||||
memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
|
||||
|
||||
auto cnt = border->ptsCnt;
|
||||
auto src = border->tags;
|
||||
auto tags = outline->types + outline->ptsCnt;
|
||||
auto cntrs = outline->cntrs + outline->cntrsCnt;
|
||||
uint16_t idx = outline->ptsCnt;
|
||||
|
||||
while (cnt > 0) {
|
||||
|
||||
if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
|
||||
else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
|
||||
else {
|
||||
//LOG: What type of stroke outline??
|
||||
}
|
||||
|
||||
if (*src & SW_STROKE_TAG_END) {
|
||||
*cntrs = idx;
|
||||
++cntrs;
|
||||
++outline->cntrsCnt;
|
||||
}
|
||||
++src;
|
||||
++tags;
|
||||
++idx;
|
||||
--cnt;
|
||||
}
|
||||
outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void strokeFree(SwStroke* stroke)
|
||||
{
|
||||
if (!stroke) return;
|
||||
|
||||
//free borders
|
||||
if (stroke->borders[0].pts) free(stroke->borders[0].pts);
|
||||
if (stroke->borders[0].tags) free(stroke->borders[0].tags);
|
||||
if (stroke->borders[1].pts) free(stroke->borders[1].pts);
|
||||
if (stroke->borders[1].tags) free(stroke->borders[1].tags);
|
||||
|
||||
fillFree(stroke->fill);
|
||||
stroke->fill = nullptr;
|
||||
|
||||
free(stroke);
|
||||
}
|
||||
|
||||
|
||||
void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
|
||||
{
|
||||
if (transform) {
|
||||
stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
|
||||
stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
|
||||
} else {
|
||||
stroke->sx = stroke->sy = 1.0f;
|
||||
}
|
||||
|
||||
stroke->width = HALF_STROKE(sdata->strokeWidth());
|
||||
stroke->cap = sdata->strokeCap();
|
||||
|
||||
//Save line join: it can be temporarily changed when stroking curves...
|
||||
stroke->joinSaved = stroke->join = sdata->strokeJoin();
|
||||
|
||||
stroke->borders[0].ptsCnt = 0;
|
||||
stroke->borders[0].start = -1;
|
||||
stroke->borders[1].ptsCnt = 0;
|
||||
stroke->borders[1].start = -1;
|
||||
}
|
||||
|
||||
|
||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
|
||||
{
|
||||
uint32_t first = 0;
|
||||
|
||||
for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
|
||||
auto last = outline.cntrs[i]; //index of last point in contour
|
||||
auto limit = outline.pts + last;
|
||||
|
||||
//Skip empty points
|
||||
if (last <= first) {
|
||||
first = last + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto start = outline.pts[first];
|
||||
auto pt = outline.pts + first;
|
||||
auto types = outline.types + first;
|
||||
auto type = types[0];
|
||||
|
||||
//A contour cannot start with a cubic control point
|
||||
if (type == SW_CURVE_TYPE_CUBIC) return false;
|
||||
|
||||
auto closed = outline.closed ? outline.closed[i]: false;
|
||||
|
||||
_beginSubPath(*stroke, start, closed);
|
||||
|
||||
while (pt < limit) {
|
||||
++pt;
|
||||
++types;
|
||||
|
||||
//emit a signel line_to
|
||||
if (types[0] == SW_CURVE_TYPE_POINT) {
|
||||
_lineTo(*stroke, *pt);
|
||||
//types cubic
|
||||
} else {
|
||||
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
|
||||
|
||||
pt += 2;
|
||||
types += 2;
|
||||
|
||||
if (pt <= limit) {
|
||||
_cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
|
||||
continue;
|
||||
}
|
||||
_cubicTo(*stroke, pt[-2], pt[-1], start);
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
|
||||
close:
|
||||
if (!stroke->firstPt) _endSubPath(*stroke);
|
||||
first = last + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
uint32_t count1, count2, count3, count4;
|
||||
|
||||
_getCounts(stroke->borders + 0, count1, count2);
|
||||
_getCounts(stroke->borders + 1, count3, count4);
|
||||
|
||||
auto ptsCnt = count1 + count3;
|
||||
auto cntrsCnt = count2 + count4;
|
||||
|
||||
auto outline = mpoolReqStrokeOutline(mpool, tid);
|
||||
if (outline->reservedPtsCnt < ptsCnt) {
|
||||
outline->pts = static_cast<SwPoint*>(realloc(outline->pts, sizeof(SwPoint) * ptsCnt));
|
||||
outline->types = static_cast<uint8_t*>(realloc(outline->types, sizeof(uint8_t) * ptsCnt));
|
||||
outline->reservedPtsCnt = ptsCnt;
|
||||
}
|
||||
if (outline->reservedCntrsCnt < cntrsCnt) {
|
||||
outline->cntrs = static_cast<uint16_t*>(realloc(outline->cntrs, sizeof(uint16_t) * cntrsCnt));
|
||||
outline->reservedCntrsCnt = cntrsCnt;
|
||||
}
|
||||
|
||||
_exportBorderOutline(*stroke, outline, 0); //left
|
||||
_exportBorderOutline(*stroke, outline, 1); //right
|
||||
|
||||
return outline;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgIteratorAccessor.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor)
|
||||
{
|
||||
while (auto child = it->next()) {
|
||||
//Access the child
|
||||
if (!func(child)) return false;
|
||||
|
||||
//Access the children of the child
|
||||
if (auto it2 = itrAccessor.iterator(child)) {
|
||||
if (!accessChildren(it2, func, itrAccessor)) {
|
||||
delete(it2);
|
||||
return false;
|
||||
}
|
||||
delete(it2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
unique_ptr<Picture> Accessor::access(unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept
|
||||
{
|
||||
auto p = picture.get();
|
||||
if (!p || !func) return picture;
|
||||
|
||||
//Use the Preorder Tree-Search
|
||||
|
||||
//Root
|
||||
if (!func(p)) return picture;
|
||||
|
||||
//Children
|
||||
IteratorAccessor itrAccessor;
|
||||
if (auto it = itrAccessor.iterator(p)) {
|
||||
accessChildren(it, func, itrAccessor);
|
||||
delete(it);
|
||||
}
|
||||
return picture;
|
||||
}
|
||||
|
||||
|
||||
Accessor::~Accessor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
Accessor::Accessor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Accessor> Accessor::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Accessor>(new Accessor);
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_ARRAY_H_
|
||||
#define _TVG_ARRAY_H_
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
template<class T>
|
||||
struct Array
|
||||
{
|
||||
T* data = nullptr;
|
||||
uint32_t count = 0;
|
||||
uint32_t reserved = 0;
|
||||
|
||||
void push(T element)
|
||||
{
|
||||
if (count + 1 > reserved) {
|
||||
reserved = (count + 1) * 2;
|
||||
auto p = data;
|
||||
data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
|
||||
if (!data) {
|
||||
data = p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
data[count++] = element;
|
||||
}
|
||||
|
||||
bool reserve(uint32_t size)
|
||||
{
|
||||
if (size > reserved) {
|
||||
reserved = size;
|
||||
auto p = data;
|
||||
data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
|
||||
if (!data) {
|
||||
data = p;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool grow(uint32_t size)
|
||||
{
|
||||
return reserve(count + size);
|
||||
}
|
||||
|
||||
T* ptr()
|
||||
{
|
||||
return data + count;
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
if (count > 0) --count;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (data) {
|
||||
free(data);
|
||||
data = nullptr;
|
||||
}
|
||||
count = reserved = 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void operator=(const Array& rhs)
|
||||
{
|
||||
reserve(rhs.count);
|
||||
if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * reserved);
|
||||
count = rhs.count;
|
||||
}
|
||||
|
||||
~Array()
|
||||
{
|
||||
if (data) free(data);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_ARRAY_H_
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include "tvgBezier.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static float _lineLength(const Point& pt1, const Point& pt2)
|
||||
{
|
||||
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
|
||||
With alpha = 1, beta = 3/8, giving results with the largest error less
|
||||
than 7% compared to the exact value. */
|
||||
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
|
||||
if (diff.x < 0) diff.x = -diff.x;
|
||||
if (diff.y < 0) diff.y = -diff.y;
|
||||
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
void bezSplit(const Bezier&cur, Bezier& left, Bezier& right)
|
||||
{
|
||||
auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
|
||||
left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
|
||||
right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
|
||||
left.start.x = cur.start.x;
|
||||
right.end.x = cur.end.x;
|
||||
left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
|
||||
right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
|
||||
left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
|
||||
|
||||
c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
|
||||
left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
|
||||
right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
|
||||
left.start.y = cur.start.y;
|
||||
right.end.y = cur.end.y;
|
||||
left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
|
||||
right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
|
||||
left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
|
||||
}
|
||||
|
||||
|
||||
float bezLength(const Bezier& cur)
|
||||
{
|
||||
Bezier left, right;
|
||||
auto len = _lineLength(cur.start, cur.ctrl1) + _lineLength(cur.ctrl1, cur.ctrl2) + _lineLength(cur.ctrl2, cur.end);
|
||||
auto chord = _lineLength(cur.start, cur.end);
|
||||
|
||||
if (fabsf(len - chord) > BEZIER_EPSILON) {
|
||||
bezSplit(cur, left, right);
|
||||
return bezLength(left) + bezLength(right);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void bezSplitLeft(Bezier& cur, float at, Bezier& left)
|
||||
{
|
||||
left.start = cur.start;
|
||||
|
||||
left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
|
||||
left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
|
||||
|
||||
left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot
|
||||
left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot
|
||||
|
||||
cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
|
||||
cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
|
||||
|
||||
cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
|
||||
cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
|
||||
|
||||
left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
|
||||
left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
|
||||
|
||||
left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
|
||||
left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
|
||||
}
|
||||
|
||||
|
||||
float bezAt(const Bezier& bz, float at)
|
||||
{
|
||||
auto len = bezLength(bz);
|
||||
auto biggest = 1.0f;
|
||||
auto smallest = 0.0f;
|
||||
auto t = 0.5f;
|
||||
|
||||
//just in case to prevent an infinite loop
|
||||
if (at <= 0) return 0.0f;
|
||||
|
||||
if (at >= len) return 1.0f;
|
||||
|
||||
while (true) {
|
||||
auto right = bz;
|
||||
Bezier left;
|
||||
bezSplitLeft(right, t, left);
|
||||
len = bezLength(left);
|
||||
|
||||
if (fabsf(len - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len < at) {
|
||||
smallest = t;
|
||||
t = (t + biggest) * 0.5f;
|
||||
} else {
|
||||
biggest = t;
|
||||
t = (smallest + t) * 0.5f;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
|
||||
{
|
||||
right = cur;
|
||||
auto t = bezAt(right, at);
|
||||
bezSplitLeft(right, t, left);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_BEZIER_H_
|
||||
#define _TVG_BEZIER_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
#define BEZIER_EPSILON 1e-4f
|
||||
|
||||
struct Bezier
|
||||
{
|
||||
Point start;
|
||||
Point ctrl1;
|
||||
Point ctrl2;
|
||||
Point end;
|
||||
};
|
||||
|
||||
void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
|
||||
float bezLength(const Bezier& cur);
|
||||
void bezSplitLeft(Bezier& cur, float at, Bezier& left);
|
||||
float bezAt(const Bezier& bz, float at);
|
||||
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_BEZIER_H_
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_BINARY_DESC_H_
|
||||
#define _TVG_BINARY_DESC_H_
|
||||
|
||||
/* TODO: Need to consider whether uin8_t is enough size for extension...
|
||||
Rather than optimal data, we can use enough size and data compress? */
|
||||
|
||||
/* Data types, do not change data types once Tvg Format is officially released,
|
||||
That would occur the abi break. */
|
||||
|
||||
using TvgBinByte = uint8_t;
|
||||
using TvgBinCounter = uint32_t;
|
||||
using TvgBinTag = TvgBinByte;
|
||||
using TvgBinFlag = TvgBinByte;
|
||||
|
||||
|
||||
//Header
|
||||
#define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
|
||||
#define TVG_HEADER_SIGNATURE "ThorVG"
|
||||
#define TVG_HEADER_SIGNATURE_LENGTH 6
|
||||
#define TVG_HEADER_VERSION "000400" //Major 00, Minor 04, Micro 00
|
||||
#define TVG_HEADER_VERSION_LENGTH 6
|
||||
#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
|
||||
#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
|
||||
//Compress Size
|
||||
#define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
|
||||
#define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
|
||||
#define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter)
|
||||
//Reserved Flag
|
||||
#define TVG_HEAD_FLAG_COMPRESSED 0x01
|
||||
|
||||
//Paint Type
|
||||
#define TVG_TAG_CLASS_PICTURE (TvgBinTag)0xfc
|
||||
#define TVG_TAG_CLASS_SHAPE (TvgBinTag)0xfd
|
||||
#define TVG_TAG_CLASS_SCENE (TvgBinTag)0xfe
|
||||
|
||||
|
||||
//Paint
|
||||
#define TVG_TAG_PAINT_OPACITY (TvgBinTag)0x10
|
||||
#define TVG_TAG_PAINT_TRANSFORM (TvgBinTag)0x11
|
||||
#define TVG_TAG_PAINT_CMP_TARGET (TvgBinTag)0x01
|
||||
#define TVG_TAG_PAINT_CMP_METHOD (TvgBinTag)0x20
|
||||
|
||||
|
||||
//Scene
|
||||
#define TVG_TAG_SCENE_RESERVEDCNT (TvgBinTag)0x30
|
||||
|
||||
|
||||
//Shape
|
||||
#define TVG_TAG_SHAPE_PATH (TvgBinTag)0x40
|
||||
#define TVG_TAG_SHAPE_STROKE (TvgBinTag)0x41
|
||||
#define TVG_TAG_SHAPE_FILL (TvgBinTag)0x42
|
||||
#define TVG_TAG_SHAPE_COLOR (TvgBinTag)0x43
|
||||
#define TVG_TAG_SHAPE_FILLRULE (TvgBinTag)0x44
|
||||
#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50
|
||||
#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51
|
||||
|
||||
//Stroke
|
||||
#define TVG_TAG_SHAPE_STROKE_WIDTH (TvgBinTag)0x52
|
||||
#define TVG_TAG_SHAPE_STROKE_COLOR (TvgBinTag)0x53
|
||||
#define TVG_TAG_SHAPE_STROKE_FILL (TvgBinTag)0x54
|
||||
#define TVG_TAG_SHAPE_STROKE_DASHPTRN (TvgBinTag)0x55
|
||||
|
||||
|
||||
//Fill
|
||||
#define TVG_TAG_FILL_LINEAR_GRADIENT (TvgBinTag)0x60
|
||||
#define TVG_TAG_FILL_RADIAL_GRADIENT (TvgBinTag)0x61
|
||||
#define TVG_TAG_FILL_COLORSTOPS (TvgBinTag)0x62
|
||||
#define TVG_TAG_FILL_FILLSPREAD (TvgBinTag)0x63
|
||||
#define TVG_TAG_FILL_TRANSFORM (TvgBinTag)0x64
|
||||
|
||||
|
||||
//Picture
|
||||
#define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70
|
||||
|
||||
#endif //_TVG_BINARY_DESC_H_
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgCanvasImpl.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::reserve(uint32_t n) noexcept
|
||||
{
|
||||
if (!pImpl->paints.reserve(n)) return Result::FailedAllocation;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::push(unique_ptr<Paint> paint) noexcept
|
||||
{
|
||||
return pImpl->push(move(paint));
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::clear(bool free) noexcept
|
||||
{
|
||||
return pImpl->clear(free);
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::draw() noexcept
|
||||
{
|
||||
return pImpl->draw();
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::update(Paint* paint) noexcept
|
||||
{
|
||||
return pImpl->update(paint, false);
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::sync() noexcept
|
||||
{
|
||||
return pImpl->sync();
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_CANVAS_IMPL_H_
|
||||
#define _TVG_CANVAS_IMPL_H_
|
||||
|
||||
#include "tvgPaint.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct Canvas::Impl
|
||||
{
|
||||
Array<Paint*> paints;
|
||||
RenderMethod* renderer;
|
||||
bool refresh = false; //if all paints should be updated by force.
|
||||
bool drawing = false; //on drawing condition?
|
||||
|
||||
Impl(RenderMethod* pRenderer):renderer(pRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
clear(true);
|
||||
delete(renderer);
|
||||
}
|
||||
|
||||
Result push(unique_ptr<Paint> paint)
|
||||
{
|
||||
//You can not push paints during rendering.
|
||||
if (drawing) return Result::InsufficientCondition;
|
||||
|
||||
auto p = paint.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
paints.push(p);
|
||||
|
||||
return update(p, true);
|
||||
}
|
||||
|
||||
Result clear(bool free)
|
||||
{
|
||||
//Clear render target before drawing
|
||||
if (!renderer || !renderer->clear()) return Result::InsufficientCondition;
|
||||
|
||||
//Free paints
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
(*paint)->pImpl->dispose(*renderer);
|
||||
if (free) delete(*paint);
|
||||
}
|
||||
|
||||
paints.clear();
|
||||
|
||||
drawing = false;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
void needRefresh()
|
||||
{
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
Result update(Paint* paint, bool force)
|
||||
{
|
||||
if (paints.count == 0 || drawing || !renderer) return Result::InsufficientCondition;
|
||||
|
||||
Array<RenderData> clips;
|
||||
auto flag = RenderUpdateFlag::None;
|
||||
if (refresh || force) flag = RenderUpdateFlag::All;
|
||||
|
||||
//Update single paint node
|
||||
if (paint) {
|
||||
//Optimize Me: Can we skip the searching?
|
||||
for (auto paint2 = paints.data; paint2 < (paints.data + paints.count); ++paint2) {
|
||||
if ((*paint2) == paint) {
|
||||
paint->pImpl->update(*renderer, nullptr, 255, clips, flag);
|
||||
return Result::Success;
|
||||
}
|
||||
}
|
||||
return Result::InvalidArguments;
|
||||
//Update all retained paint nodes
|
||||
} else {
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
(*paint)->pImpl->update(*renderer, nullptr, 255, clips, flag);
|
||||
}
|
||||
}
|
||||
|
||||
refresh = false;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result draw()
|
||||
{
|
||||
if (drawing || paints.count == 0 || !renderer || !renderer->preRender()) return Result::InsufficientCondition;
|
||||
|
||||
bool rendered = false;
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
if ((*paint)->pImpl->render(*renderer)) rendered = true;
|
||||
}
|
||||
|
||||
if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;
|
||||
|
||||
drawing = true;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result sync()
|
||||
{
|
||||
if (!drawing) return Result::InsufficientCondition;
|
||||
|
||||
if (renderer->sync()) {
|
||||
drawing = false;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _TVG_CANVAS_IMPL_H_ */
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_COMMON_H_
|
||||
#define _TVG_COMMON_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "thorvg.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace tvg;
|
||||
|
||||
//for MSVC Compat
|
||||
#ifdef _MSC_VER
|
||||
#define TVG_UNUSED
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
#else
|
||||
#define TVG_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
// Portable 'fallthrough' attribute
|
||||
#if __has_cpp_attribute(fallthrough)
|
||||
#ifdef _MSC_VER
|
||||
#define TVG_FALLTHROUGH [[fallthrough]];
|
||||
#else
|
||||
#define TVG_FALLTHROUGH __attribute__ ((fallthrough));
|
||||
#endif
|
||||
#else
|
||||
#define TVG_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(__clang__)
|
||||
#define strncpy strncpy_s
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
//TVG class identifier values
|
||||
#define TVG_CLASS_ID_UNDEFINED 0
|
||||
#define TVG_CLASS_ID_SHAPE 1
|
||||
#define TVG_CLASS_ID_SCENE 2
|
||||
#define TVG_CLASS_ID_PICTURE 3
|
||||
#define TVG_CLASS_ID_LINEAR 4
|
||||
#define TVG_CLASS_ID_RADIAL 5
|
||||
|
||||
enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
|
||||
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
#define TVGLOG(tag, fmt, ...) fprintf(stdout, tag ": " fmt "\n", ##__VA_ARGS__) //Log Message for notifying user some useful info
|
||||
#define TVGERR(tag, fmt, ...) fprintf(stderr, tag ": " fmt "\n", ##__VA_ARGS__) //Error Message for us to fix it
|
||||
#else
|
||||
#define TVGERR(...)
|
||||
#define TVGLOG(...)
|
||||
#endif
|
||||
|
||||
uint16_t THORVG_VERSION_NUMBER();
|
||||
|
||||
#endif //_TVG_COMMON_H_
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Fill::Fill():pImpl(new Impl())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Fill::~Fill()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
|
||||
{
|
||||
if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
|
||||
|
||||
if (cnt == 0) {
|
||||
if (pImpl->colorStops) {
|
||||
free(pImpl->colorStops);
|
||||
pImpl->colorStops = nullptr;
|
||||
pImpl->cnt = 0;
|
||||
}
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
if (pImpl->cnt != cnt) {
|
||||
pImpl->colorStops = static_cast<ColorStop*>(realloc(pImpl->colorStops, cnt * sizeof(ColorStop)));
|
||||
}
|
||||
|
||||
pImpl->cnt = cnt;
|
||||
memcpy(pImpl->colorStops, colorStops, cnt * sizeof(ColorStop));
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
|
||||
{
|
||||
if (colorStops) *colorStops = pImpl->colorStops;
|
||||
|
||||
return pImpl->cnt;
|
||||
}
|
||||
|
||||
|
||||
Result Fill::spread(FillSpread s) noexcept
|
||||
{
|
||||
pImpl->spread = s;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
FillSpread Fill::spread() const noexcept
|
||||
{
|
||||
return pImpl->spread;
|
||||
}
|
||||
|
||||
|
||||
Result Fill::transform(const Matrix& m) noexcept
|
||||
{
|
||||
if (!pImpl->transform) {
|
||||
pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
}
|
||||
*pImpl->transform = m;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Matrix Fill::transform() const noexcept
|
||||
{
|
||||
if (pImpl->transform) return *pImpl->transform;
|
||||
return {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
}
|
||||
|
||||
|
||||
Fill* Fill::duplicate() const noexcept
|
||||
{
|
||||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
uint32_t Fill::identifier() const noexcept
|
||||
{
|
||||
return pImpl->id;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_FILL_H_
|
||||
#define _TVG_FILL_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
template<typename T>
|
||||
struct DuplicateMethod
|
||||
{
|
||||
virtual ~DuplicateMethod() {}
|
||||
virtual T* duplicate() = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct FillDup : DuplicateMethod<Fill>
|
||||
{
|
||||
T* inst = nullptr;
|
||||
|
||||
FillDup(T* _inst) : inst(_inst) {}
|
||||
~FillDup() {}
|
||||
|
||||
Fill* duplicate() override
|
||||
{
|
||||
return inst->duplicate();
|
||||
}
|
||||
};
|
||||
|
||||
struct Fill::Impl
|
||||
{
|
||||
ColorStop* colorStops = nullptr;
|
||||
Matrix* transform = nullptr;
|
||||
uint32_t cnt = 0;
|
||||
FillSpread spread;
|
||||
DuplicateMethod<Fill>* dup = nullptr;
|
||||
uint32_t id;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (dup) delete(dup);
|
||||
free(colorStops);
|
||||
free(transform);
|
||||
}
|
||||
|
||||
void method(DuplicateMethod<Fill>* dup)
|
||||
{
|
||||
this->dup = dup;
|
||||
}
|
||||
|
||||
Fill* duplicate()
|
||||
{
|
||||
auto ret = dup->duplicate();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->cnt = cnt;
|
||||
ret->pImpl->spread = spread;
|
||||
ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
|
||||
memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
|
||||
if (transform) {
|
||||
ret->pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
*ret->pImpl->transform = *transform;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_FILL_H_
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgCanvasImpl.h"
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
#include "tvgGlRenderer.h"
|
||||
#else
|
||||
class GlRenderer : public RenderMethod
|
||||
{
|
||||
//Non Supported. Dummy Class */
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct GlCanvas::Impl
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
GlCanvas::GlCanvas() : Canvas(GlRenderer::gen()), pImpl(new Impl)
|
||||
#else
|
||||
GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(new Impl)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
GlCanvas::~GlCanvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
|
||||
{
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(buffer, stride, w, h)) return Result::Unknown;
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->needRefresh();
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<GlCanvas> GlCanvas::gen() noexcept
|
||||
{
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (GlRenderer::init() <= 0) return nullptr;
|
||||
return unique_ptr<GlCanvas>(new GlCanvas);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgLoader.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
#include "tvgSwRenderer.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
#include "tvgGlRenderer.h"
|
||||
#endif
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static int _initCnt = 0;
|
||||
static uint16_t _version = 0;
|
||||
|
||||
|
||||
static bool _buildVersionInfo()
|
||||
{
|
||||
auto SRC = THORVG_VERSION_STRING; //ex) 0.3.99
|
||||
auto p = SRC;
|
||||
const char* x;
|
||||
|
||||
char major[3];
|
||||
x = strchr(p, '.');
|
||||
if (!x) return false;
|
||||
memcpy(major, p, x - p);
|
||||
major[x - p] = '\0';
|
||||
p = x + 1;
|
||||
|
||||
char minor[3];
|
||||
x = strchr(p, '.');
|
||||
if (!x) return false;
|
||||
memcpy(minor, p, x - p);
|
||||
minor[x - p] = '\0';
|
||||
p = x + 1;
|
||||
|
||||
char micro[3];
|
||||
x = SRC + strlen(THORVG_VERSION_STRING);
|
||||
memcpy(micro, p, x - p);
|
||||
micro[x - p] = '\0';
|
||||
|
||||
char sum[7];
|
||||
snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro);
|
||||
|
||||
_version = atoi(sum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
|
||||
{
|
||||
auto nonSupport = true;
|
||||
|
||||
if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (!SwRenderer::init(threads)) return Result::FailedAllocation;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
} else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (!GlRenderer::init(threads)) return Result::FailedAllocation;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
} else {
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
if (nonSupport) return Result::NonSupport;
|
||||
|
||||
if (_initCnt++ > 0) return Result::Success;
|
||||
|
||||
if (!_buildVersionInfo()) return Result::Unknown;
|
||||
|
||||
if (!LoaderMgr::init()) return Result::Unknown;
|
||||
|
||||
TaskScheduler::init(threads);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Initializer::term(CanvasEngine engine) noexcept
|
||||
{
|
||||
if (_initCnt == 0) return Result::InsufficientCondition;
|
||||
|
||||
auto nonSupport = true;
|
||||
|
||||
if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (!SwRenderer::term()) return Result::InsufficientCondition;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
} else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (!GlRenderer::term()) return Result::InsufficientCondition;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
} else {
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
if (nonSupport) return Result::NonSupport;
|
||||
|
||||
if (--_initCnt > 0) return Result::Success;
|
||||
|
||||
TaskScheduler::term();
|
||||
|
||||
if (!LoaderMgr::term()) return Result::Unknown;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint16_t THORVG_VERSION_NUMBER()
|
||||
{
|
||||
return _version;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_ITERATOR_ACCESSOR_H_
|
||||
#define _TVG_ITERATOR_ACCESSOR_H_
|
||||
|
||||
#include "tvgPaint.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class IteratorAccessor
|
||||
{
|
||||
public:
|
||||
//Utility Method: Iterator Accessor
|
||||
Iterator* iterator(const Paint* paint)
|
||||
{
|
||||
return paint->pImpl->iterator();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_ITERATOR_ACCESSOR_H_
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct LinearGradient::Impl
|
||||
{
|
||||
float x1 = 0;
|
||||
float y1 = 0;
|
||||
float x2 = 0;
|
||||
float y2 = 0;
|
||||
|
||||
Fill* duplicate()
|
||||
{
|
||||
auto ret = LinearGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->x1 = x1;
|
||||
ret->pImpl->y1 = y1;
|
||||
ret->pImpl->x2 = x2;
|
||||
ret->pImpl->y2 = y2;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
LinearGradient::LinearGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
|
||||
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
LinearGradient::~LinearGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
|
||||
{
|
||||
pImpl->x1 = x1;
|
||||
pImpl->y1 = y1;
|
||||
pImpl->x2 = x2;
|
||||
pImpl->y2 = y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
|
||||
{
|
||||
if (x1) *x1 = pImpl->x1;
|
||||
if (x2) *x2 = pImpl->x2;
|
||||
if (y1) *y1 = pImpl->y1;
|
||||
if (y2) *y2 = pImpl->y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<LinearGradient>(new LinearGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t LinearGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_LINEAR;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_LOAD_MODULE_H_
|
||||
#define _TVG_LOAD_MODULE_H_
|
||||
|
||||
#include "tvgRender.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class LoadModule
|
||||
{
|
||||
public:
|
||||
//default view box, if any.
|
||||
float vx = 0;
|
||||
float vy = 0;
|
||||
float vw = 0;
|
||||
float vh = 0;
|
||||
float w = 0, h = 0; //default image size
|
||||
bool preserveAspect = true; //keep aspect ratio by default.
|
||||
|
||||
virtual ~LoadModule() {}
|
||||
|
||||
virtual bool open(const string& path) { return false; }
|
||||
virtual bool open(const char* data, uint32_t size, bool copy) { return false; }
|
||||
virtual bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { return false; }
|
||||
|
||||
//Override this if the vector-format has own resizing policy.
|
||||
virtual bool resize(Paint* paint, float w, float h) { return false; }
|
||||
|
||||
virtual bool read() = 0;
|
||||
virtual bool close() = 0;
|
||||
virtual unique_ptr<Surface> bitmap() { return nullptr; }
|
||||
virtual unique_ptr<Paint> paint() { return nullptr; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_LOAD_MODULE_H_
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgLoader.h"
|
||||
|
||||
#ifdef THORVG_SVG_LOADER_SUPPORT
|
||||
#include "tvgSvgLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_PNG_LOADER_SUPPORT
|
||||
#include "tvgPngLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
#include "tvgTvgLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_JPG_LOADER_SUPPORT
|
||||
#include "tvgJpgLoader.h"
|
||||
#endif
|
||||
|
||||
#include "tvgRawLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static LoadModule* _find(FileType type)
|
||||
{
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
return new TvgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Svg: {
|
||||
#ifdef THORVG_SVG_LOADER_SUPPORT
|
||||
return new SvgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Raw: {
|
||||
return new RawLoader;
|
||||
break;
|
||||
}
|
||||
case FileType::Png: {
|
||||
#ifdef THORVG_PNG_LOADER_SUPPORT
|
||||
return new PngLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Jpg: {
|
||||
#ifdef THORVG_JPG_LOADER_SUPPORT
|
||||
return new JpgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
const char *format;
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
format = "TVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Svg: {
|
||||
format = "SVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Raw: {
|
||||
format = "RAW";
|
||||
break;
|
||||
}
|
||||
case FileType::Png: {
|
||||
format = "PNG";
|
||||
break;
|
||||
}
|
||||
case FileType::Jpg: {
|
||||
format = "JPG";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
format = "???";
|
||||
break;
|
||||
}
|
||||
}
|
||||
TVGLOG("LOADER", "%s format is not supported", format);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static LoadModule* _findByPath(const string& path)
|
||||
{
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("tvg")) return _find(FileType::Tvg);
|
||||
if (!ext.compare("svg")) return _find(FileType::Svg);
|
||||
if (!ext.compare("png")) return _find(FileType::Png);
|
||||
if (!ext.compare("jpg")) return _find(FileType::Jpg);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static LoadModule* _findByType(const string& mimeType)
|
||||
{
|
||||
if (mimeType.empty()) return nullptr;
|
||||
|
||||
auto type = FileType::Unknown;
|
||||
|
||||
if (mimeType == "tvg") type = FileType::Tvg;
|
||||
else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
|
||||
else if (mimeType == "raw") type = FileType::Raw;
|
||||
else if (mimeType == "png") type = FileType::Png;
|
||||
else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
|
||||
else {
|
||||
TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _find(type);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
bool LoaderMgr::init()
|
||||
{
|
||||
//TODO:
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LoaderMgr::term()
|
||||
{
|
||||
//TODO:
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
|
||||
{
|
||||
*invalid = false;
|
||||
|
||||
if (auto loader = _findByPath(path)) {
|
||||
if (loader->open(path)) return shared_ptr<LoadModule>(loader);
|
||||
else delete(loader);
|
||||
*invalid = true;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
|
||||
{
|
||||
//Try first with the given MimeType
|
||||
if (auto loader = _findByType(mimeType)) {
|
||||
if (loader->open(data, size, copy)) {
|
||||
return shared_ptr<LoadModule>(loader);
|
||||
} else {
|
||||
TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
|
||||
delete(loader);
|
||||
}
|
||||
}
|
||||
|
||||
//Abnormal MimeType. Try with the candidates in the order
|
||||
for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
|
||||
auto loader = _find(static_cast<FileType>(i));
|
||||
if (loader) {
|
||||
if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
|
||||
else delete(loader);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<LoadModule> LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
|
||||
{
|
||||
//function is dedicated for raw images only
|
||||
auto loader = new RawLoader;
|
||||
if (loader->open(data, w, h, copy)) return shared_ptr<LoadModule>(loader);
|
||||
else delete(loader);
|
||||
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_LOADER_H_
|
||||
#define _TVG_LOADER_H_
|
||||
|
||||
#include "tvgLoadModule.h"
|
||||
|
||||
struct LoaderMgr
|
||||
{
|
||||
static bool init();
|
||||
static bool term();
|
||||
static shared_ptr<LoadModule> loader(const string& path, bool* invalid);
|
||||
static shared_ptr<LoadModule> loader(const char* data, uint32_t size, const string& mimeType, bool copy);
|
||||
static shared_ptr<LoadModule> loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy);
|
||||
};
|
||||
|
||||
#endif //_TVG_LOADER_H_
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Lempel–Ziv–Welch (LZW) encoder/decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
|
||||
|
||||
* This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
|
||||
* Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
|
||||
* are not stored in the output and the max code length in bits is 12, vs 16 in compress.
|
||||
*
|
||||
* EOI is simply detected by the end of the data stream, while CC happens if the
|
||||
* dictionary gets filled. Data is written/read from bit streams, which handle
|
||||
* byte-alignment for us in a transparent way.
|
||||
|
||||
* The decoder relies on the hardcoded data layout produced by the encoder, since
|
||||
* no additional reconstruction data is added to the output, so they must match.
|
||||
* The nice thing about LZW is that we can reconstruct the dictionary directly from
|
||||
* the stream of codes generated by the encoder, so this avoids storing additional
|
||||
* headers in the bit stream.
|
||||
|
||||
* The output code length is variable. It starts with the minimum number of bits
|
||||
* required to store the base byte-sized dictionary and automatically increases
|
||||
* as the dictionary gets larger (it starts at 9-bits and grows to 10-bits when
|
||||
* code 512 is added, then 11-bits when 1024 is added, and so on). If the dictionary
|
||||
* is filled (4096 items for a 12-bits dictionary), the whole thing is cleared and
|
||||
* the process starts over. This is the main reason why the encoder and the decoder
|
||||
* must match perfectly, since the lengths of the codes will not be specified with
|
||||
* the data itself.
|
||||
|
||||
* USEFUL LINKS:
|
||||
* https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
|
||||
* http://rosettacode.org/wiki/LZW_compression
|
||||
* http://www.cs.duke.edu/csed/curious/compression/lzw.html
|
||||
* http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
|
||||
* http://marknelson.us/1989/10/01/lzw-data-compression/
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#include <string>
|
||||
#include <memory.h>
|
||||
#include "tvgLzw.h"
|
||||
|
||||
namespace {
|
||||
//LZW Dictionary helper:
|
||||
constexpr int Nil = -1;
|
||||
constexpr int MaxDictBits = 12;
|
||||
constexpr int StartBits = 9;
|
||||
constexpr int FirstCode = (1 << (StartBits - 1)); // 256
|
||||
constexpr int MaxDictEntries = (1 << MaxDictBits); // 4096
|
||||
|
||||
|
||||
//Round up to the next power-of-two number, e.g. 37 => 64
|
||||
static int nextPowerOfTwo(int num)
|
||||
{
|
||||
--num;
|
||||
for (size_t i = 1; i < sizeof(num) * 8; i <<= 1) {
|
||||
num = num | num >> i;
|
||||
}
|
||||
return ++num;
|
||||
}
|
||||
|
||||
|
||||
struct BitStreamWriter
|
||||
{
|
||||
uint8_t* stream; //Growable buffer to store our bits. Heap allocated & owned by the class instance.
|
||||
int bytesAllocated; //Current size of heap-allocated stream buffer *in bytes*.
|
||||
int granularity; //Amount bytesAllocated multiplies by when auto-resizing in appendBit().
|
||||
int currBytePos; //Current byte being written to, from 0 to bytesAllocated-1.
|
||||
int nextBitPos; //Bit position within the current byte to access next. 0 to 7.
|
||||
int numBitsWritten; //Number of bits in use from the stream buffer, not including byte-rounding padding.
|
||||
|
||||
void internalInit()
|
||||
{
|
||||
stream = nullptr;
|
||||
bytesAllocated = 0;
|
||||
granularity = 2;
|
||||
currBytePos = 0;
|
||||
nextBitPos = 0;
|
||||
numBitsWritten = 0;
|
||||
}
|
||||
|
||||
uint8_t* allocBytes(const int bytesWanted, uint8_t * oldPtr, const int oldSize)
|
||||
{
|
||||
auto newMemory = static_cast<uint8_t *>(malloc(bytesWanted));
|
||||
memset(newMemory, 0, bytesWanted);
|
||||
|
||||
if (oldPtr) {
|
||||
memcpy(newMemory, oldPtr, oldSize);
|
||||
free(oldPtr);
|
||||
}
|
||||
return newMemory;
|
||||
}
|
||||
|
||||
BitStreamWriter()
|
||||
{
|
||||
/* 8192 bits for a start (1024 bytes). It will resize if needed.
|
||||
Default granularity is 2. */
|
||||
internalInit();
|
||||
allocate(8192);
|
||||
}
|
||||
|
||||
BitStreamWriter(const int initialSizeInBits, const int growthGranularity = 2)
|
||||
{
|
||||
internalInit();
|
||||
setGranularity(growthGranularity);
|
||||
allocate(initialSizeInBits);
|
||||
}
|
||||
|
||||
~BitStreamWriter()
|
||||
{
|
||||
free(stream);
|
||||
}
|
||||
|
||||
void allocate(int bitsWanted)
|
||||
{
|
||||
//Require at least a byte.
|
||||
if (bitsWanted <= 0) bitsWanted = 8;
|
||||
|
||||
//Round upwards if needed:
|
||||
if ((bitsWanted % 8) != 0) bitsWanted = nextPowerOfTwo(bitsWanted);
|
||||
|
||||
//We might already have the required count.
|
||||
const int sizeInBytes = bitsWanted / 8;
|
||||
if (sizeInBytes <= bytesAllocated) return;
|
||||
|
||||
stream = allocBytes(sizeInBytes, stream, bytesAllocated);
|
||||
bytesAllocated = sizeInBytes;
|
||||
}
|
||||
|
||||
void appendBit(const int bit)
|
||||
{
|
||||
const uint32_t mask = uint32_t(1) << nextBitPos;
|
||||
stream[currBytePos] = (stream[currBytePos] & ~mask) | (-bit & mask);
|
||||
++numBitsWritten;
|
||||
|
||||
if (++nextBitPos == 8) {
|
||||
nextBitPos = 0;
|
||||
if (++currBytePos == bytesAllocated) allocate(bytesAllocated * granularity * 8);
|
||||
}
|
||||
}
|
||||
|
||||
void appendBitsU64(const uint64_t num, const int bitCount)
|
||||
{
|
||||
for (int b = 0; b < bitCount; ++b) {
|
||||
const uint64_t mask = uint64_t(1) << b;
|
||||
const int bit = !!(num & mask);
|
||||
appendBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* release()
|
||||
{
|
||||
auto oldPtr = stream;
|
||||
internalInit();
|
||||
return oldPtr;
|
||||
}
|
||||
|
||||
void setGranularity(const int growthGranularity)
|
||||
{
|
||||
granularity = (growthGranularity >= 2) ? growthGranularity : 2;
|
||||
}
|
||||
|
||||
int getByteCount() const
|
||||
{
|
||||
int usedBytes = numBitsWritten / 8;
|
||||
int leftovers = numBitsWritten % 8;
|
||||
if (leftovers != 0) ++usedBytes;
|
||||
return usedBytes;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BitStreamReader
|
||||
{
|
||||
const uint8_t* stream; // Pointer to the external bit stream. Not owned by the reader.
|
||||
const int sizeInBytes; // Size of the stream *in bytes*. Might include padding.
|
||||
const int sizeInBits; // Size of the stream *in bits*, padding *not* include.
|
||||
int currBytePos = 0; // Current byte being read in the stream.
|
||||
int nextBitPos = 0; // Bit position within the current byte to access next. 0 to 7.
|
||||
int numBitsRead = 0; // Total bits read from the stream so far. Never includes byte-rounding padding.
|
||||
|
||||
BitStreamReader(const uint8_t* bitStream, const int byteCount, const int bitCount) : stream(bitStream), sizeInBytes(byteCount), sizeInBits(bitCount)
|
||||
{
|
||||
}
|
||||
|
||||
bool readNextBit(int& bitOut)
|
||||
{
|
||||
if (numBitsRead >= sizeInBits) return false; //We are done.
|
||||
|
||||
const uint32_t mask = uint32_t(1) << nextBitPos;
|
||||
bitOut = !!(stream[currBytePos] & mask);
|
||||
++numBitsRead;
|
||||
|
||||
if (++nextBitPos == 8) {
|
||||
nextBitPos = 0;
|
||||
++currBytePos;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t readBitsU64(const int bitCount)
|
||||
{
|
||||
uint64_t num = 0;
|
||||
for (int b = 0; b < bitCount; ++b) {
|
||||
int bit;
|
||||
if (!readNextBit(bit)) break;
|
||||
/* Based on a "Stanford bit-hack":
|
||||
http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
|
||||
const uint64_t mask = uint64_t(1) << b;
|
||||
num = (num & ~mask) | (-bit & mask);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
bool isEndOfStream() const
|
||||
{
|
||||
return numBitsRead >= sizeInBits;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Dictionary
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
int code;
|
||||
int value;
|
||||
};
|
||||
|
||||
//Dictionary entries 0-255 are always reserved to the byte/ASCII range.
|
||||
int size;
|
||||
Entry entries[MaxDictEntries];
|
||||
|
||||
Dictionary()
|
||||
{
|
||||
/* First 256 dictionary entries are reserved to the byte/ASCII range.
|
||||
Additional entries follow for the character sequences found in the input.
|
||||
Up to 4096 - 256 (MaxDictEntries - FirstCode). */
|
||||
size = FirstCode;
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
entries[i].code = Nil;
|
||||
entries[i].value = i;
|
||||
}
|
||||
}
|
||||
|
||||
int findIndex(const int code, const int value) const
|
||||
{
|
||||
if (code == Nil) return value;
|
||||
|
||||
//Linear search for now.
|
||||
//TODO: Worth optimizing with a proper hash-table?
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (entries[i].code == code && entries[i].value == value) return i;
|
||||
}
|
||||
return Nil;
|
||||
}
|
||||
|
||||
bool add(const int code, const int value)
|
||||
{
|
||||
if (size == MaxDictEntries) return false;
|
||||
entries[size].code = code;
|
||||
entries[size].value = value;
|
||||
++size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flush(int & codeBitsWidth)
|
||||
{
|
||||
if (size == (1 << codeBitsWidth)) {
|
||||
++codeBitsWidth;
|
||||
if (codeBitsWidth > MaxDictBits) {
|
||||
//Clear the dictionary (except the first 256 byte entries).
|
||||
codeBitsWidth = StartBits;
|
||||
size = FirstCode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static bool outputByte(int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar)
|
||||
{
|
||||
if (bytesDecodedSoFar >= outputSizeBytes) return false;
|
||||
*output++ = static_cast<uint8_t>(code);
|
||||
++bytesDecodedSoFar;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar, int& firstByte)
|
||||
{
|
||||
/* A sequence is stored backwards, so we have to write
|
||||
it to a temp then output the buffer in reverse. */
|
||||
int i = 0;
|
||||
uint8_t sequence[MaxDictEntries];
|
||||
|
||||
do {
|
||||
sequence[i++] = dict.entries[code].value;
|
||||
code = dict.entries[code].code;
|
||||
} while (code >= 0);
|
||||
|
||||
firstByte = sequence[--i];
|
||||
|
||||
for (; i >= 0; --i) {
|
||||
if (!outputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg {
|
||||
|
||||
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
|
||||
{
|
||||
int code = Nil;
|
||||
int prevCode = Nil;
|
||||
int firstByte = 0;
|
||||
int bytesDecoded = 0;
|
||||
int codeBitsWidth = StartBits;
|
||||
auto uncompressed = (uint8_t*) malloc(sizeof(uint8_t) * uncompressedSizeBytes);
|
||||
auto ptr = uncompressed;
|
||||
|
||||
/* We'll reconstruct the dictionary based on the bit stream codes.
|
||||
Unlike Huffman encoding, we don't store the dictionary as a prefix to the data. */
|
||||
Dictionary dictionary;
|
||||
BitStreamReader bitStream(compressed, compressedSizeBytes, compressedSizeBits);
|
||||
|
||||
/* We check to avoid an overflow of the user buffer.
|
||||
If the buffer is smaller than the decompressed size, we break the loop and return the current decompression count. */
|
||||
while (!bitStream.isEndOfStream()) {
|
||||
code = static_cast<int>(bitStream.readBitsU64(codeBitsWidth));
|
||||
|
||||
if (prevCode == Nil) {
|
||||
if (!outputByte(code, ptr, uncompressedSizeBytes, bytesDecoded)) break;
|
||||
firstByte = code;
|
||||
prevCode = code;
|
||||
continue;
|
||||
}
|
||||
if (code >= dictionary.size) {
|
||||
if (!outputSequence(dictionary, prevCode, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
|
||||
if (!outputByte(firstByte, ptr, uncompressedSizeBytes, bytesDecoded)) break;
|
||||
} else if (!outputSequence(dictionary, code, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
|
||||
|
||||
dictionary.add(prevCode, firstByte);
|
||||
if (dictionary.flush(codeBitsWidth)) prevCode = Nil;
|
||||
else prevCode = code;
|
||||
}
|
||||
|
||||
return uncompressed;
|
||||
}
|
||||
|
||||
|
||||
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits)
|
||||
{
|
||||
//LZW encoding context:
|
||||
int code = Nil;
|
||||
int codeBitsWidth = StartBits;
|
||||
Dictionary dictionary;
|
||||
|
||||
//Output bit stream we write to. This will allocate memory as needed to accommodate the encoded data.
|
||||
BitStreamWriter bitStream;
|
||||
|
||||
for (; uncompressedSizeBytes > 0; --uncompressedSizeBytes, ++uncompressed) {
|
||||
const int value = *uncompressed;
|
||||
const int index = dictionary.findIndex(code, value);
|
||||
|
||||
if (index != Nil) {
|
||||
code = index;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Write the dictionary code using the minimum bit-with:
|
||||
bitStream.appendBitsU64(code, codeBitsWidth);
|
||||
|
||||
//Flush it when full so we can restart the sequences.
|
||||
if (!dictionary.flush(codeBitsWidth)) {
|
||||
//There's still space for this sequence.
|
||||
dictionary.add(code, value);
|
||||
}
|
||||
code = value;
|
||||
}
|
||||
|
||||
//Residual code at the end:
|
||||
if (code != Nil) bitStream.appendBitsU64(code, codeBitsWidth);
|
||||
|
||||
//Pass ownership of the compressed data buffer to the user pointer:
|
||||
*compressedSizeBytes = bitStream.getByteCount();
|
||||
*compressedSizeBits = bitStream.numBitsWritten;
|
||||
|
||||
return bitStream.release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_LZW_H_
|
||||
#define _TVG_LZW_H_
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
|
||||
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
|
||||
}
|
||||
|
||||
#endif //_TVG_LZW_H
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_MATH_H_
|
||||
#define _TVG_MATH_H_
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
|
||||
static inline bool mathZero(float a)
|
||||
{
|
||||
return (fabsf(a) < FLT_EPSILON) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
static inline bool mathEqual(float a, float b)
|
||||
{
|
||||
return (fabsf(a - b) < FLT_EPSILON);
|
||||
}
|
||||
|
||||
|
||||
static inline bool mathRightAngle(const Matrix* m)
|
||||
{
|
||||
auto radian = fabsf(atan2(m->e21, m->e11));
|
||||
if (radian < FLT_EPSILON || mathEqual(radian, float(M_PI_2)) || mathEqual(radian, float(M_PI))) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static inline bool mathIdentity(const Matrix* m)
|
||||
{
|
||||
if (!mathEqual(m->e11, 1.0f) || !mathZero(m->e12) || !mathZero(m->e13) ||
|
||||
!mathZero(m->e21) || !mathEqual(m->e22, 1.0f) || !mathZero(m->e23) ||
|
||||
!mathZero(m->e31) || !mathZero(m->e32) || !mathEqual(m->e33, 1.0f)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool mathInverse(const Matrix* m, Matrix* out)
|
||||
{
|
||||
auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
|
||||
m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
|
||||
m->e13 * (m->e21 * m->e32 - m->e22 * m->e31);
|
||||
|
||||
if (mathZero(det)) return false;
|
||||
|
||||
auto invDet = 1 / det;
|
||||
|
||||
out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet;
|
||||
out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet;
|
||||
out->e13 = (m->e12 * m->e23 - m->e13 * m->e22) * invDet;
|
||||
out->e21 = (m->e23 * m->e31 - m->e21 * m->e33) * invDet;
|
||||
out->e22 = (m->e11 * m->e33 - m->e13 * m->e31) * invDet;
|
||||
out->e23 = (m->e21 * m->e13 - m->e11 * m->e23) * invDet;
|
||||
out->e31 = (m->e21 * m->e32 - m->e31 * m->e22) * invDet;
|
||||
out->e32 = (m->e31 * m->e12 - m->e11 * m->e32) * invDet;
|
||||
out->e33 = (m->e11 * m->e22 - m->e21 * m->e12) * invDet;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline void mathIdentity(Matrix* m)
|
||||
{
|
||||
m->e11 = 1.0f;
|
||||
m->e12 = 0.0f;
|
||||
m->e13 = 0.0f;
|
||||
m->e21 = 0.0f;
|
||||
m->e22 = 1.0f;
|
||||
m->e23 = 0.0f;
|
||||
m->e31 = 0.0f;
|
||||
m->e32 = 0.0f;
|
||||
m->e33 = 1.0f;
|
||||
}
|
||||
|
||||
|
||||
static inline void mathScale(Matrix* m, float scale)
|
||||
{
|
||||
m->e11 = scale;
|
||||
m->e22 = scale;
|
||||
}
|
||||
|
||||
|
||||
static inline void mathTranslate(Matrix* m, float x, float y)
|
||||
{
|
||||
m->e13 = x;
|
||||
m->e23 = y;
|
||||
}
|
||||
|
||||
|
||||
static inline void mathRotate(Matrix* m, float degree)
|
||||
{
|
||||
auto radian = degree / 180.0f * M_PI;
|
||||
auto cosVal = cosf(radian);
|
||||
auto sinVal = sinf(radian);
|
||||
|
||||
m->e12 = m->e11 * -sinVal;
|
||||
m->e11 *= cosVal;
|
||||
m->e21 = m->e22 * sinVal;
|
||||
m->e22 *= cosVal;
|
||||
}
|
||||
|
||||
|
||||
static inline void mathMultiply(Point* pt, const Matrix* transform)
|
||||
{
|
||||
auto tx = pt->x * transform->e11 + pt->y * transform->e12 + transform->e13;
|
||||
auto ty = pt->x * transform->e21 + pt->y * transform->e22 + transform->e23;
|
||||
pt->x = tx;
|
||||
pt->y = ty;
|
||||
}
|
||||
|
||||
|
||||
static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
|
||||
{
|
||||
Matrix m;
|
||||
|
||||
m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
|
||||
m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
|
||||
m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
|
||||
|
||||
m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
|
||||
m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
|
||||
m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
|
||||
|
||||
m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
|
||||
m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
|
||||
m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
#endif //_TVG_MATH_H_
|
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgMath.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport)
|
||||
{
|
||||
/* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
|
||||
auto shape = static_cast<Shape*>(cmpTarget);
|
||||
|
||||
//Rectangle Candidates?
|
||||
const Point* pts;
|
||||
if (shape->pathCoords(&pts) != 4) return false;
|
||||
|
||||
if (rTransform) rTransform->update();
|
||||
|
||||
//No rotational.
|
||||
if (pTransform && !mathRightAngle(&pTransform->m)) return false;
|
||||
if (rTransform && !mathRightAngle(&rTransform->m)) return false;
|
||||
|
||||
//Perpendicular Rectangle?
|
||||
auto pt1 = pts + 0;
|
||||
auto pt2 = pts + 1;
|
||||
auto pt3 = pts + 2;
|
||||
auto pt4 = pts + 3;
|
||||
|
||||
if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) ||
|
||||
(mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) {
|
||||
|
||||
auto v1 = *pt1;
|
||||
auto v2 = *pt3;
|
||||
|
||||
if (rTransform) {
|
||||
mathMultiply(&v1, &rTransform->m);
|
||||
mathMultiply(&v2, &rTransform->m);
|
||||
}
|
||||
|
||||
if (pTransform) {
|
||||
mathMultiply(&v1, &pTransform->m);
|
||||
mathMultiply(&v2, &pTransform->m);
|
||||
}
|
||||
|
||||
//sorting
|
||||
if (v1.x > v2.x) {
|
||||
auto tmp = v2.x;
|
||||
v2.x = v1.x;
|
||||
v1.x = tmp;
|
||||
}
|
||||
|
||||
if (v1.y > v2.y) {
|
||||
auto tmp = v2.y;
|
||||
v2.y = v1.y;
|
||||
v1.y = tmp;
|
||||
}
|
||||
|
||||
viewport.x = static_cast<int32_t>(v1.x);
|
||||
viewport.y = static_cast<int32_t>(v1.y);
|
||||
viewport.w = static_cast<int32_t>(v2.x - v1.x + 0.5f);
|
||||
viewport.h = static_cast<int32_t>(v2.y - v1.y + 0.5f);
|
||||
|
||||
if (viewport.w < 0) viewport.w = 0;
|
||||
if (viewport.h < 0) viewport.h = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Paint* Paint::Impl::duplicate()
|
||||
{
|
||||
auto ret = smethod->duplicate();
|
||||
|
||||
//duplicate Transform
|
||||
if (rTransform) {
|
||||
ret->pImpl->rTransform = new RenderTransform();
|
||||
*ret->pImpl->rTransform = *rTransform;
|
||||
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
|
||||
}
|
||||
|
||||
ret->pImpl->opacity = opacity;
|
||||
|
||||
if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::rotate(float degree)
|
||||
{
|
||||
if (rTransform) {
|
||||
if (mathEqual(degree, rTransform->degree)) return true;
|
||||
} else {
|
||||
if (mathZero(degree)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->degree = degree;
|
||||
if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::scale(float factor)
|
||||
{
|
||||
if (rTransform) {
|
||||
if (mathEqual(factor, rTransform->scale)) return true;
|
||||
} else {
|
||||
if (mathZero(factor)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->scale = factor;
|
||||
if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::translate(float x, float y)
|
||||
{
|
||||
if (rTransform) {
|
||||
if (mathEqual(x, rTransform->x) && mathEqual(y, rTransform->y)) return true;
|
||||
} else {
|
||||
if (mathZero(x) && mathZero(y)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->x = x;
|
||||
rTransform->y = y;
|
||||
if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::render(RenderMethod& renderer)
|
||||
{
|
||||
Compositor* cmp = nullptr;
|
||||
|
||||
//OPTIMIZE_ME: Can we replace the simple AlphaMasking with ClipPath?
|
||||
|
||||
/* Note: only ClipPath is processed in update() step.
|
||||
Create a composition image. */
|
||||
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
|
||||
auto region = smethod->bounds(renderer);
|
||||
if (region.w == 0 || region.h == 0) return true;
|
||||
cmp = renderer.target(region);
|
||||
renderer.beginComposite(cmp, CompositeMethod::None, 255);
|
||||
compData->target->pImpl->render(renderer);
|
||||
}
|
||||
|
||||
if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
|
||||
|
||||
auto ret = smethod->render(renderer);
|
||||
|
||||
if (cmp) renderer.endComposite(cmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag)
|
||||
{
|
||||
if (renderFlag & RenderUpdateFlag::Transform) {
|
||||
if (!rTransform) return nullptr;
|
||||
if (!rTransform->update()) {
|
||||
delete(rTransform);
|
||||
rTransform = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 1. Composition Pre Processing */
|
||||
void *tdata = nullptr;
|
||||
RenderRegion viewport;
|
||||
bool compFastTrack = false;
|
||||
|
||||
if (compData) {
|
||||
auto target = compData->target;
|
||||
auto method = compData->method;
|
||||
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
||||
|
||||
/* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
|
||||
we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
|
||||
auto tryFastTrack = false;
|
||||
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
|
||||
else if (method == CompositeMethod::AlphaMask && target->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||
auto shape = static_cast<Shape*>(target);
|
||||
uint8_t a;
|
||||
shape->fillColor(nullptr, nullptr, nullptr, &a);
|
||||
if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
|
||||
}
|
||||
if (tryFastTrack) {
|
||||
RenderRegion viewport2;
|
||||
if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) {
|
||||
viewport = renderer.viewport();
|
||||
viewport2.intersect(viewport);
|
||||
renderer.viewport(viewport2);
|
||||
target->pImpl->ctxFlag |= ContextFlag::FastTrack;
|
||||
}
|
||||
}
|
||||
if (!compFastTrack) {
|
||||
tdata = target->pImpl->update(renderer, pTransform, 255, clips, pFlag);
|
||||
if (method == CompositeMethod::ClipPath) clips.push(tdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Main Update */
|
||||
void *edata = nullptr;
|
||||
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
|
||||
renderFlag = RenderUpdateFlag::None;
|
||||
opacity = (opacity * this->opacity) / 255;
|
||||
|
||||
if (rTransform && pTransform) {
|
||||
RenderTransform outTransform(pTransform, rTransform);
|
||||
edata = smethod->update(renderer, &outTransform, opacity, clips, newFlag);
|
||||
} else {
|
||||
auto outTransform = pTransform ? pTransform : rTransform;
|
||||
edata = smethod->update(renderer, outTransform, opacity, clips, newFlag);
|
||||
}
|
||||
|
||||
/* 3. Composition Post Processing */
|
||||
if (compFastTrack) renderer.viewport(viewport);
|
||||
else if (tdata && compData->method == CompositeMethod::ClipPath) clips.pop();
|
||||
|
||||
return edata;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
|
||||
{
|
||||
Matrix* m = nullptr;
|
||||
|
||||
//Case: No transformed, quick return!
|
||||
if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
|
||||
|
||||
//Case: Transformed
|
||||
auto tx = 0.0f;
|
||||
auto ty = 0.0f;
|
||||
auto tw = 0.0f;
|
||||
auto th = 0.0f;
|
||||
|
||||
auto ret = smethod->bounds(&tx, &ty, &tw, &th);
|
||||
|
||||
//Get vertices
|
||||
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
|
||||
|
||||
//New bounding box
|
||||
auto x1 = FLT_MAX;
|
||||
auto y1 = FLT_MAX;
|
||||
auto x2 = -FLT_MAX;
|
||||
auto y2 = -FLT_MAX;
|
||||
|
||||
//Compute the AABB after transformation
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mathMultiply(&pt[i], m);
|
||||
|
||||
if (pt[i].x < x1) x1 = pt[i].x;
|
||||
if (pt[i].x > x2) x2 = pt[i].x;
|
||||
if (pt[i].y < y1) y1 = pt[i].y;
|
||||
if (pt[i].y > y2) y2 = pt[i].y;
|
||||
}
|
||||
|
||||
if (x) *x = x1;
|
||||
if (y) *y = y1;
|
||||
if (w) *w = x2 - x1;
|
||||
if (h) *h = y2 - y1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Paint :: Paint() : pImpl(new Impl())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Paint :: ~Paint()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Paint::rotate(float degree) noexcept
|
||||
{
|
||||
if (pImpl->rotate(degree)) return Result::Success;
|
||||
return Result::FailedAllocation;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::scale(float factor) noexcept
|
||||
{
|
||||
if (pImpl->scale(factor)) return Result::Success;
|
||||
return Result::FailedAllocation;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::translate(float x, float y) noexcept
|
||||
{
|
||||
if (pImpl->translate(x, y)) return Result::Success;
|
||||
return Result::FailedAllocation;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::transform(const Matrix& m) noexcept
|
||||
{
|
||||
if (pImpl->transform(m)) return Result::Success;
|
||||
return Result::FailedAllocation;
|
||||
}
|
||||
|
||||
|
||||
Matrix Paint::transform() noexcept
|
||||
{
|
||||
auto pTransform = pImpl->transform();
|
||||
if (pTransform) return *pTransform;
|
||||
return {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
}
|
||||
|
||||
|
||||
TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
|
||||
{
|
||||
return this->bounds(x, y, w, h, false);
|
||||
}
|
||||
|
||||
|
||||
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
|
||||
{
|
||||
if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Paint* Paint::duplicate() const noexcept
|
||||
{
|
||||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
|
||||
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
|
||||
{
|
||||
auto p = target.release();
|
||||
if (pImpl->composite(this, p, method)) return Result::Success;
|
||||
if (p) delete(p);
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
|
||||
CompositeMethod Paint::composite(const Paint** target) const noexcept
|
||||
{
|
||||
if (pImpl->compData) {
|
||||
if (target) *target = pImpl->compData->target;
|
||||
return pImpl->compData->method;
|
||||
} else {
|
||||
if (target) *target = nullptr;
|
||||
return CompositeMethod::None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Result Paint::opacity(uint8_t o) noexcept
|
||||
{
|
||||
if (pImpl->opacity == o) return Result::Success;
|
||||
|
||||
pImpl->opacity = o;
|
||||
pImpl->renderFlag |= RenderUpdateFlag::Color;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::opacity() const noexcept
|
||||
{
|
||||
return pImpl->opacity;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Paint::identifier() const noexcept
|
||||
{
|
||||
return pImpl->id;
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_PAINT_H_
|
||||
#define _TVG_PAINT_H_
|
||||
|
||||
#include "tvgRender.h"
|
||||
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
enum ContextFlag {Invalid = 0, FastTrack = 1};
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
virtual ~Iterator() {}
|
||||
virtual const Paint* next() = 0;
|
||||
virtual uint32_t count() = 0;
|
||||
virtual void begin() = 0;
|
||||
};
|
||||
|
||||
struct StrategyMethod
|
||||
{
|
||||
virtual ~StrategyMethod() {}
|
||||
|
||||
virtual bool dispose(RenderMethod& renderer) = 0;
|
||||
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag) = 0; //Return engine data if it has.
|
||||
virtual bool render(RenderMethod& renderer) = 0;
|
||||
virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
|
||||
virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
|
||||
virtual Paint* duplicate() = 0;
|
||||
virtual Iterator* iterator() = 0;
|
||||
};
|
||||
|
||||
struct Composite
|
||||
{
|
||||
Paint* target;
|
||||
Paint* source;
|
||||
CompositeMethod method;
|
||||
};
|
||||
|
||||
struct Paint::Impl
|
||||
{
|
||||
StrategyMethod* smethod = nullptr;
|
||||
RenderTransform* rTransform = nullptr;
|
||||
Composite* compData = nullptr;
|
||||
uint32_t renderFlag = RenderUpdateFlag::None;
|
||||
uint32_t ctxFlag = ContextFlag::Invalid;
|
||||
uint32_t id;
|
||||
uint8_t opacity = 255;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (compData) {
|
||||
delete(compData->target);
|
||||
free(compData);
|
||||
}
|
||||
if (smethod) delete(smethod);
|
||||
if (rTransform) delete(rTransform);
|
||||
}
|
||||
|
||||
void method(StrategyMethod* method)
|
||||
{
|
||||
smethod = method;
|
||||
}
|
||||
|
||||
bool transform(const Matrix& m)
|
||||
{
|
||||
if (!rTransform) {
|
||||
rTransform = new RenderTransform();
|
||||
if (!rTransform) return false;
|
||||
}
|
||||
rTransform->override(m);
|
||||
renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Matrix* transform()
|
||||
{
|
||||
if (rTransform) {
|
||||
rTransform->update();
|
||||
return &rTransform->m;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer) const
|
||||
{
|
||||
return smethod->bounds(renderer);
|
||||
}
|
||||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
if (compData) compData->target->pImpl->dispose(renderer);
|
||||
return smethod->dispose(renderer);
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
return smethod->iterator();
|
||||
}
|
||||
|
||||
bool composite(Paint* source, Paint* target, CompositeMethod method)
|
||||
{
|
||||
//Invalid case
|
||||
if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
|
||||
|
||||
if (compData) {
|
||||
delete(compData->target);
|
||||
//Reset scenario
|
||||
if (!target && method == CompositeMethod::None) {
|
||||
free(compData);
|
||||
compData = nullptr;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!target && method == CompositeMethod::None) return true;
|
||||
compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
|
||||
}
|
||||
compData->target = target;
|
||||
compData->source = source;
|
||||
compData->method = method;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rotate(float degree);
|
||||
bool scale(float factor);
|
||||
bool translate(float x, float y);
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool transformed);
|
||||
void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag);
|
||||
bool render(RenderMethod& renderer);
|
||||
Paint* duplicate();
|
||||
};
|
||||
|
||||
|
||||
template<class T>
|
||||
struct PaintMethod : StrategyMethod
|
||||
{
|
||||
T* inst = nullptr;
|
||||
|
||||
PaintMethod(T* _inst) : inst(_inst) {}
|
||||
~PaintMethod() {}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h) override
|
||||
{
|
||||
return inst->bounds(x, y, w, h);
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer) const override
|
||||
{
|
||||
return inst->bounds(renderer);
|
||||
}
|
||||
|
||||
bool dispose(RenderMethod& renderer) override
|
||||
{
|
||||
return inst->dispose(renderer);
|
||||
}
|
||||
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag renderFlag) override
|
||||
{
|
||||
return inst->update(renderer, transform, opacity, clips, renderFlag);
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer) override
|
||||
{
|
||||
return inst->render(renderer);
|
||||
}
|
||||
|
||||
Paint* duplicate() override
|
||||
{
|
||||
return inst->duplicate();
|
||||
}
|
||||
|
||||
Iterator* iterator() override
|
||||
{
|
||||
return inst->iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //_TVG_PAINT_H_
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgPictureImpl.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Picture::Picture() : pImpl(new Impl)
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
|
||||
Paint::pImpl->method(new PaintMethod<Picture::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
Picture::~Picture()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Picture> Picture::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Picture>(new Picture);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Picture::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_PICTURE;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::load(const std::string& path) noexcept
|
||||
{
|
||||
if (path.empty()) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(path);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::load(const char* data, uint32_t size, const string& mimeType, bool copy) noexcept
|
||||
{
|
||||
if (!data || size <= 0) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(data, size, mimeType, copy);
|
||||
}
|
||||
|
||||
|
||||
TVG_DEPRECATED Result Picture::load(const char* data, uint32_t size, bool copy) noexcept
|
||||
{
|
||||
return load(data, size, "", copy);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept
|
||||
{
|
||||
if (!data || w <= 0 || h <= 0) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(data, w, h, copy);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept
|
||||
{
|
||||
if (pImpl->viewbox(x, y, w, h)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::size(float w, float h) noexcept
|
||||
{
|
||||
if (pImpl->size(w, h)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::size(float* w, float* h) const noexcept
|
||||
{
|
||||
if (!pImpl->loader) return Result::InsufficientCondition;
|
||||
if (w) *w = pImpl->w;
|
||||
if (h) *h = pImpl->h;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept
|
||||
{
|
||||
//Try it, If not loaded yet.
|
||||
pImpl->reload();
|
||||
|
||||
if (pImpl->loader) {
|
||||
if (w) *w = static_cast<uint32_t>(pImpl->loader->w);
|
||||
if (h) *h = static_cast<uint32_t>(pImpl->loader->h);
|
||||
} else {
|
||||
if (w) *w = 0;
|
||||
if (h) *h = 0;
|
||||
}
|
||||
if (pImpl->surface) return pImpl->surface->buffer;
|
||||
else return nullptr;
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_PICTURE_IMPL_H_
|
||||
#define _TVG_PICTURE_IMPL_H_
|
||||
|
||||
#include <string>
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct PictureIterator : Iterator
|
||||
{
|
||||
Paint* paint = nullptr;
|
||||
Paint* ptr = nullptr;
|
||||
|
||||
PictureIterator(Paint* p) : paint(p) {}
|
||||
|
||||
const Paint* next() override
|
||||
{
|
||||
if (!ptr) ptr = paint;
|
||||
else ptr = nullptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint32_t count() override
|
||||
{
|
||||
if (paint) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void begin() override
|
||||
{
|
||||
ptr = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Picture::Impl
|
||||
{
|
||||
shared_ptr<LoadModule> loader = nullptr;
|
||||
|
||||
Paint* paint = nullptr; //vector picture uses
|
||||
Surface* surface = nullptr; //bitmap picture uses
|
||||
void* rdata = nullptr; //engine data
|
||||
float w = 0, h = 0;
|
||||
bool resizing = false;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (paint) delete(paint);
|
||||
free(surface);
|
||||
}
|
||||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
bool ret = true;
|
||||
if (paint) {
|
||||
ret = paint->pImpl->dispose(renderer);
|
||||
} else if (surface) {
|
||||
ret = renderer.dispose(rdata);
|
||||
rdata = nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t reload()
|
||||
{
|
||||
if (loader) {
|
||||
if (!paint) {
|
||||
if (auto p = loader->paint()) {
|
||||
paint = p.release();
|
||||
loader->close();
|
||||
if (w != loader->w || h != loader->h) {
|
||||
loader->resize(paint, w, h);
|
||||
resizing = false;
|
||||
}
|
||||
if (paint) return RenderUpdateFlag::None;
|
||||
}
|
||||
}
|
||||
free(surface);
|
||||
if ((surface = loader->bitmap().release())) {
|
||||
loader->close();
|
||||
return RenderUpdateFlag::Image;
|
||||
}
|
||||
}
|
||||
return RenderUpdateFlag::None;
|
||||
}
|
||||
|
||||
RenderTransform resizeTransform(const RenderTransform* pTransform)
|
||||
{
|
||||
//Overriding Transformation by the desired image size
|
||||
auto sx = w / loader->w;
|
||||
auto sy = h / loader->h;
|
||||
auto scale = sx < sy ? sx : sy;
|
||||
|
||||
RenderTransform tmp;
|
||||
tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
|
||||
|
||||
if (!pTransform) return tmp;
|
||||
else return RenderTransform(pTransform, &tmp);
|
||||
}
|
||||
|
||||
void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
|
||||
{
|
||||
auto flag = reload();
|
||||
|
||||
if (surface) {
|
||||
auto transform = resizeTransform(pTransform);
|
||||
rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
} else if (paint) {
|
||||
if (resizing) {
|
||||
loader->resize(paint, w, h);
|
||||
resizing = false;
|
||||
}
|
||||
rdata = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
}
|
||||
return rdata;
|
||||
}
|
||||
|
||||
bool render(RenderMethod &renderer)
|
||||
{
|
||||
if (surface) return renderer.renderImage(rdata);
|
||||
else if (paint) return paint->pImpl->render(renderer);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool viewbox(float* x, float* y, float* w, float* h) const
|
||||
{
|
||||
if (!loader) return false;
|
||||
if (x) *x = loader->vx;
|
||||
if (y) *y = loader->vy;
|
||||
if (w) *w = loader->vw;
|
||||
if (h) *h = loader->vh;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool size(float w, float h)
|
||||
{
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
resizing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h)
|
||||
{
|
||||
if (x) *x = 0;
|
||||
if (y) *y = 0;
|
||||
if (w) *w = this->w;
|
||||
if (h) *h = this->h;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer)
|
||||
{
|
||||
if (rdata) return renderer.region(rdata);
|
||||
if (paint) return paint->pImpl->bounds(renderer);
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
Result load(const string& path)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
if (loader) loader->close();
|
||||
bool invalid; //Invalid Path
|
||||
loader = LoaderMgr::loader(path, &invalid);
|
||||
if (!loader) {
|
||||
if (invalid) return Result::InvalidArguments;
|
||||
return Result::NonSupport;
|
||||
}
|
||||
if (!loader->read()) return Result::Unknown;
|
||||
w = loader->w;
|
||||
h = loader->h;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result load(const char* data, uint32_t size, const string& mimeType, bool copy)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
if (loader) loader->close();
|
||||
loader = LoaderMgr::loader(data, size, mimeType, copy);
|
||||
if (!loader) return Result::NonSupport;
|
||||
if (!loader->read()) return Result::Unknown;
|
||||
w = loader->w;
|
||||
h = loader->h;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
if (loader) loader->close();
|
||||
loader = LoaderMgr::loader(data, w, h, copy);
|
||||
if (!loader) return Result::NonSupport;
|
||||
this->w = loader->w;
|
||||
this->h = loader->h;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
reload();
|
||||
|
||||
auto ret = Picture::gen();
|
||||
|
||||
auto dup = ret.get()->pImpl;
|
||||
if (paint) dup->paint = paint->duplicate();
|
||||
|
||||
dup->loader = loader;
|
||||
if (surface) {
|
||||
dup->surface = static_cast<Surface*>(malloc(sizeof(Surface)));
|
||||
*dup->surface = *surface;
|
||||
}
|
||||
dup->w = w;
|
||||
dup->h = h;
|
||||
dup->resizing = resizing;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
reload();
|
||||
return new PictureIterator(paint);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_PICTURE_IMPL_H_
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <float.h>
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct RadialGradient::Impl
|
||||
{
|
||||
float cx = 0;
|
||||
float cy = 0;
|
||||
float radius = 0;
|
||||
|
||||
Fill* duplicate()
|
||||
{
|
||||
auto ret = RadialGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->cx = cx;
|
||||
ret->pImpl->cy = cy;
|
||||
ret->pImpl->radius = radius;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RadialGradient::RadialGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
|
||||
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
RadialGradient::~RadialGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float cx, float cy, float radius) noexcept
|
||||
{
|
||||
if (radius < 0) return Result::InvalidArguments;
|
||||
|
||||
pImpl->cx = cx;
|
||||
pImpl->cy = cy;
|
||||
pImpl->radius = radius;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
|
||||
{
|
||||
if (cx) *cx = pImpl->cx;
|
||||
if (cy) *cy = pImpl->cy;
|
||||
if (radius) *radius = pImpl->radius;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<RadialGradient>(new RadialGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t RadialGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_RADIAL;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgMath.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void RenderTransform::override(const Matrix& m)
|
||||
{
|
||||
this->m = m;
|
||||
|
||||
if (m.e11 == 0.0f && m.e12 == 0.0f && m.e13 == 0.0f &&
|
||||
m.e21 == 0.0f && m.e22 == 0.0f && m.e23 == 0.0f &&
|
||||
m.e31 == 0.0f && m.e32 == 0.0f && m.e33 == 0.0f) {
|
||||
overriding = false;
|
||||
} else overriding = true;
|
||||
}
|
||||
|
||||
|
||||
bool RenderTransform::update()
|
||||
{
|
||||
if (overriding) return true;
|
||||
|
||||
//Init Status
|
||||
if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false;
|
||||
|
||||
mathIdentity(&m);
|
||||
|
||||
mathScale(&m, scale);
|
||||
|
||||
if (!mathZero(degree)) mathRotate(&m, degree);
|
||||
|
||||
mathTranslate(&m, x, y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RenderTransform::RenderTransform()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
|
||||
{
|
||||
m = mathMultiply(&lhs->m, &rhs->m);
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_RENDER_H_
|
||||
#define _TVG_RENDER_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
|
||||
|
||||
struct Surface
|
||||
{
|
||||
//TODO: Union for multiple types
|
||||
uint32_t* buffer;
|
||||
uint32_t stride;
|
||||
uint32_t w, h;
|
||||
uint32_t cs;
|
||||
};
|
||||
|
||||
using RenderData = void*;
|
||||
|
||||
struct Compositor
|
||||
{
|
||||
CompositeMethod method;
|
||||
uint32_t opacity;
|
||||
};
|
||||
|
||||
struct RenderRegion
|
||||
{
|
||||
int32_t x, y, w, h;
|
||||
|
||||
void intersect(const RenderRegion& rhs)
|
||||
{
|
||||
auto x1 = x + w;
|
||||
auto y1 = y + h;
|
||||
auto x2 = rhs.x + rhs.w;
|
||||
auto y2 = rhs.y + rhs.h;
|
||||
|
||||
x = (x > rhs.x) ? x : rhs.x;
|
||||
y = (y > rhs.y) ? y : rhs.y;
|
||||
w = ((x1 < x2) ? x1 : x2) - x;
|
||||
h = ((y1 < y2) ? y1 : y2) - y;
|
||||
|
||||
if (w < 0) w = 0;
|
||||
if (h < 0) h = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderTransform
|
||||
{
|
||||
Matrix m; //3x3 Matrix Elements
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float degree = 0.0f; //rotation degree
|
||||
float scale = 1.0f; //scale factor
|
||||
bool overriding = false; //user transform?
|
||||
|
||||
bool update();
|
||||
void override(const Matrix& m);
|
||||
|
||||
RenderTransform();
|
||||
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
|
||||
};
|
||||
|
||||
|
||||
class RenderMethod
|
||||
{
|
||||
public:
|
||||
virtual ~RenderMethod() {}
|
||||
virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
|
||||
virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
|
||||
virtual bool preRender() = 0;
|
||||
virtual bool renderShape(RenderData data) = 0;
|
||||
virtual bool renderImage(RenderData data) = 0;
|
||||
virtual bool postRender() = 0;
|
||||
virtual bool dispose(RenderData data) = 0;
|
||||
virtual RenderRegion region(RenderData data) = 0;
|
||||
virtual RenderRegion viewport() = 0;
|
||||
virtual bool viewport(const RenderRegion& vp) = 0;
|
||||
|
||||
virtual bool clear() = 0;
|
||||
virtual bool sync() = 0;
|
||||
|
||||
virtual Compositor* target(const RenderRegion& region) = 0;
|
||||
virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0;
|
||||
virtual bool endComposite(Compositor* cmp) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_RENDER_H_
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SAVE_MODULE_H_
|
||||
#define _TVG_SAVE_MODULE_H_
|
||||
|
||||
#include "tvgIteratorAccessor.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class SaveModule : public IteratorAccessor
|
||||
{
|
||||
public:
|
||||
virtual ~SaveModule() {}
|
||||
|
||||
virtual bool save(Paint* paint, const string& path, bool compress) = 0;
|
||||
virtual bool close() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_SAVE_MODULE_H_
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgSaveModule.h"
|
||||
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
#include "tvgTvgSaver.h"
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct Saver::Impl
|
||||
{
|
||||
SaveModule* saveModule = nullptr;
|
||||
~Impl()
|
||||
{
|
||||
if (saveModule) delete(saveModule);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static SaveModule* _find(FileType type)
|
||||
{
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
return new TvgSaver;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
const char *format;
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
format = "TVG";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
format = "???";
|
||||
break;
|
||||
}
|
||||
}
|
||||
TVGLOG("SAVER", "%s format is not supported", format);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static SaveModule* _find(const string& path)
|
||||
{
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("tvg")) {
|
||||
return _find(FileType::Tvg);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Saver::Saver() : pImpl(new Impl())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Saver::~Saver()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compress) noexcept
|
||||
{
|
||||
auto p = paint.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
//Already on saving an other resource.
|
||||
if (pImpl->saveModule) {
|
||||
delete(p);
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
if (auto saveModule = _find(path)) {
|
||||
if (saveModule->save(p, path, compress)) {
|
||||
pImpl->saveModule = saveModule;
|
||||
return Result::Success;
|
||||
} else {
|
||||
delete(p);
|
||||
delete(saveModule);
|
||||
return Result::Unknown;
|
||||
}
|
||||
}
|
||||
delete(p);
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
Result Saver::sync() noexcept
|
||||
{
|
||||
if (!pImpl->saveModule) return Result::InsufficientCondition;
|
||||
pImpl->saveModule->close();
|
||||
delete(pImpl->saveModule);
|
||||
pImpl->saveModule = nullptr;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Saver> Saver::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Saver>(new Saver);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgSceneImpl.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Scene::Scene() : pImpl(new Impl())
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_SCENE;
|
||||
Paint::pImpl->method(new PaintMethod<Scene::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
Scene::~Scene()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Scene> Scene::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Scene>(new Scene);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Scene::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_SCENE;
|
||||
}
|
||||
|
||||
|
||||
Result Scene::push(unique_ptr<Paint> paint) noexcept
|
||||
{
|
||||
auto p = paint.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
pImpl->paints.push(p);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Scene::reserve(uint32_t size) noexcept
|
||||
{
|
||||
if (!pImpl->paints.reserve(size)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Scene::clear(bool free) noexcept
|
||||
{
|
||||
pImpl->clear(free);
|
||||
|
||||
return Result::Success;
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SCENE_IMPL_H_
|
||||
#define _TVG_SCENE_IMPL_H_
|
||||
|
||||
#include <float.h>
|
||||
#include "tvgPaint.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct SceneIterator : Iterator
|
||||
{
|
||||
Array<Paint*>* paints;
|
||||
uint32_t idx = 0;
|
||||
|
||||
SceneIterator(Array<Paint*>* p) : paints(p)
|
||||
{
|
||||
}
|
||||
|
||||
const Paint* next() override
|
||||
{
|
||||
if (idx >= paints->count) return nullptr;
|
||||
return paints->data[idx++];
|
||||
}
|
||||
|
||||
uint32_t count() override
|
||||
{
|
||||
return paints->count;
|
||||
}
|
||||
|
||||
void begin() override
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Scene::Impl
|
||||
{
|
||||
Array<Paint*> paints;
|
||||
uint8_t opacity; //for composition
|
||||
RenderMethod* renderer = nullptr; //keep it for explicit clear
|
||||
|
||||
~Impl()
|
||||
{
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
delete(*paint);
|
||||
}
|
||||
}
|
||||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
(*paint)->pImpl->dispose(renderer);
|
||||
}
|
||||
|
||||
this->renderer = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needComposition(uint32_t opacity)
|
||||
{
|
||||
//Half translucent requires intermediate composition.
|
||||
if (opacity == 255 || opacity == 0) return false;
|
||||
|
||||
//If scene has several children or only scene, it may require composition.
|
||||
if (paints.count > 1) return true;
|
||||
if (paints.count == 1 && (*paints.data)->identifier() == TVG_CLASS_ID_SCENE) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag)
|
||||
{
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
It must do intermeidate composition with that opacity value. */
|
||||
this->opacity = static_cast<uint8_t>(opacity);
|
||||
if (needComposition(opacity)) opacity = 255;
|
||||
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
(*paint)->pImpl->update(renderer, transform, opacity, clips, static_cast<uint32_t>(flag));
|
||||
}
|
||||
|
||||
/* FXIME: it requires to return list of children engine data
|
||||
This is necessary for scene composition */
|
||||
|
||||
this->renderer = &renderer;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer)
|
||||
{
|
||||
Compositor* cmp = nullptr;
|
||||
|
||||
if (needComposition(opacity)) {
|
||||
cmp = renderer.target(bounds(renderer));
|
||||
renderer.beginComposite(cmp, CompositeMethod::None, opacity);
|
||||
}
|
||||
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
if (!(*paint)->pImpl->render(renderer)) return false;
|
||||
}
|
||||
|
||||
if (cmp) renderer.endComposite(cmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer) const
|
||||
{
|
||||
if (paints.count == 0) return {0, 0, 0, 0};
|
||||
|
||||
int32_t x1 = INT32_MAX;
|
||||
int32_t y1 = INT32_MAX;
|
||||
int32_t x2 = 0;
|
||||
int32_t y2 = 0;
|
||||
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
auto region = (*paint)->pImpl->bounds(renderer);
|
||||
|
||||
//Merge regions
|
||||
if (region.x < x1) x1 = region.x;
|
||||
if (x2 < region.x + region.w) x2 = (region.x + region.w);
|
||||
if (region.y < y1) y1 = region.y;
|
||||
if (y2 < region.y + region.h) y2 = (region.y + region.h);
|
||||
}
|
||||
|
||||
return {x1, y1, (x2 - x1), (y2 - y1)};
|
||||
}
|
||||
|
||||
bool bounds(float* px, float* py, float* pw, float* ph)
|
||||
{
|
||||
if (paints.count == 0) return false;
|
||||
|
||||
auto x1 = FLT_MAX;
|
||||
auto y1 = FLT_MAX;
|
||||
auto x2 = -FLT_MAX;
|
||||
auto y2 = -FLT_MAX;
|
||||
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
auto x = FLT_MAX;
|
||||
auto y = FLT_MAX;
|
||||
auto w = 0.0f;
|
||||
auto h = 0.0f;
|
||||
|
||||
if ((*paint)->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
|
||||
|
||||
//Merge regions
|
||||
if (x < x1) x1 = x;
|
||||
if (x2 < x + w) x2 = (x + w);
|
||||
if (y < y1) y1 = y;
|
||||
if (y2 < y + h) y2 = (y + h);
|
||||
}
|
||||
|
||||
if (px) *px = x1;
|
||||
if (py) *py = y1;
|
||||
if (pw) *pw = (x2 - x1);
|
||||
if (ph) *ph = (y2 - y1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
auto ret = Scene::gen();
|
||||
|
||||
auto dup = ret.get()->pImpl;
|
||||
|
||||
dup->paints.reserve(paints.count);
|
||||
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
dup->paints.push((*paint)->duplicate());
|
||||
}
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
|
||||
void clear(bool free)
|
||||
{
|
||||
auto dispose = renderer ? true : false;
|
||||
|
||||
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
|
||||
if (dispose) (*paint)->pImpl->dispose(*renderer);
|
||||
if (free) delete(*paint);
|
||||
}
|
||||
paints.clear();
|
||||
renderer = nullptr;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
return new SceneIterator(&paints);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_SCENE_IMPL_H_
|
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgMath.h"
|
||||
#include "tvgShapeImpl.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
constexpr auto PATH_KAPPA = 0.552284f;
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Shape :: Shape() : pImpl(new Impl(this))
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
|
||||
Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
Shape :: ~Shape()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Shape> Shape::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Shape>(new Shape);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_SHAPE;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::reset() noexcept
|
||||
{
|
||||
pImpl->path.reset();
|
||||
pImpl->flag = RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
|
||||
{
|
||||
if (!cmds) return 0;
|
||||
|
||||
*cmds = pImpl->path.cmds;
|
||||
|
||||
return pImpl->path.cmdCnt;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::pathCoords(const Point** pts) const noexcept
|
||||
{
|
||||
if (!pts) return 0;
|
||||
|
||||
*pts = pImpl->path.pts;
|
||||
|
||||
return pImpl->path.ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept
|
||||
{
|
||||
if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
|
||||
|
||||
pImpl->path.grow(cmdCnt, ptsCnt);
|
||||
pImpl->path.append(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::moveTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->path.moveTo(x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::lineTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->path.lineTo(x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
|
||||
{
|
||||
pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::close() noexcept
|
||||
{
|
||||
pImpl->path.close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
|
||||
{
|
||||
auto rxKappa = rx * PATH_KAPPA;
|
||||
auto ryKappa = ry * PATH_KAPPA;
|
||||
|
||||
pImpl->path.grow(6, 13);
|
||||
pImpl->path.moveTo(cx, cy - ry);
|
||||
pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
|
||||
pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
|
||||
pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
|
||||
pImpl->path.close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
|
||||
{
|
||||
//just circle
|
||||
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
|
||||
|
||||
startAngle = (startAngle * M_PI) / 180.0f;
|
||||
sweep = sweep * M_PI / 180.0f;
|
||||
|
||||
auto nCurves = ceil(fabsf(sweep / float(M_PI_2)));
|
||||
auto sweepSign = (sweep < 0 ? -1 : 1);
|
||||
auto fract = fmodf(sweep, float(M_PI_2));
|
||||
fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract;
|
||||
|
||||
//Start from here
|
||||
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
|
||||
|
||||
if (pie) {
|
||||
pImpl->path.moveTo(cx, cy);
|
||||
pImpl->path.lineTo(start.x + cx, start.y + cy);
|
||||
} else {
|
||||
pImpl->path.moveTo(start.x + cx, start.y + cy);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nCurves; ++i) {
|
||||
auto endAngle = startAngle + ((i != nCurves - 1) ? float(M_PI_2) * sweepSign : fract);
|
||||
Point end = {radius * cosf(endAngle), radius * sinf(endAngle)};
|
||||
|
||||
//variables needed to calculate bezier control points
|
||||
|
||||
//get bezier control points using article:
|
||||
//(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
|
||||
auto ax = start.x;
|
||||
auto ay = start.y;
|
||||
auto bx = end.x;
|
||||
auto by = end.y;
|
||||
auto q1 = ax * ax + ay * ay;
|
||||
auto q2 = ax * bx + ay * by + q1;
|
||||
auto k2 = (4.0f/3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx));
|
||||
|
||||
start = end; //Next start point is the current end point
|
||||
|
||||
end.x += cx;
|
||||
end.y += cy;
|
||||
|
||||
Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
|
||||
Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
|
||||
|
||||
pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
|
||||
|
||||
startAngle = endAngle;
|
||||
}
|
||||
|
||||
if (pie) pImpl->path.close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
|
||||
{
|
||||
auto halfW = w * 0.5f;
|
||||
auto halfH = h * 0.5f;
|
||||
|
||||
//clamping cornerRadius by minimum size
|
||||
if (rx > halfW) rx = halfW;
|
||||
if (ry > halfH) ry = halfH;
|
||||
|
||||
//rectangle
|
||||
if (rx == 0 && ry == 0) {
|
||||
pImpl->path.grow(5, 4);
|
||||
pImpl->path.moveTo(x, y);
|
||||
pImpl->path.lineTo(x + w, y);
|
||||
pImpl->path.lineTo(x + w, y + h);
|
||||
pImpl->path.lineTo(x, y + h);
|
||||
pImpl->path.close();
|
||||
//circle
|
||||
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
|
||||
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
|
||||
} else {
|
||||
auto hrx = rx * 0.5f;
|
||||
auto hry = ry * 0.5f;
|
||||
pImpl->path.grow(10, 17);
|
||||
pImpl->path.moveTo(x + rx, y);
|
||||
pImpl->path.lineTo(x + w - rx, y);
|
||||
pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
|
||||
pImpl->path.lineTo(x + w, y + h - ry);
|
||||
pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
|
||||
pImpl->path.lineTo(x + rx, y + h);
|
||||
pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
pImpl->path.lineTo(x, y + ry);
|
||||
pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
|
||||
pImpl->path.close();
|
||||
}
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
pImpl->color[0] = r;
|
||||
pImpl->color[1] = g;
|
||||
pImpl->color[2] = b;
|
||||
pImpl->color[3] = a;
|
||||
pImpl->flag |= RenderUpdateFlag::Color;
|
||||
|
||||
if (pImpl->fill) {
|
||||
delete(pImpl->fill);
|
||||
pImpl->fill = nullptr;
|
||||
pImpl->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(unique_ptr<Fill> f) noexcept
|
||||
{
|
||||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill);
|
||||
pImpl->fill = p;
|
||||
pImpl->flag |= RenderUpdateFlag::Gradient;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
|
||||
{
|
||||
if (r) *r = pImpl->color[0];
|
||||
if (g) *g = pImpl->color[1];
|
||||
if (b) *b = pImpl->color[2];
|
||||
if (a) *a = pImpl->color[3];
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
const Fill* Shape::fill() const noexcept
|
||||
{
|
||||
return pImpl->fill;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(float width) noexcept
|
||||
{
|
||||
if (!pImpl->strokeWidth(width)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
float Shape::strokeWidth() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return 0;
|
||||
return pImpl->stroke->width;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return Result::InsufficientCondition;
|
||||
|
||||
if (r) *r = pImpl->stroke->color[0];
|
||||
if (g) *g = pImpl->stroke->color[1];
|
||||
if (b) *b = pImpl->stroke->color[2];
|
||||
if (a) *a = pImpl->stroke->color[3];
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(unique_ptr<Fill> f) noexcept
|
||||
{
|
||||
return pImpl->strokeFill(move(f));
|
||||
}
|
||||
|
||||
|
||||
const Fill* Shape::strokeFill() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return nullptr;
|
||||
|
||||
return pImpl->stroke->fill;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
|
||||
{
|
||||
if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < cnt; i++)
|
||||
if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
|
||||
|
||||
if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return 0;
|
||||
|
||||
if (dashPattern) *dashPattern = pImpl->stroke->dashPattern;
|
||||
|
||||
return pImpl->stroke->dashCnt;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(StrokeCap cap) noexcept
|
||||
{
|
||||
if (!pImpl->strokeCap(cap)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(StrokeJoin join) noexcept
|
||||
{
|
||||
if (!pImpl->strokeJoin(join)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
StrokeCap Shape::strokeCap() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return StrokeCap::Square;
|
||||
|
||||
return pImpl->stroke->cap;
|
||||
}
|
||||
|
||||
|
||||
StrokeJoin Shape::strokeJoin() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return StrokeJoin::Bevel;
|
||||
|
||||
return pImpl->stroke->join;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(FillRule r) noexcept
|
||||
{
|
||||
pImpl->rule = r;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
FillRule Shape::fillRule() const noexcept
|
||||
{
|
||||
return pImpl->rule;
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SHAPE_IMPL_H_
|
||||
#define _TVG_SHAPE_IMPL_H_
|
||||
|
||||
#include <memory.h>
|
||||
#include "tvgPaint.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct ShapeStroke
|
||||
{
|
||||
float width;
|
||||
uint8_t color[4];
|
||||
Fill *fill;
|
||||
float* dashPattern;
|
||||
uint32_t dashCnt;
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
|
||||
void copy(const ShapeStroke* src)
|
||||
{
|
||||
width = src->width;
|
||||
dashCnt = src->dashCnt;
|
||||
cap = src->cap;
|
||||
join = src->join;
|
||||
|
||||
memcpy(color, src->color, sizeof(color));
|
||||
if (dashCnt > 0) {
|
||||
dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
|
||||
memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
|
||||
}
|
||||
if (src->fill) fill = src->fill->duplicate();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (dashPattern) free(dashPattern);
|
||||
if (fill) delete(fill);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ShapePath
|
||||
{
|
||||
PathCommand* cmds = nullptr;
|
||||
uint32_t cmdCnt = 0;
|
||||
uint32_t reservedCmdCnt = 0;
|
||||
|
||||
Point *pts = nullptr;
|
||||
uint32_t ptsCnt = 0;
|
||||
uint32_t reservedPtsCnt = 0;
|
||||
|
||||
~ShapePath()
|
||||
{
|
||||
if (cmds) free(cmds);
|
||||
if (pts) free(pts);
|
||||
}
|
||||
|
||||
ShapePath()
|
||||
{
|
||||
}
|
||||
|
||||
void duplicate(const ShapePath* src)
|
||||
{
|
||||
if (src->cmdCnt == 0 || src->ptsCnt == 0) return;
|
||||
|
||||
cmdCnt = src->cmdCnt;
|
||||
reservedCmdCnt = src->reservedCmdCnt;
|
||||
ptsCnt = src->ptsCnt;
|
||||
reservedPtsCnt = src->reservedPtsCnt;
|
||||
|
||||
cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * reservedCmdCnt));
|
||||
if (!cmds) return;
|
||||
memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt);
|
||||
|
||||
pts = static_cast<Point*>(malloc(sizeof(Point) * reservedPtsCnt));
|
||||
if (!pts) {
|
||||
free(cmds);
|
||||
return;
|
||||
}
|
||||
memcpy(pts, src->pts, sizeof(Point) * ptsCnt);
|
||||
}
|
||||
|
||||
void reserveCmd(uint32_t cmdCnt)
|
||||
{
|
||||
if (cmdCnt <= reservedCmdCnt) return;
|
||||
reservedCmdCnt = cmdCnt;
|
||||
cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
|
||||
}
|
||||
|
||||
void reservePts(uint32_t ptsCnt)
|
||||
{
|
||||
if (ptsCnt <= reservedPtsCnt) return;
|
||||
reservedPtsCnt = ptsCnt;
|
||||
pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
|
||||
}
|
||||
|
||||
void grow(uint32_t cmdCnt, uint32_t ptsCnt)
|
||||
{
|
||||
reserveCmd(this->cmdCnt + cmdCnt);
|
||||
reservePts(this->ptsCnt + ptsCnt);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
cmdCnt = 0;
|
||||
ptsCnt = 0;
|
||||
}
|
||||
|
||||
void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
|
||||
{
|
||||
memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
|
||||
memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt);
|
||||
this->cmdCnt += cmdCnt;
|
||||
this->ptsCnt += ptsCnt;
|
||||
}
|
||||
|
||||
void moveTo(float x, float y)
|
||||
{
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
|
||||
|
||||
cmds[cmdCnt++] = PathCommand::MoveTo;
|
||||
pts[ptsCnt++] = {x, y};
|
||||
}
|
||||
|
||||
void lineTo(float x, float y)
|
||||
{
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
|
||||
|
||||
cmds[cmdCnt++] = PathCommand::LineTo;
|
||||
pts[ptsCnt++] = {x, y};
|
||||
}
|
||||
|
||||
void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
|
||||
{
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
|
||||
|
||||
cmds[cmdCnt++] = PathCommand::CubicTo;
|
||||
pts[ptsCnt++] = {cx1, cy1};
|
||||
pts[ptsCnt++] = {cx2, cy2};
|
||||
pts[ptsCnt++] = {x, y};
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return;
|
||||
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
cmds[cmdCnt++] = PathCommand::Close;
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h) const
|
||||
{
|
||||
if (ptsCnt == 0) return false;
|
||||
|
||||
Point min = { pts[0].x, pts[0].y };
|
||||
Point max = { pts[0].x, pts[0].y };
|
||||
|
||||
for (uint32_t i = 1; i < ptsCnt; ++i) {
|
||||
if (pts[i].x < min.x) min.x = pts[i].x;
|
||||
if (pts[i].y < min.y) min.y = pts[i].y;
|
||||
if (pts[i].x > max.x) max.x = pts[i].x;
|
||||
if (pts[i].y > max.y) max.y = pts[i].y;
|
||||
}
|
||||
|
||||
if (x) *x = min.x;
|
||||
if (y) *y = min.y;
|
||||
if (w) *w = max.x - min.x;
|
||||
if (h) *h = max.y - min.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Shape::Impl
|
||||
{
|
||||
ShapePath path;
|
||||
Fill *fill = nullptr;
|
||||
ShapeStroke *stroke = nullptr;
|
||||
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
|
||||
FillRule rule = FillRule::Winding;
|
||||
RenderData rdata = nullptr; //engine data
|
||||
Shape *shape = nullptr;
|
||||
uint32_t flag = RenderUpdateFlag::None;
|
||||
|
||||
Impl(Shape* s) : shape(s)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (fill) delete(fill);
|
||||
if (stroke) {
|
||||
stroke->clear();
|
||||
free (stroke);
|
||||
}
|
||||
}
|
||||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
auto ret = renderer.dispose(rdata);
|
||||
rdata = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer)
|
||||
{
|
||||
return renderer.renderShape(rdata);
|
||||
}
|
||||
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
|
||||
{
|
||||
this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
|
||||
flag = RenderUpdateFlag::None;
|
||||
return this->rdata;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer)
|
||||
{
|
||||
return renderer.region(rdata);
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h)
|
||||
{
|
||||
auto ret = path.bounds(x, y, w, h);
|
||||
|
||||
//Stroke feathering
|
||||
if (stroke) {
|
||||
if (x) *x -= stroke->width * 0.5f;
|
||||
if (y) *y -= stroke->width * 0.5f;
|
||||
if (w) *w += stroke->width;
|
||||
if (h) *h += stroke->width;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool strokeWidth(float width)
|
||||
{
|
||||
//TODO: Size Exception?
|
||||
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
stroke->width = width;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strokeCap(StrokeCap cap)
|
||||
{
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
stroke->cap = cap;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strokeJoin(StrokeJoin join)
|
||||
{
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
stroke->join = join;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
if (stroke->fill) {
|
||||
delete(stroke->fill);
|
||||
stroke->fill = nullptr;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
stroke->color[0] = r;
|
||||
stroke->color[1] = g;
|
||||
stroke->color[2] = b;
|
||||
stroke->color[3] = a;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result strokeFill(unique_ptr<Fill> f)
|
||||
{
|
||||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
if (stroke->fill && stroke->fill != p) delete(stroke->fill);
|
||||
stroke->fill = p;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
bool strokeDash(const float* pattern, uint32_t cnt)
|
||||
{
|
||||
//Reset dash
|
||||
if (!pattern && cnt == 0) {
|
||||
free(stroke->dashPattern);
|
||||
stroke->dashPattern = nullptr;
|
||||
} else {
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
if (stroke->dashCnt != cnt) {
|
||||
free(stroke->dashPattern);
|
||||
stroke->dashPattern = nullptr;
|
||||
}
|
||||
if (!stroke->dashPattern) {
|
||||
stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
|
||||
if (!stroke->dashPattern) return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < cnt; ++i) {
|
||||
stroke->dashPattern[i] = pattern[i];
|
||||
}
|
||||
}
|
||||
stroke->dashCnt = cnt;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
auto ret = Shape::gen();
|
||||
|
||||
auto dup = ret.get()->pImpl;
|
||||
dup->rule = rule;
|
||||
|
||||
//Color
|
||||
memcpy(dup->color, color, sizeof(color));
|
||||
dup->flag = RenderUpdateFlag::Color;
|
||||
|
||||
//Path
|
||||
dup->path.duplicate(&path);
|
||||
dup->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
//Stroke
|
||||
if (stroke) {
|
||||
dup->stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
dup->stroke->copy(stroke);
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
if (stroke->fill)
|
||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
//Fill
|
||||
if (fill) {
|
||||
dup->fill = fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_SHAPE_IMPL_H_
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgCanvasImpl.h"
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
#include "tvgSwRenderer.h"
|
||||
#else
|
||||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
//Non Supported. Dummy Class */
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct SwCanvas::Impl
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(new Impl)
|
||||
#else
|
||||
SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(new Impl)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SwCanvas::~SwCanvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result SwCanvas::mempool(MempoolPolicy policy) noexcept
|
||||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
//It can't change the policy during the running.
|
||||
if (Canvas::pImpl->paints.count > 0) return Result::InsufficientCondition;
|
||||
|
||||
if (policy == MempoolPolicy::Individual) renderer->mempool(false);
|
||||
else renderer->mempool(true);
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
|
||||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->needRefresh();
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<SwCanvas> SwCanvas::gen() noexcept
|
||||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (SwRenderer::init() <= 0) return nullptr;
|
||||
return unique_ptr<SwCanvas>(new SwCanvas);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg {
|
||||
|
||||
struct TaskQueue {
|
||||
deque<Task*> taskDeque;
|
||||
mutex mtx;
|
||||
condition_variable ready;
|
||||
bool done = false;
|
||||
|
||||
bool tryPop(Task** task)
|
||||
{
|
||||
unique_lock<mutex> lock{mtx, try_to_lock};
|
||||
if (!lock || taskDeque.empty()) return false;
|
||||
*task = taskDeque.front();
|
||||
taskDeque.pop_front();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tryPush(Task* task)
|
||||
{
|
||||
{
|
||||
unique_lock<mutex> lock{mtx, try_to_lock};
|
||||
if (!lock) return false;
|
||||
taskDeque.push_back(task);
|
||||
}
|
||||
|
||||
ready.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void complete()
|
||||
{
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
done = true;
|
||||
}
|
||||
ready.notify_all();
|
||||
}
|
||||
|
||||
bool pop(Task** task)
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
|
||||
while (taskDeque.empty() && !done) {
|
||||
ready.wait(lock);
|
||||
}
|
||||
|
||||
if (taskDeque.empty()) return false;
|
||||
|
||||
*task = taskDeque.front();
|
||||
taskDeque.pop_front();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void push(Task* task)
|
||||
{
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
taskDeque.push_back(task);
|
||||
}
|
||||
|
||||
ready.notify_one();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class TaskSchedulerImpl
|
||||
{
|
||||
public:
|
||||
unsigned threadCnt;
|
||||
vector<thread> threads;
|
||||
vector<TaskQueue> taskQueues;
|
||||
atomic<unsigned> idx{0};
|
||||
|
||||
TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
|
||||
{
|
||||
for (unsigned i = 0; i < threadCnt; ++i) {
|
||||
threads.emplace_back([&, i] { run(i); });
|
||||
}
|
||||
}
|
||||
|
||||
~TaskSchedulerImpl()
|
||||
{
|
||||
for (auto& queue : taskQueues) queue.complete();
|
||||
for (auto& thread : threads) thread.join();
|
||||
}
|
||||
|
||||
void run(unsigned i)
|
||||
{
|
||||
Task* task;
|
||||
|
||||
//Thread Loop
|
||||
while (true) {
|
||||
auto success = false;
|
||||
for (unsigned x = 0; x < threadCnt * 2; ++x) {
|
||||
if (taskQueues[(i + x) % threadCnt].tryPop(&task)) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success && !taskQueues[i].pop(&task)) break;
|
||||
(*task)(i);
|
||||
}
|
||||
}
|
||||
|
||||
void request(Task* task)
|
||||
{
|
||||
//Async
|
||||
if (threadCnt > 0) {
|
||||
task->prepare();
|
||||
auto i = idx++;
|
||||
for (unsigned n = 0; n < threadCnt; ++n) {
|
||||
if (taskQueues[(i + n) % threadCnt].tryPush(task)) return;
|
||||
}
|
||||
taskQueues[i % threadCnt].push(task);
|
||||
//Sync
|
||||
} else {
|
||||
task->run(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static TaskSchedulerImpl* inst = nullptr;
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void TaskScheduler::init(unsigned threads)
|
||||
{
|
||||
if (inst) return;
|
||||
inst = new TaskSchedulerImpl(threads);
|
||||
}
|
||||
|
||||
|
||||
void TaskScheduler::term()
|
||||
{
|
||||
if (!inst) return;
|
||||
delete(inst);
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void TaskScheduler::request(Task* task)
|
||||
{
|
||||
if (inst) inst->request(task);
|
||||
}
|
||||
|
||||
|
||||
unsigned TaskScheduler::threads()
|
||||
{
|
||||
if (inst) return inst->threadCnt;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_TASK_SCHEDULER_H_
|
||||
#define _TVG_TASK_SCHEDULER_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
struct Task;
|
||||
|
||||
struct TaskScheduler
|
||||
{
|
||||
static unsigned threads();
|
||||
static void init(unsigned threads);
|
||||
static void term();
|
||||
static void request(Task* task);
|
||||
};
|
||||
|
||||
struct Task
|
||||
{
|
||||
private:
|
||||
mutex mtx;
|
||||
condition_variable cv;
|
||||
bool ready{true};
|
||||
bool pending{false};
|
||||
|
||||
public:
|
||||
virtual ~Task() = default;
|
||||
|
||||
void done()
|
||||
{
|
||||
if (!pending) return;
|
||||
|
||||
unique_lock<mutex> lock(mtx);
|
||||
while (!ready) cv.wait(lock);
|
||||
pending = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void run(unsigned tid) = 0;
|
||||
|
||||
private:
|
||||
void operator()(unsigned tid)
|
||||
{
|
||||
run(tid);
|
||||
|
||||
lock_guard<mutex> lock(mtx);
|
||||
ready = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void prepare()
|
||||
{
|
||||
ready = false;
|
||||
pending = true;
|
||||
}
|
||||
|
||||
friend class TaskSchedulerImpl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_TASK_SCHEDULER_H_
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory.h>
|
||||
#include <turbojpeg.h>
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgJpgLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void JpgLoader::clear()
|
||||
{
|
||||
if (freeData) free(data);
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
JpgLoader::JpgLoader()
|
||||
{
|
||||
jpegDecompressor = tjInitDecompress();
|
||||
}
|
||||
|
||||
|
||||
JpgLoader::~JpgLoader()
|
||||
{
|
||||
if (freeData) free(data);
|
||||
tjDestroy(jpegDecompressor);
|
||||
|
||||
//This image is shared with raster engine.
|
||||
tjFree(image);
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::open(const string& path)
|
||||
{
|
||||
clear();
|
||||
|
||||
auto jpegFile = fopen(path.c_str(), "rb");
|
||||
if (!jpegFile) return false;
|
||||
|
||||
auto ret = false;
|
||||
|
||||
//determine size
|
||||
if (fseek(jpegFile, 0, SEEK_END) < 0) goto finalize;
|
||||
if (((size = ftell(jpegFile)) < 1)) goto finalize;
|
||||
if (fseek(jpegFile, 0, SEEK_SET)) goto finalize;
|
||||
|
||||
data = (unsigned char *) malloc(size);
|
||||
if (!data) goto finalize;
|
||||
|
||||
freeData = true;
|
||||
|
||||
if (fread(data, size, 1, jpegFile) < 1) goto failure;
|
||||
|
||||
int width, height, subSample, colorSpace;
|
||||
if (tjDecompressHeader3(jpegDecompressor, data, size, &width, &height, &subSample, &colorSpace) < 0) {
|
||||
TVGERR("JPG LOADER", "%s", tjGetErrorStr());
|
||||
goto failure;
|
||||
}
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
ret = true;
|
||||
|
||||
goto finalize;
|
||||
|
||||
failure:
|
||||
clear();
|
||||
|
||||
finalize:
|
||||
fclose(jpegFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::open(const char* data, uint32_t size, bool copy)
|
||||
{
|
||||
clear();
|
||||
|
||||
int width, height, subSample, colorSpace;
|
||||
if (tjDecompressHeader3(jpegDecompressor, (unsigned char *) data, size, &width, &height, &subSample, &colorSpace) < 0) return false;
|
||||
|
||||
if (copy) {
|
||||
this->data = (unsigned char *) malloc(size);
|
||||
if (!this->data) return false;
|
||||
memcpy((unsigned char *)this->data, data, size);
|
||||
freeData = true;
|
||||
} else {
|
||||
this->data = (unsigned char *) data;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
this->size = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::read()
|
||||
{
|
||||
if (image) tjFree(image);
|
||||
image = (unsigned char *)tjAlloc(static_cast<int>(w) * static_cast<int>(h) * tjPixelSize[TJPF_BGRX]);
|
||||
if (!image) return false;
|
||||
|
||||
//decompress jpg image
|
||||
if (tjDecompress2(jpegDecompressor, data, size, image, static_cast<int>(w), 0, static_cast<int>(h), TJPF_BGRX, 0) < 0) {
|
||||
TVGERR("JPG LOADER", "%s", tjGetErrorStr());
|
||||
tjFree(image);
|
||||
image = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::close()
|
||||
{
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Surface> JpgLoader::bitmap()
|
||||
{
|
||||
if (!image) return nullptr;
|
||||
|
||||
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
|
||||
surface->buffer = (uint32_t*)(image);
|
||||
surface->stride = w;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = SwCanvas::ARGB8888;
|
||||
|
||||
return unique_ptr<Surface>(surface);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_JPG_LOADER_H_
|
||||
#define _TVG_JPG_LOADER_H_
|
||||
|
||||
using tjhandle = void*;
|
||||
|
||||
//TODO: Use Task?
|
||||
class JpgLoader : public LoadModule
|
||||
{
|
||||
public:
|
||||
JpgLoader();
|
||||
~JpgLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
||||
tjhandle jpegDecompressor;
|
||||
unsigned char* data = nullptr;
|
||||
unsigned char *image = nullptr;
|
||||
unsigned long size = 0;
|
||||
bool freeData = false;
|
||||
};
|
||||
|
||||
#endif //_TVG_JPG_LOADER_H_
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgPngLoader.h"
|
||||
|
||||
static inline uint32_t PREMULTIPLY(uint32_t c)
|
||||
{
|
||||
auto a = (c >> 24);
|
||||
return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
|
||||
}
|
||||
|
||||
|
||||
static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
|
||||
{
|
||||
auto buffer = data;
|
||||
for (uint32_t y = 0; y < h; ++y, buffer += w) {
|
||||
auto src = buffer;
|
||||
for (uint32_t x = 0; x < w; ++x, ++src) {
|
||||
*src = PREMULTIPLY(*src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PngLoader::PngLoader()
|
||||
{
|
||||
image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
|
||||
image->version = PNG_IMAGE_VERSION;
|
||||
image->opaque = NULL;
|
||||
}
|
||||
|
||||
PngLoader::~PngLoader()
|
||||
{
|
||||
if (content) {
|
||||
free((void*)content);
|
||||
content = nullptr;
|
||||
}
|
||||
free(image);
|
||||
}
|
||||
|
||||
bool PngLoader::open(const string& path)
|
||||
{
|
||||
image->opaque = NULL;
|
||||
|
||||
if (!png_image_begin_read_from_file(image, path.c_str())) return false;
|
||||
|
||||
w = (float)image->width;
|
||||
h = (float)image->height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PngLoader::open(const char* data, uint32_t size, bool copy)
|
||||
{
|
||||
image->opaque = NULL;
|
||||
|
||||
if (!png_image_begin_read_from_memory(image, data, size)) return false;
|
||||
|
||||
w = (float)image->width;
|
||||
h = (float)image->height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PngLoader::read()
|
||||
{
|
||||
png_bytep buffer;
|
||||
image->format = PNG_FORMAT_BGRA;
|
||||
buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));
|
||||
if (!buffer) {
|
||||
//out of memory, only time when libpng doesnt free its data
|
||||
png_image_free(image);
|
||||
return false;
|
||||
}
|
||||
if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) return false;
|
||||
content = reinterpret_cast<uint32_t*>(buffer);
|
||||
|
||||
_premultiply(reinterpret_cast<uint32_t*>(buffer), image->width, image->height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PngLoader::close()
|
||||
{
|
||||
png_image_free(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
unique_ptr<Surface> PngLoader::bitmap()
|
||||
{
|
||||
if (!content) return nullptr;
|
||||
|
||||
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
|
||||
surface->buffer = (uint32_t*)(content);
|
||||
surface->stride = w;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = SwCanvas::ARGB8888;
|
||||
|
||||
return unique_ptr<Surface>(surface);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_PNG_LOADER_H_
|
||||
#define _TVG_PNG_LOADER_H_
|
||||
|
||||
#include <png.h>
|
||||
|
||||
class PngLoader : public LoadModule
|
||||
{
|
||||
public:
|
||||
PngLoader();
|
||||
~PngLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
|
||||
private:
|
||||
png_imagep image = nullptr;
|
||||
const uint32_t* content = nullptr;
|
||||
};
|
||||
|
||||
#endif //_TVG_PNG_LOADER_H_
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory.h>
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgJpgLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void JpgLoader::clear()
|
||||
{
|
||||
jpgdDelete(decoder);
|
||||
if (freeData) free(data);
|
||||
decoder = nullptr;
|
||||
data = nullptr;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
JpgLoader::~JpgLoader()
|
||||
{
|
||||
jpgdDelete(decoder);
|
||||
if (freeData) free(data);
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::open(const string& path)
|
||||
{
|
||||
clear();
|
||||
|
||||
int width, height;
|
||||
decoder = jpgdHeader(path.c_str(), &width, &height);
|
||||
if (!decoder) return false;
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::open(const char* data, uint32_t size, bool copy)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (copy) {
|
||||
this->data = (char *) malloc(size);
|
||||
if (!this->data) return false;
|
||||
memcpy((char *)this->data, data, size);
|
||||
freeData = true;
|
||||
} else {
|
||||
this->data = (char *) data;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
decoder = jpgdHeader(this->data, size, &width, &height);
|
||||
if (!decoder) return false;
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool JpgLoader::read()
|
||||
{
|
||||
if (!decoder || w <= 0 || h <= 0) return false;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::close()
|
||||
{
|
||||
this->done();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Surface> JpgLoader::bitmap()
|
||||
{
|
||||
this->done();
|
||||
|
||||
if (!image) return nullptr;
|
||||
|
||||
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
|
||||
surface->buffer = (uint32_t*)(image);
|
||||
surface->stride = w;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = SwCanvas::ARGB8888;
|
||||
|
||||
return unique_ptr<Surface>(surface);
|
||||
}
|
||||
|
||||
|
||||
void JpgLoader::run(unsigned tid)
|
||||
{
|
||||
image = jpgdDecompress(decoder);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_JPG_LOADER_H_
|
||||
#define _TVG_JPG_LOADER_H_
|
||||
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgJpgd.h"
|
||||
|
||||
class JpgLoader : public LoadModule, public Task
|
||||
{
|
||||
private:
|
||||
jpeg_decoder* decoder = nullptr;
|
||||
char* data = nullptr;
|
||||
unsigned char *image = nullptr;
|
||||
bool freeData = false;
|
||||
|
||||
void clear();
|
||||
|
||||
public:
|
||||
~JpgLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
void run(unsigned tid) override;
|
||||
};
|
||||
|
||||
#endif //_TVG_JPG_LOADER_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
// jpgd.h - C++ class for JPEG decompression.
|
||||
// Public domain, Rich Geldreich <richgel99@gmail.com>
|
||||
#ifndef _TVG_JPGD_H_
|
||||
#define _TVG_JPGD_H_
|
||||
|
||||
class jpeg_decoder;
|
||||
|
||||
jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height);
|
||||
jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height);
|
||||
unsigned char* jpgdDecompress(jpeg_decoder* decoder);
|
||||
void jpgdDelete(jpeg_decoder* decoder);
|
||||
|
||||
#endif //_TVG_JPGD_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
LodePNG version 20200306
|
||||
|
||||
Copyright (c) 2005-2020 Lode Vandevenne
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_LODEPNG_H_
|
||||
#define _TVG_LODEPNG_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*The PNG color types (also used for raw image).*/
|
||||
enum LodePNGColorType
|
||||
{
|
||||
LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
|
||||
LCT_RGB = 2, /*RGB: 8,16 bit*/
|
||||
LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
|
||||
LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
|
||||
LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
|
||||
/*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
|
||||
byte value from 0 to 255 that could be present in an invalid PNG file header. Do
|
||||
not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
|
||||
the valid color type names above, or numeric values like 1 or 7 when checking for
|
||||
particular disallowed color type byte values, or cast to integer to print it.*/
|
||||
LCT_MAX_OCTET_VALUE = 255
|
||||
};
|
||||
|
||||
/*Settings for zlib decompression*/
|
||||
struct LodePNGDecompressSettings
|
||||
{
|
||||
/* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
|
||||
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
|
||||
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
|
||||
|
||||
/*use custom zlib decoder instead of built in one (default: null)*/
|
||||
unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
|
||||
/*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
|
||||
unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
|
||||
|
||||
const void* custom_context; /*optional custom settings for custom functions*/
|
||||
};
|
||||
|
||||
/*
|
||||
Color mode of an image. Contains all information required to decode the pixel
|
||||
bits to RGBA colors. This information is the same as used in the PNG file
|
||||
format, and is used both for PNG and raw image data in LodePNG.
|
||||
*/
|
||||
struct LodePNGColorMode
|
||||
{
|
||||
/*header (IHDR)*/
|
||||
LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
|
||||
unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
|
||||
|
||||
/*
|
||||
palette (PLTE and tRNS)
|
||||
|
||||
Dynamically allocated with the colors of the palette, including alpha.
|
||||
This field may not be allocated directly, use lodepng_color_mode_init first,
|
||||
then lodepng_palette_add per color to correctly initialize it (to ensure size
|
||||
of exactly 1024 bytes).
|
||||
|
||||
The alpha channels must be set as well, set them to 255 for opaque images.
|
||||
|
||||
When decoding, by default you can ignore this palette, since LodePNG already
|
||||
fills the palette colors in the pixels of the raw RGBA output.
|
||||
|
||||
The palette is only supported for color type 3.
|
||||
*/
|
||||
unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
|
||||
size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
|
||||
|
||||
/*
|
||||
transparent color key (tRNS)
|
||||
|
||||
This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
|
||||
For grayscale PNGs, r, g and b will all 3 be set to the same.
|
||||
|
||||
When decoding, by default you can ignore this information, since LodePNG sets
|
||||
pixels with this key to transparent already in the raw RGBA output.
|
||||
|
||||
The color key is only supported for color types 0 and 2.
|
||||
*/
|
||||
unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
|
||||
unsigned key_r; /*red/grayscale component of color key*/
|
||||
unsigned key_g; /*green component of color key*/
|
||||
unsigned key_b; /*blue component of color key*/
|
||||
};
|
||||
|
||||
/*Information about the PNG image, except pixels, width and height.*/
|
||||
struct LodePNGInfo
|
||||
{
|
||||
/*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
|
||||
unsigned compression_method;/*compression method of the original file. Always 0.*/
|
||||
unsigned filter_method; /*filter method of the original file*/
|
||||
unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
|
||||
LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
|
||||
};
|
||||
|
||||
/*
|
||||
Settings for the decoder. This contains settings for the PNG and the Zlib
|
||||
decoder, but not the Info settings from the Info structs.
|
||||
*/
|
||||
struct LodePNGDecoderSettings
|
||||
{
|
||||
LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
|
||||
|
||||
/* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
|
||||
unsigned ignore_crc; /*ignore CRC checksums*/
|
||||
unsigned ignore_critical; /*ignore unknown critical chunks*/
|
||||
unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
|
||||
/* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
|
||||
errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
|
||||
strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
|
||||
in string keys, etc... */
|
||||
|
||||
unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
|
||||
};
|
||||
|
||||
/*The settings, state and information for extended encoding and decoding.*/
|
||||
struct LodePNGState
|
||||
{
|
||||
LodePNGDecoderSettings decoder; /*the decoding settings*/
|
||||
LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
|
||||
LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
|
||||
unsigned error;
|
||||
};
|
||||
|
||||
void lodepng_state_init(LodePNGState* state);
|
||||
void lodepng_state_cleanup(LodePNGState* state);
|
||||
unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
|
||||
unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
|
||||
|
||||
#endif //_TVG_LODEPNG_H_
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <memory.h>
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgPngLoader.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
static inline uint32_t PREMULTIPLY(uint32_t c)
|
||||
{
|
||||
auto a = (c >> 24);
|
||||
return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
|
||||
}
|
||||
|
||||
|
||||
static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
|
||||
{
|
||||
auto buffer = data;
|
||||
for (uint32_t y = 0; y < h; ++y, buffer += w) {
|
||||
auto src = buffer;
|
||||
for (uint32_t x = 0; x < w; ++x, ++src) {
|
||||
*src = PREMULTIPLY(*src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PngLoader::clear()
|
||||
{
|
||||
lodepng_state_cleanup(&state);
|
||||
|
||||
if (freeData) free(data);
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
PngLoader::PngLoader()
|
||||
{
|
||||
lodepng_state_init(&state);
|
||||
}
|
||||
|
||||
|
||||
PngLoader::~PngLoader()
|
||||
{
|
||||
if (freeData) free(data);
|
||||
}
|
||||
|
||||
|
||||
bool PngLoader::open(const string& path)
|
||||
{
|
||||
clear();
|
||||
|
||||
auto pngFile = fopen(path.c_str(), "rb");
|
||||
if (!pngFile) return false;
|
||||
|
||||
auto ret = false;
|
||||
|
||||
//determine size
|
||||
if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize;
|
||||
if (((size = ftell(pngFile)) < 1)) goto finalize;
|
||||
if (fseek(pngFile, 0, SEEK_SET)) goto finalize;
|
||||
|
||||
data = (unsigned char *) malloc(size);
|
||||
if (!data) goto finalize;
|
||||
|
||||
freeData = true;
|
||||
|
||||
if (fread(data, size, 1, pngFile) < 1) goto failure;
|
||||
|
||||
lodepng_state_init(&state);
|
||||
|
||||
unsigned int width, height;
|
||||
if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto failure;
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
ret = true;
|
||||
|
||||
goto finalize;
|
||||
|
||||
failure:
|
||||
clear();
|
||||
|
||||
finalize:
|
||||
fclose(pngFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PngLoader::open(const char* data, uint32_t size, bool copy)
|
||||
{
|
||||
clear();
|
||||
|
||||
lodepng_state_init(&state);
|
||||
|
||||
unsigned int width, height;
|
||||
if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false;
|
||||
|
||||
if (copy) {
|
||||
this->data = (unsigned char *) malloc(size);
|
||||
if (!this->data) return false;
|
||||
memcpy((unsigned char *)this->data, data, size);
|
||||
freeData = true;
|
||||
} else {
|
||||
this->data = (unsigned char *) data;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
this->size = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PngLoader::read()
|
||||
{
|
||||
if (!data || w <= 0 || h <= 0) return false;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PngLoader::close()
|
||||
{
|
||||
this->done();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Surface> PngLoader::bitmap()
|
||||
{
|
||||
this->done();
|
||||
|
||||
if (!image) return nullptr;
|
||||
|
||||
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
|
||||
surface->buffer = (uint32_t*)(image);
|
||||
surface->stride = w;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = SwCanvas::ARGB8888;
|
||||
|
||||
return unique_ptr<Surface>(surface);
|
||||
}
|
||||
|
||||
|
||||
void PngLoader::run(unsigned tid)
|
||||
{
|
||||
auto width = static_cast<unsigned>(w);
|
||||
auto height = static_cast<unsigned>(h);
|
||||
|
||||
lodepng_decode(&image, &width, &height, &state, data, size);
|
||||
|
||||
_premultiply((uint32_t*)(image), width, height);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_PNG_LOADER_H_
|
||||
#define _TVG_PNG_LOADER_H_
|
||||
|
||||
#include "tvgLodePng.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
|
||||
class PngLoader : public LoadModule, public Task
|
||||
{
|
||||
private:
|
||||
LodePNGState state;
|
||||
unsigned char* data = nullptr;
|
||||
unsigned char *image = nullptr;
|
||||
unsigned long size = 0;
|
||||
bool freeData = false;
|
||||
|
||||
void clear();
|
||||
|
||||
public:
|
||||
PngLoader();
|
||||
~PngLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
void run(unsigned tid) override;
|
||||
};
|
||||
|
||||
#endif //_TVG_PNG_LOADER_H_
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <fstream>
|
||||
#include <string.h>
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgRawLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RawLoader::~RawLoader()
|
||||
{
|
||||
if (copy && content) {
|
||||
free((void*)content);
|
||||
content = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy)
|
||||
{
|
||||
if (!data || w == 0 || h == 0) return false;
|
||||
|
||||
this->w = (float)w;
|
||||
this->h = (float)h;
|
||||
this->copy = copy;
|
||||
|
||||
if (copy) {
|
||||
content = (uint32_t*)malloc(sizeof(uint32_t) * w * h);
|
||||
if (!content) return false;
|
||||
memcpy((void*)content, data, sizeof(uint32_t) * w * h);
|
||||
}
|
||||
else content = data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RawLoader::read()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RawLoader::close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Surface> RawLoader::bitmap()
|
||||
{
|
||||
if (!content) return nullptr;
|
||||
|
||||
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
|
||||
surface->buffer = (uint32_t*)(content);
|
||||
surface->stride = w;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = SwCanvas::ARGB8888;
|
||||
|
||||
return unique_ptr<Surface>(surface);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_RAW_LOADER_H_
|
||||
#define _TVG_RAW_LOADER_H_
|
||||
|
||||
class RawLoader : public LoadModule
|
||||
{
|
||||
public:
|
||||
const uint32_t* content = nullptr;
|
||||
bool copy = false;
|
||||
|
||||
~RawLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_RAW_LOADER_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SVG_LOADER_H_
|
||||
#define _TVG_SVG_LOADER_H_
|
||||
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
|
||||
class SvgLoader : public LoadModule, public Task
|
||||
{
|
||||
public:
|
||||
string filePath;
|
||||
string svgPath = "";
|
||||
const char* content = nullptr;
|
||||
uint32_t size = 0;
|
||||
|
||||
SvgLoaderData loaderData;
|
||||
unique_ptr<Scene> root;
|
||||
|
||||
bool copy = false;
|
||||
|
||||
SvgLoader();
|
||||
~SvgLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool resize(Paint* paint, float w, float h) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
unique_ptr<Paint> paint() override;
|
||||
|
||||
private:
|
||||
bool header();
|
||||
void clear();
|
||||
void run(unsigned tid) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_SVG_LOADER_H_
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_SVG_LOADER_COMMON_H_
|
||||
#define _TVG_SVG_LOADER_COMMON_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
|
||||
struct SvgNode;
|
||||
struct SvgStyleGradient;
|
||||
|
||||
//NOTE: Please update simpleXmlNodeTypeToString() as well.
|
||||
enum class SvgNodeType
|
||||
{
|
||||
Doc,
|
||||
G,
|
||||
Defs,
|
||||
Animation,
|
||||
Arc,
|
||||
Circle,
|
||||
Ellipse,
|
||||
Image,
|
||||
Line,
|
||||
Path,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Rect,
|
||||
Text,
|
||||
TextArea,
|
||||
Tspan,
|
||||
Use,
|
||||
Video,
|
||||
ClipPath,
|
||||
Mask,
|
||||
Unknown
|
||||
};
|
||||
|
||||
/*
|
||||
// TODO - remove?
|
||||
enum class SvgLengthType
|
||||
{
|
||||
Percent,
|
||||
Px,
|
||||
Pc,
|
||||
Pt,
|
||||
Mm,
|
||||
Cm,
|
||||
In,
|
||||
};
|
||||
*/
|
||||
|
||||
enum class SvgFillFlags
|
||||
{
|
||||
Paint = 0x01,
|
||||
Opacity = 0x02,
|
||||
Gradient = 0x04,
|
||||
FillRule = 0x08,
|
||||
ClipPath = 0x16
|
||||
};
|
||||
|
||||
enum class SvgStrokeFlags
|
||||
{
|
||||
Paint = 0x1,
|
||||
Opacity = 0x2,
|
||||
Gradient = 0x4,
|
||||
Scale = 0x8,
|
||||
Width = 0x10,
|
||||
Cap = 0x20,
|
||||
Join = 0x40,
|
||||
Dash = 0x80,
|
||||
};
|
||||
|
||||
enum class SvgGradientType
|
||||
{
|
||||
Linear,
|
||||
Radial
|
||||
};
|
||||
|
||||
enum class SvgStyleFlags
|
||||
{
|
||||
Color = 0x01,
|
||||
Fill = 0x02,
|
||||
FillRule = 0x04,
|
||||
FillOpacity = 0x08,
|
||||
Opacity = 0x010,
|
||||
Stroke = 0x20,
|
||||
StrokeWidth = 0x40,
|
||||
StrokeLineJoin = 0x80,
|
||||
StrokeLineCap = 0x100,
|
||||
StrokeOpacity = 0x200,
|
||||
StrokeDashArray = 0x400,
|
||||
Transform = 0x800,
|
||||
ClipPath = 0x1000,
|
||||
Mask = 0x2000,
|
||||
Display = 0x4000
|
||||
};
|
||||
|
||||
enum class SvgStopStyleFlags
|
||||
{
|
||||
StopDefault = 0x0,
|
||||
StopOpacity = 0x01,
|
||||
StopColor = 0x02
|
||||
};
|
||||
|
||||
enum class SvgFillRule
|
||||
{
|
||||
Winding = 0,
|
||||
OddEven = 1
|
||||
};
|
||||
|
||||
//Length type to recalculate %, pt, pc, mm, cm etc
|
||||
enum class SvgParserLengthType
|
||||
{
|
||||
Vertical,
|
||||
Horizontal,
|
||||
//In case of, for example, radius of radial gradient
|
||||
Other
|
||||
};
|
||||
|
||||
struct SvgDocNode
|
||||
{
|
||||
float w;
|
||||
float h;
|
||||
float vx;
|
||||
float vy;
|
||||
float vw;
|
||||
float vh;
|
||||
SvgNode* defs;
|
||||
bool preserveAspect;
|
||||
};
|
||||
|
||||
struct SvgGNode
|
||||
{
|
||||
};
|
||||
|
||||
struct SvgDefsNode
|
||||
{
|
||||
Array<SvgStyleGradient*> gradients;
|
||||
};
|
||||
|
||||
struct SvgUseNode
|
||||
{
|
||||
float x, y, w, h;
|
||||
};
|
||||
|
||||
|
||||
struct SvgEllipseNode
|
||||
{
|
||||
float cx;
|
||||
float cy;
|
||||
float rx;
|
||||
float ry;
|
||||
};
|
||||
|
||||
struct SvgCircleNode
|
||||
{
|
||||
float cx;
|
||||
float cy;
|
||||
float r;
|
||||
};
|
||||
|
||||
struct SvgRectNode
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
float rx;
|
||||
float ry;
|
||||
bool hasRx;
|
||||
bool hasRy;
|
||||
};
|
||||
|
||||
struct SvgLineNode
|
||||
{
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
};
|
||||
|
||||
struct SvgImageNode
|
||||
{
|
||||
float x, y, w, h;
|
||||
char* href;
|
||||
};
|
||||
|
||||
struct SvgPathNode
|
||||
{
|
||||
char* path;
|
||||
};
|
||||
|
||||
struct SvgPolygonNode
|
||||
{
|
||||
int pointsCount;
|
||||
float* points;
|
||||
};
|
||||
|
||||
struct SvgCompositeNode
|
||||
{
|
||||
bool userSpace;
|
||||
};
|
||||
|
||||
struct SvgLinearGradient
|
||||
{
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
bool isX1Percentage;
|
||||
bool isY1Percentage;
|
||||
bool isX2Percentage;
|
||||
bool isY2Percentage;
|
||||
};
|
||||
|
||||
struct SvgRadialGradient
|
||||
{
|
||||
float cx;
|
||||
float cy;
|
||||
float fx;
|
||||
float fy;
|
||||
float r;
|
||||
bool isCxPercentage;
|
||||
bool isCyPercentage;
|
||||
bool isFxPercentage;
|
||||
bool isFyPercentage;
|
||||
bool isRPercentage;
|
||||
};
|
||||
|
||||
struct SvgComposite
|
||||
{
|
||||
char *url;
|
||||
SvgNode* node;
|
||||
bool applying; //flag for checking circular dependency.
|
||||
};
|
||||
|
||||
struct SvgColor
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
struct SvgPaint
|
||||
{
|
||||
SvgStyleGradient* gradient;
|
||||
char *url;
|
||||
SvgColor color;
|
||||
bool none;
|
||||
bool curColor;
|
||||
};
|
||||
|
||||
struct SvgDash
|
||||
{
|
||||
Array<float> array;
|
||||
};
|
||||
|
||||
struct SvgStyleGradient
|
||||
{
|
||||
SvgGradientType type;
|
||||
char* id;
|
||||
char* ref;
|
||||
FillSpread spread;
|
||||
SvgRadialGradient* radial;
|
||||
SvgLinearGradient* linear;
|
||||
Matrix* transform;
|
||||
Array<Fill::ColorStop> stops;
|
||||
bool userSpace;
|
||||
|
||||
void clear()
|
||||
{
|
||||
stops.reset();
|
||||
free(transform);
|
||||
free(radial);
|
||||
free(linear);
|
||||
free(ref);
|
||||
free(id);
|
||||
}
|
||||
};
|
||||
|
||||
struct SvgStyleFill
|
||||
{
|
||||
SvgFillFlags flags;
|
||||
SvgPaint paint;
|
||||
int opacity;
|
||||
FillRule fillRule;
|
||||
};
|
||||
|
||||
struct SvgStyleStroke
|
||||
{
|
||||
SvgStrokeFlags flags;
|
||||
SvgPaint paint;
|
||||
int opacity;
|
||||
float scale;
|
||||
float width;
|
||||
float centered;
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
SvgDash dash;
|
||||
int dashCount;
|
||||
};
|
||||
|
||||
struct SvgStyleProperty
|
||||
{
|
||||
SvgStyleFill fill;
|
||||
SvgStyleStroke stroke;
|
||||
SvgComposite clipPath;
|
||||
SvgComposite mask;
|
||||
int opacity;
|
||||
SvgColor color;
|
||||
bool curColorSet;
|
||||
SvgStyleFlags flags;
|
||||
};
|
||||
|
||||
struct SvgNode
|
||||
{
|
||||
SvgNodeType type;
|
||||
SvgNode* parent;
|
||||
Array<SvgNode*> child;
|
||||
char *id;
|
||||
SvgStyleProperty *style;
|
||||
Matrix* transform;
|
||||
union {
|
||||
SvgGNode g;
|
||||
SvgDocNode doc;
|
||||
SvgDefsNode defs;
|
||||
SvgUseNode use;
|
||||
SvgCircleNode circle;
|
||||
SvgEllipseNode ellipse;
|
||||
SvgPolygonNode polygon;
|
||||
SvgPolygonNode polyline;
|
||||
SvgRectNode rect;
|
||||
SvgPathNode path;
|
||||
SvgLineNode line;
|
||||
SvgImageNode image;
|
||||
SvgCompositeNode comp;
|
||||
} node;
|
||||
bool display;
|
||||
~SvgNode();
|
||||
};
|
||||
|
||||
struct SvgParser
|
||||
{
|
||||
SvgNode* node;
|
||||
SvgStyleGradient* styleGrad;
|
||||
Fill::ColorStop gradStop;
|
||||
SvgStopStyleFlags flags;
|
||||
struct
|
||||
{
|
||||
int x, y;
|
||||
uint32_t w, h;
|
||||
} global;
|
||||
struct
|
||||
{
|
||||
bool parsedFx;
|
||||
bool parsedFy;
|
||||
} gradient;
|
||||
};
|
||||
|
||||
struct SvgNodeIdPair
|
||||
{
|
||||
SvgNode* node;
|
||||
char *id;
|
||||
};
|
||||
|
||||
struct SvgLoaderData
|
||||
{
|
||||
Array<SvgNode *> stack = {nullptr, 0, 0};
|
||||
SvgNode* doc = nullptr;
|
||||
SvgNode* def = nullptr;
|
||||
Array<SvgStyleGradient*> gradients;
|
||||
SvgStyleGradient* latestGradient = nullptr; //For stops
|
||||
SvgParser* svgParse = nullptr;
|
||||
Array<SvgNodeIdPair> cloneNodes;
|
||||
int level = 0;
|
||||
bool result = false;
|
||||
};
|
||||
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
|
||||
*
|
||||
* src should be one of the following form :
|
||||
*
|
||||
* [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
|
||||
* [whitespace] [sign] {INF | INFINITY}
|
||||
* [whitespace] [sign] NAN [sequence]
|
||||
*
|
||||
* No hexadecimal form supported
|
||||
* no sequence supported after NAN
|
||||
*/
|
||||
float customStrtof(const char *nptr, char **endptr);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,563 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright notice for the EFL:
|
||||
|
||||
* Copyright (C) EFL developers (see AUTHORS)
|
||||
|
||||
* All rights reserved.
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
|
||||
|
||||
#include <math.h>
|
||||
#include <clocale>
|
||||
#include <ctype.h>
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgPath.h"
|
||||
#include "tvgSvgUtil.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static char* _skipComma(const char* content)
|
||||
{
|
||||
while (*content && isspace(*content)) {
|
||||
content++;
|
||||
}
|
||||
if (*content == ',') return (char*)content + 1;
|
||||
return (char*)content;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseNumber(char** content, float* number)
|
||||
{
|
||||
char* end = NULL;
|
||||
*number = svgUtilStrtof(*content, &end);
|
||||
//If the start of string is not number
|
||||
if ((*content) == end) return false;
|
||||
//Skip comma if any
|
||||
*content = _skipComma(end);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseFlag(char** content, int* number)
|
||||
{
|
||||
char* end = NULL;
|
||||
if (*(*content) != '0' && *(*content) != '1') return false;
|
||||
*number = *(*content) - '0';
|
||||
*content += 1;
|
||||
end = *content;
|
||||
*content = _skipComma(end);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, Point* curCtl, float x, float y, float rx, float ry, float angle, bool largeArc, bool sweep)
|
||||
{
|
||||
float cxp, cyp, cx, cy;
|
||||
float sx, sy;
|
||||
float cosPhi, sinPhi;
|
||||
float dx2, dy2;
|
||||
float x1p, y1p;
|
||||
float x1p2, y1p2;
|
||||
float rx2, ry2;
|
||||
float lambda;
|
||||
float c;
|
||||
float at;
|
||||
float theta1, deltaTheta;
|
||||
float nat;
|
||||
float delta, bcp;
|
||||
float cosPhiRx, cosPhiRy;
|
||||
float sinPhiRx, sinPhiRy;
|
||||
float cosTheta1, sinTheta1;
|
||||
int segments;
|
||||
|
||||
//Some helpful stuff is available here:
|
||||
//http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
sx = cur->x;
|
||||
sy = cur->y;
|
||||
|
||||
//If start and end points are identical, then no arc is drawn
|
||||
if ((fabsf(x - sx) < (1.0f / 256.0f)) && (fabsf(y - sy) < (1.0f / 256.0f))) return;
|
||||
|
||||
//Correction of out-of-range radii, see F6.6.1 (step 2)
|
||||
rx = fabsf(rx);
|
||||
ry = fabsf(ry);
|
||||
|
||||
angle = angle * M_PI / 180.0f;
|
||||
cosPhi = cosf(angle);
|
||||
sinPhi = sinf(angle);
|
||||
dx2 = (sx - x) / 2.0f;
|
||||
dy2 = (sy - y) / 2.0f;
|
||||
x1p = cosPhi * dx2 + sinPhi * dy2;
|
||||
y1p = cosPhi * dy2 - sinPhi * dx2;
|
||||
x1p2 = x1p * x1p;
|
||||
y1p2 = y1p * y1p;
|
||||
rx2 = rx * rx;
|
||||
ry2 = ry * ry;
|
||||
lambda = (x1p2 / rx2) + (y1p2 / ry2);
|
||||
|
||||
//Correction of out-of-range radii, see F6.6.2 (step 4)
|
||||
if (lambda > 1.0f) {
|
||||
//See F6.6.3
|
||||
float lambdaRoot = sqrtf(lambda);
|
||||
|
||||
rx *= lambdaRoot;
|
||||
ry *= lambdaRoot;
|
||||
//Update rx2 and ry2
|
||||
rx2 = rx * rx;
|
||||
ry2 = ry * ry;
|
||||
}
|
||||
|
||||
c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
|
||||
|
||||
//Check if there is no possible solution
|
||||
//(i.e. we can't do a square root of a negative value)
|
||||
if (c < 0.0f) {
|
||||
//Scale uniformly until we have a single solution
|
||||
//(see F6.2) i.e. when c == 0.0
|
||||
float scale = sqrtf(1.0f - c / (rx2 * ry2));
|
||||
rx *= scale;
|
||||
ry *= scale;
|
||||
//Update rx2 and ry2
|
||||
rx2 = rx * rx;
|
||||
ry2 = ry * ry;
|
||||
|
||||
//Step 2 (F6.5.2) - simplified since c == 0.0
|
||||
cxp = 0.0f;
|
||||
cyp = 0.0f;
|
||||
//Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
|
||||
cx = 0.0f;
|
||||
cy = 0.0f;
|
||||
} else {
|
||||
//Complete c calculation
|
||||
c = sqrtf(c / ((rx2 * y1p2) + (ry2 * x1p2)));
|
||||
//Inverse sign if Fa == Fs
|
||||
if (largeArc == sweep) c = -c;
|
||||
|
||||
//Step 2 (F6.5.2)
|
||||
cxp = c * (rx * y1p / ry);
|
||||
cyp = c * (-ry * x1p / rx);
|
||||
|
||||
//Step 3 (F6.5.3 first part)
|
||||
cx = cosPhi * cxp - sinPhi * cyp;
|
||||
cy = sinPhi * cxp + cosPhi * cyp;
|
||||
}
|
||||
|
||||
//Step 3 (F6.5.3 second part) we now have the center point of the ellipse
|
||||
cx += (sx + x) / 2.0f;
|
||||
cy += (sy + y) / 2.0f;
|
||||
|
||||
//Sstep 4 (F6.5.4)
|
||||
//We dont' use arccos (as per w3c doc), see
|
||||
//http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
|
||||
//Note: atan2 (0.0, 1.0) == 0.0
|
||||
at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
|
||||
theta1 = (at < 0.0f) ? 2.0f * M_PI + at : at;
|
||||
|
||||
nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
|
||||
deltaTheta = (nat < at) ? 2.0f * M_PI - at + nat : nat - at;
|
||||
|
||||
if (sweep) {
|
||||
//Ensure delta theta < 0 or else add 360 degrees
|
||||
if (deltaTheta < 0.0f) deltaTheta += (float)(2.0f * M_PI);
|
||||
} else {
|
||||
//Ensure delta theta > 0 or else substract 360 degrees
|
||||
if (deltaTheta > 0.0f) deltaTheta -= (float)(2.0f * M_PI);
|
||||
}
|
||||
|
||||
//Add several cubic bezier to approximate the arc
|
||||
//(smaller than 90 degrees)
|
||||
//We add one extra segment because we want something
|
||||
//Smaller than 90deg (i.e. not 90 itself)
|
||||
segments = static_cast<int>(fabsf(deltaTheta / float(M_PI_2)) + 1.0f);
|
||||
delta = deltaTheta / segments;
|
||||
|
||||
//http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
|
||||
bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f);
|
||||
|
||||
cosPhiRx = cosPhi * rx;
|
||||
cosPhiRy = cosPhi * ry;
|
||||
sinPhiRx = sinPhi * rx;
|
||||
sinPhiRy = sinPhi * ry;
|
||||
|
||||
cosTheta1 = cosf(theta1);
|
||||
sinTheta1 = sinf(theta1);
|
||||
|
||||
for (int i = 0; i < segments; ++i) {
|
||||
//End angle (for this segment) = current + delta
|
||||
float c1x, c1y, ex, ey, c2x, c2y;
|
||||
float theta2 = theta1 + delta;
|
||||
float cosTheta2 = cosf(theta2);
|
||||
float sinTheta2 = sinf(theta2);
|
||||
Point p[3];
|
||||
|
||||
//First control point (based on start point sx,sy)
|
||||
c1x = sx - bcp * (cosPhiRx * sinTheta1 + sinPhiRy * cosTheta1);
|
||||
c1y = sy + bcp * (cosPhiRy * cosTheta1 - sinPhiRx * sinTheta1);
|
||||
|
||||
//End point (for this segment)
|
||||
ex = cx + (cosPhiRx * cosTheta2 - sinPhiRy * sinTheta2);
|
||||
ey = cy + (sinPhiRx * cosTheta2 + cosPhiRy * sinTheta2);
|
||||
|
||||
//Second control point (based on end point ex,ey)
|
||||
c2x = ex + bcp * (cosPhiRx * sinTheta2 + sinPhiRy * cosTheta2);
|
||||
c2y = ey + bcp * (sinPhiRx * sinTheta2 - cosPhiRy * cosTheta2);
|
||||
cmds->push(PathCommand::CubicTo);
|
||||
p[0] = {c1x, c1y};
|
||||
p[1] = {c2x, c2y};
|
||||
p[2] = {ex, ey};
|
||||
pts->push(p[0]);
|
||||
pts->push(p[1]);
|
||||
pts->push(p[2]);
|
||||
*curCtl = p[1];
|
||||
*cur = p[2];
|
||||
|
||||
//Next start point is the current end point (same for angle)
|
||||
sx = ex;
|
||||
sy = ey;
|
||||
theta1 = theta2;
|
||||
//Avoid recomputations
|
||||
cosTheta1 = cosTheta2;
|
||||
sinTheta1 = sinTheta2;
|
||||
}
|
||||
}
|
||||
|
||||
static int _numberCount(char cmd)
|
||||
{
|
||||
int count = 0;
|
||||
switch (cmd) {
|
||||
case 'M':
|
||||
case 'm':
|
||||
case 'L':
|
||||
case 'l':
|
||||
case 'T':
|
||||
case 't': {
|
||||
count = 2;
|
||||
break;
|
||||
}
|
||||
case 'C':
|
||||
case 'c':
|
||||
case 'E':
|
||||
case 'e': {
|
||||
count = 6;
|
||||
break;
|
||||
}
|
||||
case 'H':
|
||||
case 'h':
|
||||
case 'V':
|
||||
case 'v': {
|
||||
count = 1;
|
||||
break;
|
||||
}
|
||||
case 'S':
|
||||
case 's':
|
||||
case 'Q':
|
||||
case 'q': {
|
||||
count = 4;
|
||||
break;
|
||||
}
|
||||
case 'A':
|
||||
case 'a': {
|
||||
count = 7;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic)
|
||||
{
|
||||
switch (cmd) {
|
||||
case 'm':
|
||||
case 'l':
|
||||
case 'c':
|
||||
case 's':
|
||||
case 'q':
|
||||
case 't': {
|
||||
for (int i = 0; i < count - 1; i += 2) {
|
||||
arr[i] = arr[i] + cur->x;
|
||||
arr[i + 1] = arr[i + 1] + cur->y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
arr[0] = arr[0] + cur->x;
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
arr[0] = arr[0] + cur->y;
|
||||
break;
|
||||
}
|
||||
case 'a': {
|
||||
arr[5] = arr[5] + cur->x;
|
||||
arr[6] = arr[6] + cur->y;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case 'm':
|
||||
case 'M': {
|
||||
Point p = {arr[0], arr[1]};
|
||||
cmds->push(PathCommand::MoveTo);
|
||||
pts->push(p);
|
||||
*cur = {arr[0], arr[1]};
|
||||
*startPoint = {arr[0], arr[1]};
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
case 'L': {
|
||||
Point p = {arr[0], arr[1]};
|
||||
cmds->push(PathCommand::LineTo);
|
||||
pts->push(p);
|
||||
*cur = {arr[0], arr[1]};
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
case 'C': {
|
||||
Point p[3];
|
||||
cmds->push(PathCommand::CubicTo);
|
||||
p[0] = {arr[0], arr[1]};
|
||||
p[1] = {arr[2], arr[3]};
|
||||
p[2] = {arr[4], arr[5]};
|
||||
pts->push(p[0]);
|
||||
pts->push(p[1]);
|
||||
pts->push(p[2]);
|
||||
*curCtl = p[1];
|
||||
*cur = p[2];
|
||||
*isQuadratic = false;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
case 'S': {
|
||||
Point p[3], ctrl;
|
||||
if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
|
||||
!(*isQuadratic)) {
|
||||
ctrl.x = 2 * cur->x - curCtl->x;
|
||||
ctrl.y = 2 * cur->y - curCtl->y;
|
||||
} else {
|
||||
ctrl = *cur;
|
||||
}
|
||||
cmds->push(PathCommand::CubicTo);
|
||||
p[0] = ctrl;
|
||||
p[1] = {arr[0], arr[1]};
|
||||
p[2] = {arr[2], arr[3]};
|
||||
pts->push(p[0]);
|
||||
pts->push(p[1]);
|
||||
pts->push(p[2]);
|
||||
*curCtl = p[1];
|
||||
*cur = p[2];
|
||||
*isQuadratic = false;
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
case 'Q': {
|
||||
Point p[3];
|
||||
float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0 / 3.0);
|
||||
float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0 / 3.0);
|
||||
float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0 / 3.0);
|
||||
float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0 / 3.0);
|
||||
cmds->push(PathCommand::CubicTo);
|
||||
p[0] = {ctrl_x0, ctrl_y0};
|
||||
p[1] = {ctrl_x1, ctrl_y1};
|
||||
p[2] = {arr[2], arr[3]};
|
||||
pts->push(p[0]);
|
||||
pts->push(p[1]);
|
||||
pts->push(p[2]);
|
||||
*curCtl = {arr[0], arr[1]};
|
||||
*cur = p[2];
|
||||
*isQuadratic = true;
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
case 'T': {
|
||||
Point p[3], ctrl;
|
||||
if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
|
||||
*isQuadratic) {
|
||||
ctrl.x = 2 * cur->x - curCtl->x;
|
||||
ctrl.y = 2 * cur->y - curCtl->y;
|
||||
} else {
|
||||
ctrl = *cur;
|
||||
}
|
||||
float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0 / 3.0);
|
||||
float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0 / 3.0);
|
||||
float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0 / 3.0);
|
||||
float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0 / 3.0);
|
||||
cmds->push(PathCommand::CubicTo);
|
||||
p[0] = {ctrl_x0, ctrl_y0};
|
||||
p[1] = {ctrl_x1, ctrl_y1};
|
||||
p[2] = {arr[0], arr[1]};
|
||||
pts->push(p[0]);
|
||||
pts->push(p[1]);
|
||||
pts->push(p[2]);
|
||||
*curCtl = {ctrl.x, ctrl.y};
|
||||
*cur = p[2];
|
||||
*isQuadratic = true;
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
case 'H': {
|
||||
Point p = {arr[0], cur->y};
|
||||
cmds->push(PathCommand::LineTo);
|
||||
pts->push(p);
|
||||
cur->x = arr[0];
|
||||
break;
|
||||
}
|
||||
case 'v':
|
||||
case 'V': {
|
||||
Point p = {cur->x, arr[0]};
|
||||
cmds->push(PathCommand::LineTo);
|
||||
pts->push(p);
|
||||
cur->y = arr[0];
|
||||
break;
|
||||
}
|
||||
case 'z':
|
||||
case 'Z': {
|
||||
cmds->push(PathCommand::Close);
|
||||
*cur = *startPoint;
|
||||
break;
|
||||
}
|
||||
case 'a':
|
||||
case 'A': {
|
||||
_pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]);
|
||||
*cur = *curCtl = {arr[5], arr[6]};
|
||||
*isQuadratic = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
|
||||
{
|
||||
int large, sweep;
|
||||
|
||||
path = _skipComma(path);
|
||||
if (isalpha(*path)) {
|
||||
*cmd = *path;
|
||||
path++;
|
||||
*count = _numberCount(*cmd);
|
||||
} else {
|
||||
if (*cmd == 'm') *cmd = 'l';
|
||||
else if (*cmd == 'M') *cmd = 'L';
|
||||
}
|
||||
if (*count == 7) {
|
||||
//Special case for arc command
|
||||
if (_parseNumber(&path, &arr[0])) {
|
||||
if (_parseNumber(&path, &arr[1])) {
|
||||
if (_parseNumber(&path, &arr[2])) {
|
||||
if (_parseFlag(&path, &large)) {
|
||||
if (_parseFlag(&path, &sweep)) {
|
||||
if (_parseNumber(&path, &arr[5])) {
|
||||
if (_parseNumber(&path, &arr[6])) {
|
||||
arr[3] = (float)large;
|
||||
arr[4] = (float)sweep;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < *count; i++) {
|
||||
if (!_parseNumber(&path, &arr[i])) {
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
path = _skipComma(path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts)
|
||||
{
|
||||
float numberArray[7];
|
||||
int numberCount = 0;
|
||||
Point cur = { 0, 0 };
|
||||
Point curCtl = { 0, 0 };
|
||||
Point startPoint = { 0, 0 };
|
||||
char cmd = 0;
|
||||
bool isQuadratic = false;
|
||||
char* path = (char*)svgPath;
|
||||
char* curLocale;
|
||||
|
||||
curLocale = setlocale(LC_NUMERIC, NULL);
|
||||
if (curLocale) curLocale = strdup(curLocale);
|
||||
setlocale(LC_NUMERIC, "POSIX");
|
||||
|
||||
while ((path[0] != '\0')) {
|
||||
path = _nextCommand(path, &cmd, numberArray, &numberCount);
|
||||
if (!path) break;
|
||||
if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
|
||||
}
|
||||
|
||||
setlocale(LC_NUMERIC, curLocale);
|
||||
if (curLocale) free(curLocale);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SVG_PATH_H_
|
||||
#define _TVG_SVG_PATH_H_
|
||||
|
||||
#include <tvgCommon.h>
|
||||
|
||||
bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts);
|
||||
|
||||
#endif //_TVG_SVG_PATH_H_
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright notice for the EFL:
|
||||
|
||||
* Copyright (C) EFL developers (see AUTHORS)
|
||||
|
||||
* All rights reserved.
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <string>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgSceneBuilder.h"
|
||||
#include "tvgSvgPath.h"
|
||||
#include "tvgSvgUtil.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct Box
|
||||
{
|
||||
float x, y, w, h;
|
||||
};
|
||||
|
||||
|
||||
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
|
||||
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask);
|
||||
|
||||
|
||||
static inline bool _isGroupType(SvgNodeType type)
|
||||
{
|
||||
if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph)
|
||||
//a stroke width should be ignored for bounding box calculations
|
||||
static Box _boundingBox(const Shape* shape)
|
||||
{
|
||||
float x, y, w, h;
|
||||
shape->bounds(&x, &y, &w, &h, false);
|
||||
|
||||
if (auto strokeW = shape->strokeWidth()) {
|
||||
x += 0.5f * strokeW;
|
||||
y += 0.5f * strokeW;
|
||||
w -= strokeW;
|
||||
h -= strokeW;
|
||||
}
|
||||
|
||||
return {x, y, w, h};
|
||||
}
|
||||
|
||||
|
||||
static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf)
|
||||
{
|
||||
gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13;
|
||||
gradTransf->e12 *= mBBox->e11;
|
||||
gradTransf->e11 *= mBBox->e11;
|
||||
|
||||
gradTransf->e23 = gradTransf->e23 * mBBox->e22 + mBBox->e23;
|
||||
gradTransf->e22 *= mBBox->e22;
|
||||
gradTransf->e21 *= mBBox->e22;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity)
|
||||
{
|
||||
Fill::ColorStop* stops;
|
||||
int stopCount = 0;
|
||||
auto fillGrad = LinearGradient::gen();
|
||||
|
||||
bool isTransform = (g->transform ? true : false);
|
||||
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if (isTransform) finalTransform = *g->transform;
|
||||
|
||||
if (g->userSpace) {
|
||||
g->linear->x1 = g->linear->x1 * vBox.w;
|
||||
g->linear->y1 = g->linear->y1 * vBox.h;
|
||||
g->linear->x2 = g->linear->x2 * vBox.w;
|
||||
g->linear->y2 = g->linear->y2 * vBox.h;
|
||||
} else {
|
||||
Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
|
||||
if (isTransform) _transformMultiply(&m, &finalTransform);
|
||||
else {
|
||||
finalTransform = m;
|
||||
isTransform = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTransform) fillGrad->transform(finalTransform);
|
||||
|
||||
fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2);
|
||||
fillGrad->spread(g->spread);
|
||||
|
||||
//Update the stops
|
||||
stopCount = g->stops.count;
|
||||
if (stopCount > 0) {
|
||||
stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
|
||||
if (!stops) return fillGrad;
|
||||
auto prevOffset = 0.0f;
|
||||
for (uint32_t i = 0; i < g->stops.count; ++i) {
|
||||
auto colorStop = &g->stops.data[i];
|
||||
//Use premultiplied color
|
||||
stops[i].r = colorStop->r;
|
||||
stops[i].g = colorStop->g;
|
||||
stops[i].b = colorStop->b;
|
||||
stops[i].a = static_cast<uint8_t>((colorStop->a * opacity) / 255);
|
||||
stops[i].offset = colorStop->offset;
|
||||
//check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes
|
||||
if (colorStop->offset < prevOffset) stops[i].offset = prevOffset;
|
||||
else if (colorStop->offset > 1) stops[i].offset = 1;
|
||||
prevOffset = stops[i].offset;
|
||||
}
|
||||
fillGrad->colorStops(stops, stopCount);
|
||||
free(stops);
|
||||
}
|
||||
return fillGrad;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity)
|
||||
{
|
||||
Fill::ColorStop *stops;
|
||||
int stopCount = 0;
|
||||
auto fillGrad = RadialGradient::gen();
|
||||
|
||||
bool isTransform = (g->transform ? true : false);
|
||||
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if (isTransform) finalTransform = *g->transform;
|
||||
|
||||
if (g->userSpace) {
|
||||
//The radius scalling is done according to the Units section:
|
||||
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
|
||||
g->radial->cx = g->radial->cx * vBox.w;
|
||||
g->radial->cy = g->radial->cy * vBox.h;
|
||||
g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
|
||||
g->radial->fx = g->radial->fx * vBox.w;
|
||||
g->radial->fy = g->radial->fy * vBox.h;
|
||||
} else {
|
||||
Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
|
||||
if (isTransform) _transformMultiply(&m, &finalTransform);
|
||||
else {
|
||||
finalTransform = m;
|
||||
isTransform = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTransform) fillGrad->transform(finalTransform);
|
||||
|
||||
//TODO: Tvg is not support to focal
|
||||
//if (g->radial->fx != 0 && g->radial->fy != 0) {
|
||||
// fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
|
||||
//}
|
||||
fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
|
||||
fillGrad->spread(g->spread);
|
||||
|
||||
//Update the stops
|
||||
stopCount = g->stops.count;
|
||||
if (stopCount > 0) {
|
||||
stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
|
||||
if (!stops) return fillGrad;
|
||||
auto prevOffset = 0.0f;
|
||||
for (uint32_t i = 0; i < g->stops.count; ++i) {
|
||||
auto colorStop = &g->stops.data[i];
|
||||
//Use premultiplied color
|
||||
stops[i].r = colorStop->r;
|
||||
stops[i].g = colorStop->g;
|
||||
stops[i].b = colorStop->b;
|
||||
stops[i].a = static_cast<uint8_t>((colorStop->a * opacity) / 255);
|
||||
stops[i].offset = colorStop->offset;
|
||||
//check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes
|
||||
if (colorStop->offset < prevOffset) stops[i].offset = prevOffset;
|
||||
else if (colorStop->offset > 1) stops[i].offset = 1;
|
||||
prevOffset = stops[i].offset;
|
||||
}
|
||||
fillGrad->colorStops(stops, stopCount);
|
||||
free(stops);
|
||||
}
|
||||
return fillGrad;
|
||||
}
|
||||
|
||||
|
||||
static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto valid = false;
|
||||
|
||||
if (_appendShape(node, shape, vBox, svgPath)) valid = true;
|
||||
|
||||
if (node->child.count > 0) {
|
||||
auto child = node->child.data;
|
||||
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
|
||||
if (_appendChildShape(*child, shape, vBox, svgPath)) valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
/* ClipPath */
|
||||
/* Do not drop in Circular Dependency for ClipPath.
|
||||
Composition can be applied recursively if its children nodes have composition target to this one. */
|
||||
if (node->style->clipPath.applying) {
|
||||
TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
|
||||
} else {
|
||||
auto compNode = node->style->clipPath.node;
|
||||
if (compNode && compNode->child.count > 0) {
|
||||
node->style->clipPath.applying = true;
|
||||
|
||||
auto comp = Shape::gen();
|
||||
comp->fill(255, 255, 255, 255);
|
||||
if (node->transform) comp->transform(*node->transform);
|
||||
|
||||
auto child = compNode->child.data;
|
||||
auto valid = false; //Composite only when valid shapes are existed
|
||||
|
||||
for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
|
||||
if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
|
||||
}
|
||||
|
||||
if (valid) paint->composite(move(comp), CompositeMethod::ClipPath);
|
||||
|
||||
node->style->clipPath.applying = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mask */
|
||||
/* Do not drop in Circular Dependency for Mask.
|
||||
Composition can be applied recursively if its children nodes have composition target to this one. */
|
||||
if (node->style->mask.applying) {
|
||||
TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
|
||||
} else {
|
||||
auto compNode = node->style->mask.node;
|
||||
if (compNode && compNode->child.count > 0) {
|
||||
node->style->mask.applying = true;
|
||||
|
||||
auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true);
|
||||
if (comp) {
|
||||
if (node->transform) comp->transform(*node->transform);
|
||||
paint->composite(move(comp), CompositeMethod::AlphaMask);
|
||||
}
|
||||
|
||||
node->style->mask.applying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
SvgStyleProperty* style = node->style;
|
||||
|
||||
if (node->transform) vg->transform(*node->transform);
|
||||
if (node->type == SvgNodeType::Doc || !node->display) return;
|
||||
|
||||
//If fill property is nullptr then do nothing
|
||||
if (style->fill.paint.none) {
|
||||
//Do nothing
|
||||
} else if (style->fill.paint.gradient) {
|
||||
Box bBox = vBox;
|
||||
if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg);
|
||||
|
||||
if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
|
||||
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
|
||||
vg->fill(move(linear));
|
||||
} else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
|
||||
auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
|
||||
vg->fill(move(radial));
|
||||
}
|
||||
} else if (style->fill.paint.url) {
|
||||
//TODO: Apply the color pointed by url
|
||||
} else if (style->fill.paint.curColor) {
|
||||
//Apply the current style color
|
||||
vg->fill(style->color.r, style->color.g, style->color.b, style->fill.opacity);
|
||||
} else {
|
||||
//Apply the fill color
|
||||
vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b, style->fill.opacity);
|
||||
}
|
||||
|
||||
//Apply the fill rule
|
||||
vg->fill((tvg::FillRule)style->fill.fillRule);
|
||||
|
||||
//Apply node opacity
|
||||
if (style->opacity < 255) vg->opacity(style->opacity);
|
||||
|
||||
if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return;
|
||||
|
||||
//Apply the stroke style property
|
||||
vg->stroke(style->stroke.width);
|
||||
vg->stroke(style->stroke.cap);
|
||||
vg->stroke(style->stroke.join);
|
||||
if (style->stroke.dash.array.count > 0) {
|
||||
vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
|
||||
}
|
||||
|
||||
//If stroke property is nullptr then do nothing
|
||||
if (style->stroke.paint.none) {
|
||||
//Do nothing
|
||||
} else if (style->stroke.paint.gradient) {
|
||||
Box bBox = vBox;
|
||||
if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg);
|
||||
|
||||
if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
|
||||
auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
|
||||
vg->stroke(move(linear));
|
||||
} else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) {
|
||||
auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
|
||||
vg->stroke(move(radial));
|
||||
}
|
||||
} else if (style->stroke.paint.url) {
|
||||
//TODO: Apply the color pointed by url
|
||||
} else if (style->stroke.paint.curColor) {
|
||||
//Apply the current style color
|
||||
vg->stroke(style->color.r, style->color.g, style->color.b, style->stroke.opacity);
|
||||
} else {
|
||||
//Apply the stroke color
|
||||
vg->stroke(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity);
|
||||
}
|
||||
|
||||
_applyComposition(vg, node, vBox, svgPath);
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto shape = Shape::gen();
|
||||
if (_appendShape(node, shape.get(), vBox, svgPath)) return shape;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
Array<PathCommand> cmds;
|
||||
Array<Point> pts;
|
||||
|
||||
switch (node->type) {
|
||||
case SvgNodeType::Path: {
|
||||
if (node->node.path.path) {
|
||||
if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
|
||||
shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Ellipse: {
|
||||
shape->appendCircle(node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry);
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Polygon: {
|
||||
if (node->node.polygon.pointsCount < 2) break;
|
||||
shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
|
||||
for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
|
||||
shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
|
||||
}
|
||||
shape->close();
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Polyline: {
|
||||
if (node->node.polygon.pointsCount < 2) break;
|
||||
shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
|
||||
for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
|
||||
shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Circle: {
|
||||
shape->appendCircle(node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r);
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Rect: {
|
||||
shape->appendRect(node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry);
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Line: {
|
||||
shape->moveTo(node->node.line.x1, node->node.line.y1);
|
||||
shape->lineTo(node->node.line.x2, node->node.line.y2);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_applyProperty(node, shape, vBox, svgPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum class imageMimeTypeEncoding
|
||||
{
|
||||
base64 = 0x1,
|
||||
utf8 = 0x2
|
||||
};
|
||||
constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
|
||||
return static_cast<imageMimeTypeEncoding>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
|
||||
return (static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
|
||||
|
||||
static constexpr struct
|
||||
{
|
||||
const char* name;
|
||||
int sz;
|
||||
imageMimeTypeEncoding encoding;
|
||||
} imageMimeTypes[] = {
|
||||
{"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64},
|
||||
{"png", sizeof("png"), imageMimeTypeEncoding::base64},
|
||||
{"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8},
|
||||
};
|
||||
|
||||
|
||||
static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mimetype, imageMimeTypeEncoding* encoding) {
|
||||
if (strncmp(*href, "image/", sizeof("image/") - 1)) return false; //not allowed mime type
|
||||
*href += sizeof("image/") - 1;
|
||||
|
||||
//RFC2397 data:[<mediatype>][;base64],<data>
|
||||
//mediatype := [ type "/" subtype ] *( ";" parameter )
|
||||
//parameter := attribute "=" value
|
||||
for (unsigned int i = 0; i < sizeof(imageMimeTypes) / sizeof(imageMimeTypes[0]); i++) {
|
||||
if (!strncmp(*href, imageMimeTypes[i].name, imageMimeTypes[i].sz - 1)) {
|
||||
*href += imageMimeTypes[i].sz - 1;
|
||||
*mimetype = imageMimeTypes[i].name;
|
||||
|
||||
while (**href && **href != ',') {
|
||||
while (**href && **href != ';') ++(*href);
|
||||
if (!**href) return false;
|
||||
++(*href);
|
||||
|
||||
if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::base64) {
|
||||
if (!strncmp(*href, "base64,", sizeof("base64,") - 1)) {
|
||||
*href += sizeof("base64,") - 1;
|
||||
*encoding = imageMimeTypeEncoding::base64;
|
||||
return true; //valid base64
|
||||
}
|
||||
}
|
||||
if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8) {
|
||||
if (!strncmp(*href, "utf8,", sizeof("utf8,") - 1)) {
|
||||
*href += sizeof("utf8,") - 1;
|
||||
*encoding = imageMimeTypeEncoding::utf8;
|
||||
return true; //valid utf8
|
||||
}
|
||||
}
|
||||
}
|
||||
//no encoding defined
|
||||
if (**href == ',' && (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8)) {
|
||||
++(*href);
|
||||
*encoding = imageMimeTypeEncoding::utf8;
|
||||
return true; //allow no encoding defined if utf8 expected
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
if (!node->node.image.href) return nullptr;
|
||||
auto picture = Picture::gen();
|
||||
|
||||
const char* href = node->node.image.href;
|
||||
if (!strncmp(href, "data:", sizeof("data:") - 1)) {
|
||||
href += sizeof("data:") - 1;
|
||||
const char* mimetype;
|
||||
imageMimeTypeEncoding encoding;
|
||||
if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
|
||||
if (encoding == imageMimeTypeEncoding::base64) {
|
||||
string decoded = svgUtilBase64Decode(href);
|
||||
if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
|
||||
} else {
|
||||
string decoded = svgUtilURLDecode(href);
|
||||
if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
|
||||
//TODO: protect against recursive svg image loading
|
||||
//Temporarily disable embedded svg:
|
||||
const char *dot = strrchr(href, '.');
|
||||
if (dot && !strcmp(dot, ".svg")) {
|
||||
TVGLOG("SVG", "Embedded svg file is disabled.");
|
||||
return nullptr;
|
||||
}
|
||||
string imagePath = href;
|
||||
if (strncmp(href, "/", 1)) {
|
||||
auto last = svgPath.find_last_of("/");
|
||||
imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1 )) + imagePath;
|
||||
}
|
||||
if (picture->load(imagePath) != Result::Success) return nullptr;
|
||||
}
|
||||
|
||||
float w, h;
|
||||
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
|
||||
auto sx = node->node.image.w / w;
|
||||
auto sy = node->node.image.h / h;
|
||||
Matrix m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
|
||||
picture->transform(m);
|
||||
}
|
||||
|
||||
_applyComposition(picture.get(), node, vBox, svgPath);
|
||||
return picture;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto scene = _sceneBuildHelper(node, vBox, svgPath, false);
|
||||
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
|
||||
scene->translate(node->node.use.x, node->node.use.y);
|
||||
}
|
||||
if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) {
|
||||
//TODO: handle width/height properties
|
||||
}
|
||||
return scene;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask)
|
||||
{
|
||||
if (_isGroupType(node->type) || mask) {
|
||||
auto scene = Scene::gen();
|
||||
if (!mask && node->transform) scene->transform(*node->transform);
|
||||
|
||||
if (node->display && node->style->opacity != 0) {
|
||||
auto child = node->child.data;
|
||||
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
|
||||
if (_isGroupType((*child)->type)) {
|
||||
if ((*child)->type == SvgNodeType::Use)
|
||||
scene->push(_useBuildHelper(*child, vBox, svgPath));
|
||||
else
|
||||
scene->push(_sceneBuildHelper(*child, vBox, svgPath, false));
|
||||
} else if ((*child)->type == SvgNodeType::Image) {
|
||||
auto image = _imageBuildHelper(*child, vBox, svgPath);
|
||||
if (image) scene->push(move(image));
|
||||
} else if ((*child)->type != SvgNodeType::Mask) {
|
||||
auto shape = _shapeBuildHelper(*child, vBox, svgPath);
|
||||
if (shape) scene->push(move(shape));
|
||||
}
|
||||
}
|
||||
_applyComposition(scene.get(), node, vBox, svgPath);
|
||||
scene->opacity(node->style->opacity);
|
||||
}
|
||||
return scene;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath)
|
||||
{
|
||||
if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
|
||||
|
||||
Box vBox = {vx, vy, vw, vh};
|
||||
auto docNode = _sceneBuildHelper(node, vBox, svgPath, false);
|
||||
|
||||
if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
|
||||
auto sx = w / vw;
|
||||
auto sy = h / vh;
|
||||
|
||||
if (preserveAspect) {
|
||||
//Scale
|
||||
auto scale = sx < sy ? sx : sy;
|
||||
docNode->scale(scale);
|
||||
//Align
|
||||
auto tvx = vx * scale;
|
||||
auto tvy = vy * scale;
|
||||
auto tvw = vw * scale;
|
||||
auto tvh = vh * scale;
|
||||
if (vw > vh) tvy -= (h - tvh) * 0.5f;
|
||||
else tvx -= (w - tvw) * 0.5f;
|
||||
docNode->translate(-tvx, -tvy);
|
||||
} else {
|
||||
//Align
|
||||
auto tvx = vx * sx;
|
||||
auto tvy = vy * sy;
|
||||
auto tvw = vw * sx;
|
||||
auto tvh = vh * sy;
|
||||
if (tvw > tvh) tvy -= (h - tvh) * 0.5f;
|
||||
else tvx -= (w - tvw) * 0.5f;
|
||||
Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
|
||||
docNode->transform(m);
|
||||
}
|
||||
} else if (!mathZero(vx) || !mathZero(vy)) {
|
||||
docNode->translate(-vx, -vy);
|
||||
}
|
||||
|
||||
auto viewBoxClip = Shape::gen();
|
||||
viewBoxClip->appendRect(0, 0, w, h, 0, 0);
|
||||
viewBoxClip->fill(0, 0, 0, 255);
|
||||
|
||||
auto compositeLayer = Scene::gen();
|
||||
compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
|
||||
compositeLayer->push(move(docNode));
|
||||
|
||||
auto root = Scene::gen();
|
||||
root->push(move(compositeLayer));
|
||||
|
||||
return root;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SVG_SCENE_BUILDER_H_
|
||||
#define _TVG_SVG_SCENE_BUILDER_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
|
||||
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath);
|
||||
|
||||
#endif //_TVG_SVG_SCENE_BUILDER_H_
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <memory.h>
|
||||
#include "tvgSvgUtil.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline bool _floatExact(float a, float b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(float)) == 0;
|
||||
}
|
||||
|
||||
static uint8_t _hexCharToDec(const char c)
|
||||
{
|
||||
if (c >= 'a') return c - 'a' + 10;
|
||||
else if (c >= 'A') return c - 'A' + 10;
|
||||
else return c - '0';
|
||||
}
|
||||
|
||||
static uint8_t _base64Value(const char chr)
|
||||
{
|
||||
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
|
||||
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
|
||||
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
|
||||
else if (chr == '+' || chr == '-') return 62;
|
||||
else return 63;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
|
||||
*
|
||||
* src should be one of the following form :
|
||||
*
|
||||
* [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
|
||||
* [whitespace] [sign] {INF | INFINITY}
|
||||
* [whitespace] [sign] NAN [sequence]
|
||||
*
|
||||
* No hexadecimal form supported
|
||||
* no sequence supported after NAN
|
||||
*/
|
||||
float svgUtilStrtof(const char *nPtr, char **endPtr)
|
||||
{
|
||||
if (endPtr) *endPtr = (char*)(nPtr);
|
||||
if (!nPtr) return 0.0f;
|
||||
|
||||
auto a = nPtr;
|
||||
auto iter = nPtr;
|
||||
auto val = 0.0f;
|
||||
unsigned long long integerPart = 0;
|
||||
int minus = 1;
|
||||
|
||||
//ignore leading whitespaces
|
||||
while (isspace(*iter)) iter++;
|
||||
|
||||
//signed or not
|
||||
if (*iter == '-') {
|
||||
minus = -1;
|
||||
iter++;
|
||||
} else if (*iter == '+') {
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (tolower(*iter) == 'i') {
|
||||
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
|
||||
else goto error;
|
||||
|
||||
if (tolower(*(iter + 3)) == 'i') {
|
||||
if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5;
|
||||
else goto error;
|
||||
}
|
||||
if (endPtr) *endPtr = (char *)(iter);
|
||||
return (minus == -1) ? -INFINITY : INFINITY;
|
||||
}
|
||||
|
||||
if (tolower(*iter) == 'n') {
|
||||
if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
|
||||
else goto error;
|
||||
|
||||
if (endPtr) *endPtr = (char *)(iter);
|
||||
return (minus == -1) ? -NAN : NAN;
|
||||
}
|
||||
|
||||
//Optional: integer part before dot
|
||||
if (isdigit(*iter)) {
|
||||
for (; isdigit(*iter); iter++) {
|
||||
integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
|
||||
}
|
||||
a = iter;
|
||||
} else if (*iter != '.') {
|
||||
goto success;
|
||||
}
|
||||
|
||||
val = static_cast<float>(integerPart);
|
||||
|
||||
//Optional: decimal part after dot
|
||||
if (*iter == '.') {
|
||||
unsigned long long decimalPart = 0;
|
||||
unsigned long long pow10 = 1;
|
||||
int count = 0;
|
||||
|
||||
iter++;
|
||||
|
||||
if (isdigit(*iter)) {
|
||||
for (; isdigit(*iter); iter++, count++) {
|
||||
if (count < 19) {
|
||||
decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0');
|
||||
pow10 *= 10ULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
|
||||
a = iter;
|
||||
}
|
||||
|
||||
//Optional: exponent
|
||||
if (*iter == 'e' || *iter == 'E') {
|
||||
++iter;
|
||||
|
||||
//Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
|
||||
if ((*iter == 'm') || (*iter == 'M')) {
|
||||
//TODO: We don't support font em unit now, but has to multiply val * font size later...
|
||||
a = iter + 1;
|
||||
goto success;
|
||||
}
|
||||
|
||||
//signed or not
|
||||
int minus_e = 1;
|
||||
|
||||
if (*iter == '-') {
|
||||
minus_e = -1;
|
||||
++iter;
|
||||
} else if (*iter == '+') {
|
||||
iter++;
|
||||
}
|
||||
|
||||
unsigned int exponentPart = 0;
|
||||
|
||||
if (isdigit(*iter)) {
|
||||
while (*iter == '0') iter++;
|
||||
for (; isdigit(*iter); iter++) {
|
||||
exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
|
||||
}
|
||||
} else if (!isdigit(*(a - 1))) {
|
||||
a = nPtr;
|
||||
goto success;
|
||||
} else if (*iter == 0) {
|
||||
goto success;
|
||||
}
|
||||
|
||||
//if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
|
||||
if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
|
||||
//val *= 1.0e-308f;
|
||||
val *= 1.0e-38f;
|
||||
a = iter;
|
||||
goto success;
|
||||
}
|
||||
|
||||
a = iter;
|
||||
auto scale = 1.0f;
|
||||
|
||||
while (exponentPart >= 8U) {
|
||||
scale *= 1E8;
|
||||
exponentPart -= 8U;
|
||||
}
|
||||
while (exponentPart > 0U) {
|
||||
scale *= 10.0f;
|
||||
exponentPart--;
|
||||
}
|
||||
val = (minus_e == -1) ? (val / scale) : (val * scale);
|
||||
} else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
|
||||
a = nPtr;
|
||||
goto success;
|
||||
}
|
||||
|
||||
success:
|
||||
if (endPtr) *endPtr = (char*)(a);
|
||||
return minus * val;
|
||||
|
||||
error:
|
||||
if (endPtr) *endPtr = (char*)(nPtr);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
string svgUtilURLDecode(const char *src)
|
||||
{
|
||||
if (!src) return nullptr;
|
||||
|
||||
auto length = strlen(src);
|
||||
if (length == 0) return nullptr;
|
||||
|
||||
string decoded;
|
||||
decoded.reserve(length);
|
||||
|
||||
char a, b;
|
||||
while (*src) {
|
||||
if (*src == '%' &&
|
||||
((a = src[1]) && (b = src[2])) &&
|
||||
(isxdigit(a) && isxdigit(b))) {
|
||||
decoded += (_hexCharToDec(a) << 4) + _hexCharToDec(b);
|
||||
src+=3;
|
||||
} else if (*src == '+') {
|
||||
decoded += ' ';
|
||||
src++;
|
||||
} else {
|
||||
decoded += *src++;
|
||||
}
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
|
||||
string svgUtilBase64Decode(const char *src)
|
||||
{
|
||||
if (!src) return nullptr;
|
||||
|
||||
auto length = strlen(src);
|
||||
if (length == 0) return nullptr;
|
||||
|
||||
string decoded;
|
||||
decoded.reserve(3*(1+(length >> 2)));
|
||||
|
||||
while (*src && *(src+1)) {
|
||||
if (*src <= 0x20) {
|
||||
++src;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value1 = _base64Value(src[0]);
|
||||
auto value2 = _base64Value(src[1]);
|
||||
decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
|
||||
|
||||
if (!src[2] || src[2] == '=' || src[2] == '.') break;
|
||||
auto value3 = _base64Value(src[2]);
|
||||
decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
|
||||
|
||||
if (!src[3] || src[3] == '=' || src[3] == '.') break;
|
||||
auto value4 = _base64Value(src[3]);
|
||||
decoded += ((value3 & 0x03) << 6) + value4;
|
||||
src += 4;
|
||||
}
|
||||
return decoded;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SVG_UTIL_H_
|
||||
#define _TVG_SVG_UTIL_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
|
||||
float svgUtilStrtof(const char *nPtr, char **endPtr);
|
||||
|
||||
string svgUtilURLDecode(const char *src);
|
||||
string svgUtilBase64Decode(const char *src);
|
||||
|
||||
#endif //_TVG_SVG_UTIL_H_
|
|
@ -0,0 +1,527 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#include "tvgXmlParser.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
|
||||
{
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
const auto attributesNum = 6;
|
||||
const struct
|
||||
{
|
||||
const char* tag;
|
||||
bool tagWildcard; //If true, it is assumed that a wildcard is used after the tag. (ex: tagName*)
|
||||
const char* value;
|
||||
} attributes[] = {
|
||||
{"id", false, nullptr},
|
||||
{"data-name", false, nullptr},
|
||||
{"overflow", false, "visible"},
|
||||
{"version", false, nullptr},
|
||||
{"xmlns", true, nullptr},
|
||||
{"xml:space", false, nullptr},
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < attributesNum; ++i) {
|
||||
if (!strncmp(tagAttribute, attributes[i].tag, attributes[i].tagWildcard ? strlen(attributes[i].tag) : strlen(tagAttribute))) {
|
||||
if (attributes[i].value && tagValue) {
|
||||
if (!strncmp(tagValue, attributes[i].value, strlen(tagValue))) {
|
||||
return true;
|
||||
} else continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
|
||||
{
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if (isspace((unsigned char)*itr)) break;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
|
||||
{
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if (!isspace((unsigned char)*itr)) break;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
|
||||
{
|
||||
for (itr--; itr > itrStart; itr--) {
|
||||
if (!isspace((unsigned char)*itr)) break;
|
||||
}
|
||||
return itr + 1;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
|
||||
{
|
||||
auto p = itr;
|
||||
while (itr < itrEnd && *itr == '&') {
|
||||
for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
|
||||
if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
|
||||
itr += xmlEntityLength[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (itr == p) break;
|
||||
p = itr;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
|
||||
{
|
||||
auto p = itr;
|
||||
while (itr > itrStart && *(itr - 1) == ';') {
|
||||
for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
|
||||
if (itr - xmlEntityLength[i] > itrStart &&
|
||||
strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
|
||||
itr -= xmlEntityLength[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (itr == p) break;
|
||||
p = itr;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd)
|
||||
{
|
||||
itr = _simpleXmlSkipWhiteSpace(itr, itrEnd);
|
||||
auto p = itr;
|
||||
while (true) {
|
||||
if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr;
|
||||
else break;
|
||||
if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr;
|
||||
else break;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart)
|
||||
{
|
||||
itr = _simpleXmlUnskipWhiteSpace(itr, itrStart);
|
||||
auto p = itr;
|
||||
while (true) {
|
||||
if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr;
|
||||
else break;
|
||||
if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr;
|
||||
else break;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
|
||||
{
|
||||
return (const char*)memchr(itr, '<', itrEnd - itr);
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
|
||||
{
|
||||
bool insideQuote = false;
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if (*itr == '"') insideQuote = !insideQuote;
|
||||
if (!insideQuote) {
|
||||
if ((*itr == '>') || (*itr == '<'))
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
|
||||
{
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
|
||||
{
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
|
||||
{
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if (*itr == '>') return itr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
|
||||
{
|
||||
toff = 0;
|
||||
if (itr[1] == '/') {
|
||||
toff = 1;
|
||||
return SimpleXMLType::Close;
|
||||
} else if (itr[1] == '?') {
|
||||
toff = 1;
|
||||
return SimpleXMLType::Processing;
|
||||
} else if (itr[1] == '!') {
|
||||
if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
|
||||
toff = sizeof("!DOCTYPE") - 1;
|
||||
return SimpleXMLType::Doctype;
|
||||
} else if (itr + sizeof("<!>") - 1 < itrEnd) {
|
||||
toff = sizeof("!") - 1;
|
||||
return SimpleXMLType::DoctypeChild;
|
||||
} else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
|
||||
toff = sizeof("![CDATA[") - 1;
|
||||
return SimpleXMLType::CData;
|
||||
} else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
|
||||
toff = sizeof("!--") - 1;
|
||||
return SimpleXMLType::Comment;
|
||||
}
|
||||
return SimpleXMLType::Open;
|
||||
}
|
||||
return SimpleXMLType::Open;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
|
||||
{
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
static const char* TYPE_NAMES[] = {
|
||||
"Svg",
|
||||
"G",
|
||||
"Defs",
|
||||
"Animation",
|
||||
"Arc",
|
||||
"Circle",
|
||||
"Ellipse",
|
||||
"Image",
|
||||
"Line",
|
||||
"Path",
|
||||
"Polygon",
|
||||
"Polyline",
|
||||
"Rect",
|
||||
"Text",
|
||||
"TextArea",
|
||||
"Tspan",
|
||||
"Use",
|
||||
"Video",
|
||||
"ClipPath",
|
||||
"Mask",
|
||||
"Unknown",
|
||||
};
|
||||
return TYPE_NAMES[(int) type];
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
|
||||
{
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
const auto elementsNum = 1;
|
||||
const char* const elements[] = { "title" };
|
||||
|
||||
for (unsigned int i = 0; i < elementsNum; ++i) {
|
||||
if (!strncmp(tagName, elements[i], strlen(tagName))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
|
||||
{
|
||||
const char *itr = buf, *itrEnd = buf + bufLength;
|
||||
char* tmpBuf = (char*)alloca(bufLength + 1);
|
||||
|
||||
if (!buf || !func) return false;
|
||||
|
||||
while (itr < itrEnd) {
|
||||
const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
|
||||
const char *key, *keyEnd, *value, *valueEnd;
|
||||
char* tval;
|
||||
|
||||
if (p == itrEnd) return true;
|
||||
|
||||
key = p;
|
||||
for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
|
||||
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
|
||||
}
|
||||
if (keyEnd == itrEnd) return false;
|
||||
if (keyEnd == key) continue;
|
||||
|
||||
if (*keyEnd == '=') value = keyEnd + 1;
|
||||
else {
|
||||
value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
|
||||
if (!value) return false;
|
||||
value++;
|
||||
}
|
||||
keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
|
||||
|
||||
value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
|
||||
if (value == itrEnd) return false;
|
||||
|
||||
if ((*value == '"') || (*value == '\'')) {
|
||||
valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
|
||||
if (!valueEnd) return false;
|
||||
value++;
|
||||
} else {
|
||||
valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
|
||||
}
|
||||
|
||||
itr = valueEnd + 1;
|
||||
|
||||
value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
|
||||
valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value);
|
||||
|
||||
memcpy(tmpBuf, key, keyEnd - key);
|
||||
tmpBuf[keyEnd - key] = '\0';
|
||||
|
||||
tval = tmpBuf + (keyEnd - key) + 1;
|
||||
int i = 0;
|
||||
while (value < valueEnd) {
|
||||
value = _simpleXmlSkipXmlEntities(value, valueEnd);
|
||||
tval[i++] = *value;
|
||||
value++;
|
||||
}
|
||||
tval[i] = '\0';
|
||||
|
||||
if (!func((void*)data, tmpBuf, tval)) {
|
||||
if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) {
|
||||
TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
|
||||
{
|
||||
const char *itr = buf, *itrEnd = buf + bufLength;
|
||||
|
||||
if (!buf || !func) return false;
|
||||
|
||||
while (itr < itrEnd) {
|
||||
if (itr[0] == '<') {
|
||||
//Invalid case
|
||||
if (itr + 1 >= itrEnd) return false;
|
||||
|
||||
size_t toff = 0;
|
||||
SimpleXMLType type = _getXMLType(itr, itrEnd, toff);
|
||||
|
||||
const char* p;
|
||||
if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
|
||||
else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
|
||||
else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
|
||||
else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
|
||||
|
||||
if (p) {
|
||||
//Invalid case: '<' nested
|
||||
if (*p == '<') return false;
|
||||
const char *start, *end;
|
||||
|
||||
start = itr + 1 + toff;
|
||||
end = p;
|
||||
|
||||
switch (type) {
|
||||
case SimpleXMLType::Open: {
|
||||
if (p[-1] == '/') {
|
||||
type = SimpleXMLType::OpenEmpty;
|
||||
end--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SimpleXMLType::CData: {
|
||||
if (!memcmp(p - 2, "]]", 2)) end -= 2;
|
||||
break;
|
||||
}
|
||||
case SimpleXMLType::Processing: {
|
||||
if (p[-1] == '?') end--;
|
||||
break;
|
||||
}
|
||||
case SimpleXMLType::Comment: {
|
||||
if (!memcmp(p - 2, "--", 2)) end -= 2;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (strip && (type != SimpleXMLType::CData)) {
|
||||
start = _skipWhiteSpacesAndXmlEntities(start, end);
|
||||
end = _unskipWhiteSpacesAndXmlEntities(end, start);
|
||||
}
|
||||
|
||||
if (!func((void*)data, type, start, (unsigned int)(end - start))) return false;
|
||||
|
||||
itr = p + 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const char *p, *end;
|
||||
|
||||
if (strip) {
|
||||
p = itr;
|
||||
p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
|
||||
if (p) {
|
||||
if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
|
||||
itr = p;
|
||||
}
|
||||
}
|
||||
|
||||
p = _simpleXmlFindStartTag(itr, itrEnd);
|
||||
if (!p) p = itrEnd;
|
||||
|
||||
end = p;
|
||||
if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
|
||||
|
||||
if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false;
|
||||
|
||||
if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false;
|
||||
|
||||
itr = p;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
|
||||
{
|
||||
const char* end;
|
||||
char* key;
|
||||
char* val;
|
||||
char* next;
|
||||
|
||||
if (!buf) return false;
|
||||
|
||||
end = buf + strlen(buf);
|
||||
key = (char*)alloca(end - buf + 1);
|
||||
val = (char*)alloca(end - buf + 1);
|
||||
|
||||
if (buf == end) return true;
|
||||
|
||||
do {
|
||||
char* sep = (char*)strchr(buf, ':');
|
||||
next = (char*)strchr(buf, ';');
|
||||
|
||||
key[0] = '\0';
|
||||
val[0] = '\0';
|
||||
|
||||
if (next == nullptr && sep != nullptr) {
|
||||
memcpy(key, buf, sep - buf);
|
||||
key[sep - buf] = '\0';
|
||||
|
||||
memcpy(val, sep + 1, end - sep - 1);
|
||||
val[end - sep - 1] = '\0';
|
||||
} else if (sep < next && sep != nullptr) {
|
||||
memcpy(key, buf, sep - buf);
|
||||
key[sep - buf] = '\0';
|
||||
|
||||
memcpy(val, sep + 1, next - sep - 1);
|
||||
val[next - sep - 1] = '\0';
|
||||
} else if (next) {
|
||||
memcpy(key, buf, next - buf);
|
||||
key[next - buf] = '\0';
|
||||
}
|
||||
|
||||
if (key[0]) {
|
||||
key = const_cast<char*>(_simpleXmlSkipWhiteSpace(key, key + strlen(key)));
|
||||
key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
|
||||
val = const_cast<char*>(_simpleXmlSkipWhiteSpace(val, val + strlen(val)));
|
||||
val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
|
||||
|
||||
if (!func((void*)data, key, val)) {
|
||||
if (!_isIgnoreUnsupportedLogAttributes(key, val)) {
|
||||
TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf = next + 1;
|
||||
} while (next != nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
|
||||
{
|
||||
const char *itr = buf, *itrEnd = buf + bufLength;
|
||||
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if (!isspace((unsigned char)*itr)) {
|
||||
//User skip tagname and already gave it the attributes.
|
||||
if (*itr == '=') return buf;
|
||||
} else {
|
||||
itr = _simpleXmlUnskipXmlEntities(itr, buf);
|
||||
if (itr == itrEnd) return nullptr;
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SIMPLE_XML_PARSER_H_
|
||||
#define _TVG_SIMPLE_XML_PARSER_H_
|
||||
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
|
||||
#define NUMBER_OF_XML_ENTITIES 8
|
||||
const char* const xmlEntity[] = {""", " ", "'", "&", "<", ">", "#", "'"};
|
||||
const int xmlEntityLength[] = {6, 6, 6, 5, 4, 4, 6, 6};
|
||||
|
||||
enum class SimpleXMLType
|
||||
{
|
||||
Open = 0, //!< \<tag attribute="value"\>
|
||||
OpenEmpty, //!< \<tag attribute="value" /\>
|
||||
Close, //!< \</tag\>
|
||||
Data, //!< tag text data
|
||||
CData, //!< \<![cdata[something]]\>
|
||||
Error, //!< error contents
|
||||
Processing, //!< \<?xml ... ?\> \<?php .. ?\>
|
||||
Doctype, //!< \<!doctype html
|
||||
Comment, //!< \<!-- something --\>
|
||||
Ignored, //!< whatever is ignored by parser, like whitespace
|
||||
DoctypeChild //!< \<!doctype_child
|
||||
};
|
||||
|
||||
typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned int length);
|
||||
typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value);
|
||||
|
||||
bool simpleXmlParseAttributes(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data);
|
||||
bool simpleXmlParse(const char* buf, unsigned buflen, bool strip, simpleXMLCb func, const void* data);
|
||||
bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data);
|
||||
const char *simpleXmlFindAttributesTag(const char* buf, unsigned buflen);
|
||||
bool isIgnoreUnsupportedLogElements(const char* tagName);
|
||||
const char* simpleXmlNodeTypeToString(SvgNodeType type);
|
||||
|
||||
#endif //_TVG_SIMPLE_XML_PARSER_H_
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <memory.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#include "tvgTvgCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct TvgBinBlock
|
||||
{
|
||||
TvgBinTag type;
|
||||
TvgBinCounter length;
|
||||
const char* data;
|
||||
const char* end;
|
||||
};
|
||||
|
||||
static Paint* _parsePaint(TvgBinBlock baseBlock);
|
||||
|
||||
|
||||
static TvgBinBlock _readBlock(const char *ptr)
|
||||
{
|
||||
TvgBinBlock block;
|
||||
block.type = *ptr;
|
||||
READ_UI32(&block.length, ptr + SIZE(TvgBinTag));
|
||||
block.data = ptr + SIZE(TvgBinTag) + SIZE(TvgBinCounter);
|
||||
block.end = block.data + block.length;
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseCmpTarget(const char *ptr, const char *end, Paint *paint)
|
||||
{
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) return false;
|
||||
|
||||
if (block.type != TVG_TAG_PAINT_CMP_METHOD) return false;
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
|
||||
auto cmpMethod = static_cast<CompositeMethod>(*block.data);
|
||||
|
||||
ptr = block.end;
|
||||
|
||||
auto cmpBlock = _readBlock(ptr);
|
||||
if (cmpBlock.end > end) return false;
|
||||
|
||||
paint->composite(unique_ptr<Paint>(_parsePaint(cmpBlock)), cmpMethod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parsePaintProperty(TvgBinBlock block, Paint *paint)
|
||||
{
|
||||
switch (block.type) {
|
||||
case TVG_TAG_PAINT_OPACITY: {
|
||||
if (block.length != SIZE(uint8_t)) return false;
|
||||
paint->opacity(*block.data);
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_PAINT_TRANSFORM: {
|
||||
if (block.length != SIZE(Matrix)) return false;
|
||||
Matrix matrix;
|
||||
memcpy(&matrix, block.data, SIZE(Matrix));
|
||||
paint->transform(matrix);
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_PAINT_CMP_TARGET: {
|
||||
if (block.length < SIZE(TvgBinTag) + SIZE(TvgBinCounter)) return false;
|
||||
return _parseCmpTarget(block.data, block.end, paint);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseScene(TvgBinBlock block, Paint *paint)
|
||||
{
|
||||
auto scene = static_cast<Scene*>(paint);
|
||||
|
||||
//Case1: scene reserve count
|
||||
if (block.type == TVG_TAG_SCENE_RESERVEDCNT) {
|
||||
if (block.length != SIZE(uint32_t)) return false;
|
||||
uint32_t reservedCnt;
|
||||
READ_UI32(&reservedCnt, block.data);
|
||||
scene->reserve(reservedCnt);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Case2: Base Paint Properties
|
||||
if (_parsePaintProperty(block, scene)) return true;
|
||||
|
||||
//Case3: A Child paint
|
||||
if (auto paint = _parsePaint(block)) {
|
||||
scene->push(unique_ptr<Paint>(paint));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShapePath(const char *ptr, const char *end, Shape *shape)
|
||||
{
|
||||
uint32_t cmdCnt, ptsCnt;
|
||||
|
||||
READ_UI32(&cmdCnt, ptr);
|
||||
ptr += SIZE(cmdCnt);
|
||||
|
||||
READ_UI32(&ptsCnt, ptr);
|
||||
ptr += SIZE(ptsCnt);
|
||||
|
||||
auto cmds = (TvgBinFlag*) ptr;
|
||||
ptr += SIZE(TvgBinFlag) * cmdCnt;
|
||||
|
||||
auto pts = (Point*) ptr;
|
||||
ptr += SIZE(Point) * ptsCnt;
|
||||
|
||||
if (ptr > end) return false;
|
||||
|
||||
/* Recover to PathCommand(4 bytes) from TvgBinFlag(1 byte) */
|
||||
PathCommand* inCmds = (PathCommand*)alloca(sizeof(PathCommand) * cmdCnt);
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
inCmds[i] = static_cast<PathCommand>(cmds[i]);
|
||||
}
|
||||
|
||||
shape->appendPath(inCmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Fill> _parseShapeFill(const char *ptr, const char *end)
|
||||
{
|
||||
unique_ptr<Fill> fillGrad;
|
||||
|
||||
while (ptr < end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) return nullptr;
|
||||
|
||||
switch (block.type) {
|
||||
case TVG_TAG_FILL_RADIAL_GRADIENT: {
|
||||
if (block.length != 3 * SIZE(float)) return nullptr;
|
||||
|
||||
auto ptr = block.data;
|
||||
float x, y, radius;
|
||||
|
||||
READ_FLOAT(&x, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&radius, ptr);
|
||||
|
||||
auto fillGradRadial = RadialGradient::gen();
|
||||
fillGradRadial->radial(x, y, radius);
|
||||
fillGrad = move(fillGradRadial);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_LINEAR_GRADIENT: {
|
||||
if (block.length != 4 * SIZE(float)) return nullptr;
|
||||
|
||||
auto ptr = block.data;
|
||||
float x1, y1, x2, y2;
|
||||
|
||||
READ_FLOAT(&x1, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y1, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&x2, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y2, ptr);
|
||||
|
||||
auto fillGradLinear = LinearGradient::gen();
|
||||
fillGradLinear->linear(x1, y1, x2, y2);
|
||||
fillGrad = move(fillGradLinear);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_FILLSPREAD: {
|
||||
if (!fillGrad) return nullptr;
|
||||
if (block.length != SIZE(TvgBinFlag)) return nullptr;
|
||||
fillGrad->spread((FillSpread) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_COLORSTOPS: {
|
||||
if (!fillGrad) return nullptr;
|
||||
if (block.length == 0 || block.length & 0x07) return nullptr;
|
||||
uint32_t stopsCnt = block.length >> 3; // 8 bytes per ColorStop
|
||||
if (stopsCnt > 1023) return nullptr;
|
||||
Fill::ColorStop* stops = (Fill::ColorStop*)alloca(sizeof(Fill::ColorStop) * stopsCnt);
|
||||
auto p = block.data;
|
||||
for (uint32_t i = 0; i < stopsCnt; i++, p += 8) {
|
||||
READ_FLOAT(&stops[i].offset, p);
|
||||
stops[i].r = p[4];
|
||||
stops[i].g = p[5];
|
||||
stops[i].b = p[6];
|
||||
stops[i].a = p[7];
|
||||
}
|
||||
fillGrad->colorStops(stops, stopsCnt);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_TRANSFORM: {
|
||||
if (!fillGrad || block.length != SIZE(Matrix)) return nullptr;
|
||||
Matrix gradTransform;
|
||||
memcpy(&gradTransform, block.data, SIZE(Matrix));
|
||||
fillGrad->transform(gradTransform);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of the fill properties, %d bytes skipped", block.type, block.type, block.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr = block.end;
|
||||
}
|
||||
return fillGrad;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShapeStrokeDashPattern(const char *ptr, const char *end, Shape *shape)
|
||||
{
|
||||
uint32_t dashPatternCnt;
|
||||
READ_UI32(&dashPatternCnt, ptr);
|
||||
ptr += SIZE(uint32_t);
|
||||
if (dashPatternCnt > 0) {
|
||||
float* dashPattern = static_cast<float*>(malloc(sizeof(float) * dashPatternCnt));
|
||||
if (!dashPattern) return false;
|
||||
memcpy(dashPattern, ptr, sizeof(float) * dashPatternCnt);
|
||||
ptr += SIZE(float) * dashPatternCnt;
|
||||
|
||||
if (ptr > end) {
|
||||
free(dashPattern);
|
||||
return false;
|
||||
}
|
||||
|
||||
shape->stroke(dashPattern, dashPatternCnt);
|
||||
free(dashPattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape)
|
||||
{
|
||||
while (ptr < end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) return false;
|
||||
|
||||
switch (block.type) {
|
||||
case TVG_TAG_SHAPE_STROKE_CAP: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
shape->stroke((StrokeCap) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_JOIN: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
shape->stroke((StrokeJoin) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_WIDTH: {
|
||||
if (block.length != SIZE(float)) return false;
|
||||
float width;
|
||||
READ_FLOAT(&width, block.data);
|
||||
shape->stroke(width);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_COLOR: {
|
||||
if (block.length != 4) return false;
|
||||
shape->stroke(block.data[0], block.data[1], block.data[2], block.data[3]);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_FILL: {
|
||||
auto fill = _parseShapeFill(block.data, block.end);
|
||||
if (!fill) return false;
|
||||
shape->stroke(move(move(fill)));
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_DASHPTRN: {
|
||||
if (!_parseShapeStrokeDashPattern(block.data, block.end, shape)) return false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of stroke properties, %d bytes skipped", block.type, block.type, block.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr = block.end;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShape(TvgBinBlock block, Paint* paint)
|
||||
{
|
||||
auto shape = static_cast<Shape*>(paint);
|
||||
|
||||
//Case1: Shape specific properties
|
||||
switch (block.type) {
|
||||
case TVG_TAG_SHAPE_PATH: {
|
||||
return _parseShapePath(block.data, block.end, shape);
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE: {
|
||||
return _parseShapeStroke(block.data, block.end, shape);
|
||||
}
|
||||
case TVG_TAG_SHAPE_FILL: {
|
||||
auto fill = _parseShapeFill(block.data, block.end);
|
||||
if (!fill) return false;
|
||||
shape->fill(move(fill));
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_SHAPE_COLOR: {
|
||||
if (block.length != 4) return false;
|
||||
shape->fill(block.data[0], block.data[1], block.data[2], block.data[3]);
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_SHAPE_FILLRULE: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
shape->fill((FillRule)*block.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Case2: Base Paint Properties
|
||||
return _parsePaintProperty(block, shape);
|
||||
}
|
||||
|
||||
|
||||
static bool _parsePicture(TvgBinBlock block, Paint* paint)
|
||||
{
|
||||
auto picture = static_cast<Picture*>(paint);
|
||||
|
||||
//Case1: Image Picture
|
||||
if (block.type == TVG_TAG_PICTURE_RAW_IMAGE) {
|
||||
if (block.length < 2 * SIZE(uint32_t)) return false;
|
||||
|
||||
auto ptr = block.data;
|
||||
uint32_t w, h;
|
||||
|
||||
READ_UI32(&w, ptr);
|
||||
ptr += SIZE(uint32_t);
|
||||
READ_UI32(&h, ptr);
|
||||
ptr += SIZE(uint32_t);
|
||||
|
||||
auto size = w * h * SIZE(uint32_t);
|
||||
if (block.length != 2 * SIZE(uint32_t) + size) return false;
|
||||
|
||||
picture->load((uint32_t*) ptr, w, h, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Case2: Base Paint Properties
|
||||
if (_parsePaintProperty(block, picture)) return true;
|
||||
|
||||
//Vector Picture won't be requested since Saver replaces it with the Scene
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static Paint* _parsePaint(TvgBinBlock baseBlock)
|
||||
{
|
||||
bool (*parser)(TvgBinBlock, Paint*);
|
||||
Paint *paint;
|
||||
|
||||
//1. Decide the type of paint.
|
||||
switch (baseBlock.type) {
|
||||
case TVG_TAG_CLASS_SCENE: {
|
||||
paint = Scene::gen().release();
|
||||
parser = _parseScene;
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_CLASS_SHAPE: {
|
||||
paint = Shape::gen().release();
|
||||
parser = _parseShape;
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_CLASS_PICTURE: {
|
||||
paint = Picture::gen().release();
|
||||
parser = _parsePicture;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto ptr = baseBlock.data;
|
||||
|
||||
//2. Read Subsquent properties of the current paint.
|
||||
while (ptr < baseBlock.end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > baseBlock.end) return paint;
|
||||
if (!parser(block, paint)) {
|
||||
TVGERR("TVG", "Encountered the wrong paint properties... Paint Class %d (0x%x)", baseBlock.type, baseBlock.type);
|
||||
return paint;
|
||||
}
|
||||
ptr = block.end;
|
||||
}
|
||||
return paint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
unique_ptr<Scene> TvgBinInterpreter::run(const char *ptr, const char* end)
|
||||
{
|
||||
auto scene = Scene::gen();
|
||||
if (!scene) return nullptr;
|
||||
|
||||
while (ptr < end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) {
|
||||
TVGERR("TVG", "Corrupted tvg file.");
|
||||
return nullptr;
|
||||
}
|
||||
scene->push(unique_ptr<Paint>(_parsePaint(block)));
|
||||
ptr = block.end;
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_TVG_COMMON_H_
|
||||
#define _TVG_TVG_COMMON_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgBinaryDesc.h"
|
||||
|
||||
#define SIZE(A) sizeof(A)
|
||||
#define READ_UI32(dst, src) memcpy(dst, (src), sizeof(uint32_t))
|
||||
#define READ_FLOAT(dst, src) memcpy(dst, (src), sizeof(float))
|
||||
|
||||
|
||||
/* Interface for Tvg Binary Interpreter */
|
||||
class TvgBinInterpreterBase
|
||||
{
|
||||
public:
|
||||
virtual ~TvgBinInterpreterBase() {}
|
||||
|
||||
/* ptr: points the tvg binary body (after header)
|
||||
end: end of the tvg binary data */
|
||||
virtual unique_ptr<Scene> run(const char* ptr, const char* end) = 0;
|
||||
};
|
||||
|
||||
|
||||
/* Version 0 */
|
||||
class TvgBinInterpreter : public TvgBinInterpreterBase
|
||||
{
|
||||
public:
|
||||
unique_ptr<Scene> run(const char* ptr, const char* end) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_TVG_COMMON_H_
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <memory.h>
|
||||
#include <fstream>
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgTvgLoader.h"
|
||||
#include "tvgLzw.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
void TvgLoader::clear()
|
||||
{
|
||||
if (copy) free((char*)data);
|
||||
ptr = data = nullptr;
|
||||
size = 0;
|
||||
copy = false;
|
||||
|
||||
if (interpreter) {
|
||||
delete(interpreter);
|
||||
interpreter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* WARNING: Header format shall not change! */
|
||||
bool TvgLoader::readHeader()
|
||||
{
|
||||
if (!ptr) return false;
|
||||
|
||||
//Make sure the size is large enough to hold the header
|
||||
if (size < TVG_HEADER_SIZE) return false;
|
||||
|
||||
//1. Signature
|
||||
if (memcmp(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH)) return false;
|
||||
ptr += TVG_HEADER_SIGNATURE_LENGTH;
|
||||
|
||||
//2. Version
|
||||
char version[TVG_HEADER_VERSION_LENGTH + 1];
|
||||
memcpy(version, ptr, TVG_HEADER_VERSION_LENGTH);
|
||||
version[TVG_HEADER_VERSION_LENGTH - 1] = '\0';
|
||||
ptr += TVG_HEADER_VERSION_LENGTH;
|
||||
this->version = atoi(version);
|
||||
if (this->version > THORVG_VERSION_NUMBER()) {
|
||||
TVGLOG("TVG", "This TVG file expects a higher version(%d) of ThorVG symbol(%d)", this->version, THORVG_VERSION_NUMBER());
|
||||
}
|
||||
|
||||
//3. View Size
|
||||
READ_FLOAT(&w, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&h, ptr);
|
||||
ptr += SIZE(float);
|
||||
|
||||
//4. Reserved
|
||||
if (*ptr & TVG_HEAD_FLAG_COMPRESSED) compressed = true;
|
||||
ptr += TVG_HEADER_RESERVED_LENGTH;
|
||||
|
||||
//5. Compressed Size if any
|
||||
if (compressed) {
|
||||
auto p = ptr;
|
||||
|
||||
//TVG_HEADER_UNCOMPRESSED_SIZE
|
||||
memcpy(&uncompressedSize, p, sizeof(uint32_t));
|
||||
p += SIZE(uint32_t);
|
||||
|
||||
//TVG_HEADER_COMPRESSED_SIZE
|
||||
memcpy(&compressedSize, p, sizeof(uint32_t));
|
||||
p += SIZE(uint32_t);
|
||||
|
||||
//TVG_HEADER_COMPRESSED_SIZE_BITS
|
||||
memcpy(&compressedSizeBits, p, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
ptr += TVG_HEADER_COMPRESS_SIZE;
|
||||
|
||||
//Decide the proper Tvg Binary Interpreter based on the current file version
|
||||
if (this->version >= 0) interpreter = new TvgBinInterpreter;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
TvgLoader::~TvgLoader()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::open(const string &path)
|
||||
{
|
||||
clear();
|
||||
|
||||
ifstream f;
|
||||
f.open(path, ifstream::in | ifstream::binary | ifstream::ate);
|
||||
|
||||
if (!f.is_open()) return false;
|
||||
|
||||
size = f.tellg();
|
||||
f.seekg(0, ifstream::beg);
|
||||
|
||||
copy = true;
|
||||
data = (char*)malloc(size);
|
||||
if (!data) {
|
||||
clear();
|
||||
f.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.read((char*)data, size))
|
||||
{
|
||||
clear();
|
||||
f.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
ptr = data;
|
||||
|
||||
return readHeader();
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::open(const char *data, uint32_t size, bool copy)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (copy) {
|
||||
this->data = (char*)malloc(size);
|
||||
if (!this->data) return false;
|
||||
memcpy((char*)this->data, data, size);
|
||||
} else this->data = data;
|
||||
|
||||
this->ptr = this->data;
|
||||
this->size = size;
|
||||
this->copy = copy;
|
||||
|
||||
return readHeader();
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::resize(Paint* paint, float w, float h)
|
||||
{
|
||||
if (!paint) return false;
|
||||
|
||||
auto sx = w / this->w;
|
||||
auto sy = h / this->h;
|
||||
|
||||
//Scale
|
||||
auto scale = sx < sy ? sx : sy;
|
||||
paint->scale(scale);
|
||||
|
||||
//Align
|
||||
float tx = 0, ty = 0;
|
||||
auto sw = this->w * scale;
|
||||
auto sh = this->h * scale;
|
||||
if (sw > sh) ty -= (h - sh) * 0.5f;
|
||||
else tx -= (w - sw) * 0.5f;
|
||||
paint->translate(-tx, -ty);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::read()
|
||||
{
|
||||
if (!ptr || size == 0) return false;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::close()
|
||||
{
|
||||
this->done();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TvgLoader::run(unsigned tid)
|
||||
{
|
||||
if (root) root.reset();
|
||||
|
||||
auto data = const_cast<char*>(ptr);
|
||||
|
||||
if (compressed) {
|
||||
data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize);
|
||||
root = interpreter->run(data, data + uncompressedSize);
|
||||
free(data);
|
||||
} else {
|
||||
root = interpreter->run(data, this->data + size);
|
||||
}
|
||||
|
||||
if (!root) clear();
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Paint> TvgLoader::paint()
|
||||
{
|
||||
this->done();
|
||||
if (root) return move(root);
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_TVG_LOADER_H_
|
||||
#define _TVG_TVG_LOADER_H_
|
||||
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgTvgCommon.h"
|
||||
|
||||
|
||||
class TvgLoader : public LoadModule, public Task
|
||||
{
|
||||
public:
|
||||
const char* data = nullptr;
|
||||
const char* ptr = nullptr;
|
||||
uint32_t size = 0;
|
||||
uint16_t version = 0;
|
||||
unique_ptr<Scene> root = nullptr;
|
||||
TvgBinInterpreterBase* interpreter = nullptr;
|
||||
uint32_t uncompressedSize = 0;
|
||||
uint32_t compressedSize = 0;
|
||||
uint32_t compressedSizeBits = 0;
|
||||
bool copy = false;
|
||||
bool compressed = false;
|
||||
|
||||
~TvgLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const string &path) override;
|
||||
bool open(const char *data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
bool resize(Paint* paint, float w, float h) override;
|
||||
unique_ptr<Paint> paint() override;
|
||||
|
||||
private:
|
||||
bool readHeader();
|
||||
void run(unsigned tid) override;
|
||||
void clear();
|
||||
};
|
||||
|
||||
#endif //_TVG_TVG_LOADER_H_
|
|
@ -0,0 +1,773 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSaveModule.h"
|
||||
#include "tvgTvgSaver.h"
|
||||
#include "tvgLzw.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
static FILE* _fopen(const char* filename, const char* mode)
|
||||
{
|
||||
#if defined(_MSC_VER) && defined(__clang__)
|
||||
FILE *fp;
|
||||
auto err = fopen_s(&fp, filename, mode);
|
||||
if (err != 0) return nullptr;
|
||||
return fp;
|
||||
#else
|
||||
auto fp = fopen(filename, mode);
|
||||
if (!fp) return nullptr;
|
||||
return fp;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define SIZE(A) sizeof(A)
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt)
|
||||
{
|
||||
return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt;
|
||||
}
|
||||
|
||||
|
||||
/* if the properties are identical, we can merge the shapes. */
|
||||
static bool _merge(Shape* from, Shape* to)
|
||||
{
|
||||
uint8_t r, g, b, a;
|
||||
uint8_t r2, g2, b2, a2;
|
||||
|
||||
//fill
|
||||
if (from->fill() || to->fill()) return false;
|
||||
|
||||
r = g = b = a = r2 = g2 = b2 = a2 = 0;
|
||||
|
||||
from->fillColor(&r, &g, &b, &a);
|
||||
to->fillColor(&r2, &g2, &b2, &a2);
|
||||
|
||||
if (r != r2 || g != g2 || b != b2 || a != a2) return false;
|
||||
|
||||
//composition
|
||||
if (from->composite(nullptr) != CompositeMethod::None) return false;
|
||||
if (to->composite(nullptr) != CompositeMethod::None) return false;
|
||||
|
||||
//opacity
|
||||
if (from->opacity() != to->opacity()) return false;
|
||||
|
||||
//transform
|
||||
auto t1 = from->transform();
|
||||
auto t2 = to->transform();
|
||||
|
||||
if (!mathEqual(t1.e11, t2.e11) || !mathEqual(t1.e12, t2.e12) || !mathEqual(t1.e13, t2.e13) ||
|
||||
!mathEqual(t1.e21, t2.e21) || !mathEqual(t1.e22, t2.e22) || !mathEqual(t1.e23, t2.e23) ||
|
||||
!mathEqual(t1.e31, t2.e31) || !mathEqual(t1.e32, t2.e32) || !mathEqual(t1.e33, t2.e33)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//stroke
|
||||
r = g = b = a = r2 = g2 = b2 = a2 = 0;
|
||||
|
||||
from->strokeColor(&r, &g, &b, &a);
|
||||
to->strokeColor(&r2, &g2, &b2, &a2);
|
||||
|
||||
if (r != r2 || g != g2 || b != b2 || a != a2) return false;
|
||||
|
||||
if (fabs(from->strokeWidth() - to->strokeWidth()) > FLT_EPSILON) return false;
|
||||
|
||||
//OPTIMIZE: Yet we can't merge outlining shapes unless we can support merging shapes feature.
|
||||
if (from->strokeWidth() > 0 || to->strokeWidth() > 0) return false;
|
||||
|
||||
if (from->strokeCap() != to->strokeCap()) return false;
|
||||
if (from->strokeJoin() != to->strokeJoin()) return false;
|
||||
if (from->strokeDash(nullptr) > 0 || to->strokeDash(nullptr) > 0) return false;
|
||||
if (from->strokeFill() || to->strokeFill()) return false;
|
||||
|
||||
//fill rule
|
||||
if (from->fillRule() != to->fillRule()) return false;
|
||||
|
||||
//Good, identical shapes, we can merge them.
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = from->pathCommands(&cmds);
|
||||
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = from->pathCoords(&pts);
|
||||
|
||||
to->appendPath(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::saveEncoding(const std::string& path)
|
||||
{
|
||||
if (!compress) return flushTo(path);
|
||||
|
||||
//Try encoding
|
||||
auto uncompressed = buffer.data + headerSize;
|
||||
auto uncompressedSize = buffer.count - headerSize;
|
||||
|
||||
uint32_t compressedSize, compressedSizeBits;
|
||||
|
||||
auto compressed = lzwEncode(uncompressed, uncompressedSize, &compressedSize, &compressedSizeBits);
|
||||
|
||||
//Failed compression.
|
||||
if (!compressed) return flushTo(path);
|
||||
|
||||
//Optimization is ineffective.
|
||||
if (compressedSize >= uncompressedSize) {
|
||||
free(compressed);
|
||||
return flushTo(path);
|
||||
}
|
||||
|
||||
TVGLOG("TVG_SAVER", "%s, compressed: %d -> %d, saved rate: %3.2f%%", path.c_str(), uncompressedSize, compressedSize, (1 - ((float) compressedSize / (float) uncompressedSize)) * 100);
|
||||
|
||||
//Update compress size in the header.
|
||||
uncompressed -= (TVG_HEADER_COMPRESS_SIZE + TVG_HEADER_RESERVED_LENGTH);
|
||||
|
||||
//Compression Flag
|
||||
*uncompressed |= TVG_HEAD_FLAG_COMPRESSED;
|
||||
uncompressed += TVG_HEADER_RESERVED_LENGTH;
|
||||
|
||||
//Uncompressed Size
|
||||
memcpy(uncompressed, &uncompressedSize, TVG_HEADER_UNCOMPRESSED_SIZE);
|
||||
uncompressed += TVG_HEADER_UNCOMPRESSED_SIZE;
|
||||
|
||||
//Comprssed Size
|
||||
memcpy(uncompressed, &compressedSize, TVG_HEADER_COMPRESSED_SIZE);
|
||||
uncompressed += TVG_HEADER_COMPRESSED_SIZE;
|
||||
|
||||
//Compressed Size Bits
|
||||
memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS);
|
||||
|
||||
//Good optimization, flush to file.
|
||||
auto fp = _fopen(path.c_str(), "w+");
|
||||
if (!fp) goto fail;
|
||||
|
||||
//write header
|
||||
if (fwrite(buffer.data, SIZE(uint8_t), headerSize, fp) == 0) goto fail;
|
||||
|
||||
//write compressed data
|
||||
if (fwrite(compressed, SIZE(uint8_t), compressedSize, fp) == 0) goto fail;
|
||||
|
||||
fclose(fp);
|
||||
free(compressed);
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if (fp) fclose(fp);
|
||||
if (compressed) free(compressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::flushTo(const std::string& path)
|
||||
{
|
||||
auto fp = _fopen(path.c_str(), "w+");
|
||||
if (!fp) return false;
|
||||
|
||||
if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* WARNING: Header format shall not changed! */
|
||||
bool TvgSaver::writeHeader()
|
||||
{
|
||||
headerSize = TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + SIZE(vsize) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE;
|
||||
|
||||
buffer.grow(headerSize);
|
||||
|
||||
//1. Signature
|
||||
auto ptr = buffer.ptr();
|
||||
memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH);
|
||||
ptr += TVG_HEADER_SIGNATURE_LENGTH;
|
||||
|
||||
//2. Version
|
||||
memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH);
|
||||
ptr += TVG_HEADER_VERSION_LENGTH;
|
||||
|
||||
buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH);
|
||||
|
||||
//3. View Size
|
||||
writeData(vsize, SIZE(vsize));
|
||||
ptr += SIZE(vsize);
|
||||
|
||||
//4. Reserved data + Compress size
|
||||
memset(ptr, 0x00, TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
|
||||
buffer.count += (TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::writeTag(TvgBinTag tag)
|
||||
{
|
||||
buffer.grow(SIZE(TvgBinTag));
|
||||
memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag));
|
||||
buffer.count += SIZE(TvgBinTag);
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::writeCount(TvgBinCounter cnt)
|
||||
{
|
||||
buffer.grow(SIZE(TvgBinCounter));
|
||||
memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter));
|
||||
buffer.count += SIZE(TvgBinCounter);
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::writeReservedCount(TvgBinCounter cnt)
|
||||
{
|
||||
memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter));
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::reserveCount()
|
||||
{
|
||||
buffer.grow(SIZE(TvgBinCounter));
|
||||
buffer.count += SIZE(TvgBinCounter);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::writeData(const void* data, TvgBinCounter cnt)
|
||||
{
|
||||
buffer.grow(cnt);
|
||||
memcpy(buffer.ptr(), data, cnt);
|
||||
buffer.count += cnt;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data)
|
||||
{
|
||||
auto growCnt = SERIAL_DONE(cnt);
|
||||
|
||||
buffer.grow(growCnt);
|
||||
|
||||
auto ptr = buffer.ptr();
|
||||
|
||||
*ptr = tag;
|
||||
++ptr;
|
||||
|
||||
memcpy(ptr, &cnt, SIZE(TvgBinCounter));
|
||||
ptr += SIZE(TvgBinCounter);
|
||||
|
||||
memcpy(ptr, data, cnt);
|
||||
ptr += cnt;
|
||||
|
||||
buffer.count += growCnt;
|
||||
|
||||
return growCnt;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag)
|
||||
{
|
||||
if (!mathIdentity(transform)) return writeTagProperty(tag, SIZE(Matrix), transform);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializePaint(const Paint* paint, const Matrix* pTransform)
|
||||
{
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//opacity
|
||||
auto opacity = paint->opacity();
|
||||
if (opacity < 255) {
|
||||
cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, SIZE(opacity), &opacity);
|
||||
}
|
||||
|
||||
//composite
|
||||
const Paint* cmpTarget = nullptr;
|
||||
auto cmpMethod = paint->composite(&cmpTarget);
|
||||
if (cmpMethod != CompositeMethod::None && cmpTarget) {
|
||||
cnt += serializeComposite(cmpTarget, cmpMethod, pTransform);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
/* Propagate parents properties to the child so that we can skip saving the parent. */
|
||||
TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, const Matrix* transform)
|
||||
{
|
||||
const Paint* compTarget = nullptr;
|
||||
auto compMethod = parent->composite(&compTarget);
|
||||
|
||||
/* If the parent & the only child have composition, we can't skip the parent...
|
||||
Or if the parent has the transform and composition, we can't skip the parent... */
|
||||
if (compMethod != CompositeMethod::None) {
|
||||
if (transform || child->composite(nullptr) != CompositeMethod::None) return 0;
|
||||
}
|
||||
|
||||
//propagate opacity
|
||||
uint32_t opacity = parent->opacity();
|
||||
|
||||
if (opacity < 255) {
|
||||
uint32_t tmp = (child->opacity() * opacity);
|
||||
if (tmp > 0) tmp /= 255;
|
||||
const_cast<Paint*>(child)->opacity(tmp);
|
||||
}
|
||||
|
||||
//propagate composition
|
||||
if (compTarget) const_cast<Paint*>(child)->composite(unique_ptr<Paint>(compTarget->duplicate()), compMethod);
|
||||
|
||||
return serialize(child, transform);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform)
|
||||
{
|
||||
auto it = this->iterator(scene);
|
||||
if (it->count() == 0) {
|
||||
delete(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Case - Only Child: Skip saving this scene.
|
||||
if (it->count() == 1) {
|
||||
auto cnt = serializeChild(scene, it->next(), cTransform);
|
||||
if (cnt > 0) {
|
||||
delete(it);
|
||||
return cnt;
|
||||
}
|
||||
}
|
||||
|
||||
it->begin();
|
||||
|
||||
//Case - Delegator Scene: This scene is just a delegator, we can skip this:
|
||||
if (scene->composite(nullptr) == CompositeMethod::None && scene->opacity() == 255) {
|
||||
auto ret = serializeChildren(it, cTransform, false);
|
||||
delete(it);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Case - Serialize Scene & its children
|
||||
writeTag(TVG_TAG_CLASS_SCENE);
|
||||
reserveCount();
|
||||
|
||||
auto cnt = serializeChildren(it, cTransform, true) + serializePaint(scene, pTransform);
|
||||
|
||||
delete(it);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform)
|
||||
{
|
||||
const Fill::ColorStop* stops = nullptr;
|
||||
auto stopsCnt = fill->colorStops(&stops);
|
||||
if (!stops || stopsCnt == 0) return 0;
|
||||
|
||||
writeTag(tag);
|
||||
reserveCount();
|
||||
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//radial fill
|
||||
if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
|
||||
float args[3];
|
||||
static_cast<const RadialGradient*>(fill)->radial(args, args + 1, args + 2);
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, SIZE(args), args);
|
||||
//linear fill
|
||||
} else {
|
||||
float args[4];
|
||||
static_cast<const LinearGradient*>(fill)->linear(args, args + 1, args + 2, args + 3);
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, SIZE(args), args);
|
||||
}
|
||||
|
||||
if (auto flag = static_cast<TvgBinFlag>(fill->spread()))
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag);
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops);
|
||||
|
||||
auto gTransform = fill->transform();
|
||||
if (pTransform) gTransform = mathMultiply(pTransform, &gTransform);
|
||||
|
||||
cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform)
|
||||
{
|
||||
writeTag(TVG_TAG_SHAPE_STROKE);
|
||||
reserveCount();
|
||||
|
||||
//width
|
||||
auto width = shape->strokeWidth();
|
||||
if (preTransform) width *= sqrtf(powf(pTransform->e11, 2.0f) + powf(pTransform->e21, 2.0f)); //we know x/y scaling factors are same.
|
||||
auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, SIZE(width), &width);
|
||||
|
||||
//cap
|
||||
if (auto flag = static_cast<TvgBinFlag>(shape->strokeCap()))
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
|
||||
|
||||
//join
|
||||
if (auto flag = static_cast<TvgBinFlag>(shape->strokeJoin()))
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag);
|
||||
|
||||
//fill
|
||||
if (auto fill = shape->strokeFill()) {
|
||||
cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr));
|
||||
} else {
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
shape->strokeColor(color, color + 1, color + 2, color + 3);
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, SIZE(color), &color);
|
||||
}
|
||||
|
||||
//dash
|
||||
const float* dashPattern = nullptr;
|
||||
auto dashCnt = shape->strokeDash(&dashPattern);
|
||||
if (dashPattern && dashCnt > 0) {
|
||||
TvgBinCounter dashCntSize = SIZE(dashCnt);
|
||||
TvgBinCounter dashPtrnSize = dashCnt * SIZE(dashPattern[0]);
|
||||
|
||||
writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN);
|
||||
writeCount(dashCntSize + dashPtrnSize);
|
||||
cnt += writeData(&dashCnt, dashCntSize);
|
||||
cnt += writeData(dashPattern, dashPtrnSize);
|
||||
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
|
||||
}
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transform, bool preTransform)
|
||||
{
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = shape->pathCommands(&cmds);
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = shape->pathCoords(&pts);
|
||||
|
||||
if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0;
|
||||
|
||||
writeTag(TVG_TAG_SHAPE_PATH);
|
||||
reserveCount();
|
||||
|
||||
/* Reduce the binary size.
|
||||
Convert PathCommand(4 bytes) to TvgBinFlag(1 byte) */
|
||||
TvgBinFlag* outCmds = (TvgBinFlag*)alloca(SIZE(TvgBinFlag) * cmdCnt);
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
outCmds[i] = static_cast<TvgBinFlag>(cmds[i]);
|
||||
}
|
||||
|
||||
auto cnt = writeData(&cmdCnt, SIZE(cmdCnt));
|
||||
cnt += writeData(&ptsCnt, SIZE(ptsCnt));
|
||||
cnt += writeData(outCmds, SIZE(TvgBinFlag) * cmdCnt);
|
||||
|
||||
//transform?
|
||||
if (preTransform) {
|
||||
if (!mathEqual(transform->e11, 1.0f) || !mathZero(transform->e12) || !mathZero(transform->e13) ||
|
||||
!mathZero(transform->e21) || !mathEqual(transform->e22, 1.0f) || !mathZero(transform->e23) ||
|
||||
!mathZero(transform->e31) || !mathZero(transform->e32) || !mathEqual(transform->e33, 1.0f)) {
|
||||
auto p = const_cast<Point*>(pts);
|
||||
for (uint32_t i = 0; i < ptsCnt; ++i) mathMultiply(p++, transform);
|
||||
}
|
||||
}
|
||||
|
||||
cnt += writeData(pts, ptsCnt * SIZE(pts[0]));
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform)
|
||||
{
|
||||
writeTag(TVG_TAG_CLASS_SHAPE);
|
||||
reserveCount();
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//fill rule
|
||||
if (auto flag = static_cast<TvgBinFlag>(shape->fillRule())) {
|
||||
cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag);
|
||||
}
|
||||
|
||||
//the pre-transformation can't be applied in the case when the stroke is dashed or irregulary scaled
|
||||
bool preTransform = true;
|
||||
|
||||
//stroke
|
||||
if (shape->strokeWidth() > 0) {
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
shape->strokeColor(color, color + 1, color + 2, color + 3);
|
||||
auto fill = shape->strokeFill();
|
||||
if (fill || color[3] > 0) {
|
||||
if (!mathEqual(cTransform->e11, cTransform->e22) || (mathZero(cTransform->e11) && !mathEqual(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false;
|
||||
cnt += serializeStroke(shape, cTransform, preTransform);
|
||||
}
|
||||
}
|
||||
|
||||
//fill
|
||||
if (auto fill = shape->fill()) {
|
||||
cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr));
|
||||
} else {
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
shape->fillColor(color, color + 1, color + 2, color + 3);
|
||||
if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
|
||||
}
|
||||
|
||||
cnt += serializePath(shape, cTransform, preTransform);
|
||||
|
||||
if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
|
||||
cnt += serializePaint(shape, pTransform);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
/* Picture has either a vector scene or a bitmap. */
|
||||
TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform)
|
||||
{
|
||||
auto it = this->iterator(picture);
|
||||
|
||||
//Case - Vector Scene:
|
||||
if (it->count() == 1) {
|
||||
auto cnt = serializeChild(picture, it->next(), cTransform);
|
||||
//Only child, Skip to save Picture...
|
||||
if (cnt > 0) {
|
||||
delete(it);
|
||||
return cnt;
|
||||
/* Unfortunately, we can't skip the Picture because it might have a compositor,
|
||||
Serialize Scene(instead of the Picture) & its scene. */
|
||||
} else {
|
||||
writeTag(TVG_TAG_CLASS_SCENE);
|
||||
reserveCount();
|
||||
auto cnt = serializeChildren(it, cTransform, true) + serializePaint(picture, pTransform);
|
||||
writeReservedCount(cnt);
|
||||
delete(it);
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
}
|
||||
delete(it);
|
||||
|
||||
//Case - Bitmap Image:
|
||||
uint32_t w, h;
|
||||
auto pixels = picture->data(&w, &h);
|
||||
if (!pixels) return 0;
|
||||
|
||||
writeTag(TVG_TAG_CLASS_PICTURE);
|
||||
reserveCount();
|
||||
|
||||
TvgBinCounter cnt = 0;
|
||||
TvgBinCounter sizeCnt = SIZE(w);
|
||||
TvgBinCounter imgSize = w * h * SIZE(pixels[0]);
|
||||
|
||||
writeTag(TVG_TAG_PICTURE_RAW_IMAGE);
|
||||
writeCount(2 * sizeCnt + imgSize);
|
||||
|
||||
cnt += writeData(&w, sizeCnt);
|
||||
cnt += writeData(&h, sizeCnt);
|
||||
cnt += writeData(pixels, imgSize);
|
||||
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
|
||||
|
||||
//Bitmap picture needs the transform info.
|
||||
cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
|
||||
|
||||
cnt += serializePaint(picture, pTransform);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform)
|
||||
{
|
||||
writeTag(TVG_TAG_PAINT_CMP_TARGET);
|
||||
reserveCount();
|
||||
|
||||
auto flag = static_cast<TvgBinFlag>(cmpMethod);
|
||||
auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag);
|
||||
|
||||
cnt += serialize(cmpTarget, pTransform, true);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeChildren(Iterator* it, const Matrix* pTransform, bool reserved)
|
||||
{
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//Merging shapes. the result is written in the children.
|
||||
Array<const Paint*> children;
|
||||
children.reserve(it->count());
|
||||
children.push(it->next());
|
||||
|
||||
while (auto child = it->next()) {
|
||||
if (child->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||
//only dosable if the previous child is a shape.
|
||||
auto target = children.ptr() - 1;
|
||||
if ((*target)->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||
if (_merge((Shape*)child, (Shape*)*target)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
//The children of a reserved scene
|
||||
if (reserved && children.count > 1) {
|
||||
cnt += writeTagProperty(TVG_TAG_SCENE_RESERVEDCNT, SIZE(children.count), &children.count);
|
||||
}
|
||||
|
||||
//Serialize merged children.
|
||||
auto child = children.data;
|
||||
for (uint32_t i = 0; i < children.count; ++i, ++child) {
|
||||
cnt += serialize(*child, pTransform);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform, bool compTarget)
|
||||
{
|
||||
if (!paint) return 0;
|
||||
|
||||
//Invisible paint, no point to save it if the paint is not the composition target...
|
||||
if (!compTarget && paint->opacity() == 0) return 0;
|
||||
|
||||
auto transform = const_cast<Paint*>(paint)->transform();
|
||||
if (pTransform) transform = mathMultiply(pTransform, &transform);
|
||||
|
||||
switch (paint->identifier()) {
|
||||
case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast<const Shape*>(paint), pTransform, &transform);
|
||||
case TVG_CLASS_ID_SCENE: return serializeScene(static_cast<const Scene*>(paint), pTransform, &transform);
|
||||
case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast<const Picture*>(paint), pTransform, &transform);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::run(unsigned tid)
|
||||
{
|
||||
if (!writeHeader()) return;
|
||||
|
||||
//Serialize Root Paint, without its transform.
|
||||
Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
|
||||
if (paint->opacity() > 0) {
|
||||
switch (paint->identifier()) {
|
||||
case TVG_CLASS_ID_SHAPE: {
|
||||
serializeShape(static_cast<const Shape*>(paint), nullptr, &transform);
|
||||
break;
|
||||
}
|
||||
case TVG_CLASS_ID_SCENE: {
|
||||
serializeScene(static_cast<const Scene*>(paint), nullptr, &transform);
|
||||
break;
|
||||
}
|
||||
case TVG_CLASS_ID_PICTURE: {
|
||||
serializePicture(static_cast<const Picture*>(paint), nullptr, &transform);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveEncoding(path)) return;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
TvgSaver::~TvgSaver()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::close()
|
||||
{
|
||||
this->done();
|
||||
|
||||
if (paint) {
|
||||
delete(paint);
|
||||
paint = nullptr;
|
||||
}
|
||||
if (path) {
|
||||
free(path);
|
||||
path = nullptr;
|
||||
}
|
||||
buffer.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::save(Paint* paint, const string& path, bool compress)
|
||||
{
|
||||
close();
|
||||
|
||||
float x, y;
|
||||
x = y = 0;
|
||||
paint->bounds(&x, &y, &vsize[0], &vsize[1], false);
|
||||
|
||||
//cut off the negative space
|
||||
if (x < 0) vsize[0] += x;
|
||||
if (y < 0) vsize[1] += y;
|
||||
|
||||
if (vsize[0] < FLT_EPSILON || vsize[1] < FLT_EPSILON) {
|
||||
TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->path = strdup(path.c_str());
|
||||
if (!this->path) return false;
|
||||
|
||||
this->paint = paint;
|
||||
this->compress = compress;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _TVG_TVGSAVER_H_
|
||||
#define _TVG_TVGSAVER_H_
|
||||
|
||||
#include "tvgArray.h"
|
||||
#include "tvgBinaryDesc.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class TvgSaver : public SaveModule, public Task
|
||||
{
|
||||
private:
|
||||
Array<TvgBinByte> buffer;
|
||||
Paint* paint = nullptr;
|
||||
char *path = nullptr;
|
||||
uint32_t headerSize;
|
||||
float vsize[2] = {0.0f, 0.0f};
|
||||
bool compress;
|
||||
|
||||
bool flushTo(const std::string& path);
|
||||
bool saveEncoding(const std::string& path);
|
||||
void reserveCount();
|
||||
|
||||
bool writeHeader();
|
||||
bool writeViewSize();
|
||||
void writeTag(TvgBinTag tag);
|
||||
void writeCount(TvgBinCounter cnt);
|
||||
void writeReservedCount(TvgBinCounter cnt);
|
||||
TvgBinCounter writeData(const void* data, TvgBinCounter cnt);
|
||||
TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data);
|
||||
TvgBinCounter writeTransform(const Matrix* transform, TvgBinTag tag);
|
||||
|
||||
TvgBinCounter serialize(const Paint* paint, const Matrix* pTransform, bool compTarget = false);
|
||||
TvgBinCounter serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform);
|
||||
TvgBinCounter serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform);
|
||||
TvgBinCounter serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform);
|
||||
TvgBinCounter serializePaint(const Paint* paint, const Matrix* pTransform);
|
||||
TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform);
|
||||
TvgBinCounter serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform);
|
||||
TvgBinCounter serializePath(const Shape* shape, const Matrix* transform, bool preTransform);
|
||||
TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform);
|
||||
TvgBinCounter serializeChildren(Iterator* it, const Matrix* transform, bool reserved);
|
||||
TvgBinCounter serializeChild(const Paint* parent, const Paint* child, const Matrix* pTransform);
|
||||
|
||||
public:
|
||||
~TvgSaver();
|
||||
|
||||
bool save(Paint* paint, const string& path, bool compress) override;
|
||||
bool close() override;
|
||||
void run(unsigned tid) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_SAVE_MODULE_H_
|
|
@ -0,0 +1,28 @@
|
|||
VERSION=0.7.0
|
||||
rm -rf AUTHORS inc LICENSE src *.zip
|
||||
curl -L -O https://github.com/Samsung/thorvg/archive/refs/tags/v$VERSION.zip
|
||||
bsdtar --strip-components=1 -xvf *.zip
|
||||
rm *.zip
|
||||
rm -rf .github docs pc res test tools .git* *.md *.txt wasm_build.sh
|
||||
find . -type f -name 'meson.build' -delete
|
||||
rm -rf src/bin src/bindings src/examples src/wasm
|
||||
rm -rf src/lib/gl_engine tvgcompat
|
||||
cat << EOF > inc/config.h
|
||||
#ifndef THORVG_CONFIG_H
|
||||
#define THORVG_CONFIG_H
|
||||
|
||||
#define THORVG_SW_RASTER_SUPPORT 1
|
||||
|
||||
#define THORVG_SVG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_PNG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_TVG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_TVG_SAVER_SUPPORT 1
|
||||
|
||||
#define THORVG_JPG_LOADER_SUPPORT 1
|
||||
|
||||
#define THORVG_VERSION_STRING "$VERSION"
|
||||
#endif
|
||||
EOF
|
Loading…
Reference in New Issue