feat(draw): add implements vector graphic APIs (#4528) (#4691)

Signed-off-by: zhangjipeng <zhangjipeng@xiaomi.com>
Co-authored-by: zhangjipeng <zhangjipeng@xiaomi.com>
Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Zhang Ji Peng
2023-11-09 18:37:33 +08:00
committed by GitHub
parent 3bb649db7f
commit 8cf0bbb558
108 changed files with 30939 additions and 12 deletions
+18 -1
View File
@@ -36,6 +36,14 @@ jobs:
run: scripts/install-prerequisites.sh
- name: Run tests
run: python tests/main.py --report test
- name: Archive screenshot errors
if: failure()
uses: actions/upload-artifact@v3
with:
name: screenshot-errors-amd64
path: |
tests/ref_imgs/**/*_err.png
test_screenshot_error.h
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: github.event_name == 'push'
@@ -91,4 +99,13 @@ jobs:
echo 'export PATH="/usr/lib/ccache:$PATH"' | tee -a ~/.bashrc
run: |
env PATH="/usr/lib/ccache:$PATH" ASAN_OPTIONS=detect_leaks=0 python3 tests/main.py test
env PATH="/usr/lib/ccache:$PATH" NON_AMD64_BUILD=1 ASAN_OPTIONS=detect_leaks=0 python3 tests/main.py test
- name: Archive screenshot errors
if: failure()
uses: actions/upload-artifact@v3
with:
name: screenshot-errors-${{ matrix.arch }}
path: |
tests/ref_imgs/**/*_err.png
test_screenshot_error.h
+4
View File
@@ -34,3 +34,7 @@ repos:
rev: v1.16.20
hooks:
- id: typos
exclude: |
(?x)^(
src/libs/
)
+20
View File
@@ -311,6 +311,12 @@ menu "LVGL configuration"
depends on LV_USE_DRAW_SW
help
Only used if software rotation is enabled in the display driver.
config LV_USE_VECTOR_GRAPHIC
bool "Use Vector Graphic APIs"
default n
help
Enable drawing support vector graphic APIs.
endmenu
menu "GPU"
@@ -1108,6 +1114,17 @@ menu "LVGL configuration"
config LV_USE_RLOTTIE
bool "Lottie library"
config LV_USE_THORVG
bool "ThorVG library"
choice
prompt "Use ThorVG config"
depends on LV_USE_THORVG
default LV_USE_THORVG_INTERNAL
config LV_USE_THORVG_INTERNAL
bool "Use ThorVG internal"
config LV_USE_THORVG_EXTERNAL
bool "Use ThorVG external"
endchoice
config LV_USE_FFMPEG
bool "FFmpeg library"
@@ -1408,6 +1425,9 @@ menu "LVGL configuration"
config LV_USE_DEMO_MULTILANG
bool "multi-language demo"
default n
config LV_USE_DEMO_VECTOR_GRAPHIC
bool "vector graphic demo"
default n
endmenu
endmenu
+3
View File
@@ -59,5 +59,8 @@ COMPONENT_SRCDIRS := . \
src/extra/widgets/tileview \
src/extra/widgets/win
ifeq ($(CONFIG_LV_USE_THORVG_INTERNAL),y)
COMPONENT_SRCDIRS += src/extra/libs/thorvg
endif
COMPONENT_ADD_INCLUDEDIRS := $(COMPONENT_SRCDIRS) .
+5
View File
@@ -92,10 +92,15 @@ static const demo_entry_info_t demos_entry_info[] = {
{ "scroll", .entry_cb = lv_demo_scroll },
#endif
#if LV_USE_DEMO_VECTOR_GRAPHIC && LV_USE_VECTOR_GRAPHIC
{ "vector_graphic", .entry_cb = lv_demo_vector_graphic },
#endif
//#if LV_USE_DEMO_BENCHMARK
// { DEMO_BENCHMARK_NAME, .entry_benchmark_cb = lv_demo_benchmark, 1 },
// { DEMO_BENCHMARK_SCENE_NAME, .entry_benchmark_scene_cb = lv_demo_benchmark_run_scene, 2 },
//#endif
{ "", .entry_cb = NULL }
};
+4
View File
@@ -51,6 +51,10 @@ extern "C" {
#include "multilang/lv_demo_multilang.h"
#endif
#if LV_USE_DEMO_VECTOR_GRAPHIC && LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL)
#include "vector_graphic/lv_demo_vector_graphic.h"
#endif
#if LV_USE_DEMO_RENDER
#include "render/lv_demo_render.h"
#endif
File diff suppressed because one or more lines are too long
@@ -0,0 +1,245 @@
/**
* @file lv_demo_vector_graphic.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_demo_vector_graphic.h"
#if LV_USE_DEMO_VECTOR_GRAPHIC
/*********************
* DEFINES
*********************/
#define WIDTH 640
#define HEIGHT 480
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void draw_pattern(lv_vector_dsc_t * ctx, lv_vector_path_t * path)
{
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_fpoint_t pts[] = {{200, 200}, {300, 200}, {300, 300}, {200, 300}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_line_to(path, &pts[1]);
lv_vector_path_quad_to(path, &pts[2], &pts[3]);
lv_vector_path_close(path);
lv_draw_image_dsc_t img_dsc;
lv_draw_image_dsc_init(&img_dsc);
LV_IMAGE_DECLARE(img_demo_vector_avatar);
img_dsc.header = img_demo_vector_avatar.header;
img_dsc.src = &img_demo_vector_avatar;
lv_vector_dsc_set_fill_image(ctx, &img_dsc);
lv_vector_dsc_translate(ctx, 250, 250);
lv_vector_dsc_rotate(ctx, 25);
lv_vector_dsc_translate(ctx, -250, -250);
lv_vector_dsc_add_path(ctx, path); // draw a path
}
static void draw_gradient(lv_vector_dsc_t * ctx, lv_vector_path_t * path)
{
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_fpoint_t pts[] = {{400, 200}, {600, 200}, {400, 400}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_quad_to(path, &pts[1], &pts[2]);
lv_vector_path_close(path);
lv_grad_dsc_t grad;
grad.dir = LV_GRAD_DIR_HOR;
grad.stops_count = 2;
grad.stops[0].color = lv_color_hex(0xff0000);
grad.stops[0].opa = LV_OPA_COVER;
grad.stops[0].frac = 0;
grad.stops[1].color = lv_color_hex(0x00ff00);
grad.stops[1].opa = LV_OPA_COVER;
grad.stops[1].frac = 255;
// grad.stops[2].color = lv_color_hex(0x0000ff);
// grad.stops[2].opa = LV_OPA_COVER;
// grad.stops[2].frac = 255;
lv_matrix_t mt;
lv_matrix_identity(&mt);
lv_matrix_rotate(&mt, 30);
lv_vector_dsc_set_fill_transform(ctx, &mt);
lv_vector_dsc_set_fill_linear_gradient(ctx, &grad, LV_VECTOR_GRADIENT_SPREAD_PAD);
lv_vector_dsc_add_path(ctx, path); // draw a path
}
static void draw_radial_gradient(lv_vector_dsc_t * ctx, lv_vector_path_t * path)
{
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_fpoint_t pts[] = {{400, 50}, {500, 50}, {500, 200}, {400, 200}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_line_to(path, &pts[1]);
lv_vector_path_line_to(path, &pts[2]);
lv_vector_path_line_to(path, &pts[3]);
lv_vector_path_close(path);
lv_grad_dsc_t grad;
grad.dir = LV_GRAD_DIR_HOR;
grad.stops_count = 2;
grad.stops[0].color = lv_color_hex(0xff0000);
grad.stops[0].opa = LV_OPA_COVER;
grad.stops[0].frac = 0;
grad.stops[1].color = lv_color_hex(0x0000ff);
grad.stops[1].opa = LV_OPA_COVER;
grad.stops[1].frac = 255;
// grad.stops[2].color = lv_color_hex(0x0000ff);
// grad.stops[2].opa = LV_OPA_COVER;
// grad.stops[2].frac = 255;
lv_vector_dsc_set_fill_radial_gradient(ctx, &grad, 50, 50, 20, LV_VECTOR_GRADIENT_SPREAD_REFLECT);
lv_vector_dsc_add_path(ctx, path); // draw a path
}
static void draw_shapes(lv_vector_dsc_t * ctx, lv_vector_path_t * path)
{
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_fpoint_t pts[] = {{50, 50}, {200, 200}, {50, 200}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_line_to(path, &pts[1]);
lv_vector_path_line_to(path, &pts[2]);
lv_vector_path_close(path);
lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xFF, 0x00, 0x00));
lv_vector_dsc_scale(ctx, 0.5, 0.5);
lv_vector_dsc_add_path(ctx, path); // draw a path
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_area_t rect = {300, 300, 400, 400};
lv_vector_path_append_rect(path, &rect, 50, 60);
lv_vector_dsc_set_fill_color(ctx, lv_color_make(0x00, 0x80, 0xff));
lv_vector_dsc_skew(ctx, 5, 0);
lv_vector_dsc_add_path(ctx, path); // draw a path
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_area_t rect2 = {100, 300, 200, 400};
lv_vector_path_append_rect(path, &rect2, 10, 10);
lv_vector_dsc_set_fill_color(ctx, lv_color_make(0x80, 0x00, 0x80));
lv_vector_path_t * path2 = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
lv_fpoint_t p = {50, 420};
lv_vector_path_append_circle(path2, &p, 50, 30);
lv_vector_path_append_path(path, path2);
lv_vector_dsc_add_path(ctx, path); // draw a path
lv_vector_path_delete(path2);
}
static void draw_lines(lv_vector_dsc_t * ctx, lv_vector_path_t * path)
{
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_fpoint_t pts[] = {{50, 50}, {200, 200}, {250, 300}, {350, 150}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_cubic_to(path, &pts[1], &pts[2], &pts[3]);
lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0x00, 0xff, 0x00));
lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_COVER);
lv_vector_dsc_set_fill_opa(ctx, LV_OPA_0);
lv_vector_dsc_set_stroke_width(ctx, 8.0f);
float dashes[] = {10, 15, 20, 12};
lv_vector_dsc_set_stroke_dash(ctx, dashes, 4);
lv_vector_dsc_add_path(ctx, path); // draw a path
lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_0);
lv_vector_dsc_set_fill_opa(ctx, LV_OPA_COVER);
}
static void draw_blend(lv_vector_dsc_t * ctx, lv_vector_path_t * path)
{
lv_vector_path_clear(path);
lv_vector_dsc_identity(ctx);
lv_fpoint_t pts[] = {{200, 200}, {400, 200}, {450, 350}, {350, 150}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_cubic_to(path, &pts[1], &pts[2], &pts[3]);
lv_vector_path_close(path);
lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xFF, 0x00, 0xFF));
lv_vector_dsc_set_blend_mode(ctx, LV_VECTOR_BLEND_SCREEN);
lv_vector_dsc_add_path(ctx, path); // draw a path
}
static void draw_vector(lv_layer_t * layer)
{
lv_vector_dsc_t * ctx = lv_vector_dsc_create(layer);
lv_area_t rect = {0, 100, 300, 300};
lv_vector_dsc_set_fill_color(ctx, lv_color_lighten(lv_color_black(), 50));
lv_vector_clear_area(ctx, &rect); // clear screen
lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
draw_shapes(ctx, path);
draw_lines(ctx, path);
draw_pattern(ctx, path);
draw_radial_gradient(ctx, path);
draw_gradient(ctx, path);
draw_blend(ctx, path);
lv_draw_vector(ctx); // submit draw
lv_vector_path_delete(path);
lv_vector_dsc_delete(ctx);
}
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_demo_vector_graphic(void)
{
static uint8_t canvas_buf[WIDTH * HEIGHT * 4];
lv_obj_t * canvas = lv_canvas_create(lv_scr_act());
lv_canvas_set_buffer(canvas, canvas_buf, WIDTH, HEIGHT, LV_COLOR_FORMAT_ARGB8888);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
draw_vector(&layer);
lv_canvas_finish_layer(canvas, &layer);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif
@@ -0,0 +1,40 @@
/**
* @file lv_demo_vector_graphic.h
*
*/
#ifndef LV_DEMO_VECTOR_GRAPHIC_H
#define LV_DEMO_VECTOR_GRAPHIC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_demos.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_demo_vector_graphic(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DEMO_VECTOR_GRAPHIC_H*/
+9
View File
@@ -19,6 +19,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
file(GLOB_RECURSE SOURCES ${LVGL_ROOT_DIR}/src/*.c)
file(GLOB_RECURSE EXAMPLE_SOURCES ${LVGL_ROOT_DIR}/examples/*.c)
file(GLOB_RECURSE DEMO_SOURCES ${LVGL_ROOT_DIR}/demos/*.c)
file(GLOB_RECURSE THORVG_SOURCES ${LVGL_ROOT_DIR}/src/libs/thorvg/*.cpp)
# Build LVGL library
add_library(lvgl ${SOURCES})
@@ -31,11 +32,19 @@ target_compile_definitions(
# Add definition of LV_CONF_PATH only if needed
if(LV_CONF_PATH)
target_compile_definitions(lvgl PUBLIC LV_CONF_PATH=${LV_CONF_PATH})
target_compile_definitions(lvgl_thorvg PUBLIC LV_CONF_PATH=${LV_CONF_PATH})
endif()
# Include root and optional parent path of LV_CONF_PATH
target_include_directories(lvgl SYSTEM PUBLIC ${LVGL_ROOT_DIR} ${LV_CONF_DIR})
if(NOT LV_CONF_BUILD_DISABLE_THORVG_INTERNAL)
add_library(lvgl_thorvg ${THORVG_SOURCES})
add_library(lvgl::thorvg ALIAS lvgl_thorvg)
target_include_directories(lvgl_thorvg SYSTEM PUBLIC ${LVGL_ROOT_DIR}/src/libs/thorvg)
endif()
# Build LVGL example library
if(NOT LV_CONF_BUILD_DISABLE_EXAMPLES)
add_library(lvgl_examples ${EXAMPLE_SOURCES})
View File
+6
View File
@@ -45,3 +45,9 @@ Draw a line to the canvas
.. lv_example:: widgets/canvas/lv_example_canvas_7
:language: c
Draw a vector graphic to the canvas
-------------------------
.. lv_example:: widgets/canvas/lv_example_canvas_8
:language: c
@@ -0,0 +1,42 @@
#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES && LV_USE_VECTOR_GRAPHIC
#define CANVAS_WIDTH 150
#define CANVAS_HEIGHT 150
/**
* Draw a path to the canvas
*/
void lv_example_canvas_8(void)
{
/*Create a buffer for the canvas*/
static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];
/*Create a canvas and initialize its palette*/
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_NATIVE);
lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER);
lv_obj_center(canvas);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_vector_dsc_t * dsc = lv_vector_dsc_create(&layer);
lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
lv_fpoint_t pts[] = {{10, 10}, {130, 130}, {10, 130}};
lv_vector_path_move_to(path, &pts[0]);
lv_vector_path_line_to(path, &pts[1]);
lv_vector_path_line_to(path, &pts[2]);
lv_vector_path_close(path);
lv_vector_dsc_set_fill_color(dsc, lv_color_make(0x00, 0x80, 0xff));
lv_vector_dsc_add_path(dsc, path);
lv_draw_vector(dsc);
lv_vector_path_delete(path);
lv_vector_dsc_delete(dsc);
lv_canvas_finish_layer(canvas, &layer);
}
#endif
+1
View File
@@ -55,6 +55,7 @@ void lv_example_canvas_4(void);
void lv_example_canvas_5(void);
void lv_example_canvas_6(void);
void lv_example_canvas_7(void);
void lv_example_canvas_8(void);
void lv_example_chart_1(void);
void lv_example_chart_2(void);
+12
View File
@@ -653,6 +653,15 @@
/*Rlottie library*/
#define LV_USE_RLOTTIE 0
/*Enable Vector Graphic APIs*/
#define LV_USE_VECTOR_GRAPHIC 0
/* Enable ThorVG (vector graphics library) from the src/libs folder */
#define LV_USE_THORVG_INTERNAL 0
/* Enable ThorVG by assuming that its installed and linked to the project */
#define LV_USE_THORVG_EXTERNAL 0
/*FFmpeg library for image decoding and playing videos
*Supports all major image formats so do not enable other image decoder with it*/
#define LV_USE_FFMPEG 0
@@ -851,6 +860,9 @@
/*Demonstrate scroll settings*/
#define LV_USE_DEMO_SCROLL 0
/*Vector graphic demo*/
#define LV_USE_DEMO_VECTOR_GRAPHIC 0
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/
+2
View File
@@ -30,6 +30,7 @@ extern "C" {
#include "src/misc/lv_log.h"
#include "src/misc/lv_timer.h"
#include "src/misc/lv_math.h"
#include "src/misc/lv_array.h"
#include "src/misc/lv_async.h"
#include "src/misc/lv_anim_timeline.h"
#include "src/misc/lv_profiler_builtin.h"
@@ -105,6 +106,7 @@ extern "C" {
#include "src/layouts/lv_layout.h"
#include "src/draw/lv_draw.h"
#include "src/draw/lv_draw_vector.h"
#include "src/themes/lv_theme.h"
+2
View File
@@ -3,4 +3,6 @@ LVGL_PATH ?= ${shell pwd}/lvgl
CSRCS += $(shell find $(LVGL_PATH)/src -type f -name '*.c')
CSRCS += $(shell find $(LVGL_PATH)/demos -type f -name '*.c')
CSRCS += $(shell find $(LVGL_PATH)/examples -type f -name '*.c')
CXXSRCS += $(shell find $(LVGL_PATH)/src/libs/thorvg -type f -name '*.cpp')
CFLAGS += "-I$(LVGL_PATH)"
+10 -9
View File
@@ -30,15 +30,16 @@
--exclude=../src/lv_conf_internal.h
--exclude=../src/core/lv_obj_style_gen.c
--exclude=../src/core/lv_obj_style_gen.h
--exclude=../src/extra/libs/gif/gifdec.c
--exclude=../src/extra/libs/gif/gifdec.h
--exclude=../src/extra/libs/lodepng/lodepng.c
--exclude=../src/extra/libs/lodepng/lodepng.h
--exclude=../src/extra/libs/qrcode/qrcodegen.c
--exclude=../src/extra/libs/qrcode/qrcodegen.h
--exclude=../src/extra/libs/tjpgd/tjpgd.c
--exclude=../src/extra/libs/tjpgd/tjpgd.h
--exclude=../src/extra/libs/tjpgd/tjpgdcnf.h
--exclude=../src/libs/gif/gifdec.c
--exclude=../src/libs/gif/gifdec.h
--exclude=../src/libs/lodepng/lodepng.c
--exclude=../src/libs/lodepng/lodepng.h
--exclude=../src/libs/qrcode/qrcodegen.c
--exclude=../src/libs/qrcode/qrcodegen.h
--exclude=../src/libs/tjpgd/tjpgd.c
--exclude=../src/libs/tjpgd/tjpgd.h
--exclude=../src/libs/tjpgd/tjpgdcnf.h
--exclude=../src/libs/thorvg
--exclude=../tests/unity/unity.c
--exclude=../tests/unity/unity_internals.h
--exclude=../tests/unity/unity_support.c
+1
View File
@@ -48,6 +48,7 @@ typedef enum {
LV_DRAW_TASK_TYPE_TRIANGLE,
LV_DRAW_TASK_TYPE_MASK_RECTANGLE,
LV_DRAW_TASK_TYPE_MASK_BITMAP,
LV_DRAW_TASK_TYPE_VECTOR,
} lv_draw_task_type_t;
typedef enum {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+20
View File
@@ -15,6 +15,13 @@
#include "../../stdlib/lv_string.h"
#include "../../core/lv_global.h"
#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL)
#if LV_USE_THORVG_EXTERNAL
#include <thorvg_capi.h>
#else
#include "../../libs/thorvg/thorvg_capi.h"
#endif
#endif
/*********************
* DEFINES
*********************/
@@ -67,10 +74,18 @@ void lv_draw_sw_init(void)
lv_thread_init(&draw_sw_unit->thread, LV_THREAD_PRIO_HIGH, render_thread_cb, 8 * 1024, draw_sw_unit);
#endif
}
#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL)
tvg_engine_init(TVG_ENGINE_SW, 0);
#endif
}
void lv_draw_sw_deinit(void)
{
#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL)
tvg_engine_term(TVG_ENGINE_SW);
#endif
#if LV_DRAW_SW_COMPLEX == 1
lv_draw_sw_mask_deinit();
#endif
@@ -189,6 +204,11 @@ static void execute_drawing(lv_draw_sw_unit_t * u)
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
lv_draw_sw_mask_rect((lv_draw_unit_t *)u, t->draw_dsc, &t->area);
break;
#if LV_USE_VECTOR_GRAPHIC
case LV_DRAW_TASK_TYPE_VECTOR:
lv_draw_sw_vector((lv_draw_unit_t *)u, t->draw_dsc);
break;
#endif
default:
break;
}
+4
View File
@@ -21,6 +21,7 @@ extern "C" {
#include "../../display/lv_display.h"
#include "../../osal/lv_os.h"
#include "../../draw/lv_draw_vector.h"
/*********************
* DEFINES
*********************/
@@ -82,6 +83,9 @@ void lv_draw_sw_transform(lv_draw_unit_t * draw_unit, const lv_area_t * dest_are
int32_t src_w, int32_t src_h, int32_t src_stride,
const lv_draw_image_dsc_t * draw_dsc, const lv_draw_image_sup_t * sup, lv_color_format_t cf, void * dest_buf);
#if LV_USE_VECTOR_GRAPHIC
void lv_draw_sw_vector(lv_draw_unit_t * draw_unit, const lv_draw_vector_task_dsc_t * dsc);
#endif
/**
* Swap the upper and lower byte of an RGB565 buffer.
+452
View File
@@ -0,0 +1,452 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw.h"
#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL)
#if LV_USE_THORVG_EXTERNAL
#include <thorvg_capi.h>
#else
#include "../../libs/thorvg/thorvg_capi.h"
#endif
#include "../../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
float x;
float y;
float w;
float h;
} _tvg_rect;
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} _tvg_color;
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void _lv_area_to_tvg(_tvg_rect * rect, const lv_area_t * area)
{
rect->x = area->x1;
rect->y = area->y1;
rect->w = lv_area_get_width(area);
rect->h = lv_area_get_height(area);
}
static void _lv_color_to_tvg(_tvg_color * color, const lv_color32_t * c, lv_opa_t opa)
{
color->r = c->red;
color->g = c->green;
color->b = c->blue;
color->a = LV_OPA_MIX2(c->alpha, opa);
}
static void _lv_matrix_to_tvg(Tvg_Matrix * tm, const lv_matrix_t * m)
{
tm->e11 = m->m[0][0];
tm->e12 = m->m[0][1];
tm->e13 = m->m[0][2];
tm->e21 = m->m[1][0];
tm->e22 = m->m[1][1];
tm->e23 = m->m[1][2];
tm->e31 = m->m[2][0];
tm->e32 = m->m[2][1];
tm->e33 = m->m[2][2];
}
static void _set_paint_matrix(Tvg_Paint * obj, const Tvg_Matrix * m)
{
tvg_paint_set_transform(obj, m);
}
static void _set_paint_shape(Tvg_Paint * obj, const lv_vector_path_t * p)
{
uint32_t pidx = 0;
for(uint32_t i = 0; i < p->ops.size; i++) {
lv_vector_path_op_t * op = LV_ARRAY_GET(&p->ops, i, uint8_t);
switch(*op) {
case LV_VECTOR_PATH_OP_MOVE_TO: {
lv_fpoint_t * pt = LV_ARRAY_GET(&p->points, pidx, lv_fpoint_t);
tvg_shape_move_to(obj, pt->x, pt->y);
pidx += 1;
}
break;
case LV_VECTOR_PATH_OP_LINE_TO: {
lv_fpoint_t * pt = LV_ARRAY_GET(&p->points, pidx, lv_fpoint_t);
tvg_shape_line_to(obj, pt->x, pt->y);
pidx += 1;
}
break;
case LV_VECTOR_PATH_OP_QUAD_TO: {
lv_fpoint_t * pt1 = LV_ARRAY_GET(&p->points, pidx, lv_fpoint_t);
lv_fpoint_t * pt2 = LV_ARRAY_GET(&p->points, pidx + 1, lv_fpoint_t);
lv_fpoint_t * last_pt = LV_ARRAY_GET(&p->points, pidx - 1, lv_fpoint_t);
lv_fpoint_t cp[2];
cp[0].x = (last_pt->x + 2 * pt1->x) * (1.0f / 3.0f);
cp[0].y = (last_pt->y + 2 * pt1->y) * (1.0f / 3.0f);
cp[1].x = (pt2->x + 2 * pt1->x) * (1.0f / 3.0f);
cp[1].y = (pt2->y + 2 * pt1->y) * (1.0f / 3.0f);
tvg_shape_cubic_to(obj, cp[0].x, cp[0].y, cp[1].x, cp[1].y, pt2->x, pt2->y);
pidx += 2;
}
break;
case LV_VECTOR_PATH_OP_CUBIC_TO: {
lv_fpoint_t * pt1 = LV_ARRAY_GET(&p->points, pidx, lv_fpoint_t);
lv_fpoint_t * pt2 = LV_ARRAY_GET(&p->points, pidx + 1, lv_fpoint_t);
lv_fpoint_t * pt3 = LV_ARRAY_GET(&p->points, pidx + 2, lv_fpoint_t);
tvg_shape_cubic_to(obj, pt1->x, pt1->y, pt2->x, pt2->y, pt3->x, pt3->y);
pidx += 3;
}
break;
case LV_VECTOR_PATH_OP_CLOSE: {
tvg_shape_close(obj);
}
break;
}
}
}
static Tvg_Stroke_Cap _lv_stroke_cap_to_tvg(lv_vector_stroke_cap_t cap)
{
switch(cap) {
case LV_VECTOR_STROKE_CAP_SQUARE:
return TVG_STROKE_CAP_SQUARE;
case LV_VECTOR_STROKE_CAP_ROUND:
return TVG_STROKE_CAP_ROUND;
case LV_VECTOR_STROKE_CAP_BUTT:
return TVG_STROKE_CAP_BUTT;
default:
return TVG_STROKE_CAP_SQUARE;
}
}
static Tvg_Stroke_Join _lv_stroke_join_to_tvg(lv_vector_stroke_join_t join)
{
switch(join) {
case LV_VECTOR_STROKE_JOIN_BEVEL:
return TVG_STROKE_JOIN_BEVEL;
case LV_VECTOR_STROKE_JOIN_ROUND:
return TVG_STROKE_JOIN_ROUND;
case LV_VECTOR_STROKE_JOIN_MITER:
return TVG_STROKE_JOIN_MITER;
default:
return TVG_STROKE_JOIN_BEVEL;
}
}
static Tvg_Stroke_Fill _lv_spread_to_tvg(lv_vector_gradient_spread_t sp)
{
switch(sp) {
case LV_VECTOR_GRADIENT_SPREAD_PAD:
return TVG_STROKE_FILL_PAD;
case LV_VECTOR_GRADIENT_SPREAD_REPEAT:
return TVG_STROKE_FILL_REPEAT;
case LV_VECTOR_GRADIENT_SPREAD_REFLECT:
return TVG_STROKE_FILL_REFLECT;
default:
return TVG_STROKE_FILL_PAD;
}
}
static void _setup_gradient(Tvg_Gradient * gradient, const lv_vector_gradient_t * grad,
const lv_matrix_t * matrix)
{
const lv_grad_dsc_t * g = &grad->grad;
Tvg_Color_Stop * stops = (Tvg_Color_Stop *)lv_malloc(sizeof(Tvg_Color_Stop) * g->stops_count);
for(uint8_t i = 0; i < g->stops_count; i++) {
const lv_gradient_stop_t * s = &(g->stops[i]);
stops[i].offset = s->frac / 255.0f;
stops[i].r = s->color.red;
stops[i].g = s->color.green;
stops[i].b = s->color.blue;
stops[i].a = s->opa;
}
tvg_gradient_set_color_stops(gradient, stops, g->stops_count);
tvg_gradient_set_spread(gradient, _lv_spread_to_tvg(grad->spread));
Tvg_Matrix mtx;
_lv_matrix_to_tvg(&mtx, matrix);
tvg_gradient_set_transform(gradient, &mtx);
lv_free(stops);
}
static void _set_paint_stroke_gradient(Tvg_Paint * obj, const lv_vector_gradient_t * g, const lv_matrix_t * m)
{
float x, y, w, h;
tvg_paint_get_bounds(obj, &x, &y, &w, &h, false);
Tvg_Gradient * grad = NULL;
if(g->style == LV_VECTOR_GRADIENT_STYLE_RADIAL) {
grad = tvg_radial_gradient_new();
tvg_radial_gradient_set(grad, g->cx + x, g->cy + y, g->cr);
_setup_gradient(grad, g, m);
tvg_shape_set_stroke_radial_gradient(obj, grad);
}
else {
grad = tvg_linear_gradient_new();
if(g->grad.dir == LV_GRAD_DIR_VER) {
tvg_linear_gradient_set(grad, x, y, x, y + h);
}
else {
tvg_linear_gradient_set(grad, x, y, x + w, y);
}
_setup_gradient(grad, g, m);
tvg_shape_set_stroke_linear_gradient(obj, grad);
}
}
static void _set_paint_stroke(Tvg_Paint * obj, const lv_vector_stroke_dsc_t * dsc)
{
if(dsc->style == LV_VECTOR_DRAW_STYLE_SOLID) {
_tvg_color c;
_lv_color_to_tvg(&c, &dsc->color, dsc->opa);
tvg_shape_set_stroke_color(obj, c.r, c.g, c.b, c.a);
}
else { // gradient
_set_paint_stroke_gradient(obj, &dsc->gradient, &dsc->matrix);
}
tvg_shape_set_stroke_width(obj, dsc->width);
tvg_shape_set_stroke_miterlimit(obj, dsc->miter_limit);
tvg_shape_set_stroke_cap(obj, _lv_stroke_cap_to_tvg(dsc->cap));
tvg_shape_set_stroke_join(obj, _lv_stroke_join_to_tvg(dsc->join));
if(!lv_array_is_empty(&dsc->dash_pattern)) {
float * dash_array = LV_ARRAY_GET(&dsc->dash_pattern, 0, float);
tvg_shape_set_stroke_dash(obj, dash_array, dsc->dash_pattern.size);
}
}
static Tvg_Fill_Rule _lv_fill_rule_to_tvg(lv_vector_fill_t rule)
{
switch(rule) {
case LV_VECTOR_FILL_NONZERO:
return TVG_FILL_RULE_WINDING;
case LV_VECTOR_FILL_EVENODD:
return TVG_FILL_RULE_EVEN_ODD;
default:
return TVG_FILL_RULE_WINDING;
}
}
static void _set_paint_fill_gradient(Tvg_Paint * obj, const lv_vector_gradient_t * g, const lv_matrix_t * m)
{
float x, y, w, h;
tvg_paint_get_bounds(obj, &x, &y, &w, &h, false);
Tvg_Gradient * grad = NULL;
if(g->style == LV_VECTOR_GRADIENT_STYLE_RADIAL) {
grad = tvg_radial_gradient_new();
tvg_radial_gradient_set(grad, g->cx + x, g->cy + y, g->cr);
_setup_gradient(grad, g, m);
tvg_shape_set_radial_gradient(obj, grad);
}
else {
grad = tvg_linear_gradient_new();
if(g->grad.dir == LV_GRAD_DIR_VER) {
tvg_linear_gradient_set(grad, x, y, x, y + h);
}
else {
tvg_linear_gradient_set(grad, x, y, x + w, y);
}
_setup_gradient(grad, g, m);
tvg_shape_set_linear_gradient(obj, grad);
}
}
static void _set_paint_fill_pattern(Tvg_Paint * obj, Tvg_Canvas * canvas, const lv_draw_image_dsc_t * p,
const lv_matrix_t * m)
{
lv_image_decoder_dsc_t decoder_dsc;
lv_result_t res = lv_image_decoder_open(&decoder_dsc, p->src, p->recolor, -1);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Failed to open image");
return;
}
if(!decoder_dsc.img_data) {
lv_image_decoder_close(&decoder_dsc);
LV_LOG_ERROR("Image not ready");
return;
}
const uint8_t * src_buf = decoder_dsc.img_data;
const lv_image_header_t * header = &decoder_dsc.header;
lv_color_format_t cf = header->cf;
if(cf != LV_COLOR_FORMAT_ARGB8888) {
lv_image_decoder_close(&decoder_dsc);
LV_LOG_ERROR("Not support image format");
return;
}
Tvg_Paint * img = tvg_picture_new();
tvg_picture_load_raw(img, (uint32_t *)src_buf, header->w, header->h, true);
Tvg_Paint * clip_path = tvg_paint_duplicate(obj);
tvg_paint_set_composite_method(img, clip_path, TVG_COMPOSITE_METHOD_CLIP_PATH);
Tvg_Matrix mtx;
_lv_matrix_to_tvg(&mtx, m);
tvg_paint_set_transform(img, &mtx);
tvg_canvas_push(canvas, img);
lv_image_decoder_close(&decoder_dsc);
}
static void _set_paint_fill(Tvg_Paint * obj, Tvg_Canvas * canvas, const lv_vector_fill_dsc_t * dsc,
const lv_matrix_t * matrix)
{
tvg_shape_set_fill_rule(obj, _lv_fill_rule_to_tvg(dsc->fill_rule));
if(dsc->style == LV_VECTOR_DRAW_STYLE_SOLID) {
_tvg_color c;
_lv_color_to_tvg(&c, &dsc->color, dsc->opa);
tvg_shape_set_fill_color(obj, c.r, c.g, c.b, c.a);
}
else if(dsc->style == LV_VECTOR_DRAW_STYLE_PATTERN) {
float x, y, w, h;
tvg_paint_get_bounds(obj, &x, &y, &w, &h, false);
lv_matrix_t imx;
lv_memcpy(&imx, matrix, sizeof(lv_matrix_t));
lv_matrix_translate(&imx, x, y);
lv_matrix_multiply(&imx, &dsc->matrix);
_set_paint_fill_pattern(obj, canvas, &dsc->img_dsc, &imx);
}
else if(dsc->style == LV_VECTOR_DRAW_STYLE_GRADIENT) {
_set_paint_fill_gradient(obj, &dsc->gradient, &dsc->matrix);
}
}
static Tvg_Blend_Method _lv_blend_to_tvg(lv_vector_blend_t blend)
{
switch(blend) {
case LV_VECTOR_BLEND_SRC_OVER:
return TVG_BLEND_METHOD_NORMAL;
case LV_VECTOR_BLEND_SCREEN:
return TVG_BLEND_METHOD_SCREEN;
case LV_VECTOR_BLEND_MULTIPLY:
return TVG_BLEND_METHOD_MULTIPLY;
case LV_VECTOR_BLEND_NONE:
return TVG_BLEND_METHOD_SRCOVER;
case LV_VECTOR_BLEND_ADDITIVE:
return TVG_BLEND_METHOD_ADD;
case LV_VECTOR_BLEND_SRC_IN:
case LV_VECTOR_BLEND_DST_OVER:
case LV_VECTOR_BLEND_DST_IN:
case LV_VECTOR_BLEND_SUBTRACTIVE:
// not support yet.
default:
return TVG_BLEND_METHOD_NORMAL;
}
}
static void _set_paint_blend_mode(Tvg_Paint * obj, lv_vector_blend_t blend)
{
tvg_paint_set_blend_method(obj, _lv_blend_to_tvg(blend));
}
static void _task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc)
{
Tvg_Canvas * canvas = (Tvg_Canvas *)ctx;
Tvg_Paint * obj = tvg_shape_new();
if(!path) { // clear
_tvg_rect rc;
_lv_area_to_tvg(&rc, &dsc->scissor_area);
_tvg_color c;
_lv_color_to_tvg(&c, &dsc->fill_dsc.color, LV_OPA_COVER);
Tvg_Matrix mtx = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
};
_set_paint_matrix(obj, &mtx);
tvg_shape_append_rect(obj, rc.x, rc.y, rc.w, rc.h, 0, 0);
tvg_shape_set_fill_color(obj, c.r, c.g, c.b, c.a);
}
else {
Tvg_Matrix mtx;
_lv_matrix_to_tvg(&mtx, &dsc->matrix);
_set_paint_matrix(obj, &mtx);
_set_paint_shape(obj, path);
_set_paint_fill(obj, canvas, &dsc->fill_dsc, &dsc->matrix);
_set_paint_stroke(obj, &dsc->stroke_dsc);
_set_paint_blend_mode(obj, dsc->blend_mode);
}
tvg_canvas_push(canvas, obj);
}
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_vector(lv_draw_unit_t * draw_unit, const lv_draw_vector_task_dsc_t * dsc)
{
LV_UNUSED(draw_unit);
if(dsc->task_list == NULL)
return;
lv_layer_t * layer = dsc->base.layer;
if(layer->buf == NULL)
return;
void * buf = layer->buf;
int32_t width = lv_area_get_width(&layer->buf_area);
int32_t height = lv_area_get_height(&layer->buf_area);
Tvg_Canvas * canvas = tvg_swcanvas_create();
tvg_swcanvas_set_target(canvas, buf, width, width, height, TVG_COLORSPACE_ARGB8888);
lv_ll_t * task_list = dsc->task_list;
_lv_vector_for_each_destroy_tasks(task_list, _task_draw_cb, canvas);
if(tvg_canvas_draw(canvas) == TVG_RESULT_SUCCESS) {
tvg_canvas_sync(canvas);
}
tvg_canvas_destroy(canvas);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_SW*/
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
#Add LVGL #if LV_USE_THORVG_INTERNAL guard
#Usage
# find -name "*.cpp" | xargs ./add_lvgl_if.sh
# find -name "t*.h" | xargs ./add_lvgl_if.sh
sed '0,/\*\/$/ {/\*\/$/ {n; s|^|\n#include "../../lv_conf_internal.h"\n#if LV_USE_THORVG_INTERNAL\n|}}' $@ -i
sed -i -e '$a\
\
#endif /* LV_USE_THORVG_INTERNAL */\
' $@ -i
+15
View File
@@ -0,0 +1,15 @@
/*
* Autogenerated by the Meson build system.
* Do not edit, your changes will be lost.
*/
#pragma once
#define THORVG_CAPI_BINDING_SUPPORT 1
#define THORVG_SVG_LOADER_SUPPORT 1
#define THORVG_SW_RASTER_SUPPORT 1
#define THORVG_VERSION_STRING "0.11.99"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#include "tvgCommon.h"
#include "tvgFrameModule.h"
#include "tvgPaint.h"
#include "tvgPicture.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Animation::Impl
{
Picture* picture = nullptr;
Impl()
{
picture = Picture::gen().release();
PP(picture)->ref();
}
~Impl()
{
if (PP(picture)->unref() == 0) {
delete(picture);
}
}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Animation::~Animation()
{
delete(pImpl);
}
Animation::Animation() : pImpl(new Impl)
{
}
Result Animation::frame(uint32_t no) noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return Result::InsufficientCondition;
if (!loader->animatable()) return Result::NonSupport;
if (static_cast<FrameModule*>(loader)->frame(no)) return Result::Success;
return Result::InsufficientCondition;
}
Picture* Animation::picture() const noexcept
{
return pImpl->picture;
}
uint32_t Animation::curFrame() const noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
return static_cast<FrameModule*>(loader)->curFrame();
}
uint32_t Animation::totalFrame() const noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
return static_cast<FrameModule*>(loader)->totalFrame();
}
float Animation::duration() const noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
return static_cast<FrameModule*>(loader)->duration();
}
unique_ptr<Animation> Animation::gen() noexcept
{
return unique_ptr<Animation>(new Animation);
}
#endif /* LV_USE_THORVG_INTERNAL */
+192
View File
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_ARRAY_H_
#define _TVG_ARRAY_H_
#include <memory.h>
#include <cstdint>
namespace tvg
{
template<class T>
struct Array
{
T* data = nullptr;
uint32_t count = 0;
uint32_t reserved = 0;
Array(){}
Array(const Array& rhs)
{
reset();
*this = rhs;
}
void push(T element)
{
if (count + 1 > reserved) {
reserved = count + (count + 2) / 2;
data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
}
data[count++] = element;
}
void push(Array<T>& rhs)
{
grow(rhs.count);
memcpy(data + count, rhs.data, rhs.count * sizeof(T));
count += rhs.count;
}
bool reserve(uint32_t size)
{
if (size > reserved) {
reserved = size;
data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
}
return true;
}
bool grow(uint32_t size)
{
return reserve(count + size);
}
const T& operator[](size_t idx) const
{
return data[idx];
}
T& operator[](size_t idx)
{
return data[idx];
}
T* end()
{
return data + count;
}
const T* end() const
{
return data + count;
}
const T& last() const
{
return data[count - 1];
}
const T& first() const
{
return data[0];
}
T& last()
{
return data[count - 1];
}
T& first()
{
return data[0];
}
void pop()
{
if (count > 0) --count;
}
void reset()
{
free(data);
data = nullptr;
count = reserved = 0;
}
void clear()
{
count = 0;
}
bool empty() const
{
return count == 0;
}
template<class COMPARE>
void sort()
{
qsort<COMPARE>(data, 0, static_cast<int32_t>(count) - 1);
}
void operator=(const Array& rhs)
{
reserve(rhs.count);
if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count);
count = rhs.count;
}
~Array()
{
free(data);
}
private:
template<class COMPARE>
void qsort(T* arr, int32_t low, int32_t high)
{
if (low < high) {
int32_t i = low;
int32_t j = high;
T tmp = arr[low];
while (i < j) {
while (i < j && !COMPARE{}(arr[j], tmp)) --j;
if (i < j) {
arr[i] = arr[j];
++i;
}
while (i < j && COMPARE{}(arr[i], tmp)) ++i;
if (i < j) {
arr[j] = arr[i];
--j;
}
}
arr[i] = tmp;
qsort<COMPARE>(arr, low, i - 1);
qsort<COMPARE>(arr, i + 1, high);
}
}
};
}
#endif //_TVG_ARRAY_H_
#endif /* LV_USE_THORVG_INTERNAL */
+199
View File
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#include "tvgMath.h"
#include "tvgBezier.h"
#define BEZIER_EPSILON 1e-4f
/************************************************************************/
/* 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, float length)
{
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 >= length) return 1.0f;
while (true) {
auto right = bz;
Bezier left;
bezSplitLeft(right, t, left);
length = bezLength(left);
if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
break;
}
if (length < 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, bezLength(right));
bezSplitLeft(right, t, left);
}
Point bezPointAt(const Bezier& bz, float t)
{
Point cur;
auto it = 1.0f - t;
auto ax = bz.start.x * it + bz.ctrl1.x * t;
auto bx = bz.ctrl1.x * it + bz.ctrl2.x * t;
auto cx = bz.ctrl2.x * it + bz.end.x * t;
ax = ax * it + bx * t;
bx = bx * it + cx * t;
cur.x = ax * it + bx * t;
float ay = bz.start.y * it + bz.ctrl1.y * t;
float by = bz.ctrl1.y * it + bz.ctrl2.y * t;
float cy = bz.ctrl2.y * it + bz.end.y * t;
ay = ay * it + by * t;
by = by * it + cy * t;
cur.y = ay * it + by * t;
return cur;
}
float bezAngleAt(const Bezier& bz, float t)
{
if (t < 0 || t > 1) return 0;
//derivate
// p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
// t^2) * p2 + t^2 * p3)
float mt = 1.0f - t;
float d = t * t;
float a = -mt * mt;
float b = 1 - 4 * t + 3 * d;
float c = 2 * t - 3 * d;
Point pt ={a * bz.start.x + b * bz.ctrl1.x + c * bz.ctrl2.x + d * bz.end.x, a * bz.start.y + b * bz.ctrl1.y + c * bz.ctrl2.y + d * bz.end.y};
pt.x *= 3;
pt.y *= 3;
return atan2(pt.x, pt.y) * 180.0f / 3.141592f;
}
}
#endif /* LV_USE_THORVG_INTERNAL */
+55
View File
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_BEZIER_H_
#define _TVG_BEZIER_H_
#include "tvgCommon.h"
namespace tvg
{
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, float length);
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
Point bezPointAt(const Bezier& bz, float t);
float bezAngleAt(const Bezier& bz, float t);
}
#endif //_TVG_BEZIER_H_
#endif /* LV_USE_THORVG_INTERNAL */
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#include "tvgCanvas.h"
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
{
}
Canvas::~Canvas()
{
delete(pImpl);
}
Result Canvas::reserve(TVG_UNUSED uint32_t n) noexcept
{
return Result::NonSupport;
}
list<Paint*>& Canvas::paints() noexcept
{
return pImpl->paints;
}
Result Canvas::push(unique_ptr<Paint> paint) noexcept
{
return pImpl->push(std::move(paint));
}
Result Canvas::clear(bool free) noexcept
{
return pImpl->clear(free);
}
Result Canvas::draw() noexcept
{
TVGLOG("COMMON", "Draw S. -------------------------------- Canvas(%p)", this);
auto ret = pImpl->draw();
TVGLOG("COMMON", "Draw E. -------------------------------- Canvas(%p)", this);
return ret;
}
Result Canvas::update(Paint* paint) noexcept
{
TVGLOG("COMMON", "Update S. ------------------------------ Canvas(%p)", this);
auto ret = pImpl->update(paint, false);
TVGLOG("COMMON", "Update E. ------------------------------ Canvas(%p)", this);
return ret;
}
Result Canvas::sync() noexcept
{
return pImpl->sync();
}
#endif /* LV_USE_THORVG_INTERNAL */
+152
View File
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_CANVAS_IMPL_H_
#define _TVG_CANVAS_IMPL_H_
#include "tvgPaint.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Canvas::Impl
{
list<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;
PP(p)->ref();
paints.push_back(p);
return update(p, true);
}
Result clear(bool free)
{
//Clear render target before drawing
if (!renderer || !renderer->clear()) return Result::InsufficientCondition;
//Free paints
if (free) {
for (auto paint : paints) {
P(paint)->unref();
if (paint->pImpl->dispose(*renderer) && P(paint)->refCnt == 0) {
delete(paint);
}
}
paints.clear();
}
drawing = false;
return Result::Success;
}
void needRefresh()
{
refresh = true;
}
Result update(Paint* paint, bool force)
{
if (paints.empty() || 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) {
if (paint2 == paint) {
paint->pImpl->update(*renderer, nullptr, clips, 255, flag);
return Result::Success;
}
}
return Result::InvalidArguments;
//Update all retained paint nodes
} else {
for (auto paint : paints) {
paint->pImpl->update(*renderer, nullptr, clips, 255, flag);
}
}
refresh = false;
return Result::Success;
}
Result draw()
{
if (drawing || paints.empty() || !renderer || !renderer->preRender()) return Result::InsufficientCondition;
bool rendered = false;
for (auto paint : paints) {
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_ */
#endif /* LV_USE_THORVG_INTERNAL */
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#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, Lottie, Raw, Png, Jpg, Webp, Unknown };
using Size = Point;
#ifdef THORVG_LOG_ENABLED
constexpr auto ErrorColor = "\033[31m"; //red
constexpr auto ErrorBgColor = "\033[41m";//bg red
constexpr auto LogColor = "\033[32m"; //green
constexpr auto LogBgColor = "\033[42m"; //bg green
constexpr auto GreyColor = "\033[90m"; //grey
constexpr auto ResetColors = "\033[0m"; //default
#define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#else
#define TVGERR(...) do {} while(0)
#define TVGLOG(...) do {} while(0)
#endif
uint16_t THORVG_VERSION_NUMBER();
#define P(A) ((A)->pImpl) //Access to pimpl.
#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
#endif //_TVG_COMMON_H_
#endif /* LV_USE_THORVG_INTERNAL */
+481
View File
@@ -0,0 +1,481 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
/*
* LempelZivWelch (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"
#include <string>
#include <memory.h>
#include "tvgCompressor.h"
namespace tvg {
/************************************************************************/
/* LZW Implementation */
/************************************************************************/
//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;
}
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();
}
/************************************************************************/
/* B64 Implementation */
/************************************************************************/
size_t b64Decode(const char* encoded, const size_t len, char** decoded)
{
static constexpr const char B64_INDEX[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
if (!decoded || !encoded || len == 0) return 0;
auto reserved = 3 * (1 + (len >> 2)) + 1;
auto output = static_cast<char*>(malloc(reserved * sizeof(char)));
if (!output) return 0;
output[reserved - 1] = '\0';
size_t idx = 0;
while (*encoded && *(encoded + 1)) {
if (*encoded <= 0x20) {
++encoded;
continue;
}
auto value1 = B64_INDEX[(size_t)encoded[0]];
auto value2 = B64_INDEX[(size_t)encoded[1]];
output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
auto value3 = B64_INDEX[(size_t)encoded[2]];
output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
auto value4 = B64_INDEX[(size_t)encoded[3]];
output[idx++] = ((value3 & 0x03) << 6) + value4;
encoded += 4;
}
*decoded = output;
return reserved;
}
}
#endif /* LV_USE_THORVG_INTERNAL */
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_COMPRESSOR_H_
#define _TVG_COMPRESSOR_H_
#include <cstdint>
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);
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
}
#endif //_TVG_COMPRESSOR_H_
#endif /* LV_USE_THORVG_INTERNAL */
+256
View File
@@ -0,0 +1,256 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Fill* RadialGradient::Impl::duplicate()
{
auto ret = RadialGradient::gen();
if (!ret) return nullptr;
ret->pImpl->cx = cx;
ret->pImpl->cy = cy;
ret->pImpl->r = r;
ret->pImpl->fx = fx;
ret->pImpl->fy = fy;
ret->pImpl->fr = fr;
return ret.release();
}
Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
{
if (r < 0 || fr < 0) return Result::InvalidArguments;
this->cx = cx;
this->cy = cy;
this->r = r;
this->fx = fx;
this->fy = fy;
this->fr = fr;
return Result::Success;
};
Fill* LinearGradient::Impl::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 */
/************************************************************************/
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;
}
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 r) noexcept
{
return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
}
Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
{
if (cx) *cx = pImpl->cx;
if (cy) *cy = pImpl->cy;
if (r) *r = pImpl->r;
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;
}
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;
}
#endif /* LV_USE_THORVG_INTERNAL */
+118
View File
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#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;
uint8_t id;
~Impl()
{
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;
}
};
struct RadialGradient::Impl
{
float cx = 0.0f, cy = 0.0f;
float fx = 0.0f, fy = 0.0f;
float r = 0.0f, fr = 0.0f;
Fill* duplicate();
Result radial(float cx, float cy, float r, float fx, float fy, float fr);
};
struct LinearGradient::Impl
{
float x1 = 0.0f;
float y1 = 0.0f;
float x2 = 0.0f;
float y2 = 0.0f;
Fill* duplicate();
};
#endif //_TVG_FILL_H_
#endif /* LV_USE_THORVG_INTERNAL */
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_FRAME_MODULE_H_
#define _TVG_FRAME_MODULE_H_
#include "tvgLoadModule.h"
namespace tvg
{
class FrameModule: public LoadModule
{
public:
virtual ~FrameModule() {}
virtual bool frame(uint32_t frameNo) = 0; //set the current frame number
virtual uint32_t totalFrame() = 0; //return the total frame count
virtual uint32_t curFrame() = 0; //return the current frame number
virtual float duration() = 0; //return the animation duration in seconds
virtual bool animatable() override { return true; }
};
}
#endif //_TVG_FRAME_MODULE_H_
#endif /* LV_USE_THORVG_INTERNAL */
+185
View File
@@ -0,0 +1,185 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#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
#ifdef THORVG_WG_RASTER_SUPPORT
#include "tvgWgRenderer.h"
#endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static int _initCnt = 0;
static uint16_t _version = 0;
//enum class operation helper
static constexpr bool operator &(CanvasEngine a, CanvasEngine b)
{
return int(a) & int(b);
}
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<int>(engine) == 0) return Result::InvalidArguments;
if (engine & CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
}
if (engine & CanvasEngine::Gl) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
}
if (engine & CanvasEngine::Wg) {
#ifdef THORVG_WG_RASTER_SUPPORT
if (!WgRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
}
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<int>(engine) == 0) return Result::InvalidArguments;
if (engine & CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
}
if (engine & CanvasEngine::Gl) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
}
if (engine & CanvasEngine::Wg) {
#ifdef THORVG_WG_RASTER_SUPPORT
if (!WgRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
}
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;
}
#endif /* LV_USE_THORVG_INTERNAL */
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2021 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_ITERATOR_ACCESSOR_H_
#define _TVG_ITERATOR_ACCESSOR_H_
#include "tvgPaint.h"
namespace tvg
{
class IteratorAccessor
{
public:
//Utility Method: Iterator Accessor
static Iterator* iterator(const Paint* paint)
{
return paint->pImpl->iterator();
}
};
}
#endif //_TVG_ITERATOR_ACCESSOR_H_
#endif /* LV_USE_THORVG_INTERNAL */
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_LOAD_MODULE_H_
#define _TVG_LOAD_MODULE_H_
#include "tvgRender.h"
namespace tvg
{
class LoadModule
{
public:
float w = 0, h = 0; //default image size
ColorSpace cs = ColorSpace::Unsupported; //must be clarified at open()
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 animatable() { return false; } //true if this loader supports animation.
virtual void sync() {}; //finish immediately if any async update jobs.
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_
#endif /* LV_USE_THORVG_INTERNAL */
+255
View File
@@ -0,0 +1,255 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#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
#ifdef THORVG_WEBP_LOADER_SUPPORT
#include "tvgWebpLoader.h"
#endif
#ifdef THORVG_LOTTIE_LOADER_SUPPORT
#include "tvgLottieLoader.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::Lottie: {
#ifdef THORVG_LOTTIE_LOADER_SUPPORT
return new LottieLoader;
#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;
}
case FileType::Webp: {
#ifdef THORVG_WEBP_LOADER_SUPPORT
return new WebpLoader;
#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::Lottie: {
format = "lottie(json)";
break;
}
case FileType::Raw: {
format = "RAW";
break;
}
case FileType::Png: {
format = "PNG";
break;
}
case FileType::Jpg: {
format = "JPG";
break;
}
case FileType::Webp: {
format = "WEBP";
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("json")) return _find(FileType::Lottie);
if (!ext.compare("lottie")) return _find(FileType::Lottie);
if (!ext.compare("png")) return _find(FileType::Png);
if (!ext.compare("jpg")) return _find(FileType::Jpg);
if (!ext.compare("webp")) return _find(FileType::Webp);
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 == "lottie") type = FileType::Lottie;
else if (mimeType == "raw") type = FileType::Raw;
else if (mimeType == "png") type = FileType::Png;
else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
else if (mimeType == "webp") type = FileType::Webp;
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 with the given MimeType
if (!mimeType.empty()) {
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.", mimeType.c_str());
delete(loader);
}
}
//Unkown MimeType. Try with the candidates in the order
} else {
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;
}
#endif /* LV_USE_THORVG_INTERNAL */
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#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_
#endif /* LV_USE_THORVG_INTERNAL */
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2021 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#include "tvgMath.h"
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;
}
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;
}
void mathRotate(Matrix* m, float degree)
{
if (degree == 0.0f) return;
auto radian = degree / 180.0f * (float)M_PI;
auto cosVal = cosf((float)radian);
auto sinVal = sinf((float)radian);
m->e12 = m->e11 * -sinVal;
m->e11 *= cosVal;
m->e21 = m->e22 * sinVal;
m->e22 *= cosVal;
}
bool mathIdentity(const Matrix* m)
{
if (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) {
return false;
}
return true;
}
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;
}
#endif /* LV_USE_THORVG_INTERNAL */
+190
View File
@@ -0,0 +1,190 @@
/*
* Copyright (c) 2021 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#ifndef _TVG_MATH_H_
#define _TVG_MATH_H_
#define _USE_MATH_DEFINES
#include <float.h>
#include <math.h>
#include "tvgCommon.h"
#define MATH_PI 3.14159265358979323846f
#define MATH_PI2 1.57079632679489661923f
#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
bool mathInverse(const Matrix* m, Matrix* out);
Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs);
void mathRotate(Matrix* m, float degree);
bool mathIdentity(const Matrix* m);
void mathMultiply(Point* pt, const Matrix* transform);
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 mathEqual(const Matrix& a, const Matrix& b)
{
if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
!mathEqual(a.e21, b.e21) || !mathEqual(a.e22, b.e22) || !mathEqual(a.e23, b.e23) ||
!mathEqual(a.e31, b.e31) || !mathEqual(a.e32, b.e32) || !mathEqual(a.e33, b.e33)) {
return false;
}
return true;
}
static inline bool mathRightAngle(const Matrix* m)
{
auto radian = fabsf(atan2f(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 mathSkewed(const Matrix* m)
{
return (fabsf(m->e21 + m->e12) > FLT_EPSILON);
}
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 mathTransform(Matrix* transform, Point* coord)
{
auto x = coord->x;
auto y = coord->y;
coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
}
static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
m->e22 *= sy;
}
static inline void mathScaleR(Matrix* m, float x, float y)
{
if (x != 1.0f) {
m->e11 *= x;
m->e21 *= x;
}
if (y != 1.0f) {
m->e22 *= y;
m->e12 *= y;
}
}
static inline void mathTranslate(Matrix* m, float x, float y)
{
m->e13 += x;
m->e23 += y;
}
static inline void mathTranslateR(Matrix* m, float x, float y)
{
if (x == 0.0f && y == 0.0f) return;
m->e13 += (x * m->e11 + y * m->e12);
m->e23 += (x * m->e21 + y * m->e22);
}
static inline void mathLog(Matrix* m)
{
TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
}
static inline float mathLength(const Point* a, const Point* b)
{
auto x = b->x - a->x;
auto y = b->y - a->y;
if (x < 0) x = -x;
if (y < 0) y = -y;
return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
}
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
}
static inline Point operator+(const Point& lhs, const Point& rhs)
{
return {lhs.x + rhs.x, lhs.y + rhs.y};
}
static inline Point operator*(const Point& lhs, float rhs)
{
return {lhs.x * rhs, lhs.y * rhs};
}
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
{
return static_cast<T>(start + (end - start) * t);
}
#endif //_TVG_MATH_H_
#endif /* LV_USE_THORVG_INTERNAL */
+437
View File
@@ -0,0 +1,437 @@
/*
* Copyright (c) 2020 - 2023 the ThorVG project. 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 "../../lv_conf_internal.h"
#if LV_USE_THORVG_INTERNAL
#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 rotation and no skewing
if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) return false;
if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&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>(ceil(v2.x - viewport.x));
viewport.h = static_cast<int32_t>(ceil(v2.y - viewport.y));
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 (mathEqual(factor, 1.0f)) 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;
/* 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 (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
compData->target->pImpl->render(renderer);
}
}
if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
renderer.blend(blendMethod);
auto ret = smethod->render(renderer);
if (cmp) renderer.endComposite(cmp);
return ret;
}
RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
if (renderFlag & RenderUpdateFlag::Transform) {
if (!rTransform) return nullptr;
if (!rTransform->update()) {
delete(rTransform);
rTransform = nullptr;
}
}
/* 1. Composition Pre Processing */
RenderData trd = nullptr; //composite target render data
RenderRegion viewport;
bool compFastTrack = false;
bool childClipper = false;
if (compData) {
auto target = compData->target;
auto method = compData->method;
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
/* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
auto tryFastTrack = false;
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
else {
auto shape = static_cast<Shape*>(target);
uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a);
//no gradient fill & no compositions of the composition target.
if (!shape->fill() && !(PP(shape)->compData)) {
if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) 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) {
childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper);
if (childClipper) clips.push(trd);
}
}
/* 2. Main Update */
RenderData rd = nullptr;
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
renderFlag = RenderUpdateFlag::None;
opacity = MULTIPLY(opacity, this->opacity);
if (rTransform && pTransform) {
RenderTransform outTransform(pTransform, rTransform);
rd = smethod->update(renderer, &outTransform, clips, opacity, newFlag, clipper);
} else {
auto outTransform = pTransform ? pTransform : rTransform;
rd = smethod->update(renderer, outTransform, clips, opacity, newFlag, clipper);
}
/* 3. Composition Post Processing */
if (compFastTrack) renderer.viewport(viewport);
else if (childClipper) clips.pop();
return rd;
}
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
{
Matrix* m = nullptr;
//Case: No transformed, quick return!
if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
//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, stroking);
//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, true)) 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;
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;
}
Result Paint::blend(BlendMethod method) const noexcept
{
pImpl->blendMethod = method;
return Result::Success;
}
BlendMethod Paint::blend() const noexcept
{
return pImpl->blendMethod;
}
#endif /* LV_USE_THORVG_INTERNAL */

Some files were not shown because too many files have changed in this diff Show More