diff --git a/.gitignore b/.gitignore index 9a645208..b796b5e9 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ testRadialGradient testGradientTransform testSvg testAsync +testCapi diff --git a/inc/meson.build b/inc/meson.build index c598ee5d..90e3fc4c 100644 --- a/inc/meson.build +++ b/inc/meson.build @@ -1,3 +1,7 @@ -install_headers([ - 'thorvg.h', - ]) +header_files = ['thorvg.h'] + +if get_option('bindings').contains('capi') == true + header_files += ['thorvg_capi.h'] +endif + +install_headers(header_files) diff --git a/inc/thorvg_capi.h b/inc/thorvg_capi.h new file mode 100644 index 00000000..d2f7f859 --- /dev/null +++ b/inc/thorvg_capi.h @@ -0,0 +1,111 @@ +#ifndef __THORVG_CAPI_H__ +#define __THORVG_CAPI_H__ + +#ifdef TVG_EXPORT + #undef TVG_EXPORT +#endif + +#ifdef TVG_BUILD + #define TVG_EXPORT __attribute__ ((visibility ("default"))) +#else + #define TVG_EXPORT +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _Tvg_Canvas Tvg_Canvas; +typedef struct _Tvg_Paint Tvg_Paint; + +#define TVG_ENGINE_SW (1 << 1) +#define TVG_ENGINE_GL (1 << 2) + +typedef enum { + TVG_RESULT_SUCCESS = 0, + TVG_RESULT_INVALID_ARGUMENT, + TVG_RESULT_INSUFFICIENT_CONDITION, + TVG_RESULT_FAILED_ALLOCATION, + TVG_RESULT_MEMORY_CORRUPTION, + TVG_RESULT_NOT_SUPPORTED, + TVG_RESULT_UNKNOWN +} Tvg_Result; + +typedef enum { + TVG_PATH_COMMAND_CLOSE = 0, + TVG_PATH_COMMAND_MOVE_TO, + TVG_PATH_COMMAND_LINE_TO, + TVG_PATH_COMMAND_CUBIC_TO +} Tvg_Path_Command; + +typedef enum { + TVG_STROKE_CAP_SQUARE = 0, + TVG_STROKE_CAP_ROUND, + TVG_STROKE_CAP_BUTT +} Tvg_Stroke_Cap; + +typedef enum { + TVG_STROKE_JOIN_BEVEL = 0, + TVG_STROKE_JOIN_ROUND, + TVG_STROKE_JOIN_MITER +} Tvg_Stroke_Join; + +typedef enum { + TVG_STROKE_FILL_PAD = 0, + TVG_STROKE_FILL_REFLECT, + TVG_STROKE_FILL_REPEAT +} Tvg_Stroke_Fill; + + +typedef struct +{ + float x, y; +} Tvg_Point; + +typedef struct +{ + float e11, e12, e13; + float e21, e22, e23; + float e31, e32, e33; +} Tvg_Matrix; + + + +TVG_EXPORT Tvg_Result tvg_engine_init(unsigned engine_method); +TVG_EXPORT Tvg_Result tvg_engine_term(unsigned engine_method); + +TVG_EXPORT Tvg_Canvas* tvg_swcanvas_create(); +TVG_EXPORT Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas* canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h); + +TVG_EXPORT Tvg_Result tvg_canvas_destroy(Tvg_Canvas* canvas); +TVG_EXPORT Tvg_Result tvg_canvas_push(Tvg_Canvas* canvas, Tvg_Paint* paint); +TVG_EXPORT Tvg_Result tvg_canvas_reserve(Tvg_Canvas* canvas, uint32_t n); +TVG_EXPORT Tvg_Result tvg_canvas_clear(Tvg_Canvas* canvas); +TVG_EXPORT Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas); +TVG_EXPORT Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint); +TVG_EXPORT Tvg_Result tvg_canvas_draw(Tvg_Canvas* canvas, unsigned char async); +TVG_EXPORT Tvg_Result tvg_canvas_sync(Tvg_Canvas* canvas); + +TVG_EXPORT Tvg_Paint* tvg_shape_new(); +TVG_EXPORT Tvg_Result tvg_shape_del(Tvg_Paint* paint); +TVG_EXPORT Tvg_Result tvg_shape_reset(Tvg_Paint* paint); +TVG_EXPORT Tvg_Result tvg_shape_move_to(Tvg_Paint* paint, float x, float y); +TVG_EXPORT Tvg_Result tvg_shape_line_to(Tvg_Paint* paint, float x, float y); +TVG_EXPORT Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1, float cx2, float cy2, float x, float y); +TVG_EXPORT Tvg_Result tvg_shape_close(Tvg_Paint* paint); +TVG_EXPORT Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry); +TVG_EXPORT Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry); +TVG_EXPORT Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt); +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width); +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt); +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint* paint, Tvg_Stroke_Cap cap); +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join join); +TVG_EXPORT Tvg_Result tvg_shape_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a); + +#ifdef __cplusplus +} +#endif + +#endif //_THORVG_CAPI_H_ diff --git a/meson.build b/meson.build index d1803a30..6da6d269 100644 --- a/meson.build +++ b/meson.build @@ -22,6 +22,10 @@ if get_option('vectors').contains('avx') == true config_h.set10('THORVG_AVX_VECTOR_SUPPORT', true) endif +if get_option('bindings').contains('capi') == true + config_h.set10('THORVG_CAPI_BINDING_SUPPORT', true) +endif + configure_file( output: 'config.h', configuration: config_h diff --git a/meson_options.txt b/meson_options.txt index 5a440fa0..59d0130d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -15,3 +15,9 @@ option('vectors', choices: ['', 'avx'], value: [''], description: 'Enable CPU Vectorization(SIMD) in thorvg') + +option('bindings', + type: 'array', + choices: ['', 'capi'], + value: ['capi'], + description: 'Enable C API binding') diff --git a/src/bindings/capi/meson.build b/src/bindings/capi/meson.build new file mode 100644 index 00000000..919442ae --- /dev/null +++ b/src/bindings/capi/meson.build @@ -0,0 +1,8 @@ +source_file = [ + 'tvgCapi.cpp', +] + +subbinding_dep += [declare_dependency( + include_directories : include_directories('.'), + sources : source_file +)] diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp new file mode 100644 index 00000000..37e01af8 --- /dev/null +++ b/src/bindings/capi/tvgCapi.cpp @@ -0,0 +1,214 @@ +#include +#include "thorvg_capi.h" + +using namespace std; +using namespace tvg; + +#ifdef __cplusplus +extern "C" { +#endif + +struct _Tvg_Canvas +{ + //Dummy for Direct Casting +}; + +struct _Tvg_Paint +{ + //Dummy for Direct Casting +}; + + +/************************************************************************/ +/* Engine API */ +/************************************************************************/ + +TVG_EXPORT Tvg_Result tvg_engine_init(unsigned engine_method) { + Result ret = Result::Success; + + if (engine_method & TVG_ENGINE_SW) ret = tvg::Initializer::init(tvg::CanvasEngine::Sw); + if (ret != Result::Success) return (Tvg_Result) ret; + + if (engine_method & TVG_ENGINE_GL) ret = tvg::Initializer::init(tvg::CanvasEngine::Gl); + return (Tvg_Result) ret; +} + + +TVG_EXPORT Tvg_Result tvg_engine_term(unsigned engine_method) { + Result ret = Result::Success; + + if (engine_method & TVG_ENGINE_SW) ret = tvg::Initializer::init(tvg::CanvasEngine::Sw); + if (ret != Result::Success) return (Tvg_Result) ret; + + if (engine_method & TVG_ENGINE_GL) ret = tvg::Initializer::init(tvg::CanvasEngine::Gl); + return (Tvg_Result) ret; +} + +/************************************************************************/ +/* Canvas API */ +/************************************************************************/ + +TVG_EXPORT Tvg_Canvas* tvg_swcanvas_create() +{ + return (Tvg_Canvas*) SwCanvas::gen().release(); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_destroy(Tvg_Canvas* canvas) +{ + delete(canvas); + return TVG_RESULT_SUCCESS; +} + + +TVG_EXPORT Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas* canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) +{ + return (Tvg_Result) reinterpret_cast(canvas)->target(buffer, stride, w, h); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_push(Tvg_Canvas* canvas, Tvg_Paint* paint) +{ + return (Tvg_Result) reinterpret_cast(canvas)->push(unique_ptr((Paint*)paint)); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_reserve(Tvg_Canvas* canvas, uint32_t n) +{ + return (Tvg_Result) reinterpret_cast(canvas)->reserve(n); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_clear(Tvg_Canvas* canvas) +{ + return (Tvg_Result) reinterpret_cast(canvas)->clear(); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas) +{ + return (Tvg_Result) reinterpret_cast(canvas)->update(); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint) +{ + return (Tvg_Result) reinterpret_cast(canvas)->update((Paint*) paint); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_draw(Tvg_Canvas* canvas, unsigned char async) +{ + return (Tvg_Result) reinterpret_cast(canvas)->draw(async); +} + + +TVG_EXPORT Tvg_Result tvg_canvas_sync(Tvg_Canvas* canvas) +{ + return (Tvg_Result) reinterpret_cast(canvas)->sync(); +} + + +/************************************************************************/ +/* Shape API */ +/************************************************************************/ + +TVG_EXPORT Tvg_Paint* tvg_shape_new() +{ + return (Tvg_Paint*) Shape::gen().release(); +} + + +TVG_EXPORT Tvg_Result tvg_shape_del(Tvg_Paint* paint) +{ + delete(paint); + return TVG_RESULT_SUCCESS; +} + + +TVG_EXPORT Tvg_Result tvg_shape_reset(Tvg_Paint* paint) +{ + return (Tvg_Result) reinterpret_cast(paint)->reset(); +} + + +TVG_EXPORT Tvg_Result tvg_shape_move_to(Tvg_Paint* paint, float x, float y) +{ + return (Tvg_Result) reinterpret_cast(paint)->moveTo(x, y); +} + + +TVG_EXPORT Tvg_Result tvg_shape_line_to(Tvg_Paint* paint, float x, float y) +{ + return (Tvg_Result) reinterpret_cast(paint)->lineTo(x, y); +} + + +TVG_EXPORT Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1, float cx2, float cy2, float x, float y) +{ + return (Tvg_Result) reinterpret_cast(paint)->cubicTo(cx1, cy1, cx2, cy2, x, y); +} + + +TVG_EXPORT Tvg_Result tvg_shape_close(Tvg_Paint* paint) +{ + return (Tvg_Result) reinterpret_cast(paint)->close(); +} + + +TVG_EXPORT Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry) +{ + return (Tvg_Result) reinterpret_cast(paint)->appendRect(x, y, w, h, rx, ry); +} + + +TVG_EXPORT Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry) +{ + return (Tvg_Result) reinterpret_cast(paint)->appendCircle(cx, cy, rx, ry); +} + + +TVG_EXPORT Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt) +{ + return (Tvg_Result) reinterpret_cast(paint)->appendPath((PathCommand*)cmds, cmdCnt, (Point*)pts, ptsCnt); +} + + +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width) +{ + return (Tvg_Result) reinterpret_cast(paint)->stroke(width); +} + + +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + return (Tvg_Result) reinterpret_cast(paint)->stroke(r, g, b, a); +} + + +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt) +{ + return (Tvg_Result) reinterpret_cast(paint)->stroke(dashPattern, cnt); +} + + +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint* paint, Tvg_Stroke_Cap cap) +{ + return (Tvg_Result) reinterpret_cast(paint)->stroke((StrokeCap)cap); +} + + +TVG_EXPORT Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join join) +{ + return (Tvg_Result) reinterpret_cast(paint)->stroke((StrokeJoin)join); +} + + +TVG_EXPORT Tvg_Result tvg_shape_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + return (Tvg_Result) reinterpret_cast(paint)->fill(r, g, b, a); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/bindings/meson.build b/src/bindings/meson.build new file mode 100644 index 00000000..2b3a1506 --- /dev/null +++ b/src/bindings/meson.build @@ -0,0 +1,11 @@ +subbinding_dep = [] + +if get_option('bindings').contains('capi') == true + subdir('capi') + message('Enable CAPI Bindings') +endif + +binding_dep = declare_dependency( + dependencies: subbinding_dep, + include_directories : include_directories('.'), +) diff --git a/src/meson.build b/src/meson.build index 68738701..ced59a59 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,10 +11,11 @@ endif subdir('lib') subdir('loaders') +subdir('bindings') subdir('examples') thread_dep = meson.get_compiler('cpp').find_library('pthread') -thorvg_lib_dep = [common_dep, loader_dep, thread_dep] +thorvg_lib_dep = [common_dep, loader_dep, binding_dep, thread_dep] thorvg_lib = library( 'thorvg', diff --git a/test/makefile b/test/makefile index 6455a0a2..ef9e28a6 100644 --- a/test/makefile +++ b/test/makefile @@ -18,3 +18,4 @@ all: gcc -o testGradientTransform testGradientTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` gcc -o testSvg testSvg.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` gcc -o testAsync testAsync.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` + gcc -o testCapi testCapi.c -g 'pkg-config --cflags --libs elementary thorvg` diff --git a/test/testCapi.c b/test/testCapi.c new file mode 100644 index 00000000..5b8b91bc --- /dev/null +++ b/test/testCapi.c @@ -0,0 +1,73 @@ +#include +#include + +#define WIDTH 800 +#define HEIGHT 800 + + +/************************************************************************/ +/* Capi Test Code */ +/************************************************************************/ + +static uint32_t buffer[WIDTH * HEIGHT]; + +void testCapi() +{ + tvg_engine_init(TVG_ENGINE_SW | TVG_ENGINE_GL); + + Tvg_Canvas* canvas = tvg_swcanvas_create(); + tvg_swcanvas_set_target(canvas, buffer, WIDTH, WIDTH, HEIGHT); + + Tvg_Paint* shape = tvg_shape_new(); + tvg_shape_append_rect(shape, 0, 0, 200, 200, 0, 0); + tvg_shape_append_rect(shape, 100, 100, 300, 300, 100, 100); + tvg_shape_append_circle(shape, 400, 400, 100, 100); + tvg_shape_append_circle(shape, 400, 500, 170, 100); + tvg_shape_fill_color(shape, 255, 255, 0, 255); + tvg_canvas_push(canvas, shape); + + tvg_canvas_draw(canvas, 1); + tvg_canvas_sync(canvas); + + tvg_canvas_destroy(canvas); + + tvg_engine_term(TVG_ENGINE_SW | TVG_ENGINE_GL); +} + + +/************************************************************************/ +/* Main Code */ +/************************************************************************/ + +void win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + + +int main(int argc, char **argv) +{ + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "ThorVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* view = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(view, WIDTH, HEIGHT); + evas_object_image_data_set(view, buffer); + evas_object_image_pixels_dirty_set(view, EINA_TRUE); + evas_object_image_data_update_add(view, 0, 0, WIDTH, HEIGHT); + evas_object_size_hint_weight_set(view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(view); + + elm_win_resize_object_add(win, view); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + testCapi(); + + elm_run(); + elm_shutdown(); + + return 0; +}