mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-20 12:32:18 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -34,3 +34,7 @@ repos:
|
||||
rev: v1.16.20
|
||||
hooks:
|
||||
- id: typos
|
||||
exclude: |
|
||||
(?x)^(
|
||||
src/libs/
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) .
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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*/
|
||||
@@ -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})
|
||||
|
||||
Executable
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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*/
|
||||
Executable
+13
@@ -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
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
/*
|
||||
* Lempel–Ziv–Welch (LZW) encoder/decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
|
||||
|
||||
* This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
|
||||
* Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
|
||||
* are not stored in the output and the max code length in bits is 12, vs 16 in compress.
|
||||
*
|
||||
* EOI is simply detected by the end of the data stream, while CC happens if the
|
||||
* dictionary gets filled. Data is written/read from bit streams, which handle
|
||||
* byte-alignment for us in a transparent way.
|
||||
|
||||
* The decoder relies on the hardcoded data layout produced by the encoder, since
|
||||
* no additional reconstruction data is added to the output, so they must match.
|
||||
* The nice thing about LZW is that we can reconstruct the dictionary directly from
|
||||
* the stream of codes generated by the encoder, so this avoids storing additional
|
||||
* headers in the bit stream.
|
||||
|
||||
* The output code length is variable. It starts with the minimum number of bits
|
||||
* required to store the base byte-sized dictionary and automatically increases
|
||||
* as the dictionary gets larger (it starts at 9-bits and grows to 10-bits when
|
||||
* code 512 is added, then 11-bits when 1024 is added, and so on). If the dictionary
|
||||
* is filled (4096 items for a 12-bits dictionary), the whole thing is cleared and
|
||||
* the process starts over. This is the main reason why the encoder and the decoder
|
||||
* must match perfectly, since the lengths of the codes will not be specified with
|
||||
* the data itself.
|
||||
|
||||
* USEFUL LINKS:
|
||||
* https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
|
||||
* http://rosettacode.org/wiki/LZW_compression
|
||||
* http://www.cs.duke.edu/csed/curious/compression/lzw.html
|
||||
* http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
|
||||
* http://marknelson.us/1989/10/01/lzw-data-compression/
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
|
||||
|
||||
#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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user