From 826f0850e0c20aae7645054809cda2678b38783c Mon Sep 17 00:00:00 2001 From: lujiale Date: Sun, 12 Nov 2023 19:18:48 +0800 Subject: [PATCH] update 2023-11-12 --- .../res/assets/default/inc/ui/images.data | 406 +- .../awtk/res/assets/default/inc/ui/main.data | 456 +- .../awtk/res/assets/default/raw/ui/images.bin | Bin 7174 -> 7175 bytes .../awtk/res/assets/default/raw/ui/main.bin | Bin 6582 -> 6584 bytes .../entry/gui_demo/lvgl/benchmark/benchmark.c | 8 + .../gui_demo/lvgl/music/demo_music_main.c | 4 +- project/entry/gui_demo/lvgl/music/music.c | 7 +- .../gui_demo/lvgl/transform/demo_transform.c | 4 + project/gui/awtk/src/base/assets_manager.c | 70 +- project/gui/awtk/src/base/assets_manager.h | 27 +- project/gui/awtk/src/base/image_manager.c | 12 + project/gui/awtk/src/base/image_manager.h | 25 +- project/gui/awtk/src/base/widget.c | 68 +- project/gui/awtk/src/base/widget_consts.h | 28 +- project/gui/awtk/src/base/window_manager.c | 8 + project/gui/awtk/src/base/window_manager.h | 2 +- project/gui/awtk/src/debugger/debugger.c | 50 +- project/gui/awtk/src/debugger/debugger.h | 42 +- .../gui/awtk/src/debugger/debugger_client.c | 20 +- .../gui/awtk/src/debugger/debugger_fscript.c | 25 +- project/gui/awtk/src/debugger/debugger_lldb.c | 91 +- project/gui/awtk/src/debugger/debugger_lldb.h | 4 +- .../gui/awtk/src/debugger/debugger_message.h | 14 +- .../gui/awtk/src/debugger/debugger_server.c | 36 +- .../awtk/src/image_loader/image_loader_stb.c | 34 +- .../input_methods/input_method_default.inc | 4 +- .../gui/awtk/src/remote_ui/client/remote_ui.c | 438 +- .../gui/awtk/src/remote_ui/client/remote_ui.h | 108 +- .../src/remote_ui/service/remote_ui_service.c | 614 ++- .../src/remote_ui/service/remote_ui_service.h | 12 +- .../remote_ui/shared/remote_ui_types_def.h | 333 +- project/gui/awtk/src/service/Makefile | 1 + project/gui/awtk/src/service/client.c | 276 ++ project/gui/awtk/src/service/client.h | 131 + project/gui/awtk/src/service/msg_header.h | 153 + project/gui/awtk/src/service/service.c | 252 ++ project/gui/awtk/src/service/service.h | 78 +- .../awtk/src/streams/serial/iostream_serial.c | 3 +- project/gui/awtk/src/streams/stream_factory.c | 5 +- project/gui/awtk/src/tkc/fs.c | 49 +- project/gui/awtk/src/tkc/fs.h | 14 +- project/gui/awtk/src/tkc/serial_helper.c | 2 +- project/gui/awtk/src/tkc/serial_helper.h | 29 +- project/gui/awtk/src/tkc/socket_helper.c | 18 +- project/gui/awtk/src/tkc/utils.c | 32 +- project/gui/awtk/src/tkc/value.c | 76 +- .../window_manager/window_manager_default.c | 146 +- project/gui/lvgl/lv_conf.h | 6 + project/gui/lvgl/lvgl.h | 2 + project/gui/lvgl/src/core/lv_global.h | 1 - project/gui/lvgl/src/core/lv_group.c | 5 + project/gui/lvgl/src/core/lv_group.h | 10 +- project/gui/lvgl/src/core/lv_obj.c | 8 +- project/gui/lvgl/src/core/lv_obj.h | 4 +- project/gui/lvgl/src/core/lv_obj_class.c | 4 +- project/gui/lvgl/src/core/lv_obj_class.h | 2 - project/gui/lvgl/src/core/lv_obj_draw.h | 2 - project/gui/lvgl/src/core/lv_obj_event.c | 4 - project/gui/lvgl/src/core/lv_obj_event.h | 1 - project/gui/lvgl/src/core/lv_obj_pos.c | 5 - project/gui/lvgl/src/core/lv_obj_pos.h | 2 - project/gui/lvgl/src/core/lv_obj_scroll.c | 4 - project/gui/lvgl/src/core/lv_obj_scroll.h | 1 - project/gui/lvgl/src/core/lv_obj_style.c | 15 +- project/gui/lvgl/src/core/lv_obj_style.h | 10 +- project/gui/lvgl/src/core/lv_obj_tree.c | 2 - project/gui/lvgl/src/core/lv_obj_tree.h | 2 - project/gui/lvgl/src/core/lv_refr.c | 14 +- project/gui/lvgl/src/core/lv_refr.h | 5 + .../lvgl/src/dev/display/drm/lv_linux_drm.c | 3 +- .../lvgl/src/dev/display/fb/lv_linux_fbdev.c | 3 +- .../src/dev/display/tft_espi/lv_tft_espi.cpp | 3 +- project/gui/lvgl/src/dev/evdev/lv_evdev.c | 3 +- .../gui/lvgl/src/dev/nuttx/lv_nuttx_fbdev.c | 9 +- project/gui/lvgl/src/dev/nuttx/lv_nuttx_lcd.c | 4 +- .../gui/lvgl/src/dev/nuttx/lv_nuttx_libuv.c | 3 +- .../gui/lvgl/src/dev/sdl/lv_sdl_keyboard.c | 5 +- project/gui/lvgl/src/dev/sdl/lv_sdl_mouse.c | 4 +- .../gui/lvgl/src/dev/sdl/lv_sdl_mousewheel.c | 3 +- project/gui/lvgl/src/dev/sdl/lv_sdl_window.c | 4 +- project/gui/lvgl/src/display/lv_display.c | 9 +- project/gui/lvgl/src/draw/Makefile | 1 + project/gui/lvgl/src/draw/lv_draw.c | 60 +- project/gui/lvgl/src/draw/lv_draw.h | 21 +- project/gui/lvgl/src/draw/lv_draw_buf.c | 5 - project/gui/lvgl/src/draw/lv_draw_buf.h | 4 +- project/gui/lvgl/src/draw/lv_draw_image.c | 4 - project/gui/lvgl/src/draw/lv_draw_image.h | 2 +- project/gui/lvgl/src/draw/lv_draw_label.c | 8 - project/gui/lvgl/src/draw/lv_draw_label.h | 10 +- project/gui/lvgl/src/draw/lv_draw_line.h | 1 - project/gui/lvgl/src/draw/lv_draw_mask.h | 1 - project/gui/lvgl/src/draw/lv_draw_rect.c | 3 - project/gui/lvgl/src/draw/lv_draw_rect.h | 1 - project/gui/lvgl/src/draw/lv_draw_vector.c | 751 ++++ project/gui/lvgl/src/draw/lv_draw_vector.h | 536 +++ project/gui/lvgl/src/draw/lv_image_decoder.c | 14 +- project/gui/lvgl/src/draw/lv_image_decoder.h | 6 +- .../lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.c | 6 +- .../gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.h | 3 +- .../src/draw/nxp/pxp/lv_draw_pxp_bg_img.c | 1 - .../lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c | 1 - .../lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c | 1 - .../lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.h | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.c | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.h | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.c | 1 - .../gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.h | 1 - .../src/draw/nxp/vglite/lv_draw_buf_vglite.c | 1 - .../lvgl/src/draw/nxp/vglite/lv_draw_vglite.c | 5 +- .../lvgl/src/draw/nxp/vglite/lv_draw_vglite.h | 3 +- .../src/draw/nxp/vglite/lv_draw_vglite_arc.c | 3 +- .../draw/nxp/vglite/lv_draw_vglite_bg_img.c | 1 - .../src/draw/nxp/vglite/lv_draw_vglite_fill.c | 3 +- .../src/draw/nxp/vglite/lv_draw_vglite_img.c | 1 - .../draw/nxp/vglite/lv_draw_vglite_label.c | 4 +- .../draw/nxp/vglite/lv_draw_vglite_layer.c | 1 - .../src/draw/nxp/vglite/lv_draw_vglite_line.c | 1 - .../lvgl/src/draw/nxp/vglite/lv_vglite_buf.c | 3 +- .../lvgl/src/draw/nxp/vglite/lv_vglite_buf.h | 1 - .../src/draw/nxp/vglite/lv_vglite_matrix.c | 1 - .../src/draw/nxp/vglite/lv_vglite_matrix.h | 1 - .../lvgl/src/draw/nxp/vglite/lv_vglite_path.c | 1 - .../lvgl/src/draw/nxp/vglite/lv_vglite_path.h | 1 - .../src/draw/nxp/vglite/lv_vglite_utils.c | 1 - .../src/draw/nxp/vglite/lv_vglite_utils.h | 1 - project/gui/lvgl/src/draw/sw/Makefile | 1 + project/gui/lvgl/src/draw/sw/lv_draw_sw.c | 108 +- project/gui/lvgl/src/draw/sw/lv_draw_sw.h | 17 + project/gui/lvgl/src/draw/sw/lv_draw_sw_arc.c | 2 - .../gui/lvgl/src/draw/sw/lv_draw_sw_border.c | 5 - .../lvgl/src/draw/sw/lv_draw_sw_box_shadow.c | 5 +- .../gui/lvgl/src/draw/sw/lv_draw_sw_fill.c | 1 - .../lvgl/src/draw/sw/lv_draw_sw_gradient.c | 2 - .../lvgl/src/draw/sw/lv_draw_sw_gradient.h | 2 - project/gui/lvgl/src/draw/sw/lv_draw_sw_img.c | 175 +- .../gui/lvgl/src/draw/sw/lv_draw_sw_line.c | 1 - .../gui/lvgl/src/draw/sw/lv_draw_sw_mask.c | 14 +- .../gui/lvgl/src/draw/sw/lv_draw_sw_mask.h | 6 +- .../lvgl/src/draw/sw/lv_draw_sw_transform.c | 19 +- .../lvgl/src/draw/sw/lv_draw_sw_triangle.c | 1 - .../gui/lvgl/src/draw/sw/lv_draw_sw_vector.c | 452 ++ project/gui/lvgl/src/layouts/lv_layout.c | 7 +- project/gui/lvgl/src/layouts/lv_layout.h | 6 +- project/gui/lvgl/src/libs/Makefile | 1 + .../gui/lvgl/src/libs/barcode/lv_barcode.c | 1 + project/gui/lvgl/src/libs/bmp/lv_bmp.c | 15 +- project/gui/lvgl/src/libs/bmp/lv_bmp.h | 1 + .../gui/lvgl/src/libs/freetype/lv_freetype.c | 3 +- .../gui/lvgl/src/libs/freetype/lv_ftsystem.c | 1 - project/gui/lvgl/src/libs/thorvg/Makefile | 34 + project/gui/lvgl/src/libs/thorvg/config.h | 15 + project/gui/lvgl/src/libs/thorvg/thorvg.h | 1925 +++++++++ .../gui/lvgl/src/libs/thorvg/thorvg_capi.h | 2334 ++++++++++ .../gui/lvgl/src/libs/thorvg/tvgAnimation.cpp | 125 + project/gui/lvgl/src/libs/thorvg/tvgArray.h | 192 + .../gui/lvgl/src/libs/thorvg/tvgBezier.cpp | 199 + project/gui/lvgl/src/libs/thorvg/tvgBezier.h | 55 + .../gui/lvgl/src/libs/thorvg/tvgCanvas.cpp | 93 + project/gui/lvgl/src/libs/thorvg/tvgCanvas.h | 152 + project/gui/lvgl/src/libs/thorvg/tvgCapi.cpp | 784 ++++ project/gui/lvgl/src/libs/thorvg/tvgCommon.h | 95 + .../lvgl/src/libs/thorvg/tvgCompressor.cpp | 481 +++ .../gui/lvgl/src/libs/thorvg/tvgCompressor.h | 41 + project/gui/lvgl/src/libs/thorvg/tvgFill.cpp | 256 ++ project/gui/lvgl/src/libs/thorvg/tvgFill.h | 118 + .../gui/lvgl/src/libs/thorvg/tvgFrameModule.h | 53 + .../lvgl/src/libs/thorvg/tvgInitializer.cpp | 185 + .../src/libs/thorvg/tvgIteratorAccessor.h | 49 + .../gui/lvgl/src/libs/thorvg/tvgLoadModule.h | 64 + .../gui/lvgl/src/libs/thorvg/tvgLoader.cpp | 255 ++ project/gui/lvgl/src/libs/thorvg/tvgLoader.h | 43 + project/gui/lvgl/src/libs/thorvg/tvgMath.cpp | 108 + project/gui/lvgl/src/libs/thorvg/tvgMath.h | 190 + project/gui/lvgl/src/libs/thorvg/tvgPaint.cpp | 437 ++ project/gui/lvgl/src/libs/thorvg/tvgPaint.h | 222 + .../gui/lvgl/src/libs/thorvg/tvgPicture.cpp | 153 + project/gui/lvgl/src/libs/thorvg/tvgPicture.h | 317 ++ .../gui/lvgl/src/libs/thorvg/tvgRawLoader.cpp | 101 + .../gui/lvgl/src/libs/thorvg/tvgRawLoader.h | 49 + .../gui/lvgl/src/libs/thorvg/tvgRender.cpp | 75 + project/gui/lvgl/src/libs/thorvg/tvgRender.h | 342 ++ .../gui/lvgl/src/libs/thorvg/tvgSaveModule.h | 48 + project/gui/lvgl/src/libs/thorvg/tvgSaver.cpp | 147 + project/gui/lvgl/src/libs/thorvg/tvgScene.cpp | 88 + project/gui/lvgl/src/libs/thorvg/tvgScene.h | 255 ++ project/gui/lvgl/src/libs/thorvg/tvgShape.cpp | 416 ++ project/gui/lvgl/src/libs/thorvg/tvgShape.h | 396 ++ project/gui/lvgl/src/libs/thorvg/tvgStr.cpp | 245 ++ project/gui/lvgl/src/libs/thorvg/tvgStr.h | 43 + .../lvgl/src/libs/thorvg/tvgSvgCssStyle.cpp | 255 ++ .../gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.h | 40 + .../gui/lvgl/src/libs/thorvg/tvgSvgLoader.cpp | 3759 +++++++++++++++++ .../gui/lvgl/src/libs/thorvg/tvgSvgLoader.h | 75 + .../lvgl/src/libs/thorvg/tvgSvgLoaderCommon.h | 576 +++ .../gui/lvgl/src/libs/thorvg/tvgSvgPath.cpp | 561 +++ project/gui/lvgl/src/libs/thorvg/tvgSvgPath.h | 36 + .../src/libs/thorvg/tvgSvgSceneBuilder.cpp | 890 ++++ .../lvgl/src/libs/thorvg/tvgSvgSceneBuilder.h | 36 + .../gui/lvgl/src/libs/thorvg/tvgSvgUtil.cpp | 76 + project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.h | 36 + .../gui/lvgl/src/libs/thorvg/tvgSwCanvas.cpp | 112 + .../gui/lvgl/src/libs/thorvg/tvgSwCommon.h | 580 +++ .../gui/lvgl/src/libs/thorvg/tvgSwFill.cpp | 785 ++++ .../gui/lvgl/src/libs/thorvg/tvgSwImage.cpp | 164 + .../gui/lvgl/src/libs/thorvg/tvgSwMath.cpp | 335 ++ .../gui/lvgl/src/libs/thorvg/tvgSwMemPool.cpp | 135 + .../gui/lvgl/src/libs/thorvg/tvgSwRaster.cpp | 1957 +++++++++ .../gui/lvgl/src/libs/thorvg/tvgSwRasterAvx.h | 197 + .../gui/lvgl/src/libs/thorvg/tvgSwRasterC.h | 169 + .../lvgl/src/libs/thorvg/tvgSwRasterNeon.h | 144 + .../lvgl/src/libs/thorvg/tvgSwRasterTexmap.h | 1213 ++++++ .../lvgl/src/libs/thorvg/tvgSwRenderer.cpp | 862 ++++ .../gui/lvgl/src/libs/thorvg/tvgSwRenderer.h | 90 + project/gui/lvgl/src/libs/thorvg/tvgSwRle.cpp | 1134 +++++ .../gui/lvgl/src/libs/thorvg/tvgSwShape.cpp | 686 +++ .../gui/lvgl/src/libs/thorvg/tvgSwStroke.cpp | 915 ++++ .../lvgl/src/libs/thorvg/tvgTaskScheduler.cpp | 209 + .../lvgl/src/libs/thorvg/tvgTaskScheduler.h | 95 + .../gui/lvgl/src/libs/thorvg/tvgXmlParser.cpp | 591 +++ .../gui/lvgl/src/libs/thorvg/tvgXmlParser.h | 64 + .../gui/lvgl/src/libs/tiny_ttf/lv_tiny_ttf.c | 22 +- .../lvgl/src/libs/tiny_ttf/stb_rect_pack.h | 4 - .../src/libs/tiny_ttf/stb_truetype_htcw.h | 16 +- project/gui/lvgl/src/lv_api_map.h | 5 +- project/gui/lvgl/src/lv_conf_internal.h | 36 + project/gui/lvgl/src/lv_init.c | 51 +- project/gui/lvgl/src/misc/Makefile | 1 + project/gui/lvgl/src/misc/lv_anim.c | 7 +- project/gui/lvgl/src/misc/lv_anim.h | 6 +- project/gui/lvgl/src/misc/lv_anim_timeline.c | 8 +- project/gui/lvgl/src/misc/lv_array.c | 130 + project/gui/lvgl/src/misc/lv_array.h | 86 + project/gui/lvgl/src/misc/lv_cache.c | 5 + project/gui/lvgl/src/misc/lv_cache.h | 6 +- project/gui/lvgl/src/misc/lv_cache_builtin.c | 5 + project/gui/lvgl/src/misc/lv_cache_builtin.h | 2 + project/gui/lvgl/src/misc/lv_color.c | 2 - project/gui/lvgl/src/misc/lv_color.h | 3 - project/gui/lvgl/src/misc/lv_fs.c | 12 +- project/gui/lvgl/src/misc/lv_fs.h | 9 +- project/gui/lvgl/src/misc/lv_lru.c | 12 +- project/gui/lvgl/src/misc/lv_text.h | 8 +- project/gui/lvgl/src/misc/lv_timer.c | 7 + project/gui/lvgl/src/misc/lv_timer.h | 5 + project/gui/lvgl/src/misc/lv_types.h | 2 + project/gui/lvgl/src/osal/Makefile | 1 + project/gui/lvgl/src/osal/lv_os.h | 2 + project/gui/lvgl/src/osal/lv_pthread.c | 8 +- project/gui/lvgl/src/osal/lv_rtthread.c | 4 +- project/gui/lvgl/src/osal/lv_windows.c | 215 + project/gui/lvgl/src/osal/lv_windows.h | 54 + .../gui/lvgl/src/others/fragment/README.md | 0 .../lvgl/src/others/fragment/lv_fragment.c | 3 +- .../lvgl/src/others/fragment/lv_fragment.h | 3 - .../src/others/fragment/lv_fragment_manager.c | 5 +- .../gui/lvgl/src/others/imgfont/lv_imgfont.c | 3 +- .../gui/lvgl/src/others/monkey/lv_monkey.c | 4 +- .../lvgl/src/others/snapshot/lv_snapshot.c | 14 +- .../gui/lvgl/src/others/sysmon/lv_sysmon.c | 20 +- .../gui/lvgl/src/others/sysmon/lv_sysmon.h | 7 + project/gui/lvgl/src/stdlib/Makefile | 1 + .../src/stdlib/builtin/lv_mem_core_builtin.c | 18 +- project/gui/lvgl/src/stdlib/lv_mem.c | 32 +- project/gui/lvgl/src/stdlib/lv_mem.h | 17 +- project/gui/lvgl/src/stdlib/rtthread/Makefile | 3 + .../stdlib/rtthread/lv_mem_core_rtthread.c | 98 + .../src/stdlib/rtthread/lv_sprintf_rtthread.c | 61 + .../src/stdlib/rtthread/lv_string_rtthread.c | 85 + .../lvgl/src/themes/basic/lv_theme_basic.c | 16 +- .../src/themes/default/lv_theme_default.c | 23 +- .../gui/lvgl/src/themes/mono/lv_theme_mono.c | 14 +- .../lvgl/src/widgets/animimage/lv_animimage.h | 5 +- project/gui/lvgl/src/widgets/arc/lv_arc.h | 3 +- project/gui/lvgl/src/widgets/bar/lv_bar.h | 4 +- .../gui/lvgl/src/widgets/button/lv_button.h | 2 +- .../widgets/buttonmatrix/lv_buttonmatrix.c | 10 - .../widgets/buttonmatrix/lv_buttonmatrix.h | 10 +- .../lvgl/src/widgets/calendar/lv_calendar.c | 1 - .../lvgl/src/widgets/calendar/lv_calendar.h | 2 +- .../calendar/lv_calendar_header_arrow.h | 2 +- .../calendar/lv_calendar_header_dropdown.h | 2 +- .../gui/lvgl/src/widgets/canvas/lv_canvas.c | 5 +- .../gui/lvgl/src/widgets/canvas/lv_canvas.h | 3 +- project/gui/lvgl/src/widgets/chart/lv_chart.h | 14 +- .../lvgl/src/widgets/checkbox/lv_checkbox.h | 2 +- project/gui/lvgl/src/widgets/image/lv_image.c | 291 +- project/gui/lvgl/src/widgets/image/lv_image.h | 78 +- .../gui/lvgl/src/widgets/imgbtn/lv_imgbtn.h | 6 +- .../lvgl/src/widgets/keyboard/lv_keyboard.h | 3 +- project/gui/lvgl/src/widgets/label/lv_label.c | 29 - project/gui/lvgl/src/widgets/label/lv_label.h | 19 +- project/gui/lvgl/src/widgets/led/lv_led.h | 3 +- project/gui/lvgl/src/widgets/line/lv_line.h | 2 +- project/gui/lvgl/src/widgets/list/lv_list.h | 6 +- project/gui/lvgl/src/widgets/menu/lv_menu.h | 19 +- .../gui/lvgl/src/widgets/msgbox/lv_msgbox.h | 6 +- .../src/widgets/objx_templ/lv_objx_templ.h | 2 +- .../gui/lvgl/src/widgets/roller/lv_roller.c | 8 - .../gui/lvgl/src/widgets/roller/lv_roller.h | 5 +- project/gui/lvgl/src/widgets/scale/lv_scale.h | 2 +- .../gui/lvgl/src/widgets/slider/lv_slider.h | 4 +- project/gui/lvgl/src/widgets/span/lv_span.h | 4 +- .../gui/lvgl/src/widgets/spinbox/lv_spinbox.h | 2 +- .../gui/lvgl/src/widgets/spinner/lv_spinner.h | 3 +- .../gui/lvgl/src/widgets/switch/lv_switch.h | 2 +- project/gui/lvgl/src/widgets/table/lv_table.c | 155 +- project/gui/lvgl/src/widgets/table/lv_table.h | 31 +- .../gui/lvgl/src/widgets/tabview/lv_tabview.h | 2 +- .../lvgl/src/widgets/textarea/lv_textarea.h | 2 +- .../lvgl/src/widgets/tileview/lv_tileview.h | 4 +- project/gui/lvgl/src/widgets/win/lv_win.h | 3 +- 315 files changed, 34144 insertions(+), 2434 deletions(-) create mode 100644 project/gui/awtk/src/service/client.c create mode 100644 project/gui/awtk/src/service/client.h create mode 100644 project/gui/awtk/src/service/msg_header.h create mode 100644 project/gui/lvgl/src/draw/lv_draw_vector.c create mode 100644 project/gui/lvgl/src/draw/lv_draw_vector.h create mode 100644 project/gui/lvgl/src/draw/sw/lv_draw_sw_vector.c create mode 100644 project/gui/lvgl/src/libs/thorvg/Makefile create mode 100644 project/gui/lvgl/src/libs/thorvg/config.h create mode 100644 project/gui/lvgl/src/libs/thorvg/thorvg.h create mode 100644 project/gui/lvgl/src/libs/thorvg/thorvg_capi.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgAnimation.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgArray.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgBezier.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgBezier.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgCanvas.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgCanvas.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgCapi.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgCommon.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgCompressor.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgCompressor.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgFill.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgFill.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgFrameModule.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgInitializer.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgIteratorAccessor.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgLoadModule.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgLoader.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgLoader.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgMath.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgMath.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgPaint.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgPaint.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgPicture.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgPicture.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgRawLoader.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgRawLoader.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgRender.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgRender.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSaveModule.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSaver.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgScene.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgScene.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgShape.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgShape.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgStr.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgStr.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgLoaderCommon.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgPath.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgPath.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwCanvas.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwCommon.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwFill.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwImage.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwMath.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwMemPool.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRaster.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRasterAvx.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRasterC.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRasterNeon.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRasterTexmap.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwRle.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwShape.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgSwStroke.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.h create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgXmlParser.cpp create mode 100644 project/gui/lvgl/src/libs/thorvg/tvgXmlParser.h create mode 100644 project/gui/lvgl/src/misc/lv_array.c create mode 100644 project/gui/lvgl/src/misc/lv_array.h create mode 100644 project/gui/lvgl/src/osal/lv_windows.c create mode 100644 project/gui/lvgl/src/osal/lv_windows.h create mode 100644 project/gui/lvgl/src/others/fragment/README.md create mode 100644 project/gui/lvgl/src/stdlib/rtthread/Makefile create mode 100644 project/gui/lvgl/src/stdlib/rtthread/lv_mem_core_rtthread.c create mode 100644 project/gui/lvgl/src/stdlib/rtthread/lv_sprintf_rtthread.c create mode 100644 project/gui/lvgl/src/stdlib/rtthread/lv_string_rtthread.c diff --git a/project/entry/gui_demo/awtk/res/assets/default/inc/ui/images.data b/project/entry/gui_demo/awtk/res/assets/default/inc/ui/images.data index 74db3f527..dfd34b467 100644 --- a/project/entry/gui_demo/awtk/res/assets/default/inc/ui/images.data +++ b/project/entry/gui_demo/awtk/res/assets/default/inc/ui/images.data @@ -1,5 +1,5 @@ TK_CONST_DATA_ALIGN(const unsigned char ui_images[]) = { -0x04,0x00,0x01,0x01,0x06,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x73,0x00,0x00, +0x04,0x00,0x01,0x01,0x07,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x73,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x12,0x12,0x22,0x11,0x77,0x69,0x6e,0x64,0x6f,0x77,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -91,273 +91,273 @@ TK_CONST_DATA_ALIGN(const unsigned char ui_images[]) = { 0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61, -0x67,0x65,0x00,0x65,0x61,0x72,0x74,0x68,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x64, -0x65,0x66,0x61,0x75,0x6c,0x74,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x67,0x65,0x00,0x67,0x72,0x61,0x79,0x5f,0x38,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00, +0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66, -0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30, -0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x64,0x65,0x66,0x61, -0x75,0x6c,0x74,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65, -0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x65,0x61,0x72,0x74,0x68,0x00, -0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x69,0x63,0x6f,0x6e,0x00,0x00,0x6c,0x61,0x62,0x65, -0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00, -0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61, -0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74, -0x65,0x78,0x74,0x00,0x69,0x63,0x6f,0x6e,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x65, -0x61,0x72,0x74,0x68,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x63,0x65,0x6e,0x74,0x65, -0x72,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c, +0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31, +0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x64,0x65,0x66, +0x61,0x75,0x6c,0x74,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f, -0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d, -0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x63,0x65,0x6e,0x74,0x65,0x72,0x00,0x00,0x00, -0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c, +0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x65,0x61,0x72,0x74,0x68, +0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x69,0x63,0x6f,0x6e,0x00,0x00,0x6c,0x61,0x62, +0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00, +0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66, +0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00, +0x74,0x65,0x78,0x74,0x00,0x69,0x63,0x6f,0x6e,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74, -0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65, -0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x29,0x00,0x00,0x69,0x6d,0x61,0x67, -0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61, -0x67,0x65,0x00,0x31,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x73,0x63,0x61,0x6c,0x65, -0x5f,0x77,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00, +0x65,0x61,0x72,0x74,0x68,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x63,0x65,0x6e,0x74, +0x65,0x72,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79, 0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68, -0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x73,0x63,0x61,0x6c,0x65,0x5f,0x77,0x00, -0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72, -0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x31,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70, -0x65,0x00,0x73,0x63,0x61,0x6c,0x65,0x5f,0x68,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73, -0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77, -0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x73, -0x63,0x61,0x6c,0x65,0x5f,0x68,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74, -0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x31,0x00,0x64, -0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x6c,0x61,0x62,0x65, -0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00, -0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61, -0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74, -0x65,0x78,0x74,0x00,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69, -0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x63,0x65,0x6e,0x74,0x65,0x72,0x00,0x00, +0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72, -0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d, -0x31,0x2c,0x63,0x3d,0x32,0x2c,0x73,0x3d,0x32,0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67, -0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61, -0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00, -0x72,0x65,0x70,0x65,0x61,0x74,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70, +0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64, +0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x29,0x00,0x00,0x69,0x6d,0x61, +0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66, -0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30, -0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65, -0x61,0x74,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d, +0x61,0x67,0x65,0x00,0x31,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x73,0x63,0x61,0x6c, +0x65,0x5f,0x77,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00, -0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73,0x00, -0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x78,0x00,0x00, -0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61, +0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c, +0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x73,0x63,0x61,0x6c,0x65,0x5f,0x77, +0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00, -0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30, -0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x78,0x00,0x00,0x00,0x00, -0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f, +0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x31,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79, +0x70,0x65,0x00,0x73,0x63,0x61,0x6c,0x65,0x5f,0x68,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74,0x79, -0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66, -0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x32,0x2c,0x73,0x3d,0x32,0x2c,0x6d,0x3d,0x32, -0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00, +0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28, +0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00, +0x73,0x63,0x61,0x6c,0x65,0x5f,0x68,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72, -0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73,0x00,0x64,0x72,0x61, -0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x00,0x00,0x6c,0x61,0x62, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73, +0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x31,0x00, +0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x6c,0x61,0x62, 0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00, 0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66, 0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00, -0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x00,0x00,0x00,0x00,0x69,0x6d,0x61, +0x74,0x65,0x78,0x74,0x00,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f, +0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64, +0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72, +0x3d,0x31,0x2c,0x63,0x3d,0x32,0x2c,0x73,0x3d,0x32,0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61, 0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d, 0x61,0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65, -0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x5f,0x69,0x6e,0x76,0x65,0x72,0x73,0x65,0x00,0x00,0x6c, -0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64, -0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64, -0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25, -0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x5f,0x69,0x6e,0x76,0x65, -0x72,0x73,0x65,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c, -0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79, -0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x2c, -0x73,0x3d,0x32,0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00, +0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74, -0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65, -0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70, -0x61,0x74,0x63,0x68,0x39,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c, +0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31, +0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70, +0x65,0x61,0x74,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f, -0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30, -0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68, -0x39,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65, +0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73, +0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x78,0x00, +0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62, -0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74, -0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70,0x61,0x74,0x63,0x68,0x33, -0x5f,0x78,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74, +0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30, +0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x78,0x00,0x00,0x00, +0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79, -0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68, -0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x78, -0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74, +0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65, +0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x32,0x2c,0x73,0x3d,0x32,0x2c,0x6d,0x3d, +0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f, -0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e, -0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f, -0x79,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73,0x00,0x64,0x72, +0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x00,0x00,0x6c,0x61, +0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00, +0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65, +0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29, +0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x00,0x00,0x00,0x00,0x69,0x6d, +0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69, +0x6d,0x61,0x67,0x65,0x00,0x62,0x72,0x69,0x63,0x6b,0x73,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70, +0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x5f,0x69,0x6e,0x76,0x65,0x72,0x73,0x65,0x00,0x00, +0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f, -0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d, -0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x79,0x00, -0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00, +0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30, +0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5f,0x79,0x5f,0x69,0x6e,0x76, +0x65,0x72,0x73,0x65,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d, -0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00, -0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x2c,0x73,0x3d,0x32,0x2c, -0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79, +0x6c,0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61, +0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33, +0x2c,0x73,0x3d,0x32,0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73, +0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72, +0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00, +0x70,0x61,0x74,0x63,0x68,0x39,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66, +0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30, +0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63, +0x68,0x39,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00, 0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62, -0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61, -0x74,0x39,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70,0x61,0x74,0x63,0x68, +0x33,0x5f,0x78,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61, +0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c, +0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f, +0x78,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62, +0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74, +0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70,0x61,0x74,0x63,0x68,0x33, +0x5f,0x79,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79, 0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68, -0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x39,0x00, -0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x79, +0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72, -0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f, -0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x33,0x5f, -0x78,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65, +0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74, +0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x2c,0x73,0x3d,0x32, +0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f, -0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d, -0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x33,0x5f,0x78, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65, +0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f, +0x62,0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65, +0x61,0x74,0x39,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61, +0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c, +0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x39, 0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f, 0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e, 0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x33, -0x5f,0x79,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x5f,0x78,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79, 0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68, 0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x33,0x5f, -0x79,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00, +0x78,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00, -0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75, -0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x31,0x29,0x00,0x00, -0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72, -0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f,0x6e,0x00,0x64, -0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x78,0x5f,0x73,0x63, -0x61,0x6c,0x65,0x5f,0x79,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f, -0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30, -0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68, -0x33,0x5f,0x78,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f,0x79,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74, -0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62, +0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74, +0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x72,0x65,0x70,0x65,0x61,0x74, +0x33,0x5f,0x79,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c, -0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28, -0x72,0x3d,0x31,0x2c,0x63,0x3d,0x31,0x30,0x29,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61, +0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c, +0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x33, +0x5f,0x79,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65, +0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f, +0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x31,0x29,0x00, +0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65, +0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72,0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f,0x6e,0x00, +0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x78,0x5f,0x73, +0x63,0x61,0x6c,0x65,0x5f,0x79,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66, +0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30, +0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63, +0x68,0x33,0x5f,0x78,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f,0x79,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73, +0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69, +0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74, +0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x31,0x30,0x29,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00, +0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73, -0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67,0x72, -0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00, -0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x79,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f,0x78,0x00,0x00,0x6c,0x61, -0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00, -0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65, -0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29, -0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x79,0x5f,0x73,0x63,0x61,0x6c,0x65, -0x5f,0x78,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65, -0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f, -0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x2c,0x73, -0x3d,0x32,0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79, -0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x69,0x6d,0x61,0x67, -0x65,0x5f,0x70,0x61,0x63,0x6b,0x65,0x64,0x5f,0x66,0x67,0x23,0x78,0x79,0x77,0x68,0x28,0x30,0x2c,0x30, -0x2c,0x31,0x30,0x36,0x2c,0x35,0x34,0x29,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x63, -0x65,0x6e,0x74,0x65,0x72,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f, -0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30, -0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x63,0x6b,0x65, -0x64,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x67, +0x72,0x65,0x65,0x6e,0x5f,0x62,0x74,0x6e,0x5f,0x6e,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65, +0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x79,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f,0x78,0x00,0x00,0x6c, +0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64, +0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64, +0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25, +0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x74,0x63,0x68,0x33,0x5f,0x79,0x5f,0x73,0x63,0x61,0x6c, +0x65,0x5f,0x78,0x00,0x00,0x00,0x00,0x00,0x6c,0x69,0x73,0x74,0x5f,0x69,0x74,0x65,0x6d,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x79,0x6c, +0x65,0x00,0x65,0x6d,0x70,0x74,0x79,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79, +0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x72,0x3d,0x31,0x2c,0x63,0x3d,0x33,0x2c, +0x73,0x3d,0x32,0x2c,0x6d,0x3d,0x32,0x29,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x69, -0x6d,0x61,0x67,0x65,0x5f,0x70,0x61,0x63,0x6b,0x65,0x64,0x5f,0x66,0x67,0x23,0x67,0x72,0x69,0x64,0x28, -0x34,0x2c,0x33,0x2c,0x30,0x2c,0x32,0x29,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x64, -0x65,0x66,0x61,0x75,0x6c,0x74,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74, +0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x69,0x6d,0x61, +0x67,0x65,0x5f,0x70,0x61,0x63,0x6b,0x65,0x64,0x5f,0x66,0x67,0x23,0x78,0x79,0x77,0x68,0x28,0x30,0x2c, +0x30,0x2c,0x31,0x30,0x36,0x2c,0x35,0x34,0x29,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00, +0x63,0x65,0x6e,0x74,0x65,0x72,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66, 0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31,0x30, 0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x63,0x6b, -0x65,0x64,0x20,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00, +0x65,0x64,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65, -0x00,0x69,0x6d,0x61,0x67,0x65,0x5f,0x70,0x61,0x63,0x6b,0x65,0x64,0x5f,0x66,0x67,0x23,0x78,0x79,0x77, -0x68,0x28,0x31,0x30,0x36,0x2c,0x30,0x2c,0x31,0x30,0x36,0x2c,0x35,0x34,0x29,0x00,0x64,0x72,0x61,0x77, -0x5f,0x74,0x79,0x70,0x65,0x00,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00, +0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67,0x65,0x00, +0x69,0x6d,0x61,0x67,0x65,0x5f,0x70,0x61,0x63,0x6b,0x65,0x64,0x5f,0x66,0x67,0x23,0x67,0x72,0x69,0x64, +0x28,0x34,0x2c,0x33,0x2c,0x30,0x2c,0x32,0x29,0x00,0x64,0x72,0x61,0x77,0x5f,0x74,0x79,0x70,0x65,0x00, +0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00, -0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74, -0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74, -0x00,0x70,0x61,0x63,0x6b,0x65,0x64,0x20,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x73, -0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x62,0x61,0x72,0x5f,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c, -0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64, -0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x72,0x69,0x67,0x68,0x74,0x2c,0x79,0x3d,0x30,0x2c,0x77, -0x3d,0x31,0x32,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x6e,0x61,0x6d,0x65,0x00,0x62,0x61,0x72, -0x00,0x76,0x61,0x6c,0x75,0x65,0x00,0x30,0x00,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c, +0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x77,0x3d,0x31, +0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78,0x74,0x00,0x70,0x61,0x63, +0x6b,0x65,0x64,0x20,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x00,0x00,0x00,0x00,0x69,0x6d,0x61,0x67,0x65, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, -0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28, -0x78,0x3d,0x63,0x65,0x6e,0x74,0x65,0x72,0x2c,0x79,0x3d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x31,0x30, -0x2c,0x77,0x3d,0x32,0x35,0x25,0x2c,0x68,0x3d,0x33,0x30,0x29,0x00,0x6e,0x61,0x6d,0x65,0x00,0x63,0x6c, -0x6f,0x73,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6c,0x6f,0x73,0x65,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,};/*7222*/ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6f,0x72,0x64,0x65,0x72,0x00,0x69,0x6d,0x61,0x67, +0x65,0x00,0x69,0x6d,0x61,0x67,0x65,0x5f,0x70,0x61,0x63,0x6b,0x65,0x64,0x5f,0x66,0x67,0x23,0x78,0x79, +0x77,0x68,0x28,0x31,0x30,0x36,0x2c,0x30,0x2c,0x31,0x30,0x36,0x2c,0x35,0x34,0x29,0x00,0x64,0x72,0x61, +0x77,0x5f,0x74,0x79,0x70,0x65,0x00,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x6c,0x61,0x62,0x65,0x6c,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00, +0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c, +0x74,0x28,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x74,0x65,0x78, +0x74,0x00,0x70,0x61,0x63,0x6b,0x65,0x64,0x20,0x73,0x63,0x61,0x6c,0x65,0x00,0x00,0x00,0x00,0x00,0x00, +0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x62,0x61,0x72,0x5f,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x0c,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00, +0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x72,0x69,0x67,0x68,0x74,0x2c,0x79,0x3d,0x30,0x2c, +0x77,0x3d,0x31,0x32,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x6e,0x61,0x6d,0x65,0x00,0x62,0x61, +0x72,0x00,0x76,0x61,0x6c,0x75,0x65,0x00,0x30,0x00,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00, +0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74, +0x28,0x78,0x3d,0x63,0x65,0x6e,0x74,0x65,0x72,0x2c,0x79,0x3d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x31, +0x30,0x2c,0x77,0x3d,0x32,0x35,0x25,0x2c,0x68,0x3d,0x33,0x30,0x29,0x00,0x6e,0x61,0x6d,0x65,0x00,0x63, +0x6c,0x6f,0x73,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6c,0x6f,0x73,0x65,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,};/*7223*/ diff --git a/project/entry/gui_demo/awtk/res/assets/default/inc/ui/main.data b/project/entry/gui_demo/awtk/res/assets/default/inc/ui/main.data index b0273f9a2..033956312 100644 --- a/project/entry/gui_demo/awtk/res/assets/default/inc/ui/main.data +++ b/project/entry/gui_demo/awtk/res/assets/default/inc/ui/main.data @@ -1,5 +1,5 @@ TK_CONST_DATA_ALIGN(const unsigned char ui_main[]) = { -0x04,0x00,0x01,0x01,0xb6,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, +0x04,0x00,0x01,0x01,0xb8,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x12,0x12,0x22,0x11,0x77,0x69,0x6e,0x64,0x6f,0x77,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -28,306 +28,306 @@ TK_CONST_DATA_ALIGN(const unsigned char ui_main[]) = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c, 0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x62,0x75,0x74, -0x74,0x6f,0x6e,0x00,0x74,0x65,0x78,0x74,0x00,0x42,0x75,0x74,0x74,0x6f,0x6e,0x73,0x00,0x00,0x00,0x62, -0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x74,0x6f,0x6e,0x00,0x74,0x72,0x5f,0x74,0x65,0x78,0x74,0x00,0x42,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, +0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, +0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x65,0x64,0x69,0x74,0x00,0x74, +0x65,0x78,0x74,0x00,0x45,0x64,0x69,0x74,0x73,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75, -0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x65,0x64,0x69,0x74,0x00,0x74,0x65,0x78, -0x74,0x00,0x45,0x64,0x69,0x74,0x73,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00, +0x6f,0x70,0x65,0x6e,0x3a,0x6b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x4b, +0x65,0x79,0x42,0x6f,0x61,0x72,0x64,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f, 0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70, -0x65,0x6e,0x3a,0x6b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x4b,0x65,0x79, -0x42,0x6f,0x61,0x72,0x64,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00, +0x65,0x6e,0x3a,0x6c,0x69,0x73,0x74,0x5f,0x76,0x69,0x65,0x77,0x00,0x74,0x65,0x78,0x74,0x00,0x4c,0x69, +0x73,0x74,0x56,0x69,0x65,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75, -0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e, -0x3a,0x6c,0x69,0x73,0x74,0x5f,0x76,0x69,0x65,0x77,0x00,0x74,0x65,0x78,0x74,0x00,0x4c,0x69,0x73,0x74, -0x56,0x69,0x65,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, +0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, +0x6e,0x3a,0x73,0x6c,0x69,0x64,0x65,0x5f,0x76,0x69,0x65,0x77,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x6c, +0x69,0x64,0x65,0x56,0x69,0x65,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73, -0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a, -0x73,0x6c,0x69,0x64,0x65,0x5f,0x76,0x69,0x65,0x77,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x6c,0x69,0x64, -0x65,0x56,0x69,0x65,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f, +0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70, +0x65,0x6e,0x3a,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x00,0x74,0x65,0x78,0x74,0x00,0x41,0x6e, +0x69,0x6d,0x61,0x74,0x65,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f, +0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75, -0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e, -0x3a,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x00,0x74,0x65,0x78,0x74,0x00,0x41,0x6e,0x69,0x6d, -0x61,0x74,0x65,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, +0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61, +0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x61,0x6e,0x69,0x6d,0x61,0x74,0x65,0x5f,0x77,0x69,0x64,0x67, +0x65,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x41,0x6e,0x69,0x6d,0x61,0x74,0x65,0x20,0x57,0x69,0x64,0x67, +0x65,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62, +0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x74,0x61, +0x62,0x5f,0x63,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x00,0x74,0x65,0x78,0x74,0x00,0x54,0x61,0x62,0x20,0x43, +0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65, -0x00,0x6f,0x70,0x65,0x6e,0x3a,0x61,0x6e,0x69,0x6d,0x61,0x74,0x65,0x5f,0x77,0x69,0x64,0x67,0x65,0x74, -0x00,0x74,0x65,0x78,0x74,0x00,0x41,0x6e,0x69,0x6d,0x61,0x74,0x65,0x20,0x57,0x69,0x64,0x67,0x65,0x74, -0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, +0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, +0x6e,0x3a,0x63,0x6f,0x6d,0x62,0x6f,0x5f,0x62,0x6f,0x78,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6d, +0x62,0x6f,0x42,0x6f,0x78,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, -0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x74,0x61,0x62,0x5f, -0x63,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x00,0x74,0x65,0x78,0x74,0x00,0x54,0x61,0x62,0x20,0x43,0x6f,0x6e, -0x74,0x72,0x6f,0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75, +0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e, +0x3a,0x72,0x69,0x63,0x68,0x5f,0x74,0x65,0x78,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x52,0x69,0x63,0x68, +0x54,0x65,0x78,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73, 0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a, -0x63,0x6f,0x6d,0x62,0x6f,0x5f,0x62,0x6f,0x78,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6d,0x62,0x6f, -0x42,0x6f,0x78,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x69,0x63,0x6b,0x65,0x72,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f, +0x6c,0x6f,0x72,0x20,0x50,0x69,0x63,0x6b,0x65,0x72,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61, -0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x72, -0x69,0x63,0x68,0x5f,0x74,0x65,0x78,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x52,0x69,0x63,0x68,0x54,0x65, -0x78,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00, +0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c, +0x74,0x28,0x78,0x3d,0x30,0x2c,0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31, +0x30,0x30,0x25,0x29,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74, +0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x63,0x3d,0x32,0x2c,0x72,0x3d,0x38,0x2c,0x6d,0x3d,0x35, +0x2c,0x73,0x3d,0x35,0x29,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62, -0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x63,0x6f, -0x6c,0x6f,0x72,0x5f,0x70,0x69,0x63,0x6b,0x65,0x72,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6c,0x6f, -0x72,0x20,0x50,0x69,0x63,0x6b,0x65,0x72,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73, +0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a, +0x74,0x69,0x6d,0x65,0x5f,0x63,0x6c,0x6f,0x63,0x6b,0x00,0x74,0x65,0x78,0x74,0x00,0x41,0x6e,0x61,0x6c, +0x6f,0x67,0x20,0x43,0x6c,0x6f,0x63,0x6b,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00, -0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28, -0x78,0x3d,0x30,0x2c,0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30, -0x25,0x29,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64, -0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x63,0x3d,0x32,0x2c,0x72,0x3d,0x38,0x2c,0x6d,0x3d,0x35,0x2c,0x73, -0x3d,0x35,0x29,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, +0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, +0x70,0x65,0x6e,0x3a,0x64,0x69,0x67,0x69,0x74,0x5f,0x63,0x6c,0x6f,0x63,0x6b,0x00,0x74,0x65,0x78,0x74, +0x00,0x44,0x69,0x67,0x69,0x74,0x20,0x43,0x6c,0x6f,0x63,0x6b,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f, +0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62, -0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x74,0x69, -0x6d,0x65,0x5f,0x63,0x6c,0x6f,0x63,0x6b,0x00,0x74,0x65,0x78,0x74,0x00,0x41,0x6e,0x61,0x6c,0x6f,0x67, -0x20,0x43,0x6c,0x6f,0x63,0x6b,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61, +0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6c,0x6f,0x63,0x61,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00, +0x4c,0x6f,0x63,0x61,0x6c,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, 0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, -0x6e,0x3a,0x64,0x69,0x67,0x69,0x74,0x5f,0x63,0x6c,0x6f,0x63,0x6b,0x00,0x74,0x65,0x78,0x74,0x00,0x44, -0x69,0x67,0x69,0x74,0x20,0x43,0x6c,0x6f,0x63,0x6b,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, +0x6e,0x3a,0x69,0x6d,0x61,0x67,0x65,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x49,0x6d,0x61,0x67,0x65,0x00, +0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00, +0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x67,0x61,0x75,0x67,0x65, +0x00,0x74,0x65,0x78,0x74,0x00,0x47,0x61,0x75,0x67,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65, -0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6c,0x6f,0x63,0x61,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x4c,0x6f, -0x63,0x61,0x6c,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73, -0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a, -0x69,0x6d,0x61,0x67,0x65,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00, -0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d, +0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x77,0x69,0x74,0x63,0x68,0x00,0x74,0x65,0x78,0x74,0x00,0x53, +0x77,0x69,0x74,0x63,0x68,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72, -0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x67,0x61,0x75,0x67,0x65,0x00,0x74, -0x65,0x78,0x74,0x00,0x47,0x61,0x75,0x67,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75, +0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e, +0x3a,0x74,0x65,0x78,0x74,0x5f,0x73,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x00,0x74,0x65,0x78,0x74,0x00, +0x53,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, +0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, +0x70,0x65,0x6e,0x3a,0x69,0x6d,0x61,0x67,0x65,0x5f,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x00, +0x74,0x65,0x78,0x74,0x00,0x49,0x6d,0x61,0x67,0x65,0x20,0x41,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e, +0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00, -0x6f,0x70,0x65,0x6e,0x3a,0x73,0x77,0x69,0x74,0x63,0x68,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x77,0x69, -0x74,0x63,0x68,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, +0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x70,0x72,0x6f,0x67, +0x72,0x65,0x73,0x73,0x5f,0x63,0x69,0x72,0x63,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x50,0x72,0x6f, +0x67,0x72,0x65,0x73,0x73,0x43,0x69,0x72,0x63,0x6c,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61, -0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x74, -0x65,0x78,0x74,0x5f,0x73,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x65, -0x6c,0x65,0x63,0x74,0x6f,0x72,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, -0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, -0x6e,0x3a,0x69,0x6d,0x61,0x67,0x65,0x5f,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x00,0x74,0x65, -0x78,0x74,0x00,0x49,0x6d,0x61,0x67,0x65,0x20,0x41,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x00,0x00, -0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d, +0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x69,0x6d,0x61,0x67,0x65,0x5f,0x76,0x61,0x6c,0x75,0x65,0x00,0x74, +0x65,0x78,0x74,0x00,0x56,0x61,0x6c,0x75,0x65,0x20,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x62,0x75, +0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, -0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x70,0x72,0x6f,0x67,0x72,0x65, -0x73,0x73,0x5f,0x63,0x69,0x72,0x63,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x50,0x72,0x6f,0x67,0x72, -0x65,0x73,0x73,0x43,0x69,0x72,0x63,0x6c,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65, +0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x76,0x67,0x5f,0x69,0x6d,0x61,0x67,0x65, +0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x56,0x47,0x20,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x62,0x75, +0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65, +0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x67,0x69,0x66,0x5f,0x69,0x6d,0x61,0x67,0x65, +0x00,0x74,0x65,0x78,0x74,0x00,0x47,0x49,0x46,0x20,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x76, +0x69,0x65,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64, +0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64, +0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30,0x2c,0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30, +0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c, +0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x63,0x3d,0x32,0x2c,0x72,0x3d, +0x38,0x2c,0x6d,0x3d,0x35,0x2c,0x73,0x3d,0x35,0x29,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00, -0x6f,0x70,0x65,0x6e,0x3a,0x69,0x6d,0x61,0x67,0x65,0x5f,0x76,0x61,0x6c,0x75,0x65,0x00,0x74,0x65,0x78, -0x74,0x00,0x56,0x61,0x6c,0x75,0x65,0x20,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74, -0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e, -0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x76,0x67,0x5f,0x69,0x6d,0x61,0x67,0x65,0x00,0x74, -0x65,0x78,0x74,0x00,0x53,0x56,0x47,0x20,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74, -0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00, +0x6f,0x70,0x65,0x6e,0x3a,0x6c,0x61,0x62,0x65,0x6c,0x00,0x74,0x65,0x78,0x74,0x00,0x4c,0x61,0x62,0x65, +0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e, -0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x67,0x69,0x66,0x5f,0x69,0x6d,0x61,0x67,0x65,0x00,0x74, -0x65,0x78,0x74,0x00,0x47,0x49,0x46,0x20,0x49,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x76,0x69,0x65, -0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00, -0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66, -0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30,0x2c,0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c, -0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79, -0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x63,0x3d,0x32,0x2c,0x72,0x3d,0x38,0x2c, -0x6d,0x3d,0x35,0x2c,0x73,0x3d,0x35,0x29,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c, +0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x69,0x61, +0x6c,0x6f,0x67,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x69,0x61,0x6c,0x6f,0x67,0x73,0x00,0x00,0x00, +0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f, -0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70, -0x65,0x6e,0x3a,0x6c,0x61,0x62,0x65,0x6c,0x00,0x74,0x65,0x78,0x74,0x00,0x4c,0x61,0x62,0x65,0x6c,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72, +0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x6c,0x69,0x64,0x65,0x5f,0x6d, +0x65,0x6e,0x75,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x6c,0x69,0x64,0x65,0x20,0x4d,0x65,0x6e,0x75,0x00, 0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00, -0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x69,0x61,0x6c,0x6f, -0x67,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x69,0x61,0x6c,0x6f,0x67,0x73,0x00,0x00,0x00,0x62,0x75, -0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x6f,0x66,0x74,0x5f, +0x6b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x75,0x73,0x74,0x6f,0x6d, +0x20,0x4b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65, -0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x6c,0x69,0x64,0x65,0x5f,0x6d,0x65,0x6e, -0x75,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x6c,0x69,0x64,0x65,0x20,0x4d,0x65,0x6e,0x75,0x00,0x00,0x00, -0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72, -0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x6f,0x66,0x74,0x5f,0x6b,0x65, -0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x75,0x73,0x74,0x6f,0x6d,0x20,0x4b, -0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00, +0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00, +0x6f,0x70,0x65,0x6e,0x3a,0x76,0x67,0x63,0x61,0x6e,0x76,0x61,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x56, +0x47,0x20,0x43,0x61,0x6e,0x76,0x61,0x73,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f, -0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70, -0x65,0x6e,0x3a,0x76,0x67,0x63,0x61,0x6e,0x76,0x61,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x56,0x47,0x20, -0x43,0x61,0x6e,0x76,0x61,0x73,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, +0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, +0x70,0x65,0x6e,0x3a,0x6c,0x69,0x6e,0x65,0x61,0x72,0x5f,0x67,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00, +0x74,0x65,0x78,0x74,0x00,0x4c,0x69,0x6e,0x65,0x61,0x72,0x20,0x47,0x72,0x61,0x64,0x69,0x65,0x6e,0x74, +0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, -0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, -0x6e,0x3a,0x6c,0x69,0x6e,0x65,0x61,0x72,0x5f,0x67,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00,0x74,0x65, -0x78,0x74,0x00,0x4c,0x69,0x6e,0x65,0x61,0x72,0x20,0x47,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00,0x00, -0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, +0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x72,0x61,0x64,0x69, +0x61,0x6c,0x5f,0x67,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x52,0x61,0x64, +0x69,0x61,0x6c,0x20,0x47,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f, +0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, -0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x72,0x61,0x64,0x69,0x61,0x6c, -0x5f,0x67,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x52,0x61,0x64,0x69,0x61, -0x6c,0x20,0x47,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, +0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61, +0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x74,0x72,0x6f,0x6b,0x65,0x5f,0x67,0x72,0x61,0x64,0x69, +0x65,0x6e,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x74,0x72,0x6f,0x6b,0x65,0x20,0x47,0x72,0x61,0x64, +0x69,0x65,0x6e,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73, +0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a, +0x6f,0x76,0x65,0x72,0x6c,0x61,0x79,0x00,0x74,0x65,0x78,0x74,0x00,0x4f,0x76,0x65,0x72,0x6c,0x61,0x79, +0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65, -0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x74,0x72,0x6f,0x6b,0x65,0x5f,0x67,0x72,0x61,0x64,0x69,0x65,0x6e, -0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x74,0x72,0x6f,0x6b,0x65,0x20,0x47,0x72,0x61,0x64,0x69,0x65, -0x6e,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, +0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6d,0x6c,0x65,0x64, +0x69,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x4d,0x75,0x6c,0x74,0x69,0x4c,0x69,0x6e,0x65,0x20,0x45,0x64, +0x69,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62, -0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6f,0x76, -0x65,0x72,0x6c,0x61,0x79,0x00,0x74,0x65,0x78,0x74,0x00,0x4f,0x76,0x65,0x72,0x6c,0x61,0x79,0x00,0x00, -0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x68,0x73, +0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6c,0x61,0x62,0x65,0x6c,0x00,0x74,0x65,0x78,0x74,0x00,0x48,0x53,0x63, +0x72,0x6f,0x6c,0x6c,0x20,0x4c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, -0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6d,0x6c,0x65,0x64,0x69,0x74, -0x00,0x74,0x65,0x78,0x74,0x00,0x4d,0x75,0x6c,0x74,0x69,0x4c,0x69,0x6e,0x65,0x20,0x45,0x64,0x69,0x74, -0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, -0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x68,0x73,0x63,0x72, -0x6f,0x6c,0x6c,0x5f,0x6c,0x61,0x62,0x65,0x6c,0x00,0x74,0x65,0x78,0x74,0x00,0x48,0x53,0x63,0x72,0x6f, -0x6c,0x6c,0x20,0x4c,0x61,0x62,0x65,0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, +0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65, +0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6d,0x65,0x6e,0x75,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6e,0x74, +0x65,0x78,0x74,0x4d,0x65,0x6e,0x75,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, -0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, -0x70,0x65,0x6e,0x3a,0x6d,0x65,0x6e,0x75,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6e,0x74,0x65,0x78, -0x74,0x4d,0x65,0x6e,0x75,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73, +0x65,0x6c,0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78, +0x3d,0x30,0x2c,0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25, +0x29,0x00,0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65, +0x66,0x61,0x75,0x6c,0x74,0x28,0x63,0x3d,0x32,0x2c,0x72,0x3d,0x38,0x2c,0x6d,0x3d,0x35,0x2c,0x73,0x3d, +0x35,0x29,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c, -0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30, -0x2c,0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00, -0x63,0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61, -0x75,0x6c,0x74,0x28,0x63,0x3d,0x32,0x2c,0x72,0x3d,0x38,0x2c,0x6d,0x3d,0x35,0x2c,0x73,0x3d,0x35,0x29, -0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c, +0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x72,0x61, +0x67,0x67,0x61,0x62,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x72,0x61,0x67,0x67,0x61,0x62,0x6c, +0x65,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00, -0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x72,0x61,0x67,0x67, -0x61,0x62,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x72,0x61,0x67,0x67,0x61,0x62,0x6c,0x65,0x00, -0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c, +0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x72,0x69,0x63, +0x68,0x5f,0x74,0x65,0x78,0x74,0x5f,0x76,0x69,0x65,0x77,0x00,0x74,0x65,0x78,0x74,0x00,0x52,0x69,0x63, +0x68,0x54,0x65,0x78,0x74,0x56,0x69,0x65,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00, -0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x72,0x69,0x63,0x68,0x5f, -0x74,0x65,0x78,0x74,0x5f,0x76,0x69,0x65,0x77,0x00,0x74,0x65,0x78,0x74,0x00,0x52,0x69,0x63,0x68,0x54, -0x65,0x78,0x74,0x56,0x69,0x65,0x77,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f, -0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70, -0x65,0x6e,0x3a,0x62,0x69,0x64,0x69,0x00,0x74,0x65,0x78,0x74,0x00,0x42,0x69,0x64,0x69,0x72,0x65,0x63, -0x74,0x69,0x6f,0x6e,0x61,0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, +0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00, +0x6f,0x70,0x65,0x6e,0x3a,0x62,0x69,0x64,0x69,0x00,0x74,0x65,0x78,0x74,0x00,0x42,0x69,0x64,0x69,0x72, +0x65,0x63,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, -0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, -0x6e,0x3a,0x67,0x72,0x69,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x47,0x72,0x69,0x64,0x00,0x00,0x00,0x62, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, +0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, +0x70,0x65,0x6e,0x3a,0x67,0x72,0x69,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x47,0x72,0x69,0x64,0x00,0x00, +0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, +0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x63,0x6f,0x6c,0x6f,0x72,0x5f, +0x74,0x69,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6c,0x6f,0x72,0x20,0x54,0x69,0x6c,0x65, +0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, +0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x66,0x6c,0x6f,0x61, +0x74,0x69,0x6e,0x67,0x5f,0x6b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x46, +0x6c,0x6f,0x61,0x74,0x69,0x6e,0x67,0x20,0x4b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x00,0x00,0x62, 0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75, -0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x74,0x69, -0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x6f,0x6c,0x6f,0x72,0x20,0x54,0x69,0x6c,0x65,0x00,0x00, -0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x6c,0x69,0x64,0x65,0x72,0x00,0x74, +0x65,0x78,0x74,0x00,0x53,0x6c,0x69,0x64,0x65,0x72,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, -0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x66,0x6c,0x6f,0x61,0x74,0x69, -0x6e,0x67,0x5f,0x6b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x74,0x65,0x78,0x74,0x00,0x46,0x6c,0x6f, -0x61,0x74,0x69,0x6e,0x67,0x20,0x4b,0x65,0x79,0x62,0x6f,0x61,0x72,0x64,0x00,0x00,0x00,0x62,0x75,0x74, -0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00, -0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x6c,0x69,0x64,0x65,0x72,0x00,0x74,0x65,0x78, -0x74,0x00,0x53,0x6c,0x69,0x64,0x65,0x72,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, +0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65, +0x00,0x6f,0x70,0x65,0x6e,0x3a,0x63,0x6c,0x6f,0x73,0x65,0x5f,0x77,0x69,0x6e,0x64,0x6f,0x77,0x00,0x74, +0x65,0x78,0x74,0x00,0x63,0x6c,0x6f,0x73,0x65,0x5f,0x77,0x69,0x6e,0x64,0x6f,0x77,0x00,0x00,0x00,0x62, +0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, -0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, -0x70,0x65,0x6e,0x3a,0x63,0x6c,0x6f,0x73,0x65,0x5f,0x77,0x69,0x6e,0x64,0x6f,0x77,0x00,0x74,0x65,0x78, -0x74,0x00,0x63,0x6c,0x6f,0x73,0x65,0x5f,0x77,0x69,0x6e,0x64,0x6f,0x77,0x00,0x00,0x00,0x62,0x75,0x74, -0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75, +0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x69,0x61,0x6c,0x6f,0x67,0x5f,0x68, +0x69,0x67,0x68,0x6c,0x69,0x67,0x68,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x69,0x61,0x6c,0x6f,0x67, +0x20,0x48,0x69,0x67,0x68,0x6c,0x69,0x67,0x68,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00, -0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x69,0x61,0x6c,0x6f,0x67,0x5f,0x68,0x69,0x67, -0x68,0x6c,0x69,0x67,0x68,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x69,0x61,0x6c,0x6f,0x67,0x20,0x48, -0x69,0x67,0x68,0x6c,0x69,0x67,0x68,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65, +0x00,0x6f,0x70,0x65,0x6e,0x3a,0x73,0x76,0x67,0x5f,0x74,0x65,0x73,0x74,0x00,0x74,0x65,0x78,0x74,0x00, +0x53,0x56,0x47,0x20,0x54,0x65,0x73,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, 0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f, -0x70,0x65,0x6e,0x3a,0x73,0x76,0x67,0x5f,0x74,0x65,0x73,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x56, -0x47,0x20,0x54,0x65,0x73,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, +0x70,0x65,0x6e,0x3a,0x73,0x70,0x69,0x6e,0x5f,0x62,0x6f,0x78,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x70, +0x69,0x6e,0x20,0x62,0x6f,0x78,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63, 0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65, -0x6e,0x3a,0x73,0x70,0x69,0x6e,0x5f,0x62,0x6f,0x78,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x70,0x69,0x6e, -0x20,0x62,0x6f,0x78,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73, -0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a, -0x6d,0x6f,0x64,0x65,0x6c,0x65,0x73,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x4d,0x6f,0x64,0x65,0x6c,0x65, -0x73,0x73,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x6e,0x3a,0x6d,0x6f,0x64,0x65,0x6c,0x65,0x73,0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x4d,0x6f,0x64,0x65, +0x6c,0x65,0x73,0x73,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66, +0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30,0x2c, +0x79,0x3d,0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x63, +0x68,0x69,0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75, +0x6c,0x74,0x28,0x63,0x3d,0x31,0x2c,0x72,0x3d,0x38,0x2c,0x6d,0x3d,0x35,0x2c,0x73,0x3d,0x35,0x29,0x00, +0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f,0x6c, -0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30,0x2c,0x79,0x3d, -0x30,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x31,0x30,0x30,0x25,0x29,0x00,0x63,0x68,0x69, -0x6c,0x64,0x72,0x65,0x6e,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74, -0x28,0x63,0x3d,0x31,0x2c,0x72,0x3d,0x38,0x2c,0x6d,0x3d,0x35,0x2c,0x73,0x3d,0x35,0x29,0x00,0x00,0x62, -0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, +0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x72,0x6f,0x70,0x5f,0x66, +0x69,0x6c,0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x72,0x6f,0x70,0x20,0x46,0x69,0x6c,0x65,0x00,0x00, +0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75, -0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x6c, -0x65,0x00,0x74,0x65,0x78,0x74,0x00,0x44,0x72,0x6f,0x70,0x20,0x46,0x69,0x6c,0x65,0x00,0x00,0x00,0x62, -0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74, +0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6d,0x65,0x6d,0x74,0x65,0x73, +0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x4d,0x65,0x6d,0x54,0x65,0x73,0x74,0x00,0x00,0x00,0x62,0x75,0x74, +0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75, -0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x6d,0x65,0x6d,0x74,0x65,0x73,0x74,0x00, -0x74,0x65,0x78,0x74,0x00,0x4d,0x65,0x6d,0x54,0x65,0x73,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f, -0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00, +0x6e,0x61,0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x63,0x61,0x6c,0x69,0x62,0x72,0x61,0x74,0x69,0x6f, +0x6e,0x5f,0x77,0x69,0x6e,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x61,0x6c,0x69,0x62,0x72,0x61,0x74,0x69, +0x6f,0x6e,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61, -0x6d,0x65,0x00,0x6f,0x70,0x65,0x6e,0x3a,0x63,0x61,0x6c,0x69,0x62,0x72,0x61,0x74,0x69,0x6f,0x6e,0x5f, -0x77,0x69,0x6e,0x00,0x74,0x65,0x78,0x74,0x00,0x43,0x61,0x6c,0x69,0x62,0x72,0x61,0x74,0x69,0x6f,0x6e, -0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62, +0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x73,0x68,0x6f,0x77,0x5f,0x66,0x70, +0x73,0x00,0x74,0x65,0x78,0x74,0x00,0x53,0x68,0x6f,0x77,0x20,0x46,0x50,0x53,0x00,0x00,0x00,0x62,0x75, +0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65, -0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x73,0x68,0x6f,0x77,0x5f,0x66,0x70,0x73,0x00, -0x74,0x65,0x78,0x74,0x00,0x53,0x68,0x6f,0x77,0x20,0x46,0x50,0x53,0x00,0x00,0x00,0x62,0x75,0x74,0x74, +0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65, +0x00,0x6e,0x61,0x6d,0x65,0x00,0x73,0x6e,0x61,0x70,0x73,0x68,0x6f,0x74,0x00,0x74,0x65,0x78,0x74,0x00, +0x54,0x61,0x6b,0x65,0x20,0x53,0x6e,0x61,0x70,0x73,0x68,0x6f,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74, 0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e, -0x61,0x6d,0x65,0x00,0x73,0x6e,0x61,0x70,0x73,0x68,0x6f,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x54,0x61, -0x6b,0x65,0x20,0x53,0x6e,0x61,0x70,0x73,0x68,0x6f,0x74,0x00,0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d, -0x65,0x00,0x72,0x65,0x6c,0x6f,0x61,0x64,0x5f,0x74,0x68,0x65,0x6d,0x65,0x00,0x74,0x65,0x78,0x74,0x00, -0x54,0x65,0x73,0x74,0x20,0x43,0x68,0x61,0x6e,0x67,0x65,0x20,0x54,0x68,0x65,0x6d,0x65,0x00,0x00,0x00, -0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x61,0x6d,0x65,0x00,0x72,0x65,0x6c,0x6f,0x61,0x64,0x5f,0x74,0x68,0x65,0x6d,0x65,0x00,0x74,0x65,0x78, +0x74,0x00,0x54,0x65,0x73,0x74,0x20,0x43,0x68,0x61,0x6e,0x67,0x65,0x20,0x54,0x68,0x65,0x6d,0x65,0x00, +0x00,0x00,0x62,0x75,0x74,0x74,0x6f,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00,0x74,0x72, -0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x65,0x78,0x69,0x74,0x00,0x74,0x65,0x78,0x74,0x00,0x45,0x78, -0x69,0x74,0x00,0x00,0x00,0x00,0x00,0x73,0x6c,0x69,0x64,0x65,0x5f,0x69,0x6e,0x64,0x69,0x63,0x61,0x74, -0x6f,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x73,0x65,0x6c,0x66,0x5f, -0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30,0x2c,0x79, -0x3d,0x62,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x32,0x30,0x29,0x00,0x74,0x72,0x61,0x6e, -0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x74,0x72,0x75,0x65,0x00,0x73,0x74,0x79,0x6c,0x65,0x00,0x62,0x6c, -0x75,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};/*6630*/ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6f,0x63,0x75,0x73,0x61,0x62,0x6c,0x65,0x00, +0x74,0x72,0x75,0x65,0x00,0x6e,0x61,0x6d,0x65,0x00,0x65,0x78,0x69,0x74,0x00,0x74,0x65,0x78,0x74,0x00, +0x45,0x78,0x69,0x74,0x00,0x00,0x00,0x00,0x00,0x73,0x6c,0x69,0x64,0x65,0x5f,0x69,0x6e,0x64,0x69,0x63, +0x61,0x74,0x6f,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x73,0x65,0x6c, +0x66,0x5f,0x6c,0x61,0x79,0x6f,0x75,0x74,0x00,0x64,0x65,0x66,0x61,0x75,0x6c,0x74,0x28,0x78,0x3d,0x30, +0x2c,0x79,0x3d,0x62,0x2c,0x77,0x3d,0x31,0x30,0x30,0x25,0x2c,0x68,0x3d,0x32,0x30,0x29,0x00,0x74,0x72, +0x61,0x6e,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x74,0x72,0x75,0x65,0x00,0x73,0x74,0x79,0x6c,0x65,0x00, +0x62,0x6c,0x75,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};/*6632*/ diff --git a/project/entry/gui_demo/awtk/res/assets/default/raw/ui/images.bin b/project/entry/gui_demo/awtk/res/assets/default/raw/ui/images.bin index f5885942bba51aaa9f215b24a3ea7d68ba5efbc3..3eb00b9e8e8e29da36858ed2f33cfa5b951b6f1c 100644 GIT binary patch delta 19 acmZp(Xt&tF&d!!zlvo*Wv6+Ycmk0nqhz4K) delta 16 XcmZp-XtUVB&OW(;LtwK6`+E@pE^q}r diff --git a/project/entry/gui_demo/awtk/res/assets/default/raw/ui/main.bin b/project/entry/gui_demo/awtk/res/assets/default/raw/ui/main.bin index 9b7a6d9de2529b51f57e5768ede2c591b1793ea6..a7d81bc5683e6155d116cd30147144ec23239cec 100644 GIT binary patch delta 30 lcmdmHyu)~dEE8{0d`W6W34>E> LV_TRIGO_SHIFT; + return (r + LV_TRIGO_SIN_MAX / 2) >> LV_TRIGO_SHIFT; } static void del_counter_timer_cb(lv_event_t * e) diff --git a/project/entry/gui_demo/lvgl/music/music.c b/project/entry/gui_demo/lvgl/music/music.c index c920adbbb..c29c6ef97 100644 --- a/project/entry/gui_demo/lvgl/music/music.c +++ b/project/entry/gui_demo/lvgl/music/music.c @@ -79,6 +79,10 @@ static const uint32_t time_list[] = { 2 * 60 + 19, }; +#if LV_USE_PERF_MONITOR || LV_DEMO_MUSIC_AUTO_PLAY + #define sysmon_perf LV_GLOBAL_DEFAULT()->sysmon_perf +#endif + int lvgl_demo_music(int argc, char *argv[]) { lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x343247), 0); @@ -214,7 +218,8 @@ static void auto_step_cb(lv_timer_t * t) lv_obj_t *num = lv_label_create(bg); lv_obj_set_style_text_font(num, font_large, 0); #if LV_USE_PERF_MONITOR - lv_label_set_text_fmt(num, "%" LV_PRIu32, lv_refr_get_fps_avg()); + const lv_sysmon_perf_info_t * info = lv_subject_get_pointer(&sysmon_perf.subject); + lv_label_set_text_fmt(num, "%" LV_PRIu32, info->calculated.cpu_avg_total); #endif lv_obj_align(num, LV_ALIGN_TOP_MID, 0, 120); diff --git a/project/entry/gui_demo/lvgl/transform/demo_transform.c b/project/entry/gui_demo/lvgl/transform/demo_transform.c index 3713364f6..0f642d84d 100644 --- a/project/entry/gui_demo/lvgl/transform/demo_transform.c +++ b/project/entry/gui_demo/lvgl/transform/demo_transform.c @@ -1,5 +1,9 @@ #include "demo_transform.h" +#if LV_FONT_MONTSERRAT_18 == 0 + #error "LV_FONT_MONTSERRAT_18 is required for lv_demo_transform. Enable it in lv_conf.h." +#endif + typedef struct { const void *image; const char *name; diff --git a/project/gui/awtk/src/base/assets_manager.c b/project/gui/awtk/src/base/assets_manager.c index dbc5c231d..62cf11e58 100644 --- a/project/gui/awtk/src/base/assets_manager.c +++ b/project/gui/awtk/src/base/assets_manager.c @@ -1,9 +1,9 @@ -/** +/** * File: assets_manager.h * Author: AWTK Develop Team * Brief: asset manager * - * Copyright (c) 2018 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -833,6 +833,67 @@ ret_t assets_manager_preload(assets_manager_t* am, asset_type_t type, const char return RET_OK; } +static const char* asset_type_to_str(asset_type_t type) { + const char* str = NULL; + switch (type) { + case ASSET_TYPE_FONT: { + str = "font"; + break; + } + case ASSET_TYPE_SCRIPT: { + str = "script"; + break; + } + case ASSET_TYPE_FLOW: { + str = "flow"; + break; + } + case ASSET_TYPE_STYLE: { + str = "style"; + break; + } + case ASSET_TYPE_STRINGS: { + str = "strings"; + break; + } + case ASSET_TYPE_IMAGE: { + str = "image"; + break; + } + case ASSET_TYPE_UI: { + str = "ui"; + break; + } + case ASSET_TYPE_XML: { + str = "xml"; + break; + } + case ASSET_TYPE_DATA: { + str = "data"; + break; + } + default: { + str = "unknown"; + break; + } + } + + return str; +} + +ret_t assets_manager_dump(assets_manager_t* am, str_t* result) { + uint32_t i = 0; + return_value_if_fail(am != NULL && result != NULL, RET_BAD_PARAMS); + + for (i = 0; i < am->assets.size; i++) { + asset_info_t* info = (asset_info_t*)darray_get(&(am->assets), i); + str_append_format(result, 1024, "%s: type=%s size=%u\n", asset_info_get_name(info), + asset_type_to_str(info->type), info->size); + } + + return RET_OK; +} + ret_t assets_manager_deinit(assets_manager_t* am) { return_value_if_fail(am != NULL, RET_BAD_PARAMS); @@ -946,7 +1007,8 @@ assets_manager_t* assets_managers_ref(const char* name) { darray_push(s_assets_managers, am); assets_manager_set_res_root(am, res_root); - assets_manager_set_fallback_load_asset(am, (assets_manager_load_asset_t)assets_manager_load_asset_fallback_default, NULL); + assets_manager_set_fallback_load_asset( + am, (assets_manager_load_asset_t)assets_manager_load_asset_fallback_default, NULL); } else { am->refcount++; } @@ -986,4 +1048,4 @@ ret_t assets_managers_set_theme(const char* theme) { } return RET_OK; -} \ No newline at end of file +} diff --git a/project/gui/awtk/src/base/assets_manager.h b/project/gui/awtk/src/base/assets_manager.h index 8488b3688..fb02a4085 100644 --- a/project/gui/awtk/src/base/assets_manager.h +++ b/project/gui/awtk/src/base/assets_manager.h @@ -1,9 +1,9 @@ -/** +/** * File: assets_manager.h * Author: AWTK Develop Team * Brief: asset manager * - * Copyright (c) 2018 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -22,6 +22,7 @@ #ifndef TK_ASSETS_MANAGER_H #define TK_ASSETS_MANAGER_H +#include "tkc/str.h" #include "tkc/darray.h" #include "tkc/emitter.h" #include "tkc/asset_info.h" @@ -219,7 +220,7 @@ ret_t assets_manager_add_data(assets_manager_t* am, const char* name, uint16_t t * @annotation ["scriptable"] * @param {assets_manager_t*} am asset manager对象。 * @param {asset_type_t} type 资源的类型。 - * @param {char*} name 资源的名称。 + * @param {const char*} name 资源的名称。 * * @return {const asset_info_t*} 返回资源。 */ @@ -232,7 +233,7 @@ const asset_info_t* assets_manager_ref(assets_manager_t* am, asset_type_t type, * @param {assets_manager_t*} am asset manager对象。 * @param {asset_type_t} type 资源的类型。 * @param {uint16_t} subtype 资源的子类型。 - * @param {char*} name 资源的名称。 + * @param {const char*} name 资源的名称。 * * @return {const asset_info_t*} 返回资源。 */ @@ -244,7 +245,7 @@ const asset_info_t* assets_manager_ref_ex(assets_manager_t* am, asset_type_t typ * 释放指定的资源。 * @annotation ["scriptable"] * @param {assets_manager_t*} am asset manager对象。 - * @param {asset_info_t*} info 资源。 + * @param {const asset_info_t*} info 资源。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ @@ -256,7 +257,7 @@ ret_t assets_manager_unref(assets_manager_t* am, const asset_info_t* info); * @param {assets_manager_t*} am asset manager对象。 * @param {asset_type_t} type 资源的类型。 * @param {uint16_t} subtype 资源的子类型。 - * @param {char*} name 资源的名称。 + * @param {const char*} name 资源的名称。 * * @return {const asset_info_t*} 返回资源。 */ @@ -281,7 +282,7 @@ asset_info_t* assets_manager_load(assets_manager_t* am, asset_type_t type, const * @param {assets_manager_t*} am asset manager对象。 * @param {asset_type_t} type 资源的类型。 * @param {uint16_t} subtype 资源的子类型。 - * @param {char*} name 资源的名称。 + * @param {const char*} name 资源的名称。 * * @return {asset_info_t*} 返回资源。 */ @@ -294,7 +295,7 @@ asset_info_t* assets_manager_load_ex(assets_manager_t* am, asset_type_t type, ui * 备注:内部使用的,不建议用户自行调用。 * @param {assets_manager_t*} am asset manager对象。 * @param {asset_type_t} type 资源的类型。 - * @param {char*} name 资源的名称。 + * @param {const char*} name 资源的名称。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ @@ -384,6 +385,16 @@ ret_t assets_manager_clear_cache_ex(assets_manager_t* am, asset_type_t type, con */ ret_t assets_manager_clear_all(assets_manager_t* am); +/** + * @method assets_manager_dump + * 输出资源管理器的信息。 + * @param {assets_manager_t*} am asset manager对象。 + * @param {str_t*} result 输出的字符串。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t assets_manager_dump(assets_manager_t* am, str_t* result); + /** * @method assets_manager_deinit * @param {assets_manager_t*} am asset manager对象。 diff --git a/project/gui/awtk/src/base/image_manager.c b/project/gui/awtk/src/base/image_manager.c index 361115f2d..f17fdf924 100644 --- a/project/gui/awtk/src/base/image_manager.c +++ b/project/gui/awtk/src/base/image_manager.c @@ -374,6 +374,18 @@ ret_t image_manager_unload_bitmap(image_manager_t* imm, bitmap_t* image) { return darray_remove_all(&(imm->images), NULL, &b); } +ret_t image_manager_dump(image_manager_t* im, str_t* result) { + uint32_t i = 0; + return_value_if_fail(im != NULL && result != NULL, RET_BAD_PARAMS); + + for(i = 0; i < im->images.size; i++) { + bitmap_cache_t* cache = (bitmap_cache_t*)darray_get(&(im->images), i); + str_append_format(result, 1024, "%s: w=%d h=%d format=%d\n", cache->name, cache->image.w, cache->image.h, cache->image.format); + } + + return RET_OK; +} + ret_t image_manager_deinit(image_manager_t* imm) { return_value_if_fail(imm != NULL, RET_BAD_PARAMS); diff --git a/project/gui/awtk/src/base/image_manager.h b/project/gui/awtk/src/base/image_manager.h index bd6a14b6e..ea6cfc03b 100644 --- a/project/gui/awtk/src/base/image_manager.h +++ b/project/gui/awtk/src/base/image_manager.h @@ -1,9 +1,9 @@ -/** +/** * File: image_manager.h * Author: AWTK Develop Team * Brief: image manager * - * Copyright (c) 2018 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -40,8 +40,7 @@ typedef struct _bitmap_header_t { uint8_t data[4]; } bitmap_header_t; -typedef ret_t (*image_manager_get_bitmap_t)(void* ctx, const char* name, - bitmap_t* image); +typedef ret_t (*image_manager_get_bitmap_t)(void* ctx, const char* name, bitmap_t* image); /** * @class image_manager_t @@ -127,7 +126,7 @@ ret_t image_manager_set_max_mem_size_of_cached_images(image_manager_t* imm, uint * * @annotation ["scriptable"] * @param {image_manager_t*} imm 图片管理器对象。 - * @param {char*} name 图片名称。 + * @param {const char*} name 图片名称。 * @param {bitmap_t*} image 用于返回图片。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 @@ -153,7 +152,7 @@ ret_t image_manager_set_fallback_get_bitmap(image_manager_t* imm, * 预加载指定的图片。 * @annotation ["scriptable"] * @param {image_manager_t*} imm 图片管理器对象。 - * @param {char*} name 图片名称。 + * @param {const char*} name 图片名称。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ @@ -231,16 +230,26 @@ ret_t image_manager_set_assets_manager(image_manager_t* imm, assets_manager_t* a /** * @method image_manager_deinit * 析构图片管理器。 - * @param {image_manager_t*} imm 图片管理器对象。 + * @param {image_manager_t*} im 图片管理器对象。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ ret_t image_manager_deinit(image_manager_t* im); +/** + * @method image_manager_dump + * 输出图片管理器的信息。 + * @param {image_manager_t*} im 图片管理器对象。 + * @param {str_t*} result 用于返回图片管理器的信息。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t image_manager_dump(image_manager_t* im, str_t* result); + /** * @method image_manager_destroy * 析构并释放图片管理器。 - * @param {image_manager_t*} imm 图片管理器对象。 + * @param {image_manager_t*} im 图片管理器对象。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ diff --git a/project/gui/awtk/src/base/widget.c b/project/gui/awtk/src/base/widget.c index 6e34a8a0c..62ec994e1 100644 --- a/project/gui/awtk/src/base/widget.c +++ b/project/gui/awtk/src/base/widget.c @@ -580,7 +580,6 @@ image_manager_t* widget_get_image_manager(widget_t* widget) { } } - static ret_t widget_apply_tr_text_before_paint(void* ctx, event_t* e) { widget_t* widget = WIDGET(ctx); if (widget->tr_text != NULL) { @@ -1182,7 +1181,23 @@ static widget_t* widget_lookup_child(widget_t* widget, const char* name) { } widget_t* widget_child(widget_t* widget, const char* path) { - return widget_lookup_child(widget, path); + return_value_if_fail(widget != NULL && path != NULL, NULL); + if (*path == '[' && path[strlen(path) - 1] == ']') { + int32_t index = tk_atoi(path + 1); + + if (index < 0) { + index = widget_count_children(widget) - index; + } + + if (index >= 0) { + return widget_get_child(widget, index); + } else { + log_debug("invalid index:%d\n", index); + return NULL; + } + } else { + return widget_lookup_child(widget, path); + } } widget_t* widget_get_focused_widget(widget_t* widget) { @@ -1607,7 +1622,8 @@ ret_t widget_draw_icon_text(widget_t* widget, canvas_t* c, const char* icon, wst } ret_t widget_draw_image_with_region(widget_t* widget, canvas_t* c, bitmap_t* img, - const char* region, const rect_t* dst, image_draw_type_t draw_type) { + const char* region, const rect_t* dst, + image_draw_type_t draw_type) { rect_t src; return_value_if_fail(widget != NULL && img != NULL, RET_BAD_PARAMS); return_value_if_fail(c != NULL && region != NULL && dst != NULL, RET_BAD_PARAMS); @@ -2026,9 +2042,9 @@ static ret_t fscript_info_prepare(fscript_info_t* info, event_t* evt) { tk_object_set_prop_object(obj, "model", e->model); break; } - case EVT_ANIM_ONCE : - case EVT_ANIM_START : - case EVT_ANIM_END : { + case EVT_ANIM_ONCE: + case EVT_ANIM_START: + case EVT_ANIM_END: { widget_animator_event_t* e = widget_animator_event_cast(evt); if (e != NULL) { widget_animator_t* animator = (widget_animator_t*)e->animator; @@ -4033,7 +4049,7 @@ float_t widget_measure_text(widget_t* widget, const wchar_t* text) { } ret_t widget_load_image(widget_t* widget, const char* name, bitmap_t* bitmap) { - char real_name[MAX_PATH+1]; + char real_name[MAX_PATH + 1]; const char* region = NULL; image_manager_t* imm = widget_get_image_manager(widget); @@ -4042,7 +4058,7 @@ ret_t widget_load_image(widget_t* widget, const char* name, bitmap_t* bitmap) { region = strrchr(name, '#'); if (region != NULL) { - tk_strncpy(real_name, name, region-name); + tk_strncpy(real_name, name, region - name); name = real_name; } @@ -4620,7 +4636,9 @@ ret_t widget_get_style(widget_t* widget, const char* state_and_name, value_t* va const char* name = NULL; const char* p_state = NULL; ret_t ret = RET_NOT_FOUND; - return_value_if_fail(widget != NULL && state_and_name != NULL && *state_and_name != '\0' && value != NULL, RET_BAD_PARAMS); + return_value_if_fail( + widget != NULL && state_and_name != NULL && *state_and_name != '\0' && value != NULL, + RET_BAD_PARAMS); memset(state, 0x0, sizeof(state)); name = strchr(state_and_name, ':'); @@ -4636,11 +4654,13 @@ ret_t widget_get_style(widget_t* widget, const char* state_and_name, value_t* va name = name + 1; } - if (style_is_mutable(widget->astyle) || tk_str_eq(p_state, widget_get_prop_str(widget, WIDGET_PROP_STATE_FOR_STYLE, NULL))) { + if (style_is_mutable(widget->astyle) || + tk_str_eq(p_state, widget_get_prop_str(widget, WIDGET_PROP_STATE_FOR_STYLE, NULL))) { ret = style_get(widget->astyle, p_state, name, value); } if (ret != RET_OK) { - const char* style_name = (widget->style != NULL && *widget->style != '\0') ? widget->style : TK_DEFAULT_STYLE; + const char* style_name = + (widget->style != NULL && *widget->style != '\0') ? widget->style : TK_DEFAULT_STYLE; const void* data = widget_get_const_style_data_for_state(widget, style_name, p_state); if (data == NULL && !tk_str_eq(p_state, WIDGET_STATE_NORMAL)) { data = widget_get_const_style_data_for_state(widget, style_name, WIDGET_STATE_NORMAL); @@ -4742,7 +4762,7 @@ bool_t widget_is_normal_window(widget_t* widget) { bool_t widget_is_fullscreen_window(widget_t* widget) { return_value_if_fail(widget != NULL && widget->vt != NULL, FALSE); - + return widget->vt->is_window && widget_get_prop_bool(widget, WIDGET_PROP_FULLSCREEN, FALSE); } @@ -4761,13 +4781,15 @@ bool_t widget_is_popup(widget_t* widget) { bool_t widget_is_support_highlighter(widget_t* widget) { return_value_if_fail(widget != NULL && widget->vt != NULL, FALSE); - return widget->vt->is_window && (tk_str_eq(widget->vt->type, WIDGET_TYPE_POPUP) || tk_str_eq(widget->vt->type, WIDGET_TYPE_DIALOG)); + return widget->vt->is_window && (tk_str_eq(widget->vt->type, WIDGET_TYPE_POPUP) || + tk_str_eq(widget->vt->type, WIDGET_TYPE_DIALOG)); } bool_t widget_has_highlighter(widget_t* widget) { return_value_if_fail(widget != NULL && widget->vt != NULL, FALSE); - return widget_is_support_highlighter(widget) && widget_get_prop_str(widget, WIDGET_PROP_HIGHLIGHT, NULL) != NULL; + return widget_is_support_highlighter(widget) && + widget_get_prop_str(widget, WIDGET_PROP_HIGHLIGHT, NULL) != NULL; } bool_t widget_is_overlay(widget_t* widget) { @@ -5284,6 +5306,10 @@ widget_t* widget_find_by_path(widget_t* widget, const char* path, bool_t recursi return widget; } else if (tk_str_eq(name, STR_PROP_WINDOW)) { return widget_get_window(widget); + } else if (tk_str_eq(name, STR_PROP_TOP_WINDOW)) { + return window_manager_get_top_window(window_manager()); + } else if (tk_str_eq(name, STR_PROP_MAIN_WINDOW)) { + return window_manager_get_top_main_window(window_manager()); } else if (tk_str_eq(name, STR_PROP_WINDOW_MANAGER)) { return widget_get_window_manager(widget); } else { @@ -5302,17 +5328,29 @@ widget_t* widget_find_by_path(widget_t* widget, const char* path, bool_t recursi iter = widget; } else if (tk_str_eq(name, STR_PROP_WINDOW)) { iter = widget_get_window(widget); + } else if (tk_str_eq(name, STR_PROP_TOP_WINDOW)) { + iter = window_manager_get_top_window(window_manager()); + } else if (tk_str_eq(name, STR_PROP_MAIN_WINDOW)) { + iter = window_manager_get_top_main_window(window_manager()); } else if (tk_str_eq(name, STR_PROP_WINDOW_MANAGER)) { iter = widget_get_window_manager(widget); } else { + widget_t* save = iter; iter = widget_child(iter, name); + if (iter == NULL) { + iter = widget_lookup_by_type_child(save, name); + } } is_first = FALSE; } else { + widget_t* save = iter; iter = widget_child(iter, name); + if (iter == NULL) { + iter = widget_lookup_by_type_child(save, name); + } } } tokenizer_deinit(t); return iter; -} \ No newline at end of file +} diff --git a/project/gui/awtk/src/base/widget_consts.h b/project/gui/awtk/src/base/widget_consts.h index 3eac71b67..bddd3ae38 100644 --- a/project/gui/awtk/src/base/widget_consts.h +++ b/project/gui/awtk/src/base/widget_consts.h @@ -1,9 +1,9 @@ -/** +/** * File: widget_consts.h * Author: AWTK Develop Team * Brief: widget property names * - * Copyright (c) 2018 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -560,12 +560,14 @@ BEGIN_C_DECLS /** * @const WIDGET_PROP_XOFFSET * X方向的偏移。(如果控件有继承 get_offset 函数指针的话,一定要和 get_offset 返回值保持一致,否则容易出现问题) + * 详情请看 docs/how_to_use_offset_in_custom_widget.md */ #define WIDGET_PROP_XOFFSET "xoffset" /** * @const WIDGET_PROP_YOFFSET * Y方向的偏移。(如果控件有继承 get_offset 函数指针的话,一定要和 get_offset 返回值保持一致,否则容易出现问题) + * 详情请看 docs/how_to_use_offset_in_custom_widget.md */ #define WIDGET_PROP_YOFFSET "yoffset" @@ -1077,7 +1079,25 @@ BEGIN_C_DECLS * @const WIDGET_PROP_DIRTY_RECT * 控件脏矩形区域。 */ -#define WIDGET_PROP_DIRTY_RECT "dirty_rect" +#define WIDGET_PROP_DIRTY_RECT "dirty_rect" + +/** + * @const WIDGET_PROP_SCREEN_SAVER_TIME + * 屏幕保护时间。 + */ +#define WIDGET_PROP_SCREEN_SAVER_TIME "screen_saver_time" + +/** + * @const WIDGET_PROP_SHOW_FPS + * 是否显示FPS。 + */ +#define WIDGET_PROP_SHOW_FPS "show_fps" + +/** + * @const WIDGET_PROP_MAX_FPS + * 最大FPS。 + */ +#define WIDGET_PROP_MAX_FPS "max_fps" /** * @enum widget_type_t @@ -1718,6 +1738,8 @@ typedef enum _window_closable_t { #define STR_PROP_SELF "self" #define STR_PROP_PARENT "parent" #define STR_PROP_WINDOW "window" +#define STR_PROP_MAIN_WINDOW "main_window" +#define STR_PROP_TOP_WINDOW "top_window" #define STR_PROP_WINDOW_MANAGER "window_manager" #define STR_PROP_MODEL "__model__" diff --git a/project/gui/awtk/src/base/window_manager.c b/project/gui/awtk/src/base/window_manager.c index f5dd074d0..4b65ae3f5 100644 --- a/project/gui/awtk/src/base/window_manager.c +++ b/project/gui/awtk/src/base/window_manager.c @@ -382,6 +382,14 @@ ret_t window_manager_switch_to(widget_t* widget, widget_t* curr_win, widget_t* t return RET_OK; } + WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i) + if (widget_is_dialog(iter)) { + return RET_FAIL; + } else if (iter == target_win) { + break; + } + WIDGET_FOR_EACH_CHILD_END(); + if (wm->vt->switch_to != NULL) { return wm->vt->switch_to(widget, curr_win, target_win, close); } else { diff --git a/project/gui/awtk/src/base/window_manager.h b/project/gui/awtk/src/base/window_manager.h index 1c0e730f0..8213c7f42 100644 --- a/project/gui/awtk/src/base/window_manager.h +++ b/project/gui/awtk/src/base/window_manager.h @@ -417,7 +417,7 @@ ret_t window_manager_back_to(widget_t* widget, const char* target); /** * @method window_manager_switch_to * 切换到指定窗口。 - * + * 备注:会受到模态窗口影响,如果切换的窗口是模态窗口之前的窗口就会失败。 * ```c * window_manager_switch_to(wm, win, widget_child(wm, "home"), FALSE); * ``` diff --git a/project/gui/awtk/src/debugger/debugger.c b/project/gui/awtk/src/debugger/debugger.c index 96f3eedc0..bd1b4efe3 100644 --- a/project/gui/awtk/src/debugger/debugger.c +++ b/project/gui/awtk/src/debugger/debugger.c @@ -142,7 +142,7 @@ tk_object_t* debugger_get_local(debugger_t* debugger, uint32_t frame_index) { return_value_if_fail(debugger != NULL && debugger->vt != NULL, NULL); return_value_if_fail(debugger->vt->get_local != NULL, NULL); - return debugger->vt->get_local(debugger, frame_index); + return debugger->vt->get_local(debugger, frame_index); } tk_object_t* debugger_get_self(debugger_t* debugger) { @@ -159,12 +159,11 @@ tk_object_t* debugger_get_global(debugger_t* debugger) { return debugger->vt->get_global(debugger); } -ret_t debugger_get_callstack(debugger_t* debugger, binary_data_t* callstack) { - return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS); - return_value_if_fail(debugger->vt->get_callstack != NULL, RET_BAD_PARAMS); - return_value_if_fail(callstack != NULL, RET_BAD_PARAMS); +tk_object_t* debugger_get_callstack(debugger_t* debugger) { + return_value_if_fail(debugger != NULL && debugger->vt != NULL, NULL); + return_value_if_fail(debugger->vt->get_callstack != NULL, NULL); - return debugger->vt->get_callstack(debugger, callstack); + return debugger->vt->get_callstack(debugger); } ret_t debugger_clear_break_points(debugger_t* debugger) { @@ -257,8 +256,8 @@ tk_object_t* debugger_get_threads(debugger_t* debugger) { return debugger->vt->get_threads(debugger); } -ret_t debugger_launch_app(debugger_t* debugger, const char* program, const char* work_dir, - int argc, char* argv[]) { +ret_t debugger_launch_app(debugger_t* debugger, const char* program, const char* work_dir, int argc, + char* argv[]) { return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS); return_value_if_fail(debugger->vt->launch_app != NULL, RET_BAD_PARAMS); return_value_if_fail(program != NULL, RET_BAD_PARAMS); @@ -273,10 +272,10 @@ ret_t debugger_set_break_point_ex(debugger_t* debugger, const char* position) { if (debugger->vt->set_break_point_ex != NULL) { return debugger->vt->set_break_point_ex(debugger, position); - } else if(debugger->vt->set_break_point != NULL) { + } else if (debugger->vt->set_break_point != NULL) { const char* p = strchr(position, ':'); - if(p != NULL) { - return debugger->vt->set_break_point(debugger, tk_atoi(p+1)); + if (p != NULL) { + return debugger->vt->set_break_point(debugger, tk_atoi(p + 1)); } return RET_NOT_IMPL; } else { @@ -290,10 +289,10 @@ ret_t debugger_remove_break_point_ex(debugger_t* debugger, const char* position) if (debugger->vt->remove_break_point_ex != NULL) { return debugger->vt->remove_break_point_ex(debugger, position); - } else if(debugger->vt->remove_break_point != NULL) { + } else if (debugger->vt->remove_break_point != NULL) { const char* p = strchr(position, ':'); - if(p != NULL) { - return debugger->vt->remove_break_point(debugger, tk_atoi(p+1)); + if (p != NULL) { + return debugger->vt->remove_break_point(debugger, tk_atoi(p + 1)); } else { return debugger->vt->remove_break_point(debugger, tk_atoi(position)); } @@ -319,16 +318,35 @@ ret_t debugger_set_current_frame(debugger_t* debugger, uint32_t frame_index) { return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS); debugger->current_frame_index = frame_index; - if(debugger->vt->set_current_frame != NULL) { + if (debugger->vt->set_current_frame != NULL) { return debugger->vt->set_current_frame(debugger, frame_index); } else { return RET_OK; } } +uint64_t debugger_get_current_thread_id(debugger_t* debugger) { + return_value_if_fail(debugger != NULL && debugger->vt != NULL, 0); + if (debugger->vt->get_current_thread_id != NULL) { + return debugger->vt->get_current_thread_id(debugger); + } else { + return 0; + } +} + +ret_t debugger_set_current_thread_id(debugger_t* debugger, uint64_t thread_id) { + return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS); + + if (debugger->vt->set_current_thread_id != NULL) { + return debugger->vt->set_current_thread_id(debugger, thread_id); + } else { + return RET_NOT_IMPL; + } +} + ret_t debugger_dispatch_messages(debugger_t* debugger) { return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS); return_value_if_fail(debugger->vt->dispatch_messages != NULL, RET_BAD_PARAMS); return debugger->vt->dispatch_messages(debugger); -} \ No newline at end of file +} diff --git a/project/gui/awtk/src/debugger/debugger.h b/project/gui/awtk/src/debugger/debugger.h index c233d577c..bb83e1a45 100644 --- a/project/gui/awtk/src/debugger/debugger.h +++ b/project/gui/awtk/src/debugger/debugger.h @@ -29,6 +29,8 @@ BEGIN_C_DECLS +#define DEBUGER_CALLSTACK_NODE_NAME "callstack" + struct _debugger_t; typedef struct _debugger_t debugger_t; /** @@ -74,7 +76,7 @@ typedef ret_t (*debugger_continue_t)(debugger_t* debugger); typedef tk_object_t* (*debugger_get_local_t)(debugger_t* debugger, uint32_t frame_index); typedef tk_object_t* (*debugger_get_self_t)(debugger_t* debugger); typedef tk_object_t* (*debugger_get_global_t)(debugger_t* debugger); -typedef ret_t (*debugger_get_callstack_t)(debugger_t* debugger, binary_data_t* callstack); +typedef tk_object_t* (*debugger_get_callstack_t)(debugger_t* debugger); typedef ret_t (*debugger_clear_break_points_t)(debugger_t* debugger); typedef ret_t (*debugger_set_break_point_t)(debugger_t* debugger, uint32_t line); typedef ret_t (*debugger_remove_break_point_t)(debugger_t* debugger, uint32_t line); @@ -92,6 +94,8 @@ typedef ret_t (*debugger_set_state_t)(debugger_t* debugger, debugger_program_sta /*扩展接口以支持lldb的DAP协议{*/ typedef tk_object_t* (*debugger_get_threads_t)(debugger_t* debugger); +typedef ret_t (*debugger_set_current_thread_id_t)(debugger_t* debugger, uint64_t thread_id); +typedef uint64_t (*debugger_get_current_thread_id_t)(debugger_t* debugger); typedef ret_t (*debugger_launch_app_t)(debugger_t* debugger, const char* program, const char* work_dir, int argc, char* argv[]); @@ -139,6 +143,8 @@ typedef struct _debugger_vtable_t { /*扩展接口以支持lldb的DAP协议{*/ debugger_get_threads_t get_threads; + debugger_get_current_thread_id_t get_current_thread_id; + debugger_set_current_thread_id_t set_current_thread_id; debugger_launch_app_t launch_app; debugger_get_var_t get_var; debugger_set_break_point_ex_t set_break_point_ex; @@ -337,13 +343,12 @@ tk_object_t* debugger_get_global(debugger_t* debugger); /** * @method debugger_get_callstack - * 获取callstack。 + * 获取当前线程的callstack。 * @param {debugger_t*} debugger debugger对象。 - * @param {binary_data_t*} callstack callstack。 * - * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + * @return {tk_object_t*} 返回堆栈信息。 */ -ret_t debugger_get_callstack(debugger_t* debugger, binary_data_t* callstack); +tk_object_t* debugger_get_callstack(debugger_t* debugger); /** * @method debugger_clear_break_points @@ -457,7 +462,7 @@ ret_t debugger_get_break_points(debugger_t* debugger, binary_data_t* break_point */ tk_object_t* debugger_get_threads(debugger_t* debugger); -/* +/** * @method debugger_launch_app * 执行程序(仅用于调试原生程序,脚本不支持)。 * @param {debugger_t*} debugger debugger对象。 @@ -513,7 +518,7 @@ tk_object_t* debugger_get_var(debugger_t* debugger, const char* path); * > 处于暂停状态才能执行本命令。 * @param {debugger_t*} debugger debugger对象。 * - * @return {uint32_t} 成功返回frame序数(0表示当前),失败返回-1。 + * @return {int32_t} 成功返回frame序数(0表示当前),失败返回-1。 */ int32_t debugger_get_current_frame(debugger_t* debugger); @@ -528,6 +533,27 @@ int32_t debugger_get_current_frame(debugger_t* debugger); */ ret_t debugger_set_current_frame(debugger_t* debugger, uint32_t frame_index); +/** + * @method debugger_get_current_thread_id + *获取当前线程 ID。 + * > 处于暂停状态才能执行本命令。 + * @param {debugger_t*} debugger debugger对象。 + * + * @return {uint64_t} 成功返回线程 ID,失败返回 0。 + */ +uint64_t debugger_get_current_thread_id(debugger_t* debugger); + +/** + * @method debugger_set_current_thread_id + * 设置当前处于哪个一个线程的上下文中。 + * > 处于暂停状态才能执行本命令。 + * @param {debugger_t*} debugger debugger对象。 + * @param {uint64_t} thread_id 线程 id + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t debugger_set_current_thread_id(debugger_t* debugger, uint64_t thread_id); + /** * @method debugger_dispatch_messages * dispatch_messages(仅适用于客户端) @@ -564,4 +590,4 @@ debugger_program_state_t debugger_get_state(debugger_t* debugger); END_C_DECLS -#endif /*TK_DEBUGGER_H*/ \ No newline at end of file +#endif /*TK_DEBUGGER_H*/ diff --git a/project/gui/awtk/src/debugger/debugger_client.c b/project/gui/awtk/src/debugger/debugger_client.c index f93c8eb6e..6cffa6187 100644 --- a/project/gui/awtk/src/debugger/debugger_client.c +++ b/project/gui/awtk/src/debugger/debugger_client.c @@ -71,7 +71,7 @@ static ret_t debugger_client_dispatch_message(debugger_t* debugger, debugger_res return_value_if_fail(obj != NULL, RET_BAD_PARAMS); line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0); file_path = tk_object_get_prop_str(obj, STR_DEBUGGER_EVENT_PROP_FILE_PATH); - debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_PAUSED); + debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_PAUSED); emitter_dispatch(EMITTER(debugger), debugger_breaked_event_init_ex(&event, line, file_path)); TK_OBJECT_UNREF(obj); break; @@ -102,7 +102,7 @@ static ret_t debugger_client_dispatch_message(debugger_t* debugger, debugger_res } case DEBUGGER_RESP_MSG_COMPLETED: { client->program_completed = TRUE; - debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_TERMINATED); + debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_TERMINATED); emitter_dispatch_simple_event(EMITTER(debugger), DEBUGGER_RESP_MSG_COMPLETED); break; } @@ -302,7 +302,7 @@ static ret_t debugger_client_continue(debugger_t* debugger) { typedef struct _visit_var_info_t { uint32_t i; str_t* str; -}visit_var_info_t; +} visit_var_info_t; static ret_t visit_var(void* ctx, const void* data) { char buff[64] = {0}; @@ -310,9 +310,9 @@ static ret_t visit_var(void* ctx, const void* data) { str_t* str = info->str; named_value_t* nv = (named_value_t*)data; - if(info->i > 0) { + if (info->i > 0) { str_append(str, ","); - } + } info->i++; str_append(str, "{"); str_append_json_str_pair(str, "name", nv->name); @@ -329,7 +329,7 @@ static ret_t visit_var(void* ctx, const void* data) { static tk_object_t* debugger_client_variables_to_dap_format(tk_object_t* obj) { str_t str; - char url[MAX_PATH+1] = {0}; + char url[MAX_PATH + 1] = {0}; visit_var_info_t info = {0, &str}; return_value_if_fail(obj != NULL, NULL); @@ -338,7 +338,7 @@ static tk_object_t* debugger_client_variables_to_dap_format(tk_object_t* obj) { tk_object_foreach_prop(obj, visit_var, &info); TK_OBJECT_UNREF(obj); str_append(&str, "]}}"); - + data_reader_mem_build_url(str.str, str.size, url); obj = conf_json_load(url, FALSE); str_reset(&str); @@ -371,11 +371,11 @@ static tk_object_t* debugger_client_get_global(debugger_t* debugger) { } } -static ret_t debugger_client_get_callstack(debugger_t* debugger, binary_data_t* callstack) { +static tk_object_t* debugger_client_get_callstack(debugger_t* debugger) { if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_CALLSTACK, 0) == RET_OK) { - return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_CALLSTACK, callstack); + return debugger_client_read_object(debugger, DEBUGGER_RESP_GET_CALLSTACK); } else { - return RET_FAIL; + return NULL; } } diff --git a/project/gui/awtk/src/debugger/debugger_fscript.c b/project/gui/awtk/src/debugger/debugger_fscript.c index 40027a943..9ff70b416 100644 --- a/project/gui/awtk/src/debugger/debugger_fscript.c +++ b/project/gui/awtk/src/debugger/debugger_fscript.c @@ -1,4 +1,4 @@ -/** +/** * File: debugger_fscript.c * Author: AWTK Develop Team * Brief: debugger @@ -21,6 +21,7 @@ #include "tkc/async.h" #include "tkc/object_default.h" +#include "conf_io/conf_ubjson.h" #include "debugger/debugger_server.h" #include "debugger/debugger_message.h" #include "debugger/debugger_fscript.h" @@ -334,23 +335,25 @@ tk_object_t* debugger_fscript_get_global(debugger_t* debugger) { return TK_OBJECT_REF(fscript_get_global_object()); } -static ret_t debugger_fscript_get_callstack(debugger_t* debugger, binary_data_t* callstack) { +static tk_object_t* debugger_fscript_get_callstack(debugger_t* debugger) { int32_t i = 0; int32_t n = 0; + tk_object_t* ret_obj = NULL; debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger); - return_value_if_fail(d != NULL && d->fscript != NULL, RET_BAD_PARAMS); + return_value_if_fail(d != NULL && d->fscript != NULL, NULL); + + ret_obj = conf_ubjson_create(); str_clear(&(d->temp_str)); n = d->call_stack_frames.size; - for (i = n - 1; i >= 0; i--) { - call_stack_frame_t* iter = (call_stack_frame_t*)darray_get(&(d->call_stack_frames), i); - str_append_more(&(d->temp_str), iter->name, "\n", NULL); + for (i = 0; i < n; i++) { + char path[MAX_PATH + 1] = {0}; + call_stack_frame_t* iter = (call_stack_frame_t*)darray_get(&(d->call_stack_frames), n - i - 1); + tk_snprintf(path, sizeof(path), "%s.[%d].name", DEBUGER_CALLSTACK_NODE_NAME, i); + tk_object_set_prop_str(ret_obj, path, iter->name); } - callstack->data = d->temp_str.str; - callstack->size = d->temp_str.size + 1; - - return RET_OK; + return ret_obj; } static ret_t debugger_fscript_get_break_points(debugger_t* debugger, binary_data_t* break_points) { @@ -864,4 +867,4 @@ ret_t debugger_fscript_set_var(fscript_t* fscript, const char* name, const value } return ret; -} \ No newline at end of file +} diff --git a/project/gui/awtk/src/debugger/debugger_lldb.c b/project/gui/awtk/src/debugger/debugger_lldb.c index 95cf2873a..188abc1d1 100644 --- a/project/gui/awtk/src/debugger/debugger_lldb.c +++ b/project/gui/awtk/src/debugger/debugger_lldb.c @@ -24,6 +24,7 @@ #include "tkc.h" #include "debugger_lldb.h" #include "conf_io/conf_json.h" +#include "conf_io/conf_ubjson.h" #include "streams/inet/iostream_tcp.h" #define N_WRITE_TIMEOUT 5000 @@ -101,11 +102,38 @@ static tk_object_t* debugger_lldb_get_callstack_impl(debugger_t* debugger, uint3 uint32_t levels); static ret_t debugger_lldb_lock(debugger_t* debugger) { - return RET_NOT_IMPL; + debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); + return_value_if_fail(lldb != NULL, RET_BAD_PARAMS); + return tk_mutex_nest_lock(lldb->mutex); } static ret_t debugger_lldb_unlock(debugger_t* debugger) { - return RET_NOT_IMPL; + debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); + return_value_if_fail(lldb != NULL, RET_BAD_PARAMS); + return tk_mutex_nest_unlock(lldb->mutex); +} + +static uint64_t debugger_lldb_get_current_thread_id(debugger_t* debugger) { + uint64_t thread_id = 0; + debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); + return_value_if_fail(lldb != NULL, thread_id); + if (debugger_lock(debugger) == RET_OK) { + thread_id = lldb->current_thread_id; + debugger_unlock(debugger); + } + return thread_id; +} + +static ret_t debugger_lldb_set_current_thread_id(debugger_t* debugger, uint64_t thread_id) { + ret_t ret = RET_OK; + debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); + return_value_if_fail(lldb != NULL, thread_id); + ret = debugger_lock(debugger); + if (ret == RET_OK) { + lldb->current_thread_id = thread_id; + debugger_unlock(debugger); + } + return ret; } static tk_object_t* debugger_lldb_get_callstack_obj(debugger_t* debugger) { @@ -169,11 +197,13 @@ static ret_t debugger_lldb_emit(debugger_t* debugger, tk_object_t* resp) { if (tk_str_eq(event, EVENT_STOPPED)) { int32_t line = 0; + int64_t stop_thread_id = 0; const char* file_path = NULL; debugger_breaked_event_t event; TK_OBJECT_UNREF(lldb->callstack); - lldb->stop_thread_id = tk_object_get_prop_int64(resp, "body.threadId", 0); + stop_thread_id = tk_object_get_prop_int64(resp, "body.threadId", 0); + debugger_lldb_set_current_thread_id(debugger, stop_thread_id); lldb->callstack = debugger_lldb_get_callstack_impl(debugger, 0, 100); file_path = debugger_lldb_get_source_path(debugger, debugger->current_frame_index); debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_PAUSED); @@ -182,7 +212,7 @@ static ret_t debugger_lldb_emit(debugger_t* debugger, tk_object_t* resp) { line = lldb->current_frame_line - 1; emitter_dispatch(EMITTER(debugger), debugger_breaked_event_init_ex(&event, line, file_path)); - log_debug("threadId = %d stopped\n", (int)lldb->stop_thread_id); + log_debug("threadId = %lld stopped\n", stop_thread_id); } else if (tk_str_eq(event, EVENT_OUTPUT)) { uint32_t line = 0; debugger_log_event_t event; @@ -725,8 +755,7 @@ static tk_object_t* debugger_lldb_create_get_callstack_req(debugger_t* debugger, uint32_t start_frame, uint32_t levels) { tk_object_t* req = NULL; tk_object_t* arguments = NULL; - debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); - int64_t thread_id = lldb->stop_thread_id; + int64_t thread_id = debugger_lldb_get_current_thread_id(debugger); req = object_default_create(); return_value_if_fail(req != NULL, NULL); @@ -864,8 +893,7 @@ static ret_t debugger_lldb_scopes_command(debugger_t* debugger, uint32_t frame_i static tk_object_t* debugger_lldb_create_simple_req(debugger_t* debugger, const char* cmd) { tk_object_t* req = NULL; tk_object_t* arguments = NULL; - debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); - int64_t thread_id = lldb->stop_thread_id; + int64_t thread_id = debugger_lldb_get_current_thread_id(debugger); req = object_default_create(); return_value_if_fail(req != NULL, NULL); @@ -989,7 +1017,8 @@ static ret_t debugger_lldb_set_current_frame(debugger_t* debugger, uint32_t fram debugger_lldb_scopes_command(debugger, lldb->current_frame_id); /*LLDB 行号从1开始*/ - debugger_frame_changed_event_init_ex(&event, lldb->current_frame_name, lldb->current_frame_line - 1, lldb->current_frame_file_path); + debugger_frame_changed_event_init_ex(&event, lldb->current_frame_name, + lldb->current_frame_line - 1, lldb->current_frame_file_path); emitter_dispatch(EMITTER(debugger), (event_t*)&(event)); return RET_OK; @@ -1073,31 +1102,47 @@ static tk_object_t* debugger_lldb_get_global(debugger_t* debugger) { return debugger_lldb_get_variables_impl(debugger, VARREF_LOCALS, 0, 0xffff); } -static ret_t debugger_lldb_get_callstack(debugger_t* debugger, binary_data_t* ret) { +static tk_object_t* debugger_lldb_get_callstack(debugger_t* debugger) { int32_t i = 0; int32_t n = 0; char path[MAX_PATH + 1] = {0}; + tk_object_t* ret_obj = NULL; tk_object_t* callstack = NULL; debugger_lldb_t* lldb = DEBUGGER_LLDB(debugger); - str_t* str = &(lldb->scallstack); - return_value_if_fail(ret != NULL, RET_BAD_PARAMS); + return_value_if_fail(lldb != NULL, NULL); debugger_lldb_dispatch_messages(debugger); callstack = debugger_lldb_get_callstack_obj(debugger); - return_value_if_fail(callstack != NULL, RET_BAD_PARAMS); + return_value_if_fail(callstack != NULL, NULL); + + ret_obj = conf_ubjson_create(); - str_clear(str); n = tk_object_get_prop_uint32(callstack, "body.stackFrames.#size", 0); for (i = 0; i < n; i++) { - tk_snprintf(path, sizeof(path) - 1, "body.stackFrames.[%d].name", i); - str_append(str, tk_object_get_prop_str(callstack, path)); - str_append(str, "\n"); - } + uint32_t line_number = 0; + const char* name = NULL; + const char* file_path = NULL; - ret->data = str->str; - ret->size = str->size; + tk_snprintf(path, sizeof(path), "body.stackFrames.[%d].name", i); + name = tk_object_get_prop_str(callstack, path); + + tk_snprintf(path, sizeof(path), "body.stackFrames.[%d].source.path", i); + file_path = tk_object_get_prop_str(callstack, path); - return RET_OK; + tk_snprintf(path, sizeof(path), "body.stackFrames.[%d].line", i); + line_number = tk_object_get_prop_uint32(callstack, path, 0); + + tk_snprintf(path, sizeof(path), "%s.[%d].name", DEBUGER_CALLSTACK_NODE_NAME, i); + tk_object_set_prop_str(ret_obj, path, name); + + tk_snprintf(path, sizeof(path), "%s.[%d].path", DEBUGER_CALLSTACK_NODE_NAME, i); + tk_object_set_prop_str(ret_obj, path, file_path); + + tk_snprintf(path, sizeof(path), "%s.[%d].line", DEBUGER_CALLSTACK_NODE_NAME, i); + tk_object_set_prop_uint32(ret_obj, path, line_number); + } + + return ret_obj; } static ret_t debugger_lldb_update_break_points(debugger_t* debugger) { @@ -1283,6 +1328,8 @@ static const debugger_vtable_t s_debugger_lldb_vtable = { .remove_break_point_ex = debugger_lldb_remove_break_point_ex, .clear_break_points = debugger_lldb_clear_break_points, .set_current_frame = debugger_lldb_set_current_frame, + .set_current_thread_id = debugger_lldb_set_current_thread_id, + .get_current_thread_id = debugger_lldb_get_current_thread_id, .deinit = debugger_lldb_deinit, }; @@ -1305,6 +1352,7 @@ static ret_t debugger_lldb_on_destroy(tk_object_t* obj) { str_reset(&(lldb->header)); str_reset(&(lldb->scallstack)); str_reset(&(lldb->sbreakpoints)); + tk_mutex_nest_destroy(lldb->mutex); TK_OBJECT_UNREF(lldb->io); TK_OBJECT_UNREF(lldb->sources); TK_OBJECT_UNREF(lldb->callstack); @@ -1335,6 +1383,7 @@ debugger_t* debugger_lldb_create_impl(tk_iostream_t* io) { str_init(&(debugger->header), 10000); str_init(&(debugger->scallstack), 10000); str_init(&(debugger->sbreakpoints), 10000); + debugger->mutex = tk_mutex_nest_create(); debugger->resps = object_default_create(); debugger->sources = object_default_create(); debugger->source_break_points = object_default_create(); diff --git a/project/gui/awtk/src/debugger/debugger_lldb.h b/project/gui/awtk/src/debugger/debugger_lldb.h index 617d8d61c..27f9d959a 100644 --- a/project/gui/awtk/src/debugger/debugger_lldb.h +++ b/project/gui/awtk/src/debugger/debugger_lldb.h @@ -51,13 +51,15 @@ typedef struct _debugger_lldb_t { str_t sbreakpoints; tk_iostream_t* io; - int64_t stop_thread_id; int64_t current_frame_id; + int64_t current_thread_id; int64_t current_frame_line; const char* current_frame_name; const char* current_frame_source; const char* current_frame_file_path; + tk_mutex_nest_t* mutex; + tk_object_t* resps; /*代码文件缓存*/ tk_object_t* sources; diff --git a/project/gui/awtk/src/debugger/debugger_message.h b/project/gui/awtk/src/debugger/debugger_message.h index 5db3a86ce..71c655051 100644 --- a/project/gui/awtk/src/debugger/debugger_message.h +++ b/project/gui/awtk/src/debugger/debugger_message.h @@ -488,7 +488,8 @@ event_t* debugger_breaked_event_init(debugger_breaked_event_t* event, uint32_t l * * @return {event_t*} 返回event对象。 */ -event_t* debugger_breaked_event_init_ex(debugger_breaked_event_t* event, uint32_t line, const char* file_path); +event_t* debugger_breaked_event_init_ex(debugger_breaked_event_t* event, uint32_t line, + const char* file_path); /** * @method debugger_breaked_event_cast @@ -514,7 +515,7 @@ typedef struct _debugger_frame_changed_event_t { * 行号。 */ uint32_t line; - + /** * @property {const char*} func * @annotation ["readable"] @@ -540,10 +541,11 @@ typedef struct _debugger_frame_changed_event_t { * * @return {event_t*} 返回event对象。 */ -event_t* debugger_frame_changed_event_init(debugger_frame_changed_event_t* event, const char* func, uint32_t line); +event_t* debugger_frame_changed_event_init(debugger_frame_changed_event_t* event, const char* func, + uint32_t line); /** - * @method debugger_frame_changed_event_init + * @method debugger_frame_changed_event_init_ex * 初始化 * * @param {debugger_frame_changed_event_t*} event event对象。 @@ -553,7 +555,9 @@ event_t* debugger_frame_changed_event_init(debugger_frame_changed_event_t* event * * @return {event_t*} 返回event对象。 */ -event_t* debugger_frame_changed_event_init_ex(debugger_frame_changed_event_t* event, const char* func, uint32_t line, const char* file_path); +event_t* debugger_frame_changed_event_init_ex(debugger_frame_changed_event_t* event, + const char* func, uint32_t line, + const char* file_path); /** * @method debugger_frame_changed_event_cast diff --git a/project/gui/awtk/src/debugger/debugger_server.c b/project/gui/awtk/src/debugger/debugger_server.c index 7588d7299..7e52dacc6 100644 --- a/project/gui/awtk/src/debugger/debugger_server.c +++ b/project/gui/awtk/src/debugger/debugger_server.c @@ -21,6 +21,8 @@ #include "tkc/thread.h" #include "tkc/buffer.h" +#include "conf_io/conf_obj.h" +#include "conf_io/conf_ubjson.h" #include "tkc/object_default.h" #include "ubjson/ubjson_writer.h" #include "debugger/debugger_factory.h" @@ -144,6 +146,34 @@ static ret_t debugger_server_send_data(debugger_server_t* server, debugger_resp_ return ret; } +static ret_t debugger_server_send_ubjson(debugger_server_t* server, debugger_resp_t* resp, + tk_object_t* obj) { + wbuffer_t wb; + ret_t ret = RET_FAIL; + ubjson_writer_t writer; + binary_data_t data = {0, NULL}; + return_value_if_fail(wbuffer_init_extendable(&wb) != NULL, RET_OOM); + + if (obj != NULL) { + conf_doc_t* doc = conf_obj_get_doc(obj); + ubjson_writer_init(&writer, (ubjson_write_callback_t)wbuffer_write_binary, &wb); + ret = conf_doc_save_ubjson(doc, &writer); + assert(ret == RET_OK); + goto_error_if_fail(ret == RET_OK); + } else { + wbuffer_write_string(&wb, "{}"); + } + + data.data = wb.data; + data.size = wb.cursor; + ret = debugger_server_send_data(server, resp, &data); + +error: + wbuffer_deinit(&wb); + + return ret; +} + static ret_t debugger_server_send_object(debugger_server_t* server, debugger_resp_t* resp, tk_object_t* obj) { wbuffer_t wb; @@ -573,10 +603,10 @@ static ret_t debugger_server_dispatch(debugger_server_t* server) { continue; } case DEBUGGER_REQ_GET_CALLSTACK: { - binary_data_t data = {0, NULL}; if (debugger_lock(debugger) == RET_OK) { - resp.error = debugger_get_callstack(debugger, &data); - ret = debugger_server_send_data(server, &resp, &data); + tk_object_t* obj = debugger_get_callstack(debugger); + ret = debugger_server_send_ubjson(server, &resp, obj); + TK_OBJECT_UNREF(obj); debugger_unlock(debugger); goto_error_if_fail(ret == RET_OK); } diff --git a/project/gui/awtk/src/image_loader/image_loader_stb.c b/project/gui/awtk/src/image_loader/image_loader_stb.c index 990b7aa03..bc7ff855a 100644 --- a/project/gui/awtk/src/image_loader/image_loader_stb.c +++ b/project/gui/awtk/src/image_loader/image_loader_stb.c @@ -3,7 +3,7 @@ * Author: AWTK Develop Team * Brief: stb image loader * - * Copyright (c) 2018 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,10 +30,6 @@ #include "base/system_info.h" #include "image_loader/image_loader_stb.h" -#ifndef WITH_FAST_LCD_PORTRAIT -#define WITH_FAST_LCD_PORTRAIT -#endif - static uint8_t* convert_2_to_4(uint8_t* src, uint32_t w, uint32_t h) { uint32_t i = 0; uint8_t* s = src; @@ -59,6 +55,31 @@ static uint8_t* convert_2_to_4(uint8_t* src, uint32_t w, uint32_t h) { return data; } +static uint8_t* convert_1_to_4(uint8_t* src, uint32_t w, uint32_t h) { + uint32_t i = 0; + uint8_t* s = src; + uint8_t* d = NULL; + uint8_t* data = NULL; + uint32_t size = w * h; + return_value_if_fail(src != NULL, NULL); + + data = TKMEM_ALLOC(size * 4); + return_value_if_fail(data != NULL, NULL); + + d = data; + for (i = 0; i < size; i++) { + d[0] = s[0]; + d[1] = s[0]; + d[2] = s[0]; + d[3] = 0xFF; + + d += 4; + s += 1; + } + + return data; +} + ret_t stb_load_image(int32_t subtype, const uint8_t* buff, uint32_t buff_size, bitmap_t* image, bitmap_format_t transparent_bitmap_format, bitmap_format_t opaque_bitmap_format, lcd_orientation_t o) { @@ -77,6 +98,9 @@ ret_t stb_load_image(int32_t subtype, const uint8_t* buff, uint32_t buff_size, b if (n == 2) { n = 4; data = convert_2_to_4(stb_data, w, h); + } else if (n == 1) { + n = 4; + data = convert_1_to_4(stb_data, w, h); } else { data = stb_data; } diff --git a/project/gui/awtk/src/input_methods/input_method_default.inc b/project/gui/awtk/src/input_methods/input_method_default.inc index 23a4c8c6d..21711c8d8 100644 --- a/project/gui/awtk/src/input_methods/input_method_default.inc +++ b/project/gui/awtk/src/input_methods/input_method_default.inc @@ -182,6 +182,7 @@ static ret_t input_method_default_restore_win_size(input_method_t* im) { static ret_t input_method_default_restore_win_position(input_method_t* im) { widget_t* widget = im->last_widget != NULL && im->keyboard != NULL ? im->last_widget : im->widget; + if (im->last_widget != NULL) { im->last_win_delta_y = im->win_delta_y; } else { @@ -222,6 +223,7 @@ static ret_t input_method_on_win_close(void* ctx, event_t* e) { window_close_force(im->keyboard); im->win = NULL; + im->busy = FALSE; im->keyboard = NULL; input_method_set_target_widget(im, NULL); } @@ -618,4 +620,4 @@ input_method_t* input_method_default_create(void) { input_engine_init(im->engine); return im; -} \ No newline at end of file +} diff --git a/project/gui/awtk/src/remote_ui/client/remote_ui.c b/project/gui/awtk/src/remote_ui/client/remote_ui.c index aedd4d9c5..ee9f0760f 100644 --- a/project/gui/awtk/src/remote_ui/client/remote_ui.c +++ b/project/gui/awtk/src/remote_ui/client/remote_ui.c @@ -20,11 +20,13 @@ */ #include "tkc/fs.h" +#include "tkc/path.h" #include "tkc/mem.h" #include "tkc/crc.h" #include "tkc/utils.h" #include "base/keys.h" #include "base/events.h" +#include "service/service.h" #include "conf_io/conf_ubjson.h" #include "tkc/object_default.h" @@ -40,94 +42,14 @@ remote_ui_t* remote_ui_create(tk_iostream_t* io) { ui = (remote_ui_t*)TKMEM_ZALLOC(remote_ui_t); return_value_if_fail(ui != NULL, NULL); - ui->io = io; ui->event_handlers = object_default_create_ex(FALSE); - wbuffer_init_extendable(&(ui->wb)); + tk_client_init(&(ui->client), io); return ui; } -static ret_t remote_ui_send_req(tk_iostream_t* io, uint32_t type, uint32_t data_type, - wbuffer_t* wb) { - int32_t ret = 0; - const void* data = NULL; - uint32_t size = 0; - uint32_t timeout = TK_OSTREAM_DEFAULT_TIMEOUT; - uint16_t crc_value = PPPINITFCS16; - remote_ui_msg_header_t header; - memset(&header, 0x00, sizeof(header)); - return_value_if_fail(io != NULL && wb != NULL, RET_BAD_PARAMS); - - size = wb->cursor; - data = wb->data; - if (size > 0) { - return_value_if_fail(data != NULL, RET_BAD_PARAMS); - } - - header.type = type; - header.size = size; - header.data_type = data_type; - - crc_value = tk_crc16(crc_value, &header, sizeof(header)); - if (data != NULL && size > 0) { - crc_value = tk_crc16(crc_value, data, size); - } - ret = tk_iostream_write_len(io, &header, sizeof(header), timeout); - return_value_if_fail(ret == sizeof(header), RET_IO); - - if (size > 0) { - timeout = TK_OSTREAM_DEFAULT_TIMEOUT * (size / 10240) + TK_OSTREAM_DEFAULT_TIMEOUT; - ret = tk_iostream_write_len(io, data, size, timeout); - return_value_if_fail(ret == size, RET_IO); - } - - ret = tk_iostream_write_len(io, &crc_value, sizeof(crc_value), TK_OSTREAM_DEFAULT_TIMEOUT); - return_value_if_fail(ret == sizeof(crc_value), RET_IO); - - return RET_OK; -} - -static ret_t remote_ui_read_resp(tk_iostream_t* io, remote_ui_msg_header_t* header, wbuffer_t* wb) { - int32_t ret = 0; - uint16_t crc_value = 0; - uint16_t real_crc_value = PPPINITFCS16; - return_value_if_fail(io != NULL && header != NULL && wb != NULL, RET_BAD_PARAMS); - - wbuffer_rewind(wb); - ret = tk_iostream_read_len(io, header, sizeof(*header), REMOTE_UI_ISTREAM_TIMEOUT); - return_value_if_fail(ret == sizeof(*header), RET_IO); - - real_crc_value = tk_crc16(real_crc_value, header, sizeof(*header)); - if (header->size > 0) { - return_value_if_fail(wbuffer_extend_capacity(wb, header->size) == RET_OK, RET_OOM); - ret = tk_iostream_read_len(io, wb->data, header->size, REMOTE_UI_ISTREAM_TIMEOUT); - return_value_if_fail(ret == header->size, RET_IO); - real_crc_value = tk_crc16(real_crc_value, wb->data, header->size); - } - - ret = tk_iostream_read_len(io, &crc_value, sizeof(crc_value), REMOTE_UI_ISTREAM_TIMEOUT); - return_value_if_fail(ret == sizeof(crc_value), RET_IO); - return_value_if_fail(real_crc_value == crc_value, RET_CRC); - - wb->cursor = header->size; - - return header->resp_code; -} - -static ret_t remote_ui_request(tk_iostream_t* io, uint32_t type, uint32_t data_type, - wbuffer_t* wb) { - ret_t ret = remote_ui_send_req(io, type, data_type, wb); - if (ret == RET_OK) { - remote_ui_msg_header_t header; - memset(&header, 0x00, sizeof(header)); - ret = remote_ui_read_resp(io, &header, wb); - } - - return ret; -} - static ubjson_writer_t* remote_ui_client_get_writer(remote_ui_t* ui) { - wbuffer_t* wb = &(ui->wb); + wbuffer_t* wb = &(ui->client.wb); ubjson_writer_t* writer = &(ui->writer); wb->cursor = 0; @@ -136,7 +58,7 @@ static ubjson_writer_t* remote_ui_client_get_writer(remote_ui_t* ui) { ret_t remote_ui_login(remote_ui_t* ui, const char* username, const char* password) { ubjson_writer_t* writer = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(username != NULL, RET_BAD_PARAMS); return_value_if_fail(password != NULL, RET_BAD_PARAMS); writer = remote_ui_client_get_writer(ui); @@ -146,26 +68,26 @@ ret_t remote_ui_login(remote_ui_t* ui, const char* username, const char* passwor ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_PASSWORD, password); ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_LOGIN, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + return tk_client_request(&(ui->client), MSG_CODE_LOGIN, MSG_DATA_TYPE_UBJSON, &(ui->client.wb)); } ret_t remote_ui_logout(remote_ui_t* ui) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - return remote_ui_request(ui->io, REMOTE_UI_REQ_LOGOUT, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + return tk_client_request(&(ui->client), MSG_CODE_LOGOUT, MSG_DATA_TYPE_NONE, &(ui->client.wb)); } ret_t remote_ui_get_dev_info(remote_ui_t* ui, remote_ui_dev_info_t* info) { ret_t ret = RET_FAIL; wbuffer_t* wb = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(info != NULL, RET_BAD_PARAMS); - wb = &(ui->wb); + wb = &(ui->client.wb); wbuffer_rewind(wb); memset(info, 0x00, sizeof(*info)); - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_GET_DEV_INFO, REMOTE_UI_DATA_TYPE_UBJSON, wb); + ret = tk_client_request(&(ui->client), REMOTE_UI_GET_DEV_INFO, MSG_DATA_TYPE_NONE, wb); if (ret == RET_OK) { tk_object_t* obj = conf_ubjson_load_from_buff(wb->data, wb->cursor, FALSE); if (obj != NULL) { @@ -202,141 +124,80 @@ ret_t remote_ui_get_dev_info(remote_ui_t* ui, remote_ui_dev_info_t* info) { } ret_t remote_ui_reboot(remote_ui_t* ui, remote_ui_reboot_type_t reboot_type) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_int32(&(ui->wb), reboot_type); - return remote_ui_request(ui->io, REMOTE_UI_REQ_REBOOT, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); -} - -ret_t remote_ui_upload_file(remote_ui_t* ui, const char* remote_file, const char* local_file) { - wbuffer_t wb; - int32_t len = 0; - ret_t ret = RET_OK; - fs_file_t* file = NULL; - uint8_t buff[4096] = {0}; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); - return_value_if_fail(local_file != NULL, RET_BAD_PARAMS); - - file = fs_open_file(os_fs(), local_file, "rb"); - return_value_if_fail(file != NULL, RET_BAD_PARAMS); - - wbuffer_init(&wb, (void*)remote_file, strlen(remote_file) + 1); - wb.cursor = wb.capacity; - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_UPLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_STRING, &wb); - goto_error_if_fail(ret == RET_OK); - - while ((len = fs_file_read(file, buff, sizeof(buff))) > 0) { - wbuffer_init(&wb, buff, len); - wb.cursor = len; - ret = - remote_ui_request(ui->io, REMOTE_UI_REQ_UPLOAD_FILE_DATA, REMOTE_UI_DATA_TYPE_BINARY, &wb); - break_if_fail(ret == RET_OK); - } - - wbuffer_rewind(&wb); - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_UPLOAD_FILE_END, REMOTE_UI_DATA_TYPE_NONE, &wb); - - fs_file_close(file); - - return ret; -error: - fs_file_close(file); - - return RET_FAIL; + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_int32(&(ui->client.wb), reboot_type); + return tk_client_request(&(ui->client), REMOTE_UI_REBOOT, MSG_DATA_TYPE_NONE, &(ui->client.wb)); } ret_t remote_ui_download_file(remote_ui_t* ui, const char* remote_file, const char* local_file) { - int32_t len = 0; - ret_t ret = RET_OK; - fs_file_t* file = NULL; - wbuffer_t* wb = NULL; - remote_ui_msg_header_t header; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); - return_value_if_fail(local_file != NULL, RET_BAD_PARAMS); - - file = fs_open_file(os_fs(), local_file, "wb+"); - return_value_if_fail(file != NULL, RET_BAD_PARAMS); - - wb = &(ui->wb); - wbuffer_rewind(wb); - wbuffer_write_string(wb, remote_file); - ret = - remote_ui_request(ui->io, REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_STRING, wb); - goto_error_if_fail(ret == RET_OK); - - memset(&header, 0x00, sizeof(header)); + return_value_if_fail(ui != NULL, RET_BAD_PARAMS); - while ((ret = remote_ui_read_resp(ui->io, &header, wb)) == RET_OK) { - if (header.type == REMOTE_UI_RESP_DOWNLOAD_FILE_DATA) { - len = fs_file_write(file, wb->data, wb->cursor); - break_if_fail(len == wb->cursor); - } else if (header.type == REMOTE_UI_RESP_DOWNLOAD_FILE_END) { - ret = RET_OK; - break; - } else { - assert(!"impossible"); - ret = RET_FAIL; - break; - } - } - fs_file_close(file); + return tk_client_download_file(&(ui->client), remote_file, local_file); +} - return ret; -error: - fs_file_close(file); +ret_t remote_ui_upload_file(remote_ui_t* ui, const char* remote_file, const char* local_file) { + return_value_if_fail(ui != NULL, RET_BAD_PARAMS); - return ret; + return tk_client_upload_file(&(ui->client), remote_file, local_file); } ret_t remote_ui_create_dir(remote_ui_t* ui, const char* remote_dir) { ret_t ret = RET_OK; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(remote_dir != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), remote_dir); - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_CREATE_DIR, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_string(&(ui->client.wb), remote_dir); + ret = tk_client_request(&(ui->client), REMOTE_UI_CREATE_DIR, MSG_DATA_TYPE_STRING, + &(ui->client.wb)); return ret; } ret_t remote_ui_remove_dir(remote_ui_t* ui, const char* remote_dir) { ret_t ret = RET_OK; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(remote_dir != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), remote_dir); - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_REMOVE_DIR, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_string(&(ui->client.wb), remote_dir); + ret = tk_client_request(&(ui->client), REMOTE_UI_REMOVE_DIR, MSG_DATA_TYPE_STRING, + &(ui->client.wb)); return ret; } ret_t remote_ui_remove_file(remote_ui_t* ui, const char* remote_file) { ret_t ret = RET_OK; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), remote_file); - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_REMOVE_FILE, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_string(&(ui->client.wb), remote_file); + ret = tk_client_request(&(ui->client), REMOTE_UI_REMOVE_FILE, MSG_DATA_TYPE_STRING, + &(ui->client.wb)); return ret; } -ret_t remote_ui_take_screen_shot(remote_ui_t* ui, const char* file) { - return remote_ui_download_file(ui, REMOTE_UI_FILE_SCREEN_SHOT, file); +ret_t remote_ui_take_snapshot(remote_ui_t* ui, const char* target, const char* file) { + char remote_file[MAX_PATH + 1] = {0}; + path_build(remote_file, sizeof(remote_file) - 1, REMOTE_UI_FILE_SNAPSHOT, target, NULL); + + return remote_ui_download_file(ui, remote_file, file); } ret_t remote_ui_get_manifest(remote_ui_t* ui, const char* file) { return remote_ui_download_file(ui, REMOTE_UI_FILE_MANIFEST, file); } -ret_t remote_ui_get_xml_source(remote_ui_t* ui, const char* file) { - return remote_ui_download_file(ui, REMOTE_UI_FILE_XML_SOURCE, file); +ret_t remote_ui_get_xml_source(remote_ui_t* ui, const char* target, const char* file) { + char remote_file[MAX_PATH + 1] = {0}; + path_build(remote_file, sizeof(remote_file) - 1, REMOTE_UI_FILE_XML_SOURCE, target, NULL); + + return remote_ui_download_file(ui, remote_file, file); } ret_t remote_ui_on_event(remote_ui_t* ui, const char* target, uint32_t event, event_func_t func, @@ -363,7 +224,8 @@ ret_t remote_ui_on_event(remote_ui_t* ui, const char* target, uint32_t event, ev ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_EVENT, event); ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_ON_EVENT, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + return tk_client_request(&(ui->client), REMOTE_UI_ON_EVENT, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); } ret_t remote_ui_off_event(remote_ui_t* ui, const char* target, uint32_t event) { @@ -383,12 +245,31 @@ ret_t remote_ui_off_event(remote_ui_t* ui, const char* target, uint32_t event) { ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_EVENT, event); ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_OFF_EVENT, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + return tk_client_request(&(ui->client), REMOTE_UI_OFF_EVENT, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); +} + +ret_t remote_ui_click(remote_ui_t* ui, const char* target) { + pointer_event_t e; + pointer_event_init(&e, EVT_CLICK, NULL, 0, 0); + + return remote_ui_send_event(ui, target, (event_t*)&e); +} + +ret_t remote_ui_key(remote_ui_t* ui, const char* target, int32_t key_code) { + key_event_t e; + key_event_init(&e, EVT_KEY_DOWN, NULL, key_code); + if (remote_ui_send_event(ui, target, (event_t*)&e) == RET_OK) { + key_event_init(&e, EVT_KEY_UP, NULL, key_code); + return remote_ui_send_event(ui, target, (event_t*)&e); + } else { + return RET_FAIL; + } } ret_t remote_ui_send_event(remote_ui_t* ui, const char* target, event_t* event) { ubjson_writer_t* writer = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(target != NULL, RET_BAD_PARAMS); return_value_if_fail(event != NULL, RET_BAD_PARAMS); @@ -419,13 +300,14 @@ ret_t remote_ui_send_event(remote_ui_t* ui, const char* target, event_t* event) } ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_SEND_EVENT, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + return tk_client_request(&(ui->client), REMOTE_UI_SEND_EVENT, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); } ret_t remote_ui_open_window(remote_ui_t* ui, const char* name, const char* xml, const char* init_json) { ubjson_writer_t* writer = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(name != NULL, RET_BAD_PARAMS); writer = remote_ui_client_get_writer(ui); @@ -439,14 +321,14 @@ ret_t remote_ui_open_window(remote_ui_t* ui, const char* name, const char* xml, } ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_OPEN_WINDOW, REMOTE_UI_DATA_TYPE_UBJSON, - &(ui->wb)); + return tk_client_request(&(ui->client), REMOTE_UI_OPEN_WINDOW, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); } static ret_t remote_ui_show_dialog(remote_ui_t* ui, const char* type, const char* title, const char* content, uint32_t duration) { ubjson_writer_t* writer = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(type != NULL, RET_BAD_PARAMS); writer = remote_ui_client_get_writer(ui); @@ -457,69 +339,71 @@ static ret_t remote_ui_show_dialog(remote_ui_t* ui, const char* type, const char ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_DURATION, duration); ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_OPEN_DIALOG, REMOTE_UI_DATA_TYPE_UBJSON, - &(ui->wb)); + return tk_client_request(&(ui->client), REMOTE_UI_OPEN_DIALOG, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); } ret_t remote_ui_show_confirm(remote_ui_t* ui, const char* title, const char* content) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(title != NULL, RET_BAD_PARAMS); - return_value_if_fail(content != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(title != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); - return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_CONFIRM, title, content, 0); + return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_CONFIRM, title, content, 0); } ret_t remote_ui_show_warn(remote_ui_t* ui, const char* title, const char* content) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(title != NULL, RET_BAD_PARAMS); - return_value_if_fail(content != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(title != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); - return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_WARN, title, content, 0); + return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_WARN, title, content, 0); } ret_t remote_ui_show_info(remote_ui_t* ui, const char* title, const char* content) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(title != NULL, RET_BAD_PARAMS); - return_value_if_fail(content != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(title != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); - return remote_ui_show_dialog(ui, NULL, title, content, 0); + return remote_ui_show_dialog(ui, NULL, title, content, 0); } ret_t remote_ui_show_toast(remote_ui_t* ui, uint32_t duration, const char* content) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(content != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(content != NULL, RET_BAD_PARAMS); - return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_TOAST, "", content, duration); + return remote_ui_show_dialog(ui, REMOTE_UI_DIALOG_TYPE_TOAST, "", content, duration); } ret_t remote_ui_close_window(remote_ui_t* ui, const char* name) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(name != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), name); - return remote_ui_request(ui->io, REMOTE_UI_REQ_CLOSE_WINDOW, REMOTE_UI_DATA_TYPE_STRING, - &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_string(&(ui->client.wb), name); + return tk_client_request(&(ui->client), REMOTE_UI_CLOSE_WINDOW, MSG_DATA_TYPE_STRING, + &(ui->client.wb)); } ret_t remote_ui_back_to_prev(remote_ui_t* ui) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - return remote_ui_request(ui->io, REMOTE_UI_REQ_BACK_TO_PREV, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + return tk_client_request(&(ui->client), REMOTE_UI_BACK_TO_PREV, MSG_DATA_TYPE_NONE, + &(ui->client.wb)); } ret_t remote_ui_back_to_home(remote_ui_t* ui) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - return remote_ui_request(ui->io, REMOTE_UI_REQ_BACK_TO_HOME, REMOTE_UI_DATA_TYPE_NONE, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + return tk_client_request(&(ui->client), REMOTE_UI_BACK_TO_HOME, MSG_DATA_TYPE_NONE, + &(ui->client.wb)); } ret_t remote_ui_set_prop(remote_ui_t* ui, const char* target, const char* name, const value_t* value) { ubjson_writer_t* writer = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(target != NULL, RET_BAD_PARAMS); return_value_if_fail(name != NULL, RET_BAD_PARAMS); return_value_if_fail(value != NULL, RET_BAD_PARAMS); @@ -531,13 +415,14 @@ ret_t remote_ui_set_prop(remote_ui_t* ui, const char* target, const char* name, ubjson_writer_write_kv_value(writer, REMOTE_UI_KEY_VALUE, value); ubjson_writer_write_object_end(writer); - return remote_ui_request(ui->io, REMOTE_UI_REQ_SET_PROP, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + return tk_client_request(&(ui->client), REMOTE_UI_SET_PROP, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); } ret_t remote_ui_get_prop(remote_ui_t* ui, const char* target, const char* name, value_t* value) { ret_t ret = RET_OK; ubjson_writer_t* writer = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(target != NULL, RET_BAD_PARAMS); return_value_if_fail(name != NULL, RET_BAD_PARAMS); return_value_if_fail(value != NULL, RET_BAD_PARAMS); @@ -549,49 +434,115 @@ ret_t remote_ui_get_prop(remote_ui_t* ui, const char* target, const char* name, ubjson_writer_write_kv_value(writer, REMOTE_UI_KEY_VALUE, value); ubjson_writer_write_object_end(writer); - ret = remote_ui_request(ui->io, REMOTE_UI_REQ_GET_PROP, REMOTE_UI_DATA_TYPE_UBJSON, &(ui->wb)); + ret = + tk_client_request(&(ui->client), REMOTE_UI_GET_PROP, MSG_DATA_TYPE_UBJSON, &(ui->client.wb)); return_value_if_fail(ret == RET_OK, ret); - value_dup_str_with_len(value, (char*)(ui->wb.data), ui->wb.cursor); + value_dup_str_with_len(value, (char*)(ui->client.wb.data), ui->client.wb.cursor); return RET_OK; } ret_t remote_ui_set_theme(remote_ui_t* ui, const char* theme) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + value_t v; + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(theme != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), theme); - return remote_ui_request(ui->io, REMOTE_UI_REQ_SET_THEME, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + return remote_ui_set_prop(ui, REMOTE_UI_TARGET_GLOBAL, REMOTE_UI_PROP_THEME, + value_set_str(&v, theme)); } ret_t remote_ui_set_language(remote_ui_t* ui, const char* language) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + value_t v; + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(language != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), language); - return remote_ui_request(ui->io, REMOTE_UI_REQ_SET_LANGUAGE, REMOTE_UI_DATA_TYPE_STRING, - &(ui->wb)); + return remote_ui_set_prop(ui, REMOTE_UI_TARGET_GLOBAL, REMOTE_UI_PROP_LANGUAGE, + value_set_str(&v, language)); } ret_t remote_ui_exec_fscript(remote_ui_t* ui, const char* script, str_t* str) { ret_t ret = RET_OK; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); return_value_if_fail(script != NULL, RET_BAD_PARAMS); - wbuffer_rewind(&(ui->wb)); - wbuffer_write_string(&(ui->wb), script); - ret = - remote_ui_request(ui->io, REMOTE_UI_REQ_EXEC_FSCRIPT, REMOTE_UI_DATA_TYPE_STRING, &(ui->wb)); + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_string(&(ui->client.wb), script); + ret = tk_client_request(&(ui->client), REMOTE_UI_EXEC_FSCRIPT, MSG_DATA_TYPE_STRING, + &(ui->client.wb)); if (ret == RET_OK) { - str_set_with_len(str, (char*)(ui->wb.data), ui->wb.cursor); + str_set_with_len(str, (char*)(ui->client.wb.data), ui->client.wb.cursor); } return ret; } +ret_t remote_ui_move_widget(remote_ui_t* ui, const char* target, int32_t x, int32_t y) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_X, x); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_Y, y); + ubjson_writer_write_object_end(writer); + + return tk_client_request(&(ui->client), REMOTE_UI_MOVE_WIDGET, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); +} + +ret_t remote_ui_resize_widget(remote_ui_t* ui, const char* target, uint32_t w, uint32_t h) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_W, w); + ubjson_writer_write_kv_int(writer, REMOTE_UI_KEY_H, h); + ubjson_writer_write_object_end(writer); + + return tk_client_request(&(ui->client), REMOTE_UI_RESIZE_WIDGET, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); +} + +ret_t remote_ui_destroy_widget(remote_ui_t* ui, const char* target) { + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + + wbuffer_rewind(&(ui->client.wb)); + wbuffer_write_string(&(ui->client.wb), target); + return tk_client_request(&(ui->client), REMOTE_UI_DESTROY_WIDGET, MSG_DATA_TYPE_STRING, + &(ui->client.wb)); +} + +ret_t remote_ui_create_widget(remote_ui_t* ui, const char* target, const char* xml) { + ubjson_writer_t* writer = NULL; + return_value_if_fail(ui != NULL && ui->client.io != NULL, RET_BAD_PARAMS); + return_value_if_fail(target != NULL, RET_BAD_PARAMS); + return_value_if_fail(xml != NULL, RET_BAD_PARAMS); + + writer = remote_ui_client_get_writer(ui); + ubjson_writer_write_object_begin(writer); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_TARGET, target); + ubjson_writer_write_kv_str(writer, REMOTE_UI_KEY_XML, xml); + ubjson_writer_write_object_end(writer); + + return tk_client_request(&(ui->client), REMOTE_UI_CREATE_WIDGET, MSG_DATA_TYPE_UBJSON, + &(ui->client.wb)); +} + +ret_t remote_ui_get_loaded_images_info(remote_ui_t* ui, const char* file) { + return remote_ui_download_file(ui, REMOTE_UI_FILE_LOADED_IMAGES_INFO, file); +} + +ret_t remote_ui_get_loaded_assets_info(remote_ui_t* ui, const char* file) { + return remote_ui_download_file(ui, REMOTE_UI_FILE_LOADED_ASSETS_INFO, file); +} + ret_t remote_ui_dispatch(remote_ui_t* ui) { /*TODO*/ return RET_OK; @@ -600,9 +551,8 @@ ret_t remote_ui_dispatch(remote_ui_t* ui) { ret_t remote_ui_destroy(remote_ui_t* ui) { return_value_if_fail(ui != NULL, RET_BAD_PARAMS); - TK_OBJECT_UNREF(ui->io); TK_OBJECT_UNREF(ui->event_handlers); - wbuffer_deinit(&(ui->wb)); + tk_client_deinit(&(ui->client)); TKMEM_FREE(ui); return RET_OK; diff --git a/project/gui/awtk/src/remote_ui/client/remote_ui.h b/project/gui/awtk/src/remote_ui/client/remote_ui.h index 81b925197..017878693 100644 --- a/project/gui/awtk/src/remote_ui/client/remote_ui.h +++ b/project/gui/awtk/src/remote_ui/client/remote_ui.h @@ -27,6 +27,7 @@ #include "tkc/iostream.h" #include "ubjson/ubjson_writer.h" #include "remote_ui/shared/remote_ui_types_def.h" +#include "service/client.h" BEGIN_C_DECLS @@ -35,15 +36,9 @@ BEGIN_C_DECLS * remote ui客户端。 */ typedef struct _remote_ui_t { - /** - * @property {tk_iostream_t*} io - * @annotation ["readable"] - * IO stream。 - */ - tk_iostream_t* io; + tk_client_t client; /*private*/ - wbuffer_t wb; ubjson_writer_t writer; tk_object_t* event_handlers; } remote_ui_t; @@ -94,6 +89,7 @@ ret_t remote_ui_get_dev_info(remote_ui_t* ui, remote_ui_dev_info_t* info); /** * @method remote_ui_reboot * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {remote_ui_reboot_type_t} type 重启类型。 * 重新启动。 * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ @@ -157,22 +153,23 @@ ret_t remote_ui_remove_dir(remote_ui_t* ui, const char* remote_dir); ret_t remote_ui_remove_file(remote_ui_t* ui, const char* remote_file); /** - * @method remote_ui_take_screen_shot + * @method remote_ui_take_snapshot * 截屏。 * * @param {remote_ui_t*} ui remote ui客户端对象。 - * @param {const char*} file 文件名。 + * @param {const char*} target 目标控件或窗口。 + * @param {const char*} file 保存截图文件名。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ -ret_t remote_ui_take_screen_shot(remote_ui_t* ui, const char* file); +ret_t remote_ui_take_snapshot(remote_ui_t* ui, const char* target, const char* file); /** * @method remote_ui_get_manifest * 获取manifest。 * * @param {remote_ui_t*} ui remote ui客户端对象。 - * @param {const char*} file 文件名。 + * @param {const char*} file 保存数据的文件名。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ @@ -183,11 +180,12 @@ ret_t remote_ui_get_manifest(remote_ui_t* ui, const char* file); * 获取当前窗口的XML源码。 * * @param {remote_ui_t*} ui remote ui客户端对象。 - * @param {const char*} file 文件名。 + * @param {const char*} target 目标控件或窗口。 + * @param {const char*} file 保存数据的文件名。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ -ret_t remote_ui_get_xml_source(remote_ui_t* ui, const char* file); +ret_t remote_ui_get_xml_source(remote_ui_t* ui, const char* target, const char* file); /** * @method remote_ui_on_event @@ -228,6 +226,29 @@ ret_t remote_ui_off_event(remote_ui_t* ui, const char* target, uint32_t event); */ ret_t remote_ui_send_event(remote_ui_t* ui, const char* target, event_t* event); +/** + * @method remote_ui_click + * 点击指定的控件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_click(remote_ui_t* ui, const char* target); + +/** + * @method remote_ui_key + * 发送按键给控件。 + * + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {int32_t} key_code 按键代码。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_key(remote_ui_t* ui, const char* target, int32_t key_code); + /** * @method remote_ui_open_window * 打开窗口。 @@ -370,9 +391,68 @@ ret_t remote_ui_set_language(remote_ui_t* ui, const char* language); * @param {const char*} script 脚本。 * @param {str_t*} result 执行结果。 * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 -*/ + */ ret_t remote_ui_exec_fscript(remote_ui_t* ui, const char* script, str_t* result); +/** + * @method remote_ui_move_widget + * 移动控件。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {int32_t} x x坐标。 + * @param {int32_t} y y坐标。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_move_widget(remote_ui_t* ui, const char* target, int32_t x, int32_t y); + +/** + * @method remote_ui_resize_widget + * 调整控件大小。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {uint32_t} w 宽度。 + * @param {uint32_t} h 高度。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_resize_widget(remote_ui_t* ui, const char* target, uint32_t w, uint32_t h); + +/** + * @method remote_ui_destroy_widget + * 销毁控件。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_destroy_widget(remote_ui_t* ui, const char* target); + +/** + * @method remote_ui_create_widget + * 创建控件。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} target 目标。 + * @param {const char*} xml XML。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_create_widget(remote_ui_t* ui, const char* target, const char* xml); + +/** + * @method remote_ui_get_loaded_images_info + * 获取已经加载的图片。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} file 保存数据的文件名。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t remote_ui_get_loaded_images_info(remote_ui_t* ui, const char* file); + +/** + * @method remote_ui_get_loaded_assets_info + * 获取已经加载的资源。 + * @param {remote_ui_t*} ui remote ui客户端对象。 + * @param {const char*} file 保存数据的文件名。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 +*/ +ret_t remote_ui_get_loaded_assets_info(remote_ui_t* ui, const char* file); + /** * @method remote_ui_dispatch * 分发事件。 diff --git a/project/gui/awtk/src/remote_ui/service/remote_ui_service.c b/project/gui/awtk/src/remote_ui/service/remote_ui_service.c index 1d22deff8..1f2eb92c5 100644 --- a/project/gui/awtk/src/remote_ui/service/remote_ui_service.c +++ b/project/gui/awtk/src/remote_ui/service/remote_ui_service.c @@ -31,6 +31,7 @@ #include "base/ui_loader.h" #include "base/dialog.h" #include "conf_io/conf_ubjson.h" +#include "conf_io/conf_json.h" #include "tkc/object_default.h" #include "ui_loader/ui_serializer.h" #include "remote_ui/shared/remote_ui_types_def.h" @@ -41,6 +42,7 @@ static ret_t remote_ui_service_dispatch(remote_ui_service_t* ui); static ret_t remote_ui_service_destroy(remote_ui_service_t* ui); +static ret_t remote_ui_service_set_language(remote_ui_service_t* ui, const char* language); tk_service_t* remote_ui_service_create(tk_iostream_t* io, void* args) { remote_ui_service_t* ui = NULL; @@ -51,11 +53,10 @@ tk_service_t* remote_ui_service_create(tk_iostream_t* io, void* args) { ui = (remote_ui_service_t*)TKMEM_ZALLOC(remote_ui_service_t); return_value_if_fail(ui != NULL, NULL); - ui->io = io; + tk_service_init(&(ui->service), io); ui->service.dispatch = (tk_service_dispatch_t)remote_ui_service_dispatch; ui->service.destroy = (tk_service_destroy_t)remote_ui_service_destroy; - wbuffer_init_extendable(&(ui->wb)); if (service_args != NULL && service_args->auth != NULL) { ui->auth = service_args->auth; } @@ -63,90 +64,17 @@ tk_service_t* remote_ui_service_create(tk_iostream_t* io, void* args) { return (tk_service_t*)ui; } -static ret_t remote_ui_service_send_resp(tk_iostream_t* io, uint32_t type, uint32_t data_type, - uint32_t resp_code, wbuffer_t* wb) { - int32_t ret = 0; - uint32_t size = 0; - const void* data = NULL; - remote_ui_msg_header_t header; - uint16_t crc_value = PPPINITFCS16; - uint32_t timeout = TK_OSTREAM_DEFAULT_TIMEOUT; - - memset(&header, 0x00, sizeof(header)); - return_value_if_fail(io != NULL && wb != NULL, RET_BAD_PARAMS); - - data = wb->data; - size = wb->cursor; - if (size > 0) { - return_value_if_fail(data != NULL, RET_BAD_PARAMS); - } - - header.type = type; - header.size = size; - header.data_type = data_type; - header.resp_code = resp_code; - - crc_value = tk_crc16(crc_value, &header, sizeof(header)); - if (data != NULL && size > 0) { - crc_value = tk_crc16(crc_value, data, size); - } - - ret = tk_iostream_write_len(io, &header, sizeof(header), timeout); - return_value_if_fail(ret == sizeof(header), RET_IO); - - if (size > 0) { - timeout = TK_OSTREAM_DEFAULT_TIMEOUT * (size / 10240) + TK_OSTREAM_DEFAULT_TIMEOUT; - ret = tk_iostream_write_len(io, data, size, timeout); - return_value_if_fail(ret == size, RET_IO); - } - - ret = tk_iostream_write_len(io, &crc_value, sizeof(crc_value), TK_OSTREAM_DEFAULT_TIMEOUT); - return_value_if_fail(ret == sizeof(crc_value), RET_IO); - - return RET_OK; -} - -static ret_t remote_ui_service_read_req(tk_iostream_t* io, remote_ui_msg_header_t* header, - wbuffer_t* wb) { - int32_t ret = 0; - uint16_t crc_value = 0; - uint16_t real_crc_value = PPPINITFCS16; - return_value_if_fail(io != NULL && header != NULL && wb != NULL, RET_BAD_PARAMS); - - wbuffer_rewind(wb); - ret = tk_iostream_read_len(io, header, sizeof(*header), TK_ISTREAM_DEFAULT_TIMEOUT); - if (ret == 0) { - return RET_IO; - } - return_value_if_fail(ret == sizeof(*header), RET_IO); - - real_crc_value = tk_crc16(real_crc_value, header, sizeof(*header)); - if (header->size > 0) { - return_value_if_fail(wbuffer_extend_capacity(wb, header->size) == RET_OK, RET_OOM); - ret = tk_iostream_read_len(io, wb->data, header->size, TK_ISTREAM_DEFAULT_TIMEOUT); - return_value_if_fail(ret == header->size, RET_IO); - real_crc_value = tk_crc16(real_crc_value, wb->data, header->size); - } - - ret = tk_iostream_read_len(io, &crc_value, sizeof(crc_value), TK_ISTREAM_DEFAULT_TIMEOUT); - return_value_if_fail(ret == sizeof(crc_value), RET_IO); - return_value_if_fail(crc_value == real_crc_value, RET_CRC); - - wb->cursor = header->size; - - return header->resp_code; -} - static ubjson_writer_t* remote_ui_service_get_writer(remote_ui_service_t* ui) { - wbuffer_t* wb = &(ui->wb); + wbuffer_t* wb = &(ui->service.wb); ubjson_writer_t* writer = &(ui->writer); wb->cursor = 0; return ubjson_writer_init(writer, (ubjson_write_callback_t)wbuffer_write_binary, wb); } -static ret_t remote_ui_service_login(remote_ui_service_t* ui, const char* username, const char* password) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); +static ret_t remote_ui_service_login(remote_ui_service_t* ui, const char* username, + const char* password) { + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); if (ui->auth != NULL) { if (ui->auth((tk_service_t*)ui, username, password) == RET_OK) { ui->is_login = TRUE; @@ -160,7 +88,7 @@ static ret_t remote_ui_service_login(remote_ui_service_t* ui, const char* userna } static ret_t remote_ui_service_logout(remote_ui_service_t* ui) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); ui->is_login = FALSE; @@ -170,7 +98,7 @@ static ret_t remote_ui_service_logout(remote_ui_service_t* ui) { static ret_t remote_ui_service_get_dev_info(remote_ui_service_t* ui, remote_ui_dev_info_t* info) { ret_t ret = RET_OK; widget_t* wm = window_manager(); - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(info != NULL, RET_BAD_PARAMS); memset(info, 0x00, sizeof(*info)); @@ -180,112 +108,28 @@ static ret_t remote_ui_service_get_dev_info(remote_ui_service_t* ui, remote_ui_d return ret; } -static ret_t remote_ui_service_reboot(remote_ui_service_t* ui, remote_ui_reboot_type_t reboot_type) { +static ret_t remote_ui_service_reboot(remote_ui_service_t* ui, + remote_ui_reboot_type_t reboot_type) { /*TODO*/ return RET_OK; } -static ret_t remote_ui_service_upload_file(remote_ui_service_t* ui, const char* filename) { - int32_t len = 0; - ret_t ret = RET_OK; - fs_file_t* file = NULL; - wbuffer_t* wb = NULL; - remote_ui_msg_header_t header; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(filename != NULL, RET_BAD_PARAMS); - - wb = &(ui->wb); - wbuffer_rewind(wb); - file = fs_open_file(os_fs(), filename, "wb+"); - if (file != NULL) { - remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_NONE, - RET_OK, wb); - } else { - remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_BEGIN, REMOTE_UI_DATA_TYPE_NONE, - RET_FAIL, wb); - } - return_value_if_fail(file != NULL, RET_BAD_PARAMS); - - memset(&header, 0x00, sizeof(header)); - while ((ret = remote_ui_service_read_req(ui->io, &header, wb)) == RET_OK) { - if (header.type == REMOTE_UI_REQ_UPLOAD_FILE_DATA) { - len = fs_file_write(file, wb->data, wb->cursor); - ret = (len == wb->cursor) ? RET_OK : RET_FAIL; - remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_DATA, REMOTE_UI_DATA_TYPE_NONE, - ret, wb); - break_if_fail(ret == RET_OK); - } else if (header.type == REMOTE_UI_REQ_UPLOAD_FILE_END) { - ret = RET_OK; - ret = remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_END, - REMOTE_UI_DATA_TYPE_NONE, ret, wb); - break_if_fail(ret == RET_OK); - break; - } else { - assert(!"impossible"); - ret = RET_FAIL; - remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_UPLOAD_FILE_END, REMOTE_UI_DATA_TYPE_NONE, - ret, wb); - break; - } - } - fs_file_close(file); - - return RET_OK; -} - -static ret_t remote_ui_service_download_file(remote_ui_service_t* ui, const char* filename) { - wbuffer_t wb; - int32_t len = 0; - ret_t ret = RET_OK; - fs_file_t* file = NULL; - uint8_t buff[4096] = {0}; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); - return_value_if_fail(filename != NULL, RET_BAD_PARAMS); - - wbuffer_init(&wb, buff, sizeof(buff)); - file = fs_open_file(os_fs(), filename, "rb"); - if (file != NULL) { - remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN, - REMOTE_UI_DATA_TYPE_NONE, RET_OK, &wb); - } else { - remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN, - REMOTE_UI_DATA_TYPE_NONE, RET_FAIL, &wb); - } - return_value_if_fail(file != NULL, RET_BAD_PARAMS); - - while ((len = fs_file_read(file, buff, sizeof(buff))) > 0) { - wbuffer_init(&wb, buff, len); - wb.cursor = len; - ret = remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_DATA, - REMOTE_UI_DATA_TYPE_BINARY, RET_OK, &wb); - break_if_fail(ret == RET_OK); - } - - wbuffer_rewind(&wb); - ret = remote_ui_service_send_resp(ui->io, REMOTE_UI_RESP_DOWNLOAD_FILE_END, - REMOTE_UI_DATA_TYPE_NONE, ret, &wb); - - fs_file_close(file); - - return RET_OK; -} - static ret_t remote_ui_service_create_dir(remote_ui_service_t* ui, const char* path) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(path != NULL, RET_BAD_PARAMS); return fs_create_dir_r(os_fs(), path); } static ret_t remote_ui_service_remove_dir(remote_ui_service_t* ui, const char* path) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(path != NULL, RET_BAD_PARAMS); return fs_remove_dir_r(os_fs(), path); } static ret_t remote_ui_service_remove_file(remote_ui_service_t* ui, const char* filename) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(filename != NULL, RET_BAD_PARAMS); return fs_remove_file(os_fs(), filename); @@ -293,7 +137,7 @@ static ret_t remote_ui_service_remove_file(remote_ui_service_t* ui, const char* static ret_t remote_ui_service_get_manifest(remote_ui_service_t* ui, str_t* result) { ret_t ret = RET_OK; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(result != NULL, RET_BAD_PARAMS); str_set(result, "todo"); @@ -302,13 +146,35 @@ static ret_t remote_ui_service_get_manifest(remote_ui_service_t* ui, str_t* resu return ret; } -static ret_t remote_ui_service_take_screen_shot(remote_ui_service_t* ui, const char* filename) { +static widget_t* remote_ui_service_get_app_window(widget_t* widget) { + return_value_if_fail(widget != NULL, NULL); + + WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i) + if (iter->visible && !widget_is_keyboard(iter) && !widget_is_always_on_top(iter)) { + return iter; + } + WIDGET_FOR_EACH_CHILD_END(); + + return NULL; +} + +static widget_t* remote_ui_service_get_target_widget(remote_ui_service_t* ui, const char* target) { + widget_t* win = remote_ui_service_get_app_window(window_manager()); + widget_t* widget = TK_STR_IS_EMPTY(target) ? win : widget_find_by_path(win, target, TRUE); + + return widget; +} + +static ret_t remote_ui_service_take_snapshot(remote_ui_service_t* ui, const char* target, const char* filename) { ret_t ret = RET_OK; bitmap_t* image = NULL; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); - image = widget_take_snapshot(window_manager()); + image = widget_take_snapshot(widget); if (image != NULL) { ret = bitmap_save_png(image, filename); } else { @@ -319,21 +185,43 @@ static ret_t remote_ui_service_take_screen_shot(remote_ui_service_t* ui, const c } static ret_t remote_ui_service_prepare_manifest(remote_ui_service_t* ui, const char* filename) { - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(filename != NULL, RET_BAD_PARAMS); return file_write(filename, "TODO", 5); } -static ret_t remote_ui_service_prepare_xml_source(remote_ui_service_t* ui, const char* filename) { +static ret_t remote_ui_service_prepare_loaded_images_info(remote_ui_service_t* ui, const char* target, const char* filename) { + str_t result; + str_init(&result, 10000); + image_manager_dump(image_manager(), &result); + file_write(filename, result.str, result.size); + str_reset(&result); + + return RET_OK; +} + +static ret_t remote_ui_service_prepare_loaded_assets_info(remote_ui_service_t* ui, const char* target, const char* filename) { + str_t result; + str_init(&result, 10000); + assets_manager_dump(assets_manager(), &result); + file_write(filename, result.str, result.size); + str_reset(&result); + + return RET_OK; +} + +static ret_t remote_ui_service_prepare_xml_source(remote_ui_service_t* ui, const char* target, const char* filename) { str_t str; ret_t ret = RET_OK; - widget_t* win = window_manager_get_top_window(window_manager()); - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); str_init(&str, 10000); - widget_to_xml(win, &str); + widget_to_xml(widget, &str); ret = file_write(filename, str.str, str.size); str_reset(&str); @@ -347,10 +235,9 @@ static ret_t remote_ui_service_on_event_func(void* ctx, event_t* e) { return RET_OK; } - -static ret_t remote_ui_service_on_event(remote_ui_service_t* ui, const char* target, uint32_t event) { - widget_t* win = window_manager_get_top_window(window_manager()); - widget_t* widget = widget_find_by_path(win, target, TRUE); +static ret_t remote_ui_service_on_event(remote_ui_service_t* ui, const char* target, + uint32_t event) { + widget_t* widget = remote_ui_service_get_target_widget(ui, target); return_value_if_fail(widget != NULL, RET_BAD_PARAMS); widget_on(widget, event, remote_ui_service_on_event_func, ui); @@ -358,9 +245,9 @@ static ret_t remote_ui_service_on_event(remote_ui_service_t* ui, const char* tar return RET_OK; } -static ret_t remote_ui_service_off_event(remote_ui_service_t* ui, const char* target, uint32_t event) { - widget_t* win = window_manager_get_top_window(window_manager()); - widget_t* widget = widget_find_by_path(win, target, TRUE); +static ret_t remote_ui_service_off_event(remote_ui_service_t* ui, const char* target, + uint32_t event) { + widget_t* widget = remote_ui_service_get_target_widget(ui, target); return_value_if_fail(widget != NULL, RET_BAD_PARAMS); widget_off_by_func(widget, event, remote_ui_service_on_event_func, ui); @@ -368,18 +255,19 @@ static ret_t remote_ui_service_off_event(remote_ui_service_t* ui, const char* ta return RET_OK; } -static ret_t remote_ui_service_send_event(remote_ui_service_t* ui, const char* target, event_t* event) { - widget_t* win = window_manager_get_top_window(window_manager()); - widget_t* widget = widget_find_by_path(win, target, TRUE); +static ret_t remote_ui_service_send_event(remote_ui_service_t* ui, const char* target, + event_t* event) { + widget_t* widget = remote_ui_service_get_target_widget(ui, target); return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + event->target = widget; + if (!widget_is_window_manager(widget) && !widget_is_window(widget)) { + return widget_dispatch_async(widget, event); + } + switch (event->type) { case EVT_CLICK: { - event->type = EVT_POINTER_DOWN; - widget_on_pointer_down(widget, pointer_event_cast(event)); - - event->type = EVT_POINTER_UP; - return widget_on_pointer_up(widget, pointer_event_cast(event)); + return widget_dispatch_async(widget, event); } case EVT_POINTER_DOWN: { return widget_on_pointer_down(widget, pointer_event_cast(event)); @@ -404,8 +292,9 @@ static ret_t remote_ui_service_send_event(remote_ui_service_t* ui, const char* t return RET_FAIL; } -static ret_t remote_ui_service_open_dialog(remote_ui_service_t* ui, const char* type, const char* title, - const char* content, uint32_t duration) { +static ret_t remote_ui_service_open_dialog(remote_ui_service_t* ui, const char* type, + const char* title, const char* content, + uint32_t duration) { if (tk_str_eq(type, REMOTE_UI_DIALOG_TYPE_CONFIRM)) { return dialog_confirm(title, content); } else if (tk_str_eq(type, REMOTE_UI_DIALOG_TYPE_INFO)) { @@ -419,8 +308,37 @@ static ret_t remote_ui_service_open_dialog(remote_ui_service_t* ui, const char* return RET_OK; } -static ret_t remote_ui_service_open_window(remote_ui_service_t* ui, const char* name, const char* xml, - const char* init_json) { +static ret_t widget_init_with_conf(widget_t* win, const char* widget_name, conf_node_t* node) { + char buff[64] = {0}; + conf_node_t* iter = conf_node_get_first_child(node); + widget_t* widget = widget_find_by_path(win, widget_name, TRUE); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); + + while (iter != NULL) { + value_t v; + const char* name = conf_node_get_name(iter); + if (conf_node_get_value(iter, &v) == RET_OK) { + log_debug("%s.%s=%s\n", widget_name, name, value_str_ex(&v, buff, sizeof(buff))); + widget_set_prop(widget, name, &v); + } + + iter = iter->next; + } + return RET_OK; +} + +static ret_t window_init_with_conf(widget_t* widget, conf_doc_t* doc) { + conf_node_t* iter = conf_node_get_first_child(doc->root); + while (iter != NULL) { + const char* name = conf_node_get_name(iter); + widget_init_with_conf(widget, name, iter); + iter = iter->next; + } + return RET_OK; +} + +static ret_t remote_ui_service_open_window(remote_ui_service_t* ui, const char* name, + const char* xml, const char* init_json) { widget_t* win = NULL; if (TK_STR_IS_NOT_EMPTY(xml)) { win = ui_loader_load_widget_from_xml(NULL, xml, strlen(xml)); @@ -431,7 +349,11 @@ static ret_t remote_ui_service_open_window(remote_ui_service_t* ui, const char* return_value_if_fail(win != NULL, RET_BAD_PARAMS); if (init_json != NULL) { - /*TODO*/ + conf_doc_t* doc = conf_doc_load_json(init_json, strlen(init_json)); + if (doc != NULL) { + window_init_with_conf(win, doc); + conf_doc_destroy(doc); + } } return RET_OK; @@ -456,32 +378,63 @@ static ret_t remote_ui_service_back_to_home(remote_ui_service_t* ui) { return RET_OK; } -static ret_t remote_ui_service_set_prop(remote_ui_service_t* ui, const char* target, const char* name, - const value_t* value) { - widget_t* win = window_manager_get_top_window(window_manager()); - widget_t* widget = widget_find_by_path(win, target, TRUE); - return_value_if_fail(widget != NULL, RET_BAD_PARAMS); +static ret_t remote_ui_service_set_prop(remote_ui_service_t* ui, const char* target, + const char* name, const value_t* value) { + if (tk_str_eq(target, REMOTE_UI_TARGET_GLOBAL)) { + if (tk_str_eq(name, REMOTE_UI_PROP_THEME)) { + return widget_set_theme(window_manager(), value_str(value)); + } else if (tk_str_eq(name, REMOTE_UI_PROP_LANGUAGE)) { + return remote_ui_service_set_language(ui, value_str(value)); + } else { + return RET_NOT_FOUND; + } + } else { + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); - return widget_set_prop(widget, name, value); + return widget_set_prop(widget, name, value); + } } -static ret_t remote_ui_service_get_prop(remote_ui_service_t* ui, const char* target, const char* name, - value_t* value) { - widget_t* win = window_manager_get_top_window(window_manager()); - widget_t* widget = widget_find_by_path(win, target, TRUE); - return_value_if_fail(widget != NULL, RET_BAD_PARAMS); - - return widget_get_prop(widget, name, value); -} +static ret_t remote_ui_service_get_prop(remote_ui_service_t* ui, const char* target, + const char* name, value_t* value) { + char buff[128] = {0}; + if (tk_str_eq(target, REMOTE_UI_TARGET_GLOBAL)) { + if (tk_str_eq(name, REMOTE_UI_PROP_THEME)) { + value_set_str(value, widget_get_theme_name(window_manager())); + return RET_OK; + } else if (tk_str_eq(name, REMOTE_UI_PROP_LANGUAGE)) { + locale_info_t* info = widget_get_locale_info(window_manager()); + tk_snprintf(buff, sizeof(buff)-1, "%s_%s", info->language, info->country); + value_dup_str(value, buff); + return RET_OK; + } else { + return RET_NOT_FOUND; + } + } else { + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + return_value_if_fail(widget != NULL, RET_BAD_PARAMS); -static ret_t remote_ui_service_set_theme(remote_ui_service_t* ui, const char* theme) { - return widget_set_theme(window_manager(), theme); + return widget_get_prop(widget, name, value); + } } -static ret_t remote_ui_service_exec_script(remote_ui_service_t* ui, const char* script, value_t* v) { +static ret_t remote_ui_service_exec_script(remote_ui_service_t* ui, const char* script, + value_t* v) { + ret_t ret = RET_FAIL; tk_object_t* obj = object_default_create(); - ret_t ret = fscript_eval(obj, script, v); - TK_OBJECT_UNREF(obj); + + if (obj != NULL) { + widget_t* wm = window_manager(); + widget_t* win = remote_ui_service_get_app_window(wm); + + tk_object_set_prop_pointer(obj, STR_PROP_SELF, win); + tk_object_set_prop_pointer(obj, STR_PROP_WINDOW, win); + tk_object_set_prop_pointer(obj, STR_PROP_WINDOW_MANAGER, wm); + + ret = fscript_eval(obj, script, v); + TK_OBJECT_UNREF(obj); + } return ret; } @@ -490,7 +443,7 @@ static ret_t remote_ui_service_set_language(remote_ui_service_t* ui, const char* const char* p = NULL; char lang[TK_NAME_LEN + 1] = {0}; char country[TK_NAME_LEN + 1] = {0}; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(language != NULL, RET_BAD_PARAMS); p = strchr(language, '_'); @@ -520,162 +473,160 @@ static ret_t remote_ui_dev_info_write(ubjson_writer_t* writer, remote_ui_dev_inf return RET_OK; } -static ret_t remote_ui_service_dispatch_impl(remote_ui_service_t* ui, remote_ui_msg_header_t* req, +static ret_t remote_ui_service_dispatch_impl(remote_ui_service_t* ui, tk_msg_header_t* req, wbuffer_t* wb) { value_t v; char buff[1024] = {0}; - ret_t ret = RET_FAIL; tk_object_t* obj = NULL; ubjson_writer_t* writer = NULL; - remote_ui_msg_header_t resp; + tk_msg_header_t resp; char local_file[MAX_PATH + 1] = {0}; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); return_value_if_fail(req != NULL && wb != NULL, RET_BAD_PARAMS); memset(&resp, 0x00, sizeof(resp)); - if (req->data_type == REMOTE_UI_DATA_TYPE_UBJSON) { + if (req->data_type == MSG_DATA_TYPE_UBJSON) { obj = conf_ubjson_load_from_buff(wb->data, wb->cursor, FALSE); } + resp.type = req->type; switch (req->type) { - case REMOTE_UI_REQ_LOGIN: { + case MSG_CODE_LOGIN: { const char* username = tk_object_get_prop_str(obj, "username"); const char* password = tk_object_get_prop_str(obj, "password"); resp.resp_code = remote_ui_service_login(ui, username, password); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_LOGIN; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_LOGOUT: { + case MSG_CODE_LOGOUT: { resp.resp_code = remote_ui_service_logout(ui); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_LOGOUT; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_GET_DEV_INFO: { + case REMOTE_UI_GET_DEV_INFO: { remote_ui_dev_info_t info; memset(&info, 0x00, sizeof(info)); resp.resp_code = remote_ui_service_get_dev_info(ui, &info); - resp.data_type = REMOTE_UI_DATA_TYPE_UBJSON; - resp.type = REMOTE_UI_RESP_GET_DEV_INFO; + resp.data_type = MSG_DATA_TYPE_UBJSON; writer = remote_ui_service_get_writer(ui); - ret = remote_ui_dev_info_write(writer, &info); + remote_ui_dev_info_write(writer, &info); break; } - case REMOTE_UI_REQ_REBOOT: { + case REMOTE_UI_REBOOT: { rbuffer_t rb; uint32_t reboot_type = REMOTE_UI_REBOOT_DEFAULT; rbuffer_init(&rb, wb->data, wb->cursor); - + rbuffer_read_uint32(&rb, &reboot_type); resp.resp_code = remote_ui_service_reboot(ui, reboot_type); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_REBOOT; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_UPLOAD_FILE_BEGIN: { + case MSG_CODE_UPLOAD_FILE_BEGIN: { const char* filename = (const char*)(wb->data); filename = path_prepend_app_root(local_file, filename); - resp.resp_code = remote_ui_service_upload_file(ui, filename); + resp.resp_code = tk_service_upload_file(&(ui->service), filename); return RET_OK; break; } - case REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN: { + case MSG_CODE_DOWNLOAD_FILE_BEGIN: { const char* filename = (const char*)(wb->data); - if (tk_str_eq(filename, REMOTE_UI_FILE_SCREEN_SHOT)) { - filename = path_prepend_temp_path(local_file, filename); - resp.resp_code = remote_ui_service_take_screen_shot(ui, filename); + if (tk_str_start_with(filename, REMOTE_UI_FILE_SNAPSHOT)) { + const char* target = filename + strlen(REMOTE_UI_FILE_SNAPSHOT) + 1; + filename = path_prepend_temp_path(local_file, "shot.png"); + resp.resp_code = remote_ui_service_take_snapshot(ui, target, filename); } else if (tk_str_eq(filename, REMOTE_UI_FILE_MANIFEST)) { - filename = path_prepend_temp_path(local_file, filename); + filename = path_prepend_temp_path(local_file, "manifest.txt"); resp.resp_code = remote_ui_service_prepare_manifest(ui, filename); - } else if (tk_str_eq(filename, REMOTE_UI_FILE_XML_SOURCE)) { - filename = path_prepend_temp_path(local_file, filename); - resp.resp_code = remote_ui_service_prepare_xml_source(ui, filename); + } else if (tk_str_start_with(filename, REMOTE_UI_FILE_XML_SOURCE)) { + const char* target = filename + strlen(REMOTE_UI_FILE_XML_SOURCE) + 1; + filename = path_prepend_temp_path(local_file, "source.xml"); + resp.resp_code = remote_ui_service_prepare_xml_source(ui, target, filename); + } else if (tk_str_start_with(filename, REMOTE_UI_FILE_LOADED_IMAGES_INFO)) { + const char* target = filename + strlen(REMOTE_UI_FILE_LOADED_IMAGES_INFO) + 1; + filename = path_prepend_temp_path(local_file, "loaded_images_info.txt"); + resp.resp_code = remote_ui_service_prepare_loaded_images_info(ui, target, filename); + } else if (tk_str_start_with(filename, REMOTE_UI_FILE_LOADED_ASSETS_INFO)) { + const char* target = filename + strlen(REMOTE_UI_FILE_LOADED_ASSETS_INFO) + 1; + filename = path_prepend_temp_path(local_file, "loaded_images_info.txt"); + resp.resp_code = remote_ui_service_prepare_loaded_assets_info(ui, target, filename); } else { filename = path_prepend_app_root(local_file, filename); } - resp.resp_code = remote_ui_service_download_file(ui, filename); + resp.resp_code = tk_service_download_file(&(ui->service), filename); return RET_OK; } - case REMOTE_UI_REQ_CREATE_DIR: { + case REMOTE_UI_CREATE_DIR: { const char* filename = (const char*)(wb->data); filename = path_prepend_app_root(local_file, filename); resp.resp_code = remote_ui_service_create_dir(ui, filename); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_CREATE_DIR; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_REMOVE_DIR: { + case REMOTE_UI_REMOVE_DIR: { const char* filename = (const char*)(wb->data); filename = path_prepend_app_root(local_file, filename); resp.resp_code = remote_ui_service_remove_dir(ui, filename); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_REMOVE_DIR; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_REMOVE_FILE: { + case REMOTE_UI_REMOVE_FILE: { const char* filename = (const char*)(wb->data); filename = path_prepend_app_root(local_file, filename); resp.resp_code = remote_ui_service_remove_file(ui, filename); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_REMOVE_FILE; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_OPEN_WINDOW: { + case REMOTE_UI_OPEN_WINDOW: { const char* name = tk_object_get_prop_str(obj, REMOTE_UI_KEY_NAME); const char* xml = tk_object_get_prop_str(obj, REMOTE_UI_KEY_XML); const char* init_json = tk_object_get_prop_str(obj, REMOTE_UI_KEY_INIT); resp.resp_code = remote_ui_service_open_window(ui, name, xml, init_json); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_OPEN_WINDOW; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_OPEN_DIALOG: { + case REMOTE_UI_OPEN_DIALOG: { const char* type = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TYPE); const char* title = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TITLE); const char* content = tk_object_get_prop_str(obj, REMOTE_UI_KEY_CONTENT); uint32_t duration = tk_object_get_prop_uint32(obj, REMOTE_UI_KEY_DURATION, 3000); resp.resp_code = remote_ui_service_open_dialog(ui, type, title, content, duration); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_OPEN_WINDOW; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_BACK_TO_PREV: { + case REMOTE_UI_BACK_TO_PREV: { resp.resp_code = remote_ui_service_back_to_prev(ui); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_BACK_TO_PREV; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_BACK_TO_HOME: { + case REMOTE_UI_BACK_TO_HOME: { resp.resp_code = remote_ui_service_back_to_home(ui); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_BACK_TO_HOME; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_CLOSE_WINDOW: { + case REMOTE_UI_CLOSE_WINDOW: { const char* name = (const char*)(wb->data); resp.resp_code = remote_ui_service_close_window(ui, name); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_CLOSE_WINDOW; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_SET_PROP: { + case REMOTE_UI_SET_PROP: { value_t v; const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); const char* name = tk_object_get_prop_str(obj, REMOTE_UI_KEY_NAME); @@ -685,70 +636,52 @@ static ret_t remote_ui_service_dispatch_impl(remote_ui_service_t* ui, remote_ui_ } else { resp.resp_code = RET_FAIL; } - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_GET_PROP: { + case REMOTE_UI_GET_PROP: { const char* str = NULL; const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); const char* name = tk_object_get_prop_str(obj, REMOTE_UI_KEY_NAME); value_set_int(&v, 0); resp.resp_code = remote_ui_service_get_prop(ui, target, name, &v); - resp.data_type = REMOTE_UI_DATA_TYPE_STRING; + resp.data_type = MSG_DATA_TYPE_STRING; str = value_str_ex(&v, buff, sizeof(buff)); wbuffer_rewind(wb); wbuffer_write_string(wb, str); + value_reset(&v); break; } - case REMOTE_UI_REQ_SET_LANGUAGE: { - const char* language = (const char*)(wb->data); - resp.resp_code = remote_ui_service_set_language(ui, language); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_SET_LANGUAGE; - wbuffer_rewind(wb); - break; - } - case REMOTE_UI_REQ_SET_THEME: { - const char* theme = (const char*)(wb->data); - resp.resp_code = remote_ui_service_set_theme(ui, theme); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_SET_THEME; - wbuffer_rewind(wb); - break; - } - case REMOTE_UI_REQ_EXEC_FSCRIPT: { + case REMOTE_UI_EXEC_FSCRIPT: { const char* script = (const char*)(wb->data); value_set_int(&v, 0); resp.resp_code = remote_ui_service_exec_script(ui, script, &v); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_EXEC_FSCRIPT; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); wbuffer_write_string(wb, value_str_ex(&v, buff, sizeof(buff))); break; } - case REMOTE_UI_REQ_ON_EVENT: { + case REMOTE_UI_ON_EVENT: { const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); uint32_t event_type = tk_object_get_prop_int(obj, REMOTE_UI_KEY_EVENT, 0); resp.resp_code = remote_ui_service_on_event(ui, target, event_type); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_ON_EVENT; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_OFF_EVENT: { + case REMOTE_UI_OFF_EVENT: { const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); uint32_t event_type = tk_object_get_prop_int(obj, REMOTE_UI_KEY_EVENT, 0); resp.resp_code = remote_ui_service_off_event(ui, target, event_type); - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_OFF_EVENT; + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } - case REMOTE_UI_REQ_SEND_EVENT: { + case REMOTE_UI_SEND_EVENT: { event_t* e = NULL; const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); uint32_t event_type = tk_object_get_prop_int(obj, REMOTE_UI_KEY_EVENT, 0); @@ -779,37 +712,94 @@ static ret_t remote_ui_service_dispatch_impl(remote_ui_service_t* ui, remote_ui_ } else { resp.resp_code = RET_FAIL; } - resp.data_type = REMOTE_UI_DATA_TYPE_NONE; - resp.type = REMOTE_UI_RESP_SEND_EVENT; + resp.data_type = MSG_DATA_TYPE_NONE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_MOVE_WIDGET: { + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + int x = tk_object_get_prop_int(obj, REMOTE_UI_KEY_X, 0); + int y = tk_object_get_prop_int(obj, REMOTE_UI_KEY_Y, 0); + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + if (widget != NULL) { + resp.resp_code = widget_move(widget, x, y); + } else { + resp.resp_code = RET_NOT_FOUND; + } + + resp.data_type = MSG_DATA_TYPE_NONE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_RESIZE_WIDGET: { + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + int w = tk_object_get_prop_int(obj, REMOTE_UI_KEY_W, 0); + int h = tk_object_get_prop_int(obj, REMOTE_UI_KEY_H, 0); + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + if (widget != NULL) { + resp.resp_code = widget_resize(widget, w, h); + } else { + resp.resp_code = RET_NOT_FOUND; + } + resp.data_type = MSG_DATA_TYPE_NONE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_DESTROY_WIDGET: { + const char* target = (const char*)(wb->data); + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + if (widget != NULL) { + resp.resp_code = widget_destroy(widget); + } else { + resp.resp_code = RET_NOT_FOUND; + } + resp.data_type = MSG_DATA_TYPE_NONE; + wbuffer_rewind(wb); + break; + } + case REMOTE_UI_CREATE_WIDGET: { + const char* target = tk_object_get_prop_str(obj, REMOTE_UI_KEY_TARGET); + const char* xml = tk_object_get_prop_str(obj, REMOTE_UI_KEY_XML); + widget_t* widget = remote_ui_service_get_target_widget(ui, target); + if (widget != NULL) { + widget_t* new_widget = ui_loader_load_widget_from_xml(widget, xml, strlen(xml)); + if (new_widget != NULL) { + resp.resp_code = RET_OK; + } else { + resp.resp_code = RET_FAIL; + } + } else { + resp.resp_code = RET_NOT_FOUND; + } + resp.data_type = MSG_DATA_TYPE_NONE; wbuffer_rewind(wb); break; } default: { - ret = RET_NOT_IMPL; + resp.resp_code = RET_NOT_IMPL; break; } } TK_OBJECT_UNREF(obj); - return remote_ui_service_send_resp(ui->io, resp.type, resp.data_type, resp.resp_code, wb); + return tk_service_send_resp(&(ui->service), resp.type, resp.data_type, resp.resp_code, wb); } static ret_t remote_ui_service_dispatch(remote_ui_service_t* ui) { ret_t ret = RET_OK; - remote_ui_msg_header_t header; - return_value_if_fail(ui != NULL && ui->io != NULL, RET_BAD_PARAMS); + tk_msg_header_t header; + return_value_if_fail(ui != NULL && ui->service.io != NULL, RET_BAD_PARAMS); memset(&header, 0x00, sizeof(header)); - ret = remote_ui_service_read_req(ui->io, &header, &(ui->wb)); + ret = tk_service_read_req(&(ui->service), &header, &(ui->service.wb)); return_value_if_fail(ret == RET_OK, ret); - return remote_ui_service_dispatch_impl(ui, &header, &(ui->wb)); + return remote_ui_service_dispatch_impl(ui, &header, &(ui->service.wb)); } static ret_t remote_ui_service_destroy(remote_ui_service_t* ui) { return_value_if_fail(ui != NULL, RET_BAD_PARAMS); - wbuffer_deinit(&(ui->wb)); TKMEM_FREE(ui); return RET_OK; diff --git a/project/gui/awtk/src/remote_ui/service/remote_ui_service.h b/project/gui/awtk/src/remote_ui/service/remote_ui_service.h index 54968c1d5..7ab09f070 100644 --- a/project/gui/awtk/src/remote_ui/service/remote_ui_service.h +++ b/project/gui/awtk/src/remote_ui/service/remote_ui_service.h @@ -51,24 +51,16 @@ typedef struct _remote_ui_service_args_t { typedef struct _remote_ui_service_t { tk_service_t service; - /** - * @property {tk_iostream_t*} io - * @annotation ["readable"] - * IO stream。 - */ - tk_iostream_t* io; - /*private*/ - wbuffer_t wb; + bool_t is_login; ubjson_writer_t writer; tk_object_t* event_handlers; - bool_t is_login; tk_service_auth_t auth; } remote_ui_service_t; /** * @method remote_ui_service_create - * 创建remote ui客户端。 + * 创建remote ui服务端。 * * @param {tk_iostream_t*} io IO流(由service释放)。 * @param {void*} args 参数。 diff --git a/project/gui/awtk/src/remote_ui/shared/remote_ui_types_def.h b/project/gui/awtk/src/remote_ui/shared/remote_ui_types_def.h index aac731f5f..ef5bfa5f6 100644 --- a/project/gui/awtk/src/remote_ui/shared/remote_ui_types_def.h +++ b/project/gui/awtk/src/remote_ui/shared/remote_ui_types_def.h @@ -22,7 +22,7 @@ #ifndef TK_REMOTE_UI_TYPES_DEF_H #define TK_REMOTE_UI_TYPES_DEF_H -#include "tkc/types_def.h" +#include "service/msg_header.h" BEGIN_C_DECLS @@ -34,271 +34,112 @@ BEGIN_C_DECLS */ typedef enum _remote_ui_msg_code_t { /** - * @const REMOTE_UI_MSG_NONE - * 无效消息。 - */ - REMOTE_UI_MSG_NONE = 0, - /** - * @const REMOTE_UI_REQ_LOGIN - * 登录请求。 - */ - REMOTE_UI_REQ_LOGIN, - /** - * @const REMOTE_UI_REQ_LOGOUT - * 登出请求。 - */ - REMOTE_UI_REQ_LOGOUT, - /** - * @const REMOTE_UI_REQ_GET_DEV_INFO + * @const REMOTE_UI_GET_DEV_INFO * 获取设备信息。 */ - REMOTE_UI_REQ_GET_DEV_INFO, + REMOTE_UI_GET_DEV_INFO = MSG_USER_START, /** - * @const REMOTE_UI_REQ_REBOOT + * @const REMOTE_UI_REBOOT * 重新加载请求。 */ - REMOTE_UI_REQ_REBOOT, + REMOTE_UI_REBOOT, /** - * @const REMOTE_UI_REQ_UPLOAD_FILE_BEGIN - * 上传文件请求开始。 - */ - REMOTE_UI_REQ_UPLOAD_FILE_BEGIN, - /** - * @const REMOTE_UI_REQ_UPLOAD_FILE_DATA - * 上传文件请求数据。 - */ - REMOTE_UI_REQ_UPLOAD_FILE_DATA, - /** - * @const REMOTE_UI_REQ_UPLOAD_FILE_END - * 上传文件请求结束。 - */ - REMOTE_UI_REQ_UPLOAD_FILE_END, - /** - * @const REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN - * 下载文件请求。 - */ - REMOTE_UI_REQ_DOWNLOAD_FILE_BEGIN, - /** - * @const REMOTE_UI_REQ_CREATE_DIR + * @const REMOTE_UI_CREATE_DIR * 创建目录请求。 */ - REMOTE_UI_REQ_CREATE_DIR, + REMOTE_UI_CREATE_DIR, /** - * @const REMOTE_UI_REQ_REMOVE_DIR + * @const REMOTE_UI_REMOVE_DIR * 删除目录请求。 */ - REMOTE_UI_REQ_REMOVE_DIR, + REMOTE_UI_REMOVE_DIR, /** - * @const REMOTE_UI_REQ_REMOVE_FILE + * @const REMOTE_UI_REMOVE_FILE * 删除文件请求。 */ - REMOTE_UI_REQ_REMOVE_FILE, + REMOTE_UI_REMOVE_FILE, /** - * @const REMOTE_UI_REQ_ON_EVENT + * @const REMOTE_UI_ON_EVENT * 注册事件请求。 */ - REMOTE_UI_REQ_ON_EVENT, + REMOTE_UI_ON_EVENT, /** - * @const REMOTE_UI_REQ_OFF_EVENT + * @const REMOTE_UI_OFF_EVENT * 注销事件请求。 */ - REMOTE_UI_REQ_OFF_EVENT, + REMOTE_UI_OFF_EVENT, /** - * @const REMOTE_UI_REQ_SEND_EVENT + * @const REMOTE_UI_SEND_EVENT * 发送事件请求。 */ - REMOTE_UI_REQ_SEND_EVENT, + REMOTE_UI_SEND_EVENT, /** - * @const REMOTE_UI_REQ_OPEN_WINDOW + * @const REMOTE_UI_OPEN_WINDOW * 打开窗口请求。 */ - REMOTE_UI_REQ_OPEN_WINDOW, + REMOTE_UI_OPEN_WINDOW, /** - * @const REMOTE_UI_REQ_OPEN_DIALOG + * @const REMOTE_UI_OPEN_DIALOG * 打开基本对话框请求。 */ - REMOTE_UI_REQ_OPEN_DIALOG, + REMOTE_UI_OPEN_DIALOG, /** - * @const REMOTE_UI_REQ_CLOSE_WINDOW + * @const REMOTE_UI_CLOSE_WINDOW * 关闭窗口请求。 */ - REMOTE_UI_REQ_CLOSE_WINDOW, + REMOTE_UI_CLOSE_WINDOW, /** - * @const REMOTE_UI_REQ_BACK_TO_PREV + * @const REMOTE_UI_BACK_TO_PREV * 返回请求。 */ - REMOTE_UI_REQ_BACK_TO_PREV, + REMOTE_UI_BACK_TO_PREV, /** - * @const REMOTE_UI_REQ_BACK_TO_HOME + * @const REMOTE_UI_BACK_TO_HOME * 返回主页请求。 */ - REMOTE_UI_REQ_BACK_TO_HOME, + REMOTE_UI_BACK_TO_HOME, /** - * @const REMOTE_UI_REQ_SET_PROP + * @const REMOTE_UI_SET_PROP * 设置属性请求。 */ - REMOTE_UI_REQ_SET_PROP, + REMOTE_UI_SET_PROP, /** - * @const REMOTE_UI_REQ_GET_PROP + * @const REMOTE_UI_GET_PROP * 获取属性请求。 */ - REMOTE_UI_REQ_GET_PROP, - /** - * @const REMOTE_UI_REQ_SET_THEME - * 设置主题请求。 - */ - REMOTE_UI_REQ_SET_THEME, + REMOTE_UI_GET_PROP, /** - * @const REMOTE_UI_REQ_SET_LANGUAGE - * 设置语言请求。 - */ - REMOTE_UI_REQ_SET_LANGUAGE, - /** - * @const REMOTE_UI_REQ_GET_XML_SOURCE + * @const REMOTE_UI_GET_XML_SOURCE * 获取xml源码请求。 */ - REMOTE_UI_REQ_GET_XML_SOURCE, + REMOTE_UI_GET_XML_SOURCE, /** - * @const REMOTE_UI_REQ_EXEC_FSCRIPT + * @const REMOTE_UI_EXEC_FSCRIPT * 执行脚本请求。 */ - REMOTE_UI_REQ_EXEC_FSCRIPT, + REMOTE_UI_EXEC_FSCRIPT, /** - * @const REMOTE_UI_RESP_LOGIN - * 登录响应。 - */ - REMOTE_UI_RESP_LOGIN, - /** - * @const REMOTE_UI_RESP_LOGOUT - * 登出响应。 - */ - REMOTE_UI_RESP_LOGOUT, - /** - * @const REMOTE_UI_RESP_GET_DEV_INFO - * 获取设备信息响应。 - */ - REMOTE_UI_RESP_GET_DEV_INFO, - /** - * @const REMOTE_UI_RESP_REBOOT - * 重新加载响应。 - */ - REMOTE_UI_RESP_REBOOT, - /** - * @const REMOTE_UI_RESP_UPLOAD_FILE_BEGIN - * 上传文件开始响应。 - */ - REMOTE_UI_RESP_UPLOAD_FILE_BEGIN, - /** - * @const REMOTE_UI_RESP_UPLOAD_FILE_DATA - * 上传文件数据响应。 - */ - REMOTE_UI_RESP_UPLOAD_FILE_DATA, - /** - * @const REMOTE_UI_RESP_UPLOAD_FILE_END - * 上传文件结束响应。 - */ - REMOTE_UI_RESP_UPLOAD_FILE_END, - /** - * @const REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN - * 下载文件开始响应。 - */ - REMOTE_UI_RESP_DOWNLOAD_FILE_BEGIN, - /** - * @const REMOTE_UI_RESP_DOWNLOAD_FILE_DATA - * 下载文件数据响应。 - */ - REMOTE_UI_RESP_DOWNLOAD_FILE_DATA, - /** - * @const REMOTE_UI_RESP_DOWNLOAD_FILE_END - * 下载文件数据响应。 - */ - REMOTE_UI_RESP_DOWNLOAD_FILE_END, - /** - * @const REMOTE_UI_RESP_CREATE_DIR - * 创建目录响应。 - */ - REMOTE_UI_RESP_CREATE_DIR, - /** - * @const REMOTE_UI_RESP_REMOVE_DIR - * 删除目录响应。 - */ - REMOTE_UI_RESP_REMOVE_DIR, - /** - * @const REMOTE_UI_RESP_REMOVE_FILE - * 删除文件响应。 - */ - REMOTE_UI_RESP_REMOVE_FILE, - /** - * @const REMOTE_UI_RESP_ON_EVENT - * 注册事件响应。 - */ - REMOTE_UI_RESP_ON_EVENT, - /** - * @const REMOTE_UI_RESP_OFF_EVENT - * 注销事件响应。 - */ - REMOTE_UI_RESP_OFF_EVENT, - /** - * @const REMOTE_UI_RESP_SEND_EVENT - * 发送事件响应。 - */ - REMOTE_UI_RESP_SEND_EVENT, - /** - * @const REMOTE_UI_RESP_OPEN_WINDOW - * 打开窗口响应。 - */ - REMOTE_UI_RESP_OPEN_WINDOW, - /** - * @const REMOTE_UI_RESP_OPEN_DIALOG - * 打开基本对话框响应。 - */ - REMOTE_UI_RESP_OPEN_DIALOG, - /** - * @const REMOTE_UI_RESP_CLOSE_WINDOW - * 关闭窗口响应。 - */ - REMOTE_UI_RESP_CLOSE_WINDOW, - /** - * @const REMOTE_UI_RESP_BACK_TO_PREV - * 返回响应。 - */ - REMOTE_UI_RESP_BACK_TO_PREV, - /** - * @const REMOTE_UI_RESP_BACK_TO_HOME - * 返回主页响应。 - */ - REMOTE_UI_RESP_BACK_TO_HOME, - /** - * @const REMOTE_UI_RESP_SET_PROP - * 设置属性响应。 - */ - REMOTE_UI_RESP_SET_PROP, - /** - * @const REMOTE_UI_RESP_GET_PROP - * 获取属性响应。 - */ - REMOTE_UI_RESP_GET_PROP, - /** - * @const REMOTE_UI_RESP_SET_THEME - * 设置主题响应。 - */ - REMOTE_UI_RESP_SET_THEME, + * @const REMOTE_UI_MOVE_WIDGET + * 移动控件请求。 + */ + REMOTE_UI_MOVE_WIDGET, /** - * @const REMOTE_UI_RESP_SET_LANGUAGE - * 设置语言响应。 - */ - REMOTE_UI_RESP_SET_LANGUAGE, + * @const REMOTE_UI_RESIZE_WIDGET + * 调整控件大小请求。 + */ + REMOTE_UI_RESIZE_WIDGET, /** - * @const REMOTE_UI_RESP_GET_XML_SOURCE - * 获取xml源码响应。 - */ - REMOTE_UI_RESP_GET_XML_SOURCE, + * @const REMOTE_UI_CREATE_WIDGET + * 创建控件请求。 + */ + REMOTE_UI_CREATE_WIDGET, /** - * @const REMOTE_UI_RESP_EXEC_FSCRIPT - * 执行脚本响应。 - */ - REMOTE_UI_RESP_EXEC_FSCRIPT, + * @const REMOTE_UI_DESTROY_WIDGET + * 销毁控件请求。 + */ + REMOTE_UI_DESTROY_WIDGET, + /** * @const REMOTE_UI_NOTIFY * 事件通知。 @@ -306,61 +147,6 @@ typedef enum _remote_ui_msg_code_t { REMOTE_UI_NOTIFY, } remote_ui_msg_code_t; -/** - * @enum remote_ui_data_type_t - * @prefix REMOTE_UI_DATA_TYPE_ - * 数据类型。 -*/ -typedef enum _remote_ui_data_type_t { - /** - * @const REMOTE_UI_DATA_TYPE_NONE - * 无效数据类型。 - */ - REMOTE_UI_DATA_TYPE_NONE = 0, - /** - * @const REMOTE_UI_DATA_TYPE_UBJSON - * JSON数据类型。 - */ - REMOTE_UI_DATA_TYPE_UBJSON, - /** - * @const REMOTE_UI_DATA_TYPE_STRING - * 字符串数据类型。 - */ - REMOTE_UI_DATA_TYPE_STRING, - /** - * @const REMOTE_UI_DATA_TYPE_BINARY - * 二进制数据类型。 - */ - REMOTE_UI_DATA_TYPE_BINARY -} remote_ui_data_type_t; - -/** - * @class remote_ui_msg_header_t - * 消息头。 -*/ -typedef struct _remote_ui_msg_header_t { - /** - * @property {uint32_t} size - * 消息体的大小。 - */ - uint32_t size; - /** - * @property {uint16_t} type - * 消息类型。 - */ - uint16_t type; - /** - * @property {uint8_t} data_type - * 数据类型。 - */ - uint8_t data_type; - /** - * @property {uint8_t} resp_code - * 响应码(仅适用于resp)。 - */ - uint8_t resp_code; -} remote_ui_msg_header_t; - /** * @class remote_ui_dev_info_t * 设备信息。 @@ -446,6 +232,8 @@ typedef enum _remote_ui_reboot_type_t { #define REMOTE_UI_KEY_INIT "init" #define REMOTE_UI_KEY_X "x" #define REMOTE_UI_KEY_Y "y" +#define REMOTE_UI_KEY_W "w" +#define REMOTE_UI_KEY_H "h" #define REMOTE_UI_KEY_CODE "code" #define REMOTE_UI_KEY_TYPE "type" #define REMOTE_UI_KEY_TITLE "title" @@ -453,13 +241,24 @@ typedef enum _remote_ui_reboot_type_t { #define REMOTE_UI_KEY_DURATION "duration" #define REMOTE_UI_FILE_MANIFEST "__manifest__.txt" #define REMOTE_UI_FILE_XML_SOURCE "__xml_source__.xml" -#define REMOTE_UI_FILE_SCREEN_SHOT "__screen_shot__.png" +#define REMOTE_UI_FILE_SNAPSHOT "__snapshot__." + +#define REMOTE_UI_FILE_LOADED_IMAGES_INFO "__images_info__" +#define REMOTE_UI_FILE_LOADED_ASSETS_INFO "__assets_info__" #define REMOTE_UI_DIALOG_TYPE_CONFIRM "confirm" #define REMOTE_UI_DIALOG_TYPE_WARN "warn" #define REMOTE_UI_DIALOG_TYPE_INFO "info" #define REMOTE_UI_DIALOG_TYPE_TOAST "toast" +#define REMOTE_UI_TARGET_GLOBAL "global" +#define REMOTE_UI_PROP_THEME "theme" +#define REMOTE_UI_PROP_LANGUAGE "language" + +#ifndef REMOTE_UI_URL +#define REMOTE_UI_URL "tcp://localhost:2233" +#endif/*REMOTE_UI_URL*/ + END_C_DECLS #endif /*TK_REMOTE_UI_TYPES_DEF_H*/ diff --git a/project/gui/awtk/src/service/Makefile b/project/gui/awtk/src/service/Makefile index 2da17fb20..ec9aa47b2 100644 --- a/project/gui/awtk/src/service/Makefile +++ b/project/gui/awtk/src/service/Makefile @@ -1 +1,2 @@ +obj-y += client.o obj-y += service.o diff --git a/project/gui/awtk/src/service/client.c b/project/gui/awtk/src/service/client.c new file mode 100644 index 000000000..e378d1ab9 --- /dev/null +++ b/project/gui/awtk/src/service/client.c @@ -0,0 +1,276 @@ +/** + * File: client.c + * Author: AWTK Develop Team + * Brief: client + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-05 Li XianJing created + * + */ +#include "tkc/url.h" +#include "tkc/crc.h" +#include "tkc/utils.h" +#include "service/client.h" +#include "tkc/event_source_fd.h" +#include "streams/inet/iostream_tcp.h" +#include "streams/serial/iostream_serial.h" + +ret_t tk_client_init(tk_client_t* client, tk_iostream_t* io) { + return_value_if_fail(client != NULL, RET_BAD_PARAMS); + + client->io = io; + wbuffer_init_extendable(&(client->wb)); + wbuffer_extend_capacity(&(client->wb), 1024); + + return RET_OK; +} + +ret_t tk_client_deinit(tk_client_t* client) { + return_value_if_fail(client != NULL, RET_BAD_PARAMS); + + TK_OBJECT_UNREF(client->io); + wbuffer_deinit(&(client->wb)); + + return RET_OK; +} + +static ret_t tk_client_send_req_impl(tk_client_t* client, uint32_t type, uint32_t data_type, + wbuffer_t* wb) { + int32_t ret = 0; + uint32_t size = 0; + const void* data = NULL; + tk_msg_header_t header; + tk_iostream_t* io = NULL; + uint16_t crc_value = PPPINITFCS16; + uint32_t timeout = TK_OSTREAM_DEFAULT_TIMEOUT; + + memset(&header, 0x00, sizeof(header)); + return_value_if_fail(wb != NULL, RET_BAD_PARAMS); + return_value_if_fail(client != NULL && client->io != NULL, RET_BAD_PARAMS); + + io = client->io; + data = wb->data; + size = wb->cursor; + if (size > 0) { + return_value_if_fail(data != NULL, RET_BAD_PARAMS); + } + + header.type = type; + header.size = size; + header.data_type = data_type; + + crc_value = tk_crc16(crc_value, &header, sizeof(header)); + if (data != NULL && size > 0) { + crc_value = tk_crc16(crc_value, data, size); + } + ret = tk_iostream_write_len(io, &header, sizeof(header), timeout); + return_value_if_fail(ret == sizeof(header), RET_IO); + + if (size > 0) { + timeout = TK_OSTREAM_DEFAULT_TIMEOUT * (size / 10240) + TK_OSTREAM_DEFAULT_TIMEOUT; + ret = tk_iostream_write_len(io, data, size, timeout); + return_value_if_fail(ret == size, RET_IO); + } + + ret = tk_iostream_write_len(io, &crc_value, sizeof(crc_value), TK_OSTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + + return RET_OK; +} + +ret_t tk_client_send_req(tk_client_t* client, uint32_t type, uint32_t data_type, wbuffer_t* wb) { + int32_t len = 0; + int32_t retry_times = 0; + tk_msg_header_t header; + + while (retry_times < TK_MAX_RETRY_TIMES) { + ret_t ret = tk_client_send_req_impl(client, type, data_type, wb); + break_if_fail(ret == RET_OK); + + memset(&header, 0x00, sizeof(header)); + len = tk_iostream_read_len(client->io, &header, sizeof(header), TK_ISTREAM_DEFAULT_TIMEOUT); + break_if_fail(len == sizeof(header)); + + if (header.resp_code == RET_OK) { + return RET_OK; + } + + if (header.resp_code == RET_CRC) { + retry_times++; + log_debug("crc error, retry times: %d", retry_times); + continue; + } else { + break; + } + } + + return RET_FAIL; +} + +static ret_t tk_client_confirm_packet(tk_client_t* client, bool_t valid) { + int32_t ret = 0; + tk_msg_header_t header; + + header.size = 0; + header.type = MSG_CODE_CONFIRM; + header.data_type = MSG_DATA_TYPE_NONE; + header.resp_code = valid ? RET_OK : RET_CRC; + + ret = tk_iostream_write_len(client->io, &header, sizeof(header), TK_OSTREAM_DEFAULT_TIMEOUT); + + return ret == sizeof(header) ? RET_OK : RET_FAIL; +} + +static ret_t tk_client_read_resp_impl(tk_client_t* client, tk_msg_header_t* header, wbuffer_t* wb) { + int32_t ret = 0; + uint16_t crc_value = 0; + tk_iostream_t* io = NULL; + uint16_t real_crc_value = PPPINITFCS16; + return_value_if_fail(client != NULL && client->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(header != NULL && wb != NULL, RET_BAD_PARAMS); + + io = client->io; + wbuffer_rewind(wb); + ret = tk_iostream_read_len(io, header, sizeof(*header), TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(*header), RET_IO); + + real_crc_value = tk_crc16(real_crc_value, header, sizeof(*header)); + if (header->size > 0) { + return_value_if_fail(wbuffer_extend_capacity(wb, header->size) == RET_OK, RET_OOM); + ret = tk_iostream_read_len(io, wb->data, header->size, TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == header->size, RET_IO); + real_crc_value = tk_crc16(real_crc_value, wb->data, header->size); + } + + ret = tk_iostream_read_len(io, &crc_value, sizeof(crc_value), TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + return_value_if_fail(real_crc_value == crc_value, RET_CRC); + + wb->cursor = header->size; + + return RET_OK; +} + +ret_t tk_client_read_resp(tk_client_t* client, tk_msg_header_t* header, wbuffer_t* wb) { + int32_t retry_times = 0; + + while (retry_times < TK_MAX_RETRY_TIMES) { + ret_t ret = tk_client_read_resp_impl(client, header, wb); + if (ret != RET_IO) { + tk_client_confirm_packet(client, ret != RET_CRC); + } + + if (ret == RET_CRC) { + retry_times++; + log_debug("crc error, retry times: %d", retry_times); + continue; + } else { + return ret; + } + } + + return RET_FAIL; +} + +ret_t tk_client_request(tk_client_t* client, uint32_t type, uint32_t data_type, wbuffer_t* wb) { + ret_t ret = tk_client_send_req(client, type, data_type, wb); + if (ret == RET_OK) { + tk_msg_header_t header; + memset(&header, 0x00, sizeof(header)); + ret = tk_client_read_resp(client, &header, wb); + } + + return ret; +} + +ret_t tk_client_upload_file(tk_client_t* client, const char* remote_file, const char* local_file) { + wbuffer_t wb; + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + uint8_t buff[4096] = {0}; + return_value_if_fail(client != NULL && client->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); + return_value_if_fail(local_file != NULL, RET_BAD_PARAMS); + + file = fs_open_file(os_fs(), local_file, "rb"); + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + wbuffer_init(&wb, (void*)remote_file, strlen(remote_file) + 1); + wb.cursor = wb.capacity; + ret = tk_client_request(client, MSG_CODE_UPLOAD_FILE_BEGIN, MSG_DATA_TYPE_STRING, &wb); + goto_error_if_fail(ret == RET_OK); + + while ((len = fs_file_read(file, buff, sizeof(buff))) > 0) { + wbuffer_init(&wb, buff, len); + wb.cursor = len; + ret = tk_client_request(client, MSG_CODE_UPLOAD_FILE_DATA, MSG_DATA_TYPE_BINARY, &wb); + break_if_fail(ret == RET_OK); + } + + wbuffer_rewind(&wb); + ret = tk_client_request(client, MSG_CODE_UPLOAD_FILE_END, MSG_DATA_TYPE_NONE, &wb); + + fs_file_close(file); + + return ret; +error: + fs_file_close(file); + + return RET_FAIL; +} + +ret_t tk_client_download_file(tk_client_t* client, const char* remote_file, + const char* local_file) { + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + wbuffer_t* wb = NULL; + tk_msg_header_t header; + return_value_if_fail(client != NULL && client->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(remote_file != NULL, RET_BAD_PARAMS); + return_value_if_fail(local_file != NULL, RET_BAD_PARAMS); + + file = fs_open_file(os_fs(), local_file, "wb+"); + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + wb = &(client->wb); + wbuffer_rewind(wb); + wbuffer_write_string(wb, remote_file); + ret = tk_client_request(client, MSG_CODE_DOWNLOAD_FILE_BEGIN, MSG_DATA_TYPE_STRING, wb); + goto_error_if_fail(ret == RET_OK); + + memset(&header, 0x00, sizeof(header)); + + while ((ret = tk_client_read_resp(client, &header, wb)) == RET_OK) { + if (header.type == MSG_CODE_DOWNLOAD_FILE_DATA) { + len = fs_file_write(file, wb->data, wb->cursor); + break_if_fail(len == wb->cursor); + } else if (header.type == MSG_CODE_DOWNLOAD_FILE_END) { + ret = RET_OK; + break; + } else { + assert(!"impossible"); + ret = RET_FAIL; + break; + } + } + fs_file_close(file); + + return ret; +error: + fs_file_close(file); + + return ret; +} diff --git a/project/gui/awtk/src/service/client.h b/project/gui/awtk/src/service/client.h new file mode 100644 index 000000000..87c84d504 --- /dev/null +++ b/project/gui/awtk/src/service/client.h @@ -0,0 +1,131 @@ +/** + * File: client + * Author: AWTK Develop Team + * Brief: client interface + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-05 Li XianJing created + * + */ + +#ifndef TK_CLIENT_H +#define TK_CLIENT_H + +#include "tkc/buffer.h" +#include "tkc/iostream.h" +#include "service/msg_header.h" + +BEGIN_C_DECLS + +struct _tk_client_t; +typedef struct _tk_client_t tk_client_t; + +/** + * @class tk_client_t + * 客户端接口。 + */ +struct _tk_client_t { + /** + * @property {wbuffer_t} wb + * 用于接收/发送数据打包。 + */ + wbuffer_t wb; + /** + * @property {tk_iostream_t*} io + * IO对象。 + */ + tk_iostream_t* io; +}; + +/** + * @method tk_client_init + * 初始化(仅供子类使用)。 + * @param {tk_client_t*} client 服务对象。 + * @param {tk_iostream_t*} io io对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_init(tk_client_t* client, tk_iostream_t* io); + +/** + * @method tk_client_deinit + * 释放资源(仅供子类使用)。 + * @param {tk_client_t*} client 服务对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_deinit(tk_client_t* client); + +/** + * @method tk_client_send_req + * 客户端发送请求。 + * @param {tk_client_t*} client client对象。 + * @param {uint32_t} type 消息类型。 + * @param {uint32_t} data_type 数据类型。 + * @param {wbuffer_t*} wb 要发送的数据。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_send_req(tk_client_t* client, uint32_t type, uint32_t data_type, wbuffer_t* wb); + +/** + * @method tk_client_read_resp + * 客户端读取响应。 + * @param {tk_client_t*} client client对象。 + * @param {tk_msg_header_t*} header 返回消息头。 + * @param {wbuffer_t*} wb 返回读取的数据。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_read_resp(tk_client_t* client, tk_msg_header_t* header, wbuffer_t* wb); + +/** + * @method tk_client_request + * 客户端发送请求,并读取响应。 + * @param {tk_client_t*} client client对象。 + * @param {uint32_t} type 消息类型。 + * @param {uint32_t} data_type 数据类型。 + * @param {wbuffer_t*} wb 要发送的数据/返回读取的数据。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_request(tk_client_t* client, uint32_t type, uint32_t data_type, wbuffer_t* wb); + +/** + * @method tk_client_download_file + * 客户端下载文件。 + * @param {tk_client_t*} client client对象。 + * @param {const char*} remote_file 远程文件。 + * @param {const char*} local_file 本地文件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_download_file(tk_client_t* client, const char* remote_file, const char* local_file); + +/** + * @method tk_client_upload_file + * 客户端上传文件。 + * @param {tk_client_t*} client client对象。 + * @param {const char*} remote_file 远程文件。 + * @param {const char*} local_file 本地文件。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_client_upload_file(tk_client_t* client, const char* remote_file, const char* local_file); + +#define TK_CLIENT(obj) ((obj) != NULL ? &((obj)->client) : NULL) + +END_C_DECLS + +#endif /*TK_CLIENT_H*/ diff --git a/project/gui/awtk/src/service/msg_header.h b/project/gui/awtk/src/service/msg_header.h new file mode 100644 index 000000000..b1746ef25 --- /dev/null +++ b/project/gui/awtk/src/service/msg_header.h @@ -0,0 +1,153 @@ +/** + * File: msg_header.h + * Author: AWTK Develop Team + * Brief: msg header + * + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-11-05 Li XianJing created + * + */ + +#ifndef TK_MSG_HEADER_H +#define TK_MSG_HEADER_H + +#include "tkc/types_def.h" + +BEGIN_C_DECLS + +/** + * @enum tk_msg_data_type_t + * @prefix MSG_DATA_TYPE_ + * 数据类型。 +*/ +typedef enum _tk_msg_data_type_t { + /** + * @const MSG_DATA_TYPE_NONE + * 无效数据类型。 + */ + MSG_DATA_TYPE_NONE = 0, + /** + * @const MSG_DATA_TYPE_UBJSON + * JSON数据类型。 + */ + MSG_DATA_TYPE_UBJSON, + /** + * @const MSG_DATA_TYPE_STRING + * 字符串数据类型。 + */ + MSG_DATA_TYPE_STRING, + /** + * @const MSG_DATA_TYPE_BINARY + * 二进制数据类型。 + */ + MSG_DATA_TYPE_BINARY +} tk_msg_data_type_t; + +/** + * @enum tk_msg_code_t + * @prefix MSG_ + * 消息类型。 + * + */ +typedef enum _tk_msg_code_t { + /** + * @const MSG_NONE + * 无效消息。 + */ + MSG_NONE = 0, + /** + * @const MSG_CODE_CONFIRM + * 数据包确认。 + */ + MSG_CODE_CONFIRM, + /** + * @const MSG_CODE_LOGIN + * 登录请求。 + */ + MSG_CODE_LOGIN, + /** + * @const MSG_CODE_LOGOUT + * 登出请求。 + */ + MSG_CODE_LOGOUT, + /** + * @const MSG_CODE_UPLOAD_FILE_BEGIN + * 上传文件请求开始。 + */ + MSG_CODE_UPLOAD_FILE_BEGIN, + /** + * @const MSG_CODE_UPLOAD_FILE_DATA + * 上传文件请求数据。 + */ + MSG_CODE_UPLOAD_FILE_DATA, + /** + * @const MSG_CODE_UPLOAD_FILE_END + * 上传文件请求结束。 + */ + MSG_CODE_UPLOAD_FILE_END, + /** + * @const MSG_CODE_DOWNLOAD_FILE_BEGIN + * 下载文件请求。 + */ + MSG_CODE_DOWNLOAD_FILE_BEGIN, + /** + * @const MSG_CODE_DOWNLOAD_FILE_DATA + * 下载文件请求数据。 + */ + MSG_CODE_DOWNLOAD_FILE_DATA, + /** + * @const MSG_CODE_DOWNLOAD_FILE_END + * 下载文件请求结束。 + */ + MSG_CODE_DOWNLOAD_FILE_END, + + /** + * @const MSG_USER_START + * 用户扩展消息起始值。 + */ + MSG_USER_START = 100 +} tk_msg_code_t; + +/** + * @class tk_msg_header_t + * 消息头。 +*/ +typedef struct _tk_msg_header_t { + /** + * @property {uint32_t} size + * 消息体的大小。 + */ + uint32_t size; + /** + * @property {uint16_t} type + * 消息类型。 + */ + uint16_t type; + /** + * @property {uint8_t} data_type + * 数据类型。 + */ + uint8_t data_type; + /** + * @property {uint8_t} resp_code + * 响应码(仅适用于resp)。 + */ + uint8_t resp_code; +} tk_msg_header_t; + +#define TK_MAX_RETRY_TIMES 3 + +END_C_DECLS + +#endif /*TK_MSG_HEADER_H*/ diff --git a/project/gui/awtk/src/service/service.c b/project/gui/awtk/src/service/service.c index 638a30cc3..5116c25e5 100644 --- a/project/gui/awtk/src/service/service.c +++ b/project/gui/awtk/src/service/service.c @@ -19,6 +19,7 @@ * */ #include "tkc/url.h" +#include "tkc/crc.h" #include "tkc/utils.h" #include "service/service.h" #include "tkc/event_source_fd.h" @@ -27,6 +28,16 @@ #include "tkc/socket_helper.h" +ret_t tk_service_init(tk_service_t* service, tk_iostream_t* io) { + return_value_if_fail(service != NULL, RET_BAD_PARAMS); + + service->io = io; + wbuffer_init_extendable(&(service->wb)); + wbuffer_extend_capacity(&(service->wb), 1024); + + return RET_OK; +} + ret_t tk_service_dispatch(tk_service_t* service) { return_value_if_fail(service != NULL && service->dispatch != NULL, RET_BAD_PARAMS); @@ -36,6 +47,9 @@ ret_t tk_service_dispatch(tk_service_t* service) { ret_t tk_service_destroy(tk_service_t* service) { return_value_if_fail(service != NULL && service->destroy != NULL, RET_BAD_PARAMS); + wbuffer_deinit(&(service->wb)); + TK_OBJECT_UNREF(service->io); + return service->destroy(service); } @@ -84,6 +98,17 @@ static ret_t tk_service_tcp_on_client(event_source_t* source) { return RET_OK; } +static ret_t on_source_destroy(void* ctx, event_t* e) { + event_source_fd_t* source = EVENT_SOURCE_FD(e->target); + int listen_sock = source->fd; + int port = tk_pointer_to_int(ctx); + + log_debug("stop service: socket=%d port=%d\n", listen_sock, port); + socket_close(listen_sock); + + return RET_OK; +} + static ret_t tk_service_start_tcp(event_source_manager_t* esm, const char* url, tk_service_create_t create, void* args) { int port = 0; @@ -103,6 +128,8 @@ static ret_t tk_service_start_tcp(event_source_manager_t* esm, const char* url, return_value_if_fail(source != NULL, RET_OOM); EVENT_SOURCE_FD(source)->ctx2 = args; event_source_manager_add(esm, source); + emitter_on(EMITTER(source), EVT_DESTROY, on_source_destroy, tk_pointer_from_int(port)); + OBJECT_UNREF(source); log_debug("service start: %s\n", url); @@ -145,3 +172,228 @@ ret_t tk_service_start(event_source_manager_t* esm, const char* url, tk_service_ return RET_NOT_IMPL; } } + +static ret_t tk_service_confirm_packet(tk_service_t* service, bool_t valid) { + int32_t ret = 0; + tk_msg_header_t header; + + header.size = 0; + header.type = MSG_CODE_CONFIRM; + header.data_type = MSG_DATA_TYPE_NONE; + header.resp_code = valid ? RET_OK : RET_CRC; + + ret = tk_iostream_write_len(service->io, &header, sizeof(header), TK_OSTREAM_DEFAULT_TIMEOUT); + + return ret == sizeof(header) ? RET_OK : RET_FAIL; +} + +ret_t tk_service_send_resp_impl(tk_service_t* service, uint32_t type, uint32_t data_type, + uint32_t resp_code, wbuffer_t* wb) { + int32_t ret = 0; + uint32_t size = 0; + const void* data = NULL; + tk_msg_header_t header; + tk_iostream_t* io = NULL; + uint16_t crc_value = PPPINITFCS16; + uint32_t timeout = TK_OSTREAM_DEFAULT_TIMEOUT; + + memset(&header, 0x00, sizeof(header)); + return_value_if_fail(wb != NULL, RET_BAD_PARAMS); + return_value_if_fail(service != NULL && service->io != NULL, RET_BAD_PARAMS); + + io = service->io; + data = wb->data; + size = wb->cursor; + if (size > 0) { + return_value_if_fail(data != NULL, RET_BAD_PARAMS); + } + + header.type = type; + header.size = size; + header.data_type = data_type; + header.resp_code = resp_code; + + crc_value = tk_crc16(crc_value, &header, sizeof(header)); + if (data != NULL && size > 0) { + crc_value = tk_crc16(crc_value, data, size); + } + + ret = tk_iostream_write_len(io, &header, sizeof(header), timeout); + return_value_if_fail(ret == sizeof(header), RET_IO); + + if (size > 0) { + timeout = TK_OSTREAM_DEFAULT_TIMEOUT * (size / 10240) + TK_OSTREAM_DEFAULT_TIMEOUT; + ret = tk_iostream_write_len(io, data, size, timeout); + return_value_if_fail(ret == size, RET_IO); + } + + ret = tk_iostream_write_len(io, &crc_value, sizeof(crc_value), TK_OSTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + + return RET_OK; +} + +ret_t tk_service_send_resp(tk_service_t* service, uint32_t type, uint32_t data_type, + uint32_t resp_code, wbuffer_t* wb) { + int32_t len = 0; + int32_t retry_times = 0; + tk_msg_header_t header; + return_value_if_fail(service != NULL && wb != NULL, RET_BAD_PARAMS); + + while (retry_times < TK_MAX_RETRY_TIMES) { + ret_t ret = tk_service_send_resp_impl(service, type, data_type, resp_code, wb); + break_if_fail(ret == RET_OK); + + /*读取确认包*/ + memset(&header, 0x00, sizeof(header)); + len = tk_iostream_read_len(service->io, &header, sizeof(header), TK_ISTREAM_DEFAULT_TIMEOUT); + break_if_fail(len == sizeof(header)); + + if (header.resp_code == RET_OK) { + return RET_OK; + } + + if (header.resp_code == RET_CRC) { + retry_times++; + log_debug("crc error, retry times: %d", retry_times); + continue; + } else { + log_debug("unknown error\n."); + break; + } + } + + return RET_FAIL; +} + +ret_t tk_service_read_req_impl(tk_service_t* service, tk_msg_header_t* header, wbuffer_t* wb) { + int32_t ret = 0; + uint16_t crc_value = 0; + tk_iostream_t* io = NULL; + uint16_t real_crc_value = PPPINITFCS16; + return_value_if_fail(service != NULL && service->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(header != NULL && wb != NULL, RET_BAD_PARAMS); + + io = service->io; + wbuffer_rewind(wb); + ret = tk_iostream_read_len(io, header, sizeof(*header), TK_ISTREAM_DEFAULT_TIMEOUT); + if (ret == 0) { + return RET_IO; + } + return_value_if_fail(ret == sizeof(*header), RET_IO); + + real_crc_value = tk_crc16(real_crc_value, header, sizeof(*header)); + if (header->size > 0) { + return_value_if_fail(wbuffer_extend_capacity(wb, header->size) == RET_OK, RET_OOM); + ret = tk_iostream_read_len(io, wb->data, header->size, TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == header->size, RET_IO); + real_crc_value = tk_crc16(real_crc_value, wb->data, header->size); + } + + ret = tk_iostream_read_len(io, &crc_value, sizeof(crc_value), TK_ISTREAM_DEFAULT_TIMEOUT); + return_value_if_fail(ret == sizeof(crc_value), RET_IO); + return_value_if_fail(crc_value == real_crc_value, RET_CRC); + + wb->cursor = header->size; + + return RET_OK; +} + +ret_t tk_service_read_req(tk_service_t* service, tk_msg_header_t* header, wbuffer_t* wb) { + int32_t retry_times = 0; + return_value_if_fail(service != NULL && header != NULL && wb != NULL, RET_BAD_PARAMS); + + while (retry_times < TK_MAX_RETRY_TIMES) { + ret_t ret = tk_service_read_req_impl(service, header, wb); + if (ret != RET_IO) { + tk_service_confirm_packet(service, ret != RET_CRC); + } + + if (ret == RET_CRC) { + retry_times++; + log_debug("crc error, retry times: %d", retry_times); + continue; + } else { + return ret; + } + } + + return RET_FAIL; +} + +ret_t tk_service_upload_file(tk_service_t* service, const char* filename) { + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + wbuffer_t* wb = NULL; + tk_msg_header_t header; + return_value_if_fail(service != NULL && service->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + wb = &(service->wb); + wbuffer_rewind(wb); + file = fs_open_file(os_fs(), filename, "wb+"); + if (file != NULL) { + tk_service_send_resp(service, MSG_CODE_UPLOAD_FILE_BEGIN, MSG_DATA_TYPE_NONE, RET_OK, wb); + } else { + tk_service_send_resp(service, MSG_CODE_UPLOAD_FILE_BEGIN, MSG_DATA_TYPE_NONE, RET_FAIL, wb); + } + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + memset(&header, 0x00, sizeof(header)); + while ((ret = tk_service_read_req(service, &header, wb)) == RET_OK) { + if (header.type == MSG_CODE_UPLOAD_FILE_DATA) { + len = fs_file_write(file, wb->data, wb->cursor); + ret = (len == wb->cursor) ? RET_OK : RET_FAIL; + tk_service_send_resp(service, MSG_CODE_UPLOAD_FILE_DATA, MSG_DATA_TYPE_NONE, ret, wb); + break_if_fail(ret == RET_OK); + } else if (header.type == MSG_CODE_UPLOAD_FILE_END) { + ret = RET_OK; + ret = tk_service_send_resp(service, MSG_CODE_UPLOAD_FILE_END, MSG_DATA_TYPE_NONE, ret, wb); + break_if_fail(ret == RET_OK); + break; + } else { + assert(!"impossible"); + ret = RET_FAIL; + tk_service_send_resp(service, MSG_CODE_UPLOAD_FILE_END, MSG_DATA_TYPE_NONE, ret, wb); + break; + } + } + fs_file_close(file); + + return RET_OK; +} + +ret_t tk_service_download_file(tk_service_t* service, const char* filename) { + wbuffer_t wb; + int32_t len = 0; + ret_t ret = RET_OK; + fs_file_t* file = NULL; + uint8_t buff[4096] = {0}; + return_value_if_fail(service != NULL && service->io != NULL, RET_BAD_PARAMS); + return_value_if_fail(filename != NULL, RET_BAD_PARAMS); + + wbuffer_init(&wb, buff, sizeof(buff)); + file = fs_open_file(os_fs(), filename, "rb"); + if (file != NULL) { + tk_service_send_resp(service, MSG_CODE_DOWNLOAD_FILE_BEGIN, MSG_DATA_TYPE_NONE, RET_OK, &wb); + } else { + tk_service_send_resp(service, MSG_CODE_DOWNLOAD_FILE_BEGIN, MSG_DATA_TYPE_NONE, RET_FAIL, &wb); + } + return_value_if_fail(file != NULL, RET_BAD_PARAMS); + + while ((len = fs_file_read(file, buff, sizeof(buff))) > 0) { + wbuffer_init(&wb, buff, len); + wb.cursor = len; + ret = tk_service_send_resp(service, MSG_CODE_DOWNLOAD_FILE_DATA, MSG_DATA_TYPE_BINARY, RET_OK, + &wb); + break_if_fail(ret == RET_OK); + } + + wbuffer_rewind(&wb); + ret = tk_service_send_resp(service, MSG_CODE_DOWNLOAD_FILE_END, MSG_DATA_TYPE_NONE, ret, &wb); + + fs_file_close(file); + + return RET_OK; +} diff --git a/project/gui/awtk/src/service/service.h b/project/gui/awtk/src/service/service.h index 5fa4a391d..d07522f0e 100644 --- a/project/gui/awtk/src/service/service.h +++ b/project/gui/awtk/src/service/service.h @@ -22,7 +22,9 @@ #ifndef TK_SERVICE_H #define TK_SERVICE_H +#include "tkc/buffer.h" #include "tkc/iostream.h" +#include "service/msg_header.h" #include "tkc/event_source_manager.h" BEGIN_C_DECLS @@ -42,15 +44,36 @@ typedef ret_t (*tk_service_auth_t)(tk_service_t* service, const char* username, * 服务接口。 */ struct _tk_service_t { + /** + * @property {wbuffer_t} wb + * 用于接收/发送数据打包。 + */ + wbuffer_t wb; + /** + * @property {tk_iostream_t*} io + * IO对象。 + */ + tk_iostream_t* io; + + /*private*/ tk_service_dispatch_t dispatch; tk_service_destroy_t destroy; - tk_iostream_t* io; }; +/** + * @method tk_service_init + * 初始化服务对象(仅供子类使用)。 + * @param {tk_service_t*} service 服务对象。 + * @param {tk_iostream_t*} io IO对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_init(tk_service_t* service, tk_iostream_t* io); + /** * @method tk_service_dispatch * 处理服务器请求。 - * + * > 返回非RET_OK,停止服务器,并销毁service对象。 * @param {tk_service_t*} service 服务对象。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 @@ -60,7 +83,7 @@ ret_t tk_service_dispatch(tk_service_t* service); /** * @method tk_service_destroy * 销毁服务对象。 - * + * > 服务负责销毁IO对象。 * @param {tk_service_t*} service 服务对象。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 @@ -74,12 +97,57 @@ ret_t tk_service_destroy(tk_service_t* service); * @param {event_source_manager_t*} esm 事件源管理器。 * @param {const char*} url 服务地址。 * @param {tk_service_create_t} create 创建服务对象的函数。 - * @param {void*} args 参数。 + * @param {void*} args 参数(对于TCP服务,该参数必须持续有效,使用全局或静态变量)。 * * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 */ ret_t tk_service_start(event_source_manager_t* esm, const char* url, tk_service_create_t create, - void* args); + void* args); + +/** + * @method tk_service_read_req + * 服务端读取请求。 + * @param {tk_service_t*} service service对象。 + * @param {tk_msg_header_t*} header 返回消息头。 + * @param {wbuffer_t*} wb 返回对其的数据。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_read_req(tk_service_t* service, tk_msg_header_t* header, wbuffer_t* wb); + +/** + * @method tk_service_send_resp + * 服务端发送响应。 + * @param {tk_service_t*} service service对象。 + * @param {uint32_t} type 消息类型。 + * @param {uint32_t} data_type 数据类型。 + * @param {uint32_t} resp_code 响应码。 + * @param {wbuffer_t*} wb 要发送的数据。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_send_resp(tk_service_t* service, uint32_t type, uint32_t data_type, uint32_t resp_code, + wbuffer_t* wb); + +/** + * @method tk_service_upload_file + * 处理上传文件。 + * @param {tk_service_t*} service service对象。 + * @param {const char*} filename 文件名。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_upload_file(tk_service_t* service, const char* filename); + +/** + * @method tk_service_download_file + * 处理下载文件。 + * @param {tk_service_t*} service service对象。 + * @param {const char*} filename 文件名。 + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t tk_service_download_file(tk_service_t* service, const char* filename); + +#define TK_SERVICE(obj) ((obj) != NULL ? &((obj)->service) : NULL) END_C_DECLS diff --git a/project/gui/awtk/src/streams/serial/iostream_serial.c b/project/gui/awtk/src/streams/serial/iostream_serial.c index 602fe868b..4ad67ebbc 100644 --- a/project/gui/awtk/src/streams/serial/iostream_serial.c +++ b/project/gui/awtk/src/streams/serial/iostream_serial.c @@ -143,7 +143,7 @@ tk_iostream_t* tk_iostream_serial_create(const char* port) { return_value_if_fail(port != NULL, NULL); fd = serial_open(port); - return_value_if_fail(fd > 0, NULL); + return_value_if_fail(fd >= 0, NULL); obj = tk_object_create(&s_tk_iostream_serial_vtable); iostream_serial = TK_IOSTREAM_SERIAL(obj); @@ -198,4 +198,3 @@ ret_t tk_iostream_serial_wait_for_data(tk_iostream_t* iostream, uint32_t timeout tk_iostream_t* tk_iostream_serial_create_ex(const char* url) { return tk_stream_factory_create_iostream(url); } - diff --git a/project/gui/awtk/src/streams/stream_factory.c b/project/gui/awtk/src/streams/stream_factory.c index aadb16851..ec23144fb 100644 --- a/project/gui/awtk/src/streams/stream_factory.c +++ b/project/gui/awtk/src/streams/stream_factory.c @@ -37,9 +37,13 @@ tk_iostream_t* tk_stream_factory_create_iostream(const char* url) { return_value_if_fail(aurl != NULL, NULL); if (tk_str_start_with(url, STR_SCHEMA_TCP)) { +#ifdef WITH_SOCKET io = tk_iostream_tcp_create_client(aurl->host, aurl->port); +#endif/*WITH_SOCKET*/ } else if (tk_str_start_with(url, STR_SCHEMA_UDP)) { +#ifdef WITH_SOCKET io = tk_iostream_udp_create_client(aurl->host, aurl->port); +#endif/*WITH_SOCKET*/ } else if (tk_str_eq(aurl->schema, "serial")) { char filename[MAX_PATH + 1] = {0}; @@ -62,4 +66,3 @@ tk_iostream_t* tk_stream_factory_create_iostream(const char* url) { return io; } - diff --git a/project/gui/awtk/src/tkc/fs.c b/project/gui/awtk/src/tkc/fs.c index e095c29c8..f2b2bf590 100644 --- a/project/gui/awtk/src/tkc/fs.c +++ b/project/gui/awtk/src/tkc/fs.c @@ -338,7 +338,7 @@ ret_t fs_test_file(fs_t* fs) { char buff[32]; fs_file_t* fp = NULL; const char* filename = "./test.txt"; - char path[MAX_PATH+1] = {0}; + char path[MAX_PATH + 1] = {0}; assert(fs_get_cwd(os_fs(), path) == RET_OK); assert(fs_get_exe(os_fs(), path) == RET_OK); @@ -661,6 +661,53 @@ ret_t fs_copy_file(fs_t* fs, const char* src, const char* dst) { return ret; } +bool_t fs_file_equal(fs_t* fs, const char* src, const char* dst) { + int32_t slen = 0; + int32_t dlen = 0; + bool_t ret = FALSE; + fs_file_t* fsrc = NULL; + fs_file_t* fdst = NULL; + uint8_t sbuff[1024] = {0}; + uint8_t dbuff[1024] = {0}; + return_value_if_fail(fs != NULL && src != NULL && dst != NULL, FALSE); + return_value_if_fail(file_exist(src), FALSE); + return_value_if_fail(file_exist(dst), FALSE); + fdst = fs_open_file(fs, dst, "rb"); + return_value_if_fail(fdst != NULL, FALSE); + + fsrc = fs_open_file(fs, src, "rb"); + if (fsrc != NULL) { + if (fs_file_size(fsrc) != fs_file_size(fdst)) { + goto end; + } + + while (TRUE) { + slen = fs_file_read(fsrc, sbuff, sizeof(sbuff)); + dlen = fs_file_read(fdst, dbuff, sizeof(dbuff)); + if (slen != dlen) { + goto end; + } + if (memcmp(sbuff, dbuff, slen) != 0) { + goto end; + } + + if (fs_file_eof(fsrc)) { + ret = TRUE; + break; + } + } + } +end: + if (fsrc != NULL) { + fs_file_close(fsrc); + } + + if (fdst != NULL) { + fs_file_close(fdst); + } + return ret; +} + static ret_t fs_copy_item(fs_t* fs, fs_item_t* item, const char* src, const char* dst, bool_t overwrite) { char subsrc[MAX_PATH + 1]; diff --git a/project/gui/awtk/src/tkc/fs.h b/project/gui/awtk/src/tkc/fs.h index 9c014b338..a37f4fdc3 100644 --- a/project/gui/awtk/src/tkc/fs.h +++ b/project/gui/awtk/src/tkc/fs.h @@ -123,7 +123,6 @@ typedef struct _fs_file_vtable_t { /** * @class fs_file_t - * @annotation ["fake"] * 文件接口。 * * 示例: @@ -516,6 +515,19 @@ ret_t fs_file_rename(fs_t* fs, const char* name, const char* new_name); */ ret_t fs_copy_file(fs_t* fs, const char* src, const char* dst); +/** + * @method fs_file_equal + * + * 比较二进制文件。 + * + * @param {fs_t*} fs 文件系统对象,一般赋值为os_fs()。 + * @param {const char*} src 源文件名。 + * @param {const char*} dst 目标文件名。 + * + * @return {bool_t} 返回TRUE表示相同,否则表示不同。 + */ +bool_t fs_file_equal(fs_t* fs, const char* src, const char* dst); + /** * @method fs_copy_dir * diff --git a/project/gui/awtk/src/tkc/serial_helper.c b/project/gui/awtk/src/tkc/serial_helper.c index d1c77abd5..567f215e4 100644 --- a/project/gui/awtk/src/tkc/serial_helper.c +++ b/project/gui/awtk/src/tkc/serial_helper.c @@ -522,7 +522,7 @@ ret_t serial_wait_for_data(serial_handle_t handle, uint32_t timeout_ms) { return tk_socket_wait_for_data(fd, timeout_ms); } -#else +#elif defined(LINUX) || defined(MACOS) #include #include diff --git a/project/gui/awtk/src/tkc/serial_helper.h b/project/gui/awtk/src/tkc/serial_helper.h index c99e0e4f2..e167b4df7 100644 --- a/project/gui/awtk/src/tkc/serial_helper.h +++ b/project/gui/awtk/src/tkc/serial_helper.h @@ -53,11 +53,14 @@ typedef struct _serial_info_t { typedef struct _serial_info_t* serial_handle_t; #else +#if defined(LINUX) || defined(MACOS) #include #include #include #include #include +#endif/*LINUX || MACOS*/ + typedef int serial_dev_t; typedef struct _serial_info_t { @@ -71,27 +74,27 @@ typedef struct _serial_info_t* serial_handle_t; * @enum bytesize_t * 串口字节位数。 */ -typedef enum { +typedef enum { /** * @const fivebits * 每字节5位。 */ - fivebits = 5, + fivebits = 5, /** * @const sixbits * 每字节6位。 */ - sixbits = 6, + sixbits = 6, /** * @const sevenbits * 每字节7位。 */ - sevenbits = 7, + sevenbits = 7, /** * @const eightbits * 每字节8位。 */ - eightbits = 8 + eightbits = 8 } bytesize_t; /** @@ -130,44 +133,44 @@ typedef enum { * @enum stopbits_t * 串口停止位。 */ -typedef enum { +typedef enum { /** * @const stopbits_one * 1位。 */ - stopbits_one = 1, + stopbits_one = 1, /** * @const stopbits_two * 2位。 */ - stopbits_two = 2, + stopbits_two = 2, /** * @const stopbits_one_point_five * 1.5位。 */ - stopbits_one_point_five + stopbits_one_point_five } stopbits_t; /** * @enum flowcontrol_t * 串口流控。 */ -typedef enum { +typedef enum { /** * @const flowcontrol_none * 无。 */ - flowcontrol_none = 0, + flowcontrol_none = 0, /** * @const flowcontrol_software * 软件。 */ - flowcontrol_software, + flowcontrol_software, /** * @const flowcontrol_hardware * 硬件。 */ - flowcontrol_hardware + flowcontrol_hardware } flowcontrol_t; /** diff --git a/project/gui/awtk/src/tkc/socket_helper.c b/project/gui/awtk/src/tkc/socket_helper.c index 8dc623d2e..270f965b7 100644 --- a/project/gui/awtk/src/tkc/socket_helper.c +++ b/project/gui/awtk/src/tkc/socket_helper.c @@ -28,6 +28,16 @@ #ifdef WITH_SOCKET +#if defined(LINUX) || defined(MACOS) +#include +static ret_t tk_ignore_sig_pipe(void) { + signal(SIGPIPE, SIG_IGN); + return RET_OK; +} +#else +#define tk_ignore_sig_pipe() +#endif/*LINUX*/ + #ifdef WIN32 #pragma comment(lib, "ws2_32") ret_t tk_socket_init() { @@ -38,6 +48,7 @@ ret_t tk_socket_init() { log_debug("WSAStartup failed: %d\n", iResult); return RET_FAIL; } + tk_ignore_sig_pipe(); return RET_OK; } @@ -55,6 +66,7 @@ ret_t tk_socket_close(int sock) { #else ret_t tk_socket_init() { + tk_ignore_sig_pipe(); return RET_OK; } ret_t tk_socket_deinit() { @@ -318,8 +330,8 @@ int32_t tk_socket_send(int sock, const void* buffer, uint32_t size, int flags) { return send(sock, buffer, size, flags); } -int32_t tk_socket_sendto(int sock, const void* buffer, uint32_t size, int flags, - const struct sockaddr *dest_addr, uint32_t dest_len) { +int32_t tk_socket_sendto(int sock, const void* buffer, uint32_t size, int flags, + const struct sockaddr* dest_addr, uint32_t dest_len) { return_value_if_fail(buffer != NULL, 0); return_value_if_fail(dest_addr != NULL, 0); return sendto(sock, buffer, size, flags, dest_addr, (socklen_t)dest_len); @@ -330,7 +342,7 @@ int32_t tk_socket_recv(int sock, void* buffer, uint32_t size, int flags) { } int32_t tk_socket_recvfrom(int sock, void* buffer, uint32_t size, int flags, - struct sockaddr *dest_addr, uint32_t* dest_len) { + struct sockaddr* dest_addr, uint32_t* dest_len) { return_value_if_fail(buffer != NULL, 0); return_value_if_fail(dest_addr != NULL && dest_len != NULL, 0); diff --git a/project/gui/awtk/src/tkc/utils.c b/project/gui/awtk/src/tkc/utils.c index 27a2a47fc..4c2d7e12b 100644 --- a/project/gui/awtk/src/tkc/utils.c +++ b/project/gui/awtk/src/tkc/utils.c @@ -34,10 +34,6 @@ #include "tkc/data_reader_factory.h" #include "tkc/data_writer_factory.h" -#ifndef WITH_DATA_READER_WRITER -#define WITH_DATA_READER_WRITER 1 -#endif - #define IS_ADDRESS_ALIGN_4(addr) !((((size_t)(addr)) & 0x3) | 0x0) const char* tk_skip_to_num(const char* str) { @@ -569,31 +565,7 @@ int tk_vsnprintf(char* str, size_t size, const char* format, va_list ap) { } ret_t filename_to_name_ex(const char* filename, char* str, uint32_t size, bool_t remove_extname) { - char* p = NULL; - const char* name = filename; - return_value_if_fail(filename != NULL && str != NULL, RET_BAD_PARAMS); - - name = strrchr(filename, '/'); - if (name == NULL) { - name = strrchr(filename, '\\'); - } - - if (name == NULL) { - name = filename; - } else { - name += 1; - } - - tk_strncpy(str, name, size - 1); - - if (remove_extname) { - p = strrchr(str, '.'); - if (p != NULL) { - *p = '\0'; - } - } - - return RET_OK; + return path_basename_ex(filename, remove_extname, str, size); } ret_t filename_to_name(const char* filename, char* str, uint32_t size) { @@ -2021,4 +1993,4 @@ int tk_sscanf(const char* str, const char* format, ...) { return ret; } -#endif /*HAS_NO_VSSCANF*/ \ No newline at end of file +#endif /*HAS_NO_VSSCANF*/ diff --git a/project/gui/awtk/src/tkc/value.c b/project/gui/awtk/src/tkc/value.c index b5262f2ad..dd3d38006 100644 --- a/project/gui/awtk/src/tkc/value.c +++ b/project/gui/awtk/src/tkc/value.c @@ -1,9 +1,9 @@ -/** +/** * File: value.h * Author: AWTK Develop Team * Brief: generic value type * - * Copyright (c) 2018 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -179,6 +179,10 @@ int64_t value_int64(const value_t* v) { return v->value.u64; } else if (v->type == VALUE_TYPE_STRING) { return tk_atol(v->value.str); + } else if (v->type == VALUE_TYPE_UINT32) { + return (int64_t)value_uint32(v); + } else if (v->type == VALUE_TYPE_UINT64) { + return (int64_t)value_uint64(v); } else { return (int64_t)value_int(v); } @@ -433,6 +437,16 @@ value_t* value_set_wstr(value_t* v, const wchar_t* value) { return value_init(v, VALUE_TYPE_WSTRING); } +value_t* value_dup_wstr(value_t* v, const wchar_t* value) { + return_value_if_fail(v != NULL, NULL); + + value_init(v, VALUE_TYPE_WSTRING); + v->value.wstr = tk_wstrdup(value); + v->free_handle = TRUE; + + return v; +} + const char* value_str(const value_t* v) { return_value_if_fail(v != NULL, NULL); return_value_if_fail(v->type == VALUE_TYPE_STRING, NULL); @@ -2307,36 +2321,34 @@ ret_t value_max(value_t* arr, uint32_t size, value_t* result) { return RET_OK; } -static const char* s_type_names[] = { - [VALUE_TYPE_BOOL] = "bool", - [VALUE_TYPE_INT8] = "int8", - [VALUE_TYPE_INT16] = "int16", - [VALUE_TYPE_INT32] = "int32", - [VALUE_TYPE_INT64] = "int64", - [VALUE_TYPE_UINT8] = "uint8", - [VALUE_TYPE_UINT16] = "uint16", - [VALUE_TYPE_UINT32] = "uint32", - [VALUE_TYPE_UINT64] = "uint64", - [VALUE_TYPE_FLOAT] = "float", - [VALUE_TYPE_FLOAT32] = "float32", - [VALUE_TYPE_DOUBLE] = "double", - [VALUE_TYPE_STRING] = "char*", - [VALUE_TYPE_WSTRING] = "wchar_t*", - [VALUE_TYPE_POINTER] = "pointer", - [VALUE_TYPE_OBJECT] = "object", - [VALUE_TYPE_BINARY] = "binary", - [VALUE_TYPE_UBJSON] = "ubjson", - [VALUE_TYPE_ID] = "id", - [VALUE_TYPE_TOKEN] = "token", - [VALUE_TYPE_RECT] = "rect", - [VALUE_TYPE_FUNC] = "func", - [VALUE_TYPE_GRADIENT] = "gradient", - [VALUE_TYPE_FUNC_DEF] = "func_def", - [VALUE_TYPE_BITMAP] = "bitmap", - [VALUE_TYPE_SIZED_STRING] = "char(with size)*", - [VALUE_TYPE_POINTER_REF] = "pointer ref" -}; +static const char* s_type_names[] = {[VALUE_TYPE_BOOL] = "bool", + [VALUE_TYPE_INT8] = "int8", + [VALUE_TYPE_INT16] = "int16", + [VALUE_TYPE_INT32] = "int32", + [VALUE_TYPE_INT64] = "int64", + [VALUE_TYPE_UINT8] = "uint8", + [VALUE_TYPE_UINT16] = "uint16", + [VALUE_TYPE_UINT32] = "uint32", + [VALUE_TYPE_UINT64] = "uint64", + [VALUE_TYPE_FLOAT] = "float", + [VALUE_TYPE_FLOAT32] = "float32", + [VALUE_TYPE_DOUBLE] = "double", + [VALUE_TYPE_STRING] = "char*", + [VALUE_TYPE_WSTRING] = "wchar_t*", + [VALUE_TYPE_POINTER] = "pointer", + [VALUE_TYPE_OBJECT] = "object", + [VALUE_TYPE_BINARY] = "binary", + [VALUE_TYPE_UBJSON] = "ubjson", + [VALUE_TYPE_ID] = "id", + [VALUE_TYPE_TOKEN] = "token", + [VALUE_TYPE_RECT] = "rect", + [VALUE_TYPE_FUNC] = "func", + [VALUE_TYPE_GRADIENT] = "gradient", + [VALUE_TYPE_FUNC_DEF] = "func_def", + [VALUE_TYPE_BITMAP] = "bitmap", + [VALUE_TYPE_SIZED_STRING] = "char(with size)*", + [VALUE_TYPE_POINTER_REF] = "pointer ref"}; const char* value_type_name(value_type_t type) { return s_type_names[type]; -} \ No newline at end of file +} diff --git a/project/gui/awtk/src/window_manager/window_manager_default.c b/project/gui/awtk/src/window_manager/window_manager_default.c index b560ab207..409a5a219 100644 --- a/project/gui/awtk/src/window_manager/window_manager_default.c +++ b/project/gui/awtk/src/window_manager/window_manager_default.c @@ -95,7 +95,8 @@ static widget_t* window_manager_find_prev_window(widget_t* widget) { nr = widget->children->size; for (i = nr - 2; i >= 0; i--) { widget_t* iter = (widget_t*)(widget->children->elms[i]); - if (widget_is_normal_window(iter) || widget_is_dialog(iter) || widget_is_popup(iter) || (widget_is_overlay(iter) && !widget_is_always_on_top(iter))) { + if (widget_is_normal_window(iter) || widget_is_dialog(iter) || widget_is_popup(iter) || + (widget_is_overlay(iter) && !widget_is_always_on_top(iter))) { return iter; } } @@ -118,30 +119,32 @@ static widget_t* window_manager_find_prev_normal_window(widget_t* widget) { return NULL; } -static ret_t window_manager_default_set_paint_system_bar_by_window_animator(widget_t* widget, rect_t* rect) { +static ret_t window_manager_default_set_paint_system_bar_by_window_animator(widget_t* widget, + rect_t* rect) { window_manager_default_t* wm = WINDOW_MANAGER_DEFAULT(widget); WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i) - if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR) && !wm->is_animator_paint_system_bar_top) { - rect_t r = rect_init(iter->x, iter->y, iter->w, iter->h); - rect_t dr = rect_intersect(rect, &r); - if (dr.w == 0 || dr.h == 0) { - wm->is_animator_paint_system_bar_top = TRUE; - } else if (dr.w == r.w && dr.h == r.h) { - wm->is_animator_paint_system_bar_top = FALSE; - } else { - wm->is_animator_paint_system_bar_top = TRUE; - } - } else if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR_BOTTOM) && !wm->is_animator_paint_system_bar_bottom) { - rect_t r = rect_init(iter->x, iter->y, iter->w, iter->h); - rect_t dr = rect_intersect(rect, &r); - if (dr.w == 0 || dr.h == 0) { - wm->is_animator_paint_system_bar_bottom = TRUE; - } else if (dr.w == r.w && dr.h == r.h) { - wm->is_animator_paint_system_bar_bottom = FALSE; - } else { - wm->is_animator_paint_system_bar_bottom = TRUE; - } + if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR) && !wm->is_animator_paint_system_bar_top) { + rect_t r = rect_init(iter->x, iter->y, iter->w, iter->h); + rect_t dr = rect_intersect(rect, &r); + if (dr.w == 0 || dr.h == 0) { + wm->is_animator_paint_system_bar_top = TRUE; + } else if (dr.w == r.w && dr.h == r.h) { + wm->is_animator_paint_system_bar_top = FALSE; + } else { + wm->is_animator_paint_system_bar_top = TRUE; + } + } else if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR_BOTTOM) && + !wm->is_animator_paint_system_bar_bottom) { + rect_t r = rect_init(iter->x, iter->y, iter->w, iter->h); + rect_t dr = rect_intersect(rect, &r); + if (dr.w == 0 || dr.h == 0) { + wm->is_animator_paint_system_bar_bottom = TRUE; + } else if (dr.w == r.w && dr.h == r.h) { + wm->is_animator_paint_system_bar_bottom = FALSE; + } else { + wm->is_animator_paint_system_bar_bottom = TRUE; } + } WIDGET_FOR_EACH_CHILD_END() return RET_OK; } @@ -205,22 +208,24 @@ static ret_t window_manager_default_snap_prev_window_draw_dialog_highlighter_and return RET_FAIL; } -static ret_t window_manager_default_snap_prev_window_get_system_bar_rect(widget_t* widget, slist_t* system_bar_top_list, slist_t* system_bar_bottom_list) { +static ret_t window_manager_default_snap_prev_window_get_system_bar_rect( + widget_t* widget, slist_t* system_bar_top_list, slist_t* system_bar_bottom_list) { WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i) - if (iter->vt->is_window) { - if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR)) { - rect_t* r = rect_create(iter->x, iter->y, iter->w, iter->h); - slist_append(system_bar_top_list, r); - } else if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR_BOTTOM)) { - rect_t* r = rect_create(iter->x, iter->y, iter->w, iter->h); - slist_append(system_bar_bottom_list, r); - } + if (iter->vt->is_window) { + if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR)) { + rect_t* r = rect_create(iter->x, iter->y, iter->w, iter->h); + slist_append(system_bar_top_list, r); + } else if (tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR_BOTTOM)) { + rect_t* r = rect_create(iter->x, iter->y, iter->w, iter->h); + slist_append(system_bar_bottom_list, r); } + } WIDGET_FOR_EACH_CHILD_END() return RET_OK; } -static bool_t window_manager_default_snap_prev_window_check_paint_system_bar(slist_t* diff_rect_list) { +static bool_t window_manager_default_snap_prev_window_check_paint_system_bar( + slist_t* diff_rect_list) { bool_t su = FALSE; rect_t* rect = NULL; slist_node_t* iter = NULL; @@ -236,7 +241,8 @@ static bool_t window_manager_default_snap_prev_window_check_paint_system_bar(sli return su; } -static ret_t window_manager_default_snap_prev_window_get_system_bar_rect_diff_on_visit(void* ctx, const void* data) { +static ret_t window_manager_default_snap_prev_window_get_system_bar_rect_diff_on_visit( + void* ctx, const void* data) { uint32_t i = 0; rect_t diff_rects[4]; void** arges = (void**)ctx; @@ -257,14 +263,16 @@ static ret_t window_manager_default_snap_prev_window_get_system_bar_rect_diff_on return RET_OK; } -static ret_t window_manager_default_snap_prev_window_get_system_bar_rect_diff(slist_t* rect_list, rect_t* r) { +static ret_t window_manager_default_snap_prev_window_get_system_bar_rect_diff(slist_t* rect_list, + rect_t* r) { void* arges[2]; void* iter = NULL; slist_t diff_rect_list; slist_init(&diff_rect_list, NULL, NULL); arges[0] = &diff_rect_list; arges[1] = r; - slist_foreach(rect_list, window_manager_default_snap_prev_window_get_system_bar_rect_diff_on_visit, arges); + slist_foreach(rect_list, + window_manager_default_snap_prev_window_get_system_bar_rect_diff_on_visit, arges); if (!slist_is_empty(&diff_rect_list)) { while ((iter = slist_head_pop(&diff_rect_list)) != NULL) { slist_append(rect_list, iter); @@ -274,14 +282,16 @@ static ret_t window_manager_default_snap_prev_window_get_system_bar_rect_diff(sl return RET_OK; } -static ret_t window_manager_default_snap_prev_window_system_bar_top_push_clip_rect(void* ctx, const void* data) { +static ret_t window_manager_default_snap_prev_window_system_bar_top_push_clip_rect( + void* ctx, const void* data) { rect_t* r = (rect_t*)data; dialog_highlighter_t* dialog_highlighter = (dialog_highlighter_t*)ctx; dialog_highlighter_system_bar_top_append_clip_rect(dialog_highlighter, r); return RET_OK; } -static ret_t window_manager_default_snap_prev_window_system_bar_bottom_push_clip_rect(void* ctx, const void* data) { +static ret_t window_manager_default_snap_prev_window_system_bar_bottom_push_clip_rect( + void* ctx, const void* data) { rect_t* r = (rect_t*)data; dialog_highlighter_t* dialog_highlighter = (dialog_highlighter_t*)ctx; dialog_highlighter_system_bar_bottom_append_clip_rect(dialog_highlighter, r); @@ -336,7 +346,8 @@ ret_t window_manager_default_snap_prev_window(widget_t* widget, widget_t* prev_w slist_init(&system_bar_top_rect_list, default_destroy, NULL); slist_init(&system_bar_bottom_rect_list, default_destroy, NULL); if (!is_fullscreen_window) { - window_manager_default_snap_prev_window_get_system_bar_rect(widget, &system_bar_top_rect_list, &system_bar_bottom_rect_list); + window_manager_default_snap_prev_window_get_system_bar_rect(widget, &system_bar_top_rect_list, + &system_bar_bottom_rect_list); } for (; start <= end; ++start) { widget_t* iter = children[start]; @@ -347,31 +358,41 @@ ret_t window_manager_default_snap_prev_window(widget_t* widget, widget_t* prev_w /* 给前面的高亮对话框叠加黑色色块 */ if (widget_is_support_highlighter(iter)) { uint8_t a = 0x0; - if (window_manager_default_snap_prev_window_draw_dialog_highlighter_and_get_alpha(iter, canvas, &a) == RET_OK) { + if (window_manager_default_snap_prev_window_draw_dialog_highlighter_and_get_alpha( + iter, canvas, &a) == RET_OK) { /* 计算最终叠加后的透明度值 */ alpha = alpha * (1 - a / 255.0f); } } /* 如果不是全屏的话,就削减 system_bar 的显示裁剪区 */ if (!is_fullscreen_window && !widget_is_normal_window(iter)) { - window_manager_default_snap_prev_window_get_system_bar_rect_diff(&system_bar_top_rect_list, &iter_rect); - window_manager_default_snap_prev_window_get_system_bar_rect_diff(&system_bar_bottom_rect_list, &iter_rect); + window_manager_default_snap_prev_window_get_system_bar_rect_diff( + &system_bar_top_rect_list, &iter_rect); + window_manager_default_snap_prev_window_get_system_bar_rect_diff( + &system_bar_bottom_rect_list, &iter_rect); } rect_merge(&r, &iter_rect); ENSURE(widget_paint(iter, canvas) == RET_OK); } } /* 检查是否还有 system_bar 的显示区域,如果有则让其绘图 */ - wm->is_animator_paint_system_bar_top = window_manager_default_snap_prev_window_check_paint_system_bar(&system_bar_top_rect_list); - wm->is_animator_paint_system_bar_bottom = window_manager_default_snap_prev_window_check_paint_system_bar(&system_bar_bottom_rect_list); + wm->is_animator_paint_system_bar_top = + window_manager_default_snap_prev_window_check_paint_system_bar(&system_bar_top_rect_list); + wm->is_animator_paint_system_bar_bottom = + window_manager_default_snap_prev_window_check_paint_system_bar( + &system_bar_bottom_rect_list); } if (dialog_highlighter != NULL) { dialog_highlighter_set_bg_clip_rect(dialog_highlighter, &r); if (!is_fullscreen_window) { /* 把 system_bar 的显示裁剪区域追加到高亮对象中 */ - slist_foreach(&system_bar_top_rect_list, window_manager_default_snap_prev_window_system_bar_top_push_clip_rect, dialog_highlighter); - slist_foreach(&system_bar_bottom_rect_list, window_manager_default_snap_prev_window_system_bar_bottom_push_clip_rect, dialog_highlighter); + slist_foreach(&system_bar_top_rect_list, + window_manager_default_snap_prev_window_system_bar_top_push_clip_rect, + dialog_highlighter); + slist_foreach(&system_bar_bottom_rect_list, + window_manager_default_snap_prev_window_system_bar_bottom_push_clip_rect, + dialog_highlighter); } if (curr_highlight != NULL) { dialog_highlighter_set_system_bar_alpha(dialog_highlighter, 0xFF - alpha); @@ -984,7 +1005,8 @@ static bool_t window_manager_default_is_dialog_highlighter(widget_t* widget) { value_t v; return_value_if_fail(widget != NULL, FALSE); - if (widget_is_support_highlighter(widget) && widget_get_prop(widget, WIDGET_PROP_HIGHLIGHT, &v) == RET_OK) { + if (widget_is_support_highlighter(widget) && + widget_get_prop(widget, WIDGET_PROP_HIGHLIGHT, &v) == RET_OK) { return TRUE; } @@ -1266,6 +1288,15 @@ static ret_t window_manager_default_get_prop(widget_t* widget, const char* name, } else if (tk_str_eq(name, WIDGET_PROP_CURR_WIN)) { value_set_pointer(v, wm->curr_win); return RET_OK; + } else if (tk_str_eq(name, WIDGET_PROP_SCREEN_SAVER_TIME)) { + value_set_uint32(v, wm->screen_saver_time); + return RET_OK; + } else if (tk_str_eq(name, WIDGET_PROP_SHOW_FPS)) { + value_set_bool(v, WINDOW_MANAGER(wm)->show_fps); + return RET_OK; + } else if (tk_str_eq(name, WIDGET_PROP_MAX_FPS)) { + value_set_uint32(v, WINDOW_MANAGER(wm)->max_fps); + return RET_OK; } return RET_NOT_FOUND; @@ -1280,6 +1311,13 @@ static ret_t window_manager_default_set_prop(widget_t* widget, const char* name, } else if (tk_str_eq(name, WIDGET_PROP_CURR_WIN)) { wm->curr_win = value_pointer(v); return RET_OK; + } else if (tk_str_eq(name, WIDGET_PROP_SCREEN_SAVER_TIME)) { + return window_manager_set_screen_saver_time(widget, value_uint32(v)); + return RET_OK; + } else if (tk_str_eq(name, WIDGET_PROP_SHOW_FPS)) { + return window_manager_set_show_fps(widget, value_bool(v)); + } else if (tk_str_eq(name, WIDGET_PROP_MAX_FPS)) { + return window_manager_set_max_fps(widget, value_uint32(v)); } return RET_NOT_FOUND; @@ -1657,10 +1695,10 @@ ret_t window_manager_paint_system_bar_top(widget_t* widget, canvas_t* c) { return_value_if_fail(wm != NULL && c != NULL, RET_BAD_PARAMS); WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i) - if (iter->vt->is_window && tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR)) { - widget_paint(iter, c); - break; - } + if (iter->vt->is_window && tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR)) { + widget_paint(iter, c); + break; + } WIDGET_FOR_EACH_CHILD_END() return RET_OK; @@ -1671,10 +1709,10 @@ ret_t window_manager_paint_system_bar_bottom(widget_t* widget, canvas_t* c) { return_value_if_fail(wm != NULL && c != NULL, RET_BAD_PARAMS); WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i) - if (iter->vt->is_window && tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR_BOTTOM)) { - widget_paint(iter, c); - break; - } + if (iter->vt->is_window && tk_str_eq(iter->vt->type, WIDGET_TYPE_SYSTEM_BAR_BOTTOM)) { + widget_paint(iter, c); + break; + } WIDGET_FOR_EACH_CHILD_END() return RET_OK; diff --git a/project/gui/lvgl/lv_conf.h b/project/gui/lvgl/lv_conf.h index e935b4017..66758b843 100644 --- a/project/gui/lvgl/lv_conf.h +++ b/project/gui/lvgl/lv_conf.h @@ -138,6 +138,8 @@ #define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning +#define LV_ATTRIBUTE_EXTERN_DATA + #define LV_USE_FLOAT 0 #define LV_FONT_MONTSERRAT_8 1 @@ -328,6 +330,10 @@ #define LV_USE_RLOTTIE 1 +#define LV_USE_VECTOR_GRAPHIC 0 +#define LV_USE_THORVG_INTERNAL 0 +#define LV_USE_THORVG_EXTERNAL 0 + #define LV_USE_FFMPEG 0 #if LV_USE_FFMPEG #define LV_FFMPEG_DUMP_FORMAT 0 diff --git a/project/gui/lvgl/lvgl.h b/project/gui/lvgl/lvgl.h index ee2162ef3..8f1e69146 100644 --- a/project/gui/lvgl/lvgl.h +++ b/project/gui/lvgl/lvgl.h @@ -34,6 +34,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_area.h" #include "src/misc/lv_async.h" #include "src/misc/lv_anim_timeline.h" @@ -113,6 +114,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" diff --git a/project/gui/lvgl/src/core/lv_global.h b/project/gui/lvgl/src/core/lv_global.h index cdacdee62..de64dd760 100644 --- a/project/gui/lvgl/src/core/lv_global.h +++ b/project/gui/lvgl/src/core/lv_global.h @@ -194,7 +194,6 @@ typedef struct _lv_global_t { void * user_data; } lv_global_t; - /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/core/lv_group.c b/project/gui/lvgl/src/core/lv_group.c index 1b5e5c93b..218698d81 100644 --- a/project/gui/lvgl/src/core/lv_group.c +++ b/project/gui/lvgl/src/core/lv_group.c @@ -48,6 +48,11 @@ void _lv_group_init(void) _lv_ll_init(group_ll_p, sizeof(lv_group_t)); } +void _lv_group_deinit(void) +{ + _lv_ll_clear(group_ll_p); +} + lv_group_t * lv_group_create(void) { lv_group_t * group = _lv_ll_ins_head(group_ll_p); diff --git a/project/gui/lvgl/src/core/lv_group.h b/project/gui/lvgl/src/core/lv_group.h index f7404c1ec..987784e6f 100644 --- a/project/gui/lvgl/src/core/lv_group.h +++ b/project/gui/lvgl/src/core/lv_group.h @@ -46,8 +46,6 @@ typedef _lv_key_t lv_key_t; typedef uint8_t lv_key_t; #endif /*DOXYGEN*/ - - /********************** * TYPEDEFS **********************/ @@ -81,7 +79,6 @@ typedef struct _lv_group_t { of list.*/ } lv_group_t; - typedef enum { LV_GROUP_REFOCUS_POLICY_NEXT = 0, LV_GROUP_REFOCUS_POLICY_PREV = 1 @@ -97,6 +94,12 @@ typedef enum { */ void _lv_group_init(void); +/** + * Deinit. the group module + * @remarks Internal function, do not call directly. + */ +void _lv_group_deinit(void); + /** * Create a new object group * @return pointer to the new object group @@ -194,7 +197,6 @@ void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb); */ void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb); - /** * Set whether the next or previous item in a group is focused if the currently focused obj is * deleted. diff --git a/project/gui/lvgl/src/core/lv_obj.c b/project/gui/lvgl/src/core/lv_obj.c index b73b9874d..605044e85 100644 --- a/project/gui/lvgl/src/core/lv_obj.c +++ b/project/gui/lvgl/src/core/lv_obj.c @@ -262,12 +262,10 @@ void lv_obj_allocate_spec_attr(lv_obj_t * obj) LV_ASSERT_OBJ(obj, MY_CLASS); if(obj->spec_attr == NULL) { - obj->spec_attr = lv_malloc(sizeof(_lv_obj_spec_attr_t)); + obj->spec_attr = lv_malloc_zeroed(sizeof(_lv_obj_spec_attr_t)); LV_ASSERT_MALLOC(obj->spec_attr); if(obj->spec_attr == NULL) return; - lv_memzero(obj->spec_attr, sizeof(_lv_obj_spec_attr_t)); - obj->spec_attr->scroll_dir = LV_DIR_ALL; obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO; } @@ -727,9 +725,7 @@ static void update_obj_state(lv_obj_t * obj, lv_state_t new_state) obj->state = new_state; - - _lv_obj_style_transition_dsc_t * ts = lv_malloc(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX); - lv_memzero(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX); + _lv_obj_style_transition_dsc_t * ts = lv_malloc_zeroed(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX); uint32_t tsi = 0; uint32_t i; for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) { diff --git a/project/gui/lvgl/src/core/lv_obj.h b/project/gui/lvgl/src/core/lv_obj.h index eacbbae39..6f9e198fa 100644 --- a/project/gui/lvgl/src/core/lv_obj.h +++ b/project/gui/lvgl/src/core/lv_obj.h @@ -196,7 +196,7 @@ enum { /** * Make the base object's class publicly available. */ -extern const lv_obj_class_t lv_obj_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_obj_class; /** * Special, rarely used attributes. @@ -256,7 +256,6 @@ typedef struct _lv_obj_t { */ lv_obj_t * lv_obj_create(lv_obj_t * parent); - /*===================== * Setter functions *====================*/ @@ -467,7 +466,6 @@ void lv_objid_builtin_destroy(void); # define LV_TRACE_OBJ_CREATE(...) #endif - #ifdef __cplusplus } /*extern "C"*/ #endif diff --git a/project/gui/lvgl/src/core/lv_obj_class.c b/project/gui/lvgl/src/core/lv_obj_class.c index cab5fd281..fd483ee05 100644 --- a/project/gui/lvgl/src/core/lv_obj_class.c +++ b/project/gui/lvgl/src/core/lv_obj_class.c @@ -47,9 +47,8 @@ lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * pa { LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent); uint32_t s = get_instance_size(class_p); - lv_obj_t * obj = lv_malloc(s); + lv_obj_t * obj = lv_malloc_zeroed(s); if(obj == NULL) return NULL; - lv_memzero(obj, s); obj->class_p = class_p; obj->parent = parent; @@ -180,7 +179,6 @@ static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj) obj->class_p = original_class_p; } - if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(class_p, obj); } diff --git a/project/gui/lvgl/src/core/lv_obj_class.h b/project/gui/lvgl/src/core/lv_obj_class.h index f3b3df7e2..2c59badac 100644 --- a/project/gui/lvgl/src/core/lv_obj_class.h +++ b/project/gui/lvgl/src/core/lv_obj_class.h @@ -22,7 +22,6 @@ extern "C" { * DEFINES *********************/ - /********************** * TYPEDEFS **********************/ @@ -104,7 +103,6 @@ bool lv_obj_is_group_def(struct _lv_obj_t * obj); * MACROS **********************/ - #ifdef __cplusplus } /*extern "C"*/ #endif diff --git a/project/gui/lvgl/src/core/lv_obj_draw.h b/project/gui/lvgl/src/core/lv_obj_draw.h index a378ec389..dd505c6db 100644 --- a/project/gui/lvgl/src/core/lv_obj_draw.h +++ b/project/gui/lvgl/src/core/lv_obj_draw.h @@ -67,7 +67,6 @@ void lv_obj_init_draw_label_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_l */ void lv_obj_init_draw_image_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_image_dsc_t * draw_dsc); - /** * Initialize a line draw descriptor from an object's styles in its current state * @param obj pointer to an object @@ -108,7 +107,6 @@ void lv_obj_refresh_ext_draw_size(struct _lv_obj_t * obj); */ int32_t _lv_obj_get_ext_draw_size(const struct _lv_obj_t * obj); - lv_layer_type_t _lv_obj_get_layer_type(const struct _lv_obj_t * obj); /********************** diff --git a/project/gui/lvgl/src/core/lv_obj_event.c b/project/gui/lvgl/src/core/lv_obj_event.c index eaa206038..10fd470c8 100644 --- a/project/gui/lvgl/src/core/lv_obj_event.c +++ b/project/gui/lvgl/src/core/lv_obj_event.c @@ -69,7 +69,6 @@ lv_result_t lv_obj_send_event(lv_obj_t * obj, lv_event_code_t event_code, void * return res; } - lv_result_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e) { const lv_obj_class_t * base; @@ -109,7 +108,6 @@ uint32_t lv_obj_get_event_count(lv_obj_t * obj) return lv_event_get_count(&obj->spec_attr->event_list); } - lv_event_dsc_t * lv_obj_get_event_dsc(lv_obj_t * obj, uint32_t index) { LV_ASSERT_NULL(obj); @@ -151,7 +149,6 @@ lv_obj_t * lv_event_get_target_obj(lv_event_t * e) return lv_event_get_target(e); } - lv_indev_t * lv_event_get_indev(lv_event_t * e) { @@ -337,7 +334,6 @@ static lv_result_t event_send_core(lv_event_t * e) return res; } - static bool event_is_bubbled(lv_event_t * e) { if(e->stop_bubbling) return false; diff --git a/project/gui/lvgl/src/core/lv_obj_event.h b/project/gui/lvgl/src/core/lv_obj_event.h index 15522d068..f4df61392 100644 --- a/project/gui/lvgl/src/core/lv_obj_event.h +++ b/project/gui/lvgl/src/core/lv_obj_event.h @@ -40,7 +40,6 @@ typedef struct { bool res; /**< true: `point` can click the object; false: it cannot*/ } lv_hit_test_info_t; - /** Cover check results.*/ typedef enum { LV_COVER_RES_COVER = 0, diff --git a/project/gui/lvgl/src/core/lv_obj_pos.c b/project/gui/lvgl/src/core/lv_obj_pos.c index c34d0910f..bc653aaaf 100644 --- a/project/gui/lvgl/src/core/lv_obj_pos.c +++ b/project/gui/lvgl/src/core/lv_obj_pos.c @@ -527,7 +527,6 @@ int32_t lv_obj_get_y_aligned(const lv_obj_t * obj) return lv_obj_get_style_y(obj, LV_PART_MAIN); } - int32_t lv_obj_get_width(const lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -602,7 +601,6 @@ void lv_obj_refr_pos(lv_obj_t * obj) { if(lv_obj_is_layout_positioned(obj)) return; - lv_obj_t * parent = lv_obj_get_parent(obj); int32_t x = lv_obj_get_style_x(obj, LV_PART_MAIN); int32_t y = lv_obj_get_style_y(obj, LV_PART_MAIN); @@ -797,7 +795,6 @@ void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, bool re area->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y); } - void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -941,8 +938,6 @@ int32_t lv_clamp_height(int32_t height, int32_t min_height, int32_t max_height, return LV_CLAMP(min_height, height, max_height); } - - /********************** * STATIC FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/core/lv_obj_pos.h b/project/gui/lvgl/src/core/lv_obj_pos.h index 97718db81..19128ae95 100644 --- a/project/gui/lvgl/src/core/lv_obj_pos.h +++ b/project/gui/lvgl/src/core/lv_obj_pos.h @@ -186,7 +186,6 @@ static inline void lv_obj_center(struct _lv_obj_t * obj) lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0); } - /** * Copy the coordinates of an object to an area * @param obj pointer to an object @@ -330,7 +329,6 @@ void lv_obj_refr_pos(struct _lv_obj_t * obj); void lv_obj_move_to(struct _lv_obj_t * obj, int32_t x, int32_t y); - void lv_obj_move_children_by(struct _lv_obj_t * obj, int32_t x_diff, int32_t y_diff, bool ignore_floating); /** diff --git a/project/gui/lvgl/src/core/lv_obj_scroll.c b/project/gui/lvgl/src/core/lv_obj_scroll.c index 45cec5244..9b86e0d17 100644 --- a/project/gui/lvgl/src/core/lv_obj_scroll.c +++ b/project/gui/lvgl/src/core/lv_obj_scroll.c @@ -301,7 +301,6 @@ void lv_obj_scroll_by_bounded(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_en } } - void lv_obj_scroll_by(lv_obj_t * obj, int32_t dx, int32_t dy, lv_anim_enable_t anim_en) { if(dx == 0 && dy == 0) return; @@ -427,7 +426,6 @@ lv_result_t _lv_obj_scroll_by_raw(lv_obj_t * obj, int32_t x, int32_t y) return LV_RESULT_OK; } - bool lv_obj_is_scrolling(const lv_obj_t * obj) { lv_indev_t * indev = lv_indev_get_next(NULL); @@ -482,7 +480,6 @@ void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_draw = true; } - bool hor_draw = false; if((dir & LV_DIR_HOR) && ((sm == LV_SCROLLBAR_MODE_ON) || @@ -670,7 +667,6 @@ void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en) * STATIC FUNCTIONS **********************/ - static void scroll_x_anim(void * obj, int32_t v) { _lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0); diff --git a/project/gui/lvgl/src/core/lv_obj_scroll.h b/project/gui/lvgl/src/core/lv_obj_scroll.h index 1884ff0eb..1de391286 100644 --- a/project/gui/lvgl/src/core/lv_obj_scroll.h +++ b/project/gui/lvgl/src/core/lv_obj_scroll.h @@ -258,7 +258,6 @@ void lv_obj_scroll_to_view(struct _lv_obj_t * obj, lv_anim_enable_t anim_en); */ void lv_obj_scroll_to_view_recursive(struct _lv_obj_t * obj, lv_anim_enable_t anim_en); - /** * Low level function to scroll by given x and y coordinates. * `LV_EVENT_SCROLL` is sent. diff --git a/project/gui/lvgl/src/core/lv_obj_style.c b/project/gui/lvgl/src/core/lv_obj_style.c index 88241d373..c0b650aaf 100644 --- a/project/gui/lvgl/src/core/lv_obj_style.c +++ b/project/gui/lvgl/src/core/lv_obj_style.c @@ -79,6 +79,11 @@ void _lv_obj_style_init(void) _lv_ll_init(style_trans_ll_p, sizeof(trans_t)); } +void _lv_obj_style_deinit(void) +{ + _lv_ll_clear(style_trans_ll_p); +} + void lv_obj_add_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector) { LV_ASSERT(obj->style_cnt < 63); @@ -119,7 +124,6 @@ void lv_obj_add_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selecto obj->styles[i].style = style; obj->styles[i].selector = selector; - #if LV_OBJ_STYLE_CACHE uint32_t * prop_is_set = part == LV_PART_MAIN ? &obj->style_main_prop_is_set : &obj->style_other_prop_is_set; if(lv_style_is_const(style)) { @@ -313,7 +317,6 @@ void lv_obj_enable_style_refresh(bool en) style_refr = en; } - static inline lv_style_value_t lv_style_prop_get_default_inlined(lv_style_prop_t prop) { const lv_color_t black = LV_COLOR_MAKE(0x00, 0x00, 0x00); @@ -378,7 +381,6 @@ static inline lv_style_value_t lv_style_prop_get_default_inlined(lv_style_prop_t } } - lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop) { LV_ASSERT_NULL(obj) @@ -466,7 +468,6 @@ void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_ } #endif - lv_obj_refresh_style(obj, selector, prop); } @@ -563,7 +564,6 @@ void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t lv_anim_start(&a); } - lv_style_value_t _lv_obj_style_apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v) { if(obj == NULL) return v; @@ -661,7 +661,6 @@ void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay) lv_anim_start(&a); } - lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt) { lv_text_align_t align = lv_obj_get_style_text_align(obj, part); @@ -703,7 +702,6 @@ lv_opa_t lv_obj_get_style_opa_recursive(const lv_obj_t * obj, lv_part_t part) return opa_final; } - /********************** * STATIC FUNCTIONS **********************/ @@ -771,7 +769,6 @@ static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t se obj->styles[i] = obj->styles[i - 1]; } - lv_memzero(&obj->styles[0], sizeof(_lv_obj_style_t)); obj->styles[0].style = lv_malloc(sizeof(lv_style_t)); lv_style_init((lv_style_t *)obj->styles[0].style); @@ -781,7 +778,6 @@ static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t se return &obj->styles[0]; } - static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v) { @@ -1052,7 +1048,6 @@ static lv_layer_type_t calculate_layer_type(lv_obj_t * obj) return LV_LAYER_TYPE_NONE; } - static void full_cache_refresh(lv_obj_t * obj, lv_part_t part) { #if LV_OBJ_STYLE_CACHE diff --git a/project/gui/lvgl/src/core/lv_obj_style.h b/project/gui/lvgl/src/core/lv_obj_style.h index 477cc54d1..04d0ef802 100644 --- a/project/gui/lvgl/src/core/lv_obj_style.h +++ b/project/gui/lvgl/src/core/lv_obj_style.h @@ -46,7 +46,6 @@ typedef enum { _LV_STYLE_STATE_CMP_DIFF_LAYOUT, /*The differences can be shown with a simple redraw*/ } _lv_style_state_cmp_t; - typedef uint32_t lv_style_selector_t; typedef struct { @@ -75,6 +74,12 @@ typedef struct { */ void _lv_obj_style_init(void); +/** + * Deinitialize the object related style manager module. + * Called by LVGL in `lv_deinit()` + */ +void _lv_obj_style_deinit(void); + /** * Add a style to an object. * @param obj pointer to an object @@ -213,7 +218,6 @@ void lv_obj_fade_in(struct _lv_obj_t * obj, uint32_t time, uint32_t delay); */ void lv_obj_fade_out(struct _lv_obj_t * obj, uint32_t time, uint32_t delay); - static inline lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector) { return selector & 0xFFFF; @@ -332,7 +336,6 @@ static inline int32_t lv_obj_get_style_transform_scale_y_safe(const struct _lv_o return zoom != 0 ? zoom : 1; } - /** * Get the `opa` style property from all parents and multiply and `>> 8` them. * @param obj the object whose opacity should be get @@ -341,7 +344,6 @@ static inline int32_t lv_obj_get_style_transform_scale_y_safe(const struct _lv_o */ lv_opa_t lv_obj_get_style_opa_recursive(const struct _lv_obj_t * obj, lv_part_t part); - /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/core/lv_obj_tree.c b/project/gui/lvgl/src/core/lv_obj_tree.c index 00b1e1418..6ea8d3195 100644 --- a/project/gui/lvgl/src/core/lv_obj_tree.c +++ b/project/gui/lvgl/src/core/lv_obj_tree.c @@ -341,7 +341,6 @@ lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t id) return lv_obj_get_child(parent, (int32_t)lv_obj_get_index(obj) + id); } - uint32_t lv_obj_get_child_cnt(const lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -483,7 +482,6 @@ static void obj_delete_core(lv_obj_t * obj) lv_free(obj); } - static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data) { lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT; diff --git a/project/gui/lvgl/src/core/lv_obj_tree.h b/project/gui/lvgl/src/core/lv_obj_tree.h index c881386d2..8bb2a4ea7 100644 --- a/project/gui/lvgl/src/core/lv_obj_tree.h +++ b/project/gui/lvgl/src/core/lv_obj_tree.h @@ -22,7 +22,6 @@ extern "C" { * DEFINES *********************/ - /********************** * TYPEDEFS **********************/ @@ -187,7 +186,6 @@ void lv_obj_dump_tree(struct _lv_obj_t * start_ob); * MACROS **********************/ - #ifdef __cplusplus } /*extern "C"*/ #endif diff --git a/project/gui/lvgl/src/core/lv_refr.c b/project/gui/lvgl/src/core/lv_refr.c index a8d918d41..e2105af1b 100644 --- a/project/gui/lvgl/src/core/lv_refr.c +++ b/project/gui/lvgl/src/core/lv_refr.c @@ -70,6 +70,10 @@ void _lv_refr_init(void) { } +void _lv_refr_deinit(void) +{ +} + void lv_refr_now(lv_display_t * disp) { lv_anim_refr_now(); @@ -401,7 +405,6 @@ void _lv_display_refr_timer(lv_timer_t * tmr) lv_memzero(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined)); disp_refr->inv_p = 0; - refr_finish: #if LV_DRAW_SW_COMPLEX == 1 @@ -483,7 +486,6 @@ static void refr_sync_areas(void) uint32_t hor_res = lv_display_get_horizontal_resolution(disp_refr); uint32_t ver_res = lv_display_get_vertical_resolution(disp_refr); - /*Iterate through invalidated areas to see if sync area should be copied*/ uint16_t i; int8_t j; @@ -588,6 +590,7 @@ static void refr_area(const lv_area_t * area_p) layer->buf_area.y1 = 0; layer->buf_area.x2 = lv_display_get_horizontal_resolution(disp_refr) - 1; layer->buf_area.y2 = lv_display_get_vertical_resolution(disp_refr) - 1; + layer->buf_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), layer->color_format); lv_area_t disp_area; lv_area_set(&disp_area, 0, 0, lv_display_get_horizontal_resolution(disp_refr) - 1, lv_display_get_vertical_resolution(disp_refr) - 1); @@ -625,6 +628,7 @@ static void refr_area(const lv_area_t * area_p) sub_area.y2 = row + max_row - 1; layer->buf = disp_refr->buf_act; layer->buf_area = sub_area; + layer->buf_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), layer->color_format); layer->clip_area = sub_area; if(sub_area.y2 > y2) sub_area.y2 = y2; row_last = sub_area.y2; @@ -658,7 +662,7 @@ static void refr_area_part(lv_layer_t * layer) } /*If the screen is transparent initialize it when the flushing is ready*/ if(lv_color_format_has_alpha(disp_refr->color_format)) { - uint32_t w = lv_area_get_width(&layer->buf_area); + uint32_t w = layer->buf_stride; uint32_t h = lv_area_get_height(&layer->buf_area); lv_draw_buf_clear(layer->buf, w, h, layer->color_format, &disp_refr->refreshed_area); } @@ -797,7 +801,6 @@ static void refr_obj_and_children(lv_layer_t * layer, lv_obj_t * top_obj) } } - static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_type_t layer_type, lv_area_t * layer_area_out) { @@ -857,7 +860,6 @@ static bool alpha_test_area_on_obj(lv_obj_t * obj, const lv_area_t * area) else return true; } - void refr_obj(lv_layer_t * layer, lv_obj_t * obj) { if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return; @@ -927,7 +929,6 @@ void refr_obj(lv_layer_t * layer, lv_obj_t * obj) } } - static uint32_t get_max_row(lv_display_t * disp, int32_t area_w, int32_t area_h) { bool has_alpha = lv_color_format_has_alpha(disp->color_format); @@ -1038,7 +1039,6 @@ static void call_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * LV_PROFILER_END; } - static void wait_for_flushing(lv_display_t * disp) { LV_PROFILER_BEGIN; diff --git a/project/gui/lvgl/src/core/lv_refr.h b/project/gui/lvgl/src/core/lv_refr.h index 5bc6c667f..2181824bb 100644 --- a/project/gui/lvgl/src/core/lv_refr.h +++ b/project/gui/lvgl/src/core/lv_refr.h @@ -46,6 +46,11 @@ extern "C" { */ void _lv_refr_init(void); +/** + * Deinitialize the screen refresh subsystem + */ +void _lv_refr_deinit(void); + /** * Redraw the invalidated areas now. * Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process diff --git a/project/gui/lvgl/src/dev/display/drm/lv_linux_drm.c b/project/gui/lvgl/src/dev/display/drm/lv_linux_drm.c index 183e681e4..ee1c71090 100644 --- a/project/gui/lvgl/src/dev/display/drm/lv_linux_drm.c +++ b/project/gui/lvgl/src/dev/display/drm/lv_linux_drm.c @@ -110,10 +110,9 @@ static void drm_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_ lv_display_t * lv_linux_drm_create(void) { - drm_dev_t * drm_dev = lv_malloc(sizeof(drm_dev_t)); + drm_dev_t * drm_dev = lv_malloc_zeroed(sizeof(drm_dev_t)); LV_ASSERT_MALLOC(drm_dev); if(drm_dev == NULL) return NULL; - lv_memzero(drm_dev, sizeof(drm_dev_t)); lv_display_t * disp = lv_display_create(800, 480); if(disp == NULL) { diff --git a/project/gui/lvgl/src/dev/display/fb/lv_linux_fbdev.c b/project/gui/lvgl/src/dev/display/fb/lv_linux_fbdev.c index ea0c18af5..8dbc149ea 100644 --- a/project/gui/lvgl/src/dev/display/fb/lv_linux_fbdev.c +++ b/project/gui/lvgl/src/dev/display/fb/lv_linux_fbdev.c @@ -91,10 +91,9 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * colo lv_display_t * lv_linux_fbdev_create(void) { - lv_linux_fb_t * dsc = lv_malloc(sizeof(lv_linux_fb_t)); + lv_linux_fb_t * dsc = lv_malloc_zeroed(sizeof(lv_linux_fb_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_linux_fb_t)); lv_display_t * disp = lv_display_create(800, 480); if(disp == NULL) { diff --git a/project/gui/lvgl/src/dev/display/tft_espi/lv_tft_espi.cpp b/project/gui/lvgl/src/dev/display/tft_espi/lv_tft_espi.cpp index 0dc9cfdcc..070c8af51 100644 --- a/project/gui/lvgl/src/dev/display/tft_espi/lv_tft_espi.cpp +++ b/project/gui/lvgl/src/dev/display/tft_espi/lv_tft_espi.cpp @@ -41,10 +41,9 @@ static void flush_cb(lv_disp_t * disp, const lv_area_t * area, lv_color_t * colo lv_disp_t * lv_tft_espi_create(uint32_t hor_res, uint32_t ver_res, void * buf, uint32_t buf_size_bytes) { - lv_tft_espi_t * dsc = (lv_tft_espi_t *)lv_malloc(sizeof(lv_tft_espi_t)); + lv_tft_espi_t * dsc = lv_malloc_zeroed(sizeof(lv_tft_espi_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_tft_espi_t)); lv_disp_t * disp = lv_disp_create(hor_res, ver_res); if(disp == NULL) { diff --git a/project/gui/lvgl/src/dev/evdev/lv_evdev.c b/project/gui/lvgl/src/dev/evdev/lv_evdev.c index 069b4ea6b..4ebd0608d 100644 --- a/project/gui/lvgl/src/dev/evdev/lv_evdev.c +++ b/project/gui/lvgl/src/dev/evdev/lv_evdev.c @@ -164,10 +164,9 @@ static void _evdev_read(lv_indev_t * indev, lv_indev_data_t * data) lv_indev_t * lv_evdev_create(lv_indev_type_t indev_type, const char * dev_path) { - lv_evdev_t * dsc = lv_malloc(sizeof(lv_evdev_t)); + lv_evdev_t * dsc = lv_malloc_zeroed(sizeof(lv_evdev_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_evdev_t)); dsc->fd = open(dev_path, O_RDONLY | O_NOCTTY | O_CLOEXEC); if(dsc->fd < 0) { diff --git a/project/gui/lvgl/src/dev/nuttx/lv_nuttx_fbdev.c b/project/gui/lvgl/src/dev/nuttx/lv_nuttx_fbdev.c index b63b79769..d4786f170 100644 --- a/project/gui/lvgl/src/dev/nuttx/lv_nuttx_fbdev.c +++ b/project/gui/lvgl/src/dev/nuttx/lv_nuttx_fbdev.c @@ -64,10 +64,9 @@ static void _display_refr_timer_cb(lv_timer_t * tmr); lv_display_t * lv_nuttx_fbdev_create(void) { - lv_nuttx_fb_t * dsc = lv_malloc(sizeof(lv_nuttx_fb_t)); + lv_nuttx_fb_t * dsc = lv_malloc_zeroed(sizeof(lv_nuttx_fb_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_nuttx_fb_t)); lv_display_t * disp = lv_display_create(800, 480); if(disp == NULL) { @@ -154,7 +153,7 @@ static void _display_refr_timer_cb(lv_timer_t * tmr) lv_nuttx_fb_t * dsc = lv_display_get_driver_data(disp); struct pollfd pfds[1]; - lv_memset(pfds, 0, sizeof(pfds)); + lv_memzero(pfds, sizeof(pfds)); pfds[0].fd = dsc->fd; pfds[0].events = POLLOUT; @@ -225,7 +224,7 @@ static int fbdev_get_pinfo(int fd, FAR struct fb_planeinfo_s * pinfo) } LV_LOG_INFO("PlaneInfo (plane %d):", pinfo->display); - LV_LOG_INFO(" mem: %p", pinfo->mem); + LV_LOG_INFO(" mem: %p", pinfo->fbmem); LV_LOG_INFO(" fblen: %zu", pinfo->fblen); LV_LOG_INFO(" stride: %u", pinfo->stride); LV_LOG_INFO(" display: %u", pinfo->display); @@ -250,7 +249,7 @@ static int fbdev_init_mem2(lv_nuttx_fb_t * dsc) struct fb_planeinfo_s pinfo; int ret; - lv_memset(&pinfo, 0, sizeof(pinfo)); + lv_memzero(&pinfo, sizeof(pinfo)); /* Get display[1] planeinfo */ diff --git a/project/gui/lvgl/src/dev/nuttx/lv_nuttx_lcd.c b/project/gui/lvgl/src/dev/nuttx/lv_nuttx_lcd.c index d7bd69653..d14fa735e 100644 --- a/project/gui/lvgl/src/dev/nuttx/lv_nuttx_lcd.c +++ b/project/gui/lvgl/src/dev/nuttx/lv_nuttx_lcd.c @@ -114,7 +114,6 @@ static int32_t align_round_up(int32_t v, uint16_t align) return (v + align - 1) & ~(align - 1); } - static void rounder_cb(lv_event_t * e) { lv_nuttx_lcd_t * lcd = lv_event_get_user_data(e); @@ -151,13 +150,12 @@ static lv_display_t * lcd_init(int fd, int hor_res, int ver_res) { lv_color_t * draw_buf = NULL; lv_color_t * draw_buf_2 = NULL; - lv_nuttx_lcd_t * lcd = lv_malloc(sizeof(lv_nuttx_lcd_t)); + lv_nuttx_lcd_t * lcd = lv_malloc_zeroed(sizeof(lv_nuttx_lcd_t)); LV_ASSERT_MALLOC(lcd); if(lcd == NULL) { LV_LOG_ERROR("lv_nuttx_lcd_t malloc failed"); return NULL; } - lv_memzero(lcd, sizeof(lv_nuttx_lcd_t)); lv_display_t * disp = lv_display_create(hor_res, ver_res); if(disp == NULL) { diff --git a/project/gui/lvgl/src/dev/nuttx/lv_nuttx_libuv.c b/project/gui/lvgl/src/dev/nuttx/lv_nuttx_libuv.c index 58642f1cc..87a99902a 100644 --- a/project/gui/lvgl/src/dev/nuttx/lv_nuttx_libuv.c +++ b/project/gui/lvgl/src/dev/nuttx/lv_nuttx_libuv.c @@ -75,10 +75,9 @@ void * lv_nuttx_uv_init(lv_nuttx_uv_t * uv_info) lv_nuttx_uv_ctx_t * uv_ctx; int ret; - uv_ctx = lv_malloc(sizeof(lv_nuttx_uv_ctx_t)); + uv_ctx = lv_malloc_zeroed(sizeof(lv_nuttx_uv_ctx_t)); LV_ASSERT_MALLOC(uv_ctx); if(uv_ctx == NULL) return NULL; - lv_memset(uv_ctx, 0, sizeof(lv_nuttx_uv_ctx_t)); if((ret = lv_nuttx_uv_timer_init(uv_info, uv_ctx)) < 0) { LV_LOG_ERROR("lv_nuttx_uv_timer_init fail : %d", ret); diff --git a/project/gui/lvgl/src/dev/sdl/lv_sdl_keyboard.c b/project/gui/lvgl/src/dev/sdl/lv_sdl_keyboard.c index a7574130d..0d803be6b 100644 --- a/project/gui/lvgl/src/dev/sdl/lv_sdl_keyboard.c +++ b/project/gui/lvgl/src/dev/sdl/lv_sdl_keyboard.c @@ -42,10 +42,9 @@ static uint32_t keycode_to_ctrl_key(SDL_Keycode sdl_key); lv_indev_t * lv_sdl_keyboard_create(void) { - lv_sdl_keyboard_t * dsc = lv_malloc(sizeof(lv_sdl_keyboard_t)); + lv_sdl_keyboard_t * dsc = lv_malloc_zeroed(sizeof(lv_sdl_keyboard_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_sdl_keyboard_t)); lv_indev_t * indev = lv_indev_create(); LV_ASSERT_MALLOC(indev); @@ -115,7 +114,6 @@ void _lv_sdl_keyboard_handler(SDL_Event * event) if(indev == NULL) return; lv_sdl_keyboard_t * dsc = lv_indev_get_driver_data(indev); - /* We only care about SDL_KEYDOWN and SDL_TEXTINPUT events */ switch(event->type) { case SDL_KEYDOWN: { /*Button press*/ @@ -141,7 +139,6 @@ void _lv_sdl_keyboard_handler(SDL_Event * event) } } - /** * Convert a SDL key code to it's LV_KEY_* counterpart or return '\0' if it's not a control character. * @param sdl_key the key code diff --git a/project/gui/lvgl/src/dev/sdl/lv_sdl_mouse.c b/project/gui/lvgl/src/dev/sdl/lv_sdl_mouse.c index 0fdedcd12..47b34fb57 100644 --- a/project/gui/lvgl/src/dev/sdl/lv_sdl_mouse.c +++ b/project/gui/lvgl/src/dev/sdl/lv_sdl_mouse.c @@ -42,10 +42,9 @@ typedef struct { lv_indev_t * lv_sdl_mouse_create(void) { - lv_sdl_mouse_t * dsc = lv_malloc(sizeof(lv_sdl_mouse_t)); + lv_sdl_mouse_t * dsc = lv_malloc_zeroed(sizeof(lv_sdl_mouse_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_sdl_mouse_t)); lv_indev_t * indev = lv_indev_create(); LV_ASSERT_MALLOC(indev); @@ -75,7 +74,6 @@ static void sdl_mouse_read(lv_indev_t * indev, lv_indev_data_t * data) data->state = dsc->left_button_down ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; } - void _lv_sdl_mouse_handler(SDL_Event * event) { uint32_t win_id = UINT32_MAX; diff --git a/project/gui/lvgl/src/dev/sdl/lv_sdl_mousewheel.c b/project/gui/lvgl/src/dev/sdl/lv_sdl_mousewheel.c index 822b8d1b3..ca687ac82 100644 --- a/project/gui/lvgl/src/dev/sdl/lv_sdl_mousewheel.c +++ b/project/gui/lvgl/src/dev/sdl/lv_sdl_mousewheel.c @@ -38,10 +38,9 @@ typedef struct { lv_indev_t * lv_sdl_mousewheel_create(void) { - lv_sdl_mousewheel_t * dsc = lv_malloc(sizeof(lv_sdl_mousewheel_t)); + lv_sdl_mousewheel_t * dsc = lv_malloc_zeroed(sizeof(lv_sdl_mousewheel_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_sdl_mousewheel_t)); lv_indev_t * indev = lv_indev_create(); if(indev == NULL) { diff --git a/project/gui/lvgl/src/dev/sdl/lv_sdl_window.c b/project/gui/lvgl/src/dev/sdl/lv_sdl_window.c index 02cc6678c..cb3cdb68b 100644 --- a/project/gui/lvgl/src/dev/sdl/lv_sdl_window.c +++ b/project/gui/lvgl/src/dev/sdl/lv_sdl_window.c @@ -78,10 +78,9 @@ lv_display_t * lv_sdl_window_create(int32_t hor_res, int32_t ver_res) inited = true; } - lv_sdl_window_t * dsc = lv_malloc(sizeof(lv_sdl_window_t)); + lv_sdl_window_t * dsc = lv_malloc_zeroed(sizeof(lv_sdl_window_t)); LV_ASSERT_MALLOC(dsc); if(dsc == NULL) return NULL; - lv_memzero(dsc, sizeof(lv_sdl_window_t)); lv_display_t * disp = lv_display_create(hor_res, ver_res); if(disp == NULL) { @@ -162,7 +161,6 @@ void lv_sdl_quit() * STATIC FUNCTIONS **********************/ - static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) { lv_sdl_window_t * dsc = lv_display_get_driver_data(disp); diff --git a/project/gui/lvgl/src/display/lv_display.c b/project/gui/lvgl/src/display/lv_display.c index 3a30b75ae..2c8fcc2b6 100644 --- a/project/gui/lvgl/src/display/lv_display.c +++ b/project/gui/lvgl/src/display/lv_display.c @@ -72,16 +72,16 @@ lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res) disp->dpi = LV_DPI_DEF; disp->color_format = LV_COLOR_FORMAT_NATIVE; - disp->layer_head = lv_malloc(sizeof(lv_layer_t)); + disp->layer_head = lv_malloc_zeroed(sizeof(lv_layer_t)); LV_ASSERT_MALLOC(disp->layer_head); if(disp->layer_head == NULL) return NULL; - lv_memzero(disp->layer_head, sizeof(lv_layer_t)); if(disp->layer_init) disp->layer_init(disp, disp->layer_head); disp->layer_head->buf_area.x1 = 0; disp->layer_head->buf_area.y1 = 0; disp->layer_head->buf_area.x2 = hor_res - 1; disp->layer_head->buf_area.y2 = ver_res - 1; + disp->layer_head->buf_stride = lv_draw_buf_width_to_stride(hor_res, LV_COLOR_FORMAT_NATIVE); disp->layer_head->color_format = disp->color_format; disp->inv_en_cnt = 1; @@ -135,7 +135,6 @@ lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res) return disp; } - void lv_display_remove(lv_display_t * disp) { bool was_default = false; @@ -398,7 +397,6 @@ void lv_display_set_flush_cb(lv_display_t * disp, lv_display_flush_cb_t flush_cb disp->flush_cb = flush_cb; } - void lv_display_set_flush_wait_cb(lv_display_t * disp, lv_display_flush_wait_cb_t wait_cb) { if(disp == NULL) disp = lv_display_get_default(); @@ -440,7 +438,6 @@ bool lv_display_get_antialiasing(lv_display_t * disp) return disp->antialiasing; } - LV_ATTRIBUTE_FLUSH_READY void lv_display_flush_ready(lv_display_t * disp) { disp->flushing = 0; @@ -565,7 +562,6 @@ void lv_screen_load_anim(lv_obj_t * new_scr, lv_screen_load_anim_t anim_type, ui lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0); lv_obj_remove_local_style_prop(lv_screen_active(), LV_STYLE_OPA, 0); - /*Shortcut for immediate load*/ if(time == 0 && delay == 0) { @@ -688,7 +684,6 @@ uint32_t lv_display_get_event_count(lv_display_t * disp) return lv_event_get_count(&disp->event_list); } - lv_event_dsc_t * lv_display_get_event_dsc(lv_display_t * disp, uint32_t index) { LV_ASSERT_NULL(disp); diff --git a/project/gui/lvgl/src/draw/Makefile b/project/gui/lvgl/src/draw/Makefile index c21fa3b1c..248592154 100644 --- a/project/gui/lvgl/src/draw/Makefile +++ b/project/gui/lvgl/src/draw/Makefile @@ -12,3 +12,4 @@ obj-y += lv_draw_triangle.o obj-y += lv_draw.o obj-y += lv_image_buf.o obj-y += lv_image_decoder.o +obj-y += lv_draw_vector.o diff --git a/project/gui/lvgl/src/draw/lv_draw.c b/project/gui/lvgl/src/draw/lv_draw.c index c35deca1e..63ca87030 100644 --- a/project/gui/lvgl/src/draw/lv_draw.c +++ b/project/gui/lvgl/src/draw/lv_draw.c @@ -50,10 +50,26 @@ void lv_draw_init(void) #endif } +void lv_draw_deinit(void) +{ +#if LV_USE_OS + lv_thread_sync_delete(&_draw_info.sync); +#endif + + lv_draw_unit_t * u = _draw_info.unit_head; + while(u) { + lv_draw_unit_t * cur_unit = u; + u = u->next; + + if(cur_unit->delete_cb) cur_unit->delete_cb(cur_unit); + lv_free(cur_unit); + } + _draw_info.unit_head = NULL; +} + void * lv_draw_create_unit(size_t size) { - lv_draw_unit_t * new_unit = lv_malloc(size); - lv_memzero(new_unit, size); + lv_draw_unit_t * new_unit = lv_malloc_zeroed(size); new_unit->next = _draw_info.unit_head; _draw_info.unit_head = new_unit; @@ -64,8 +80,7 @@ void * lv_draw_create_unit(size_t size) lv_draw_task_t * lv_draw_add_task(lv_layer_t * layer, const lv_area_t * coords) { LV_PROFILER_BEGIN; - lv_draw_task_t * new_task = lv_malloc(sizeof(lv_draw_task_t)); - lv_memzero(new_task, sizeof(*new_task)); + lv_draw_task_t * new_task = lv_malloc_zeroed(sizeof(lv_draw_task_t)); new_task->area = *coords; new_task->clip_area = layer->clip_area; @@ -86,7 +101,6 @@ lv_draw_task_t * lv_draw_add_task(lv_layer_t * layer, const lv_area_t * coords) return new_task; } - void lv_draw_finalize_task_creation(lv_layer_t * layer, lv_draw_task_t * t) { lv_draw_dsc_base_t * base_dsc = t->draw_dsc; @@ -132,17 +146,17 @@ void lv_draw_finalize_task_creation(lv_layer_t * layer, lv_draw_task_t * t) void lv_draw_dispatch(void) { LV_PROFILER_BEGIN; - bool one_taken = false; + bool render_running = false; lv_display_t * disp = lv_display_get_next(NULL); while(disp) { lv_layer_t * layer = disp->layer_head; while(layer) { - bool ret = lv_draw_dispatch_layer(disp, layer); - if(ret) one_taken = true; + if(lv_draw_dispatch_layer(disp, layer)) + render_running = true; layer = layer->next; } - if(!one_taken) { + if(!render_running) { lv_draw_dispatch_request(); } disp = lv_display_get_next(disp); @@ -208,7 +222,7 @@ bool lv_draw_dispatch_layer(struct _lv_display_t * disp, lv_layer_t * layer) t = t_next; } - bool one_taken = false; + bool render_running = false; /*This layer is ready, enable blending its buffer*/ if(layer->parent && layer->all_tasks_added && layer->draw_task_head == NULL) { @@ -246,17 +260,13 @@ bool lv_draw_dispatch_layer(struct _lv_display_t * disp, lv_layer_t * layer) lv_draw_unit_t * u = _draw_info.unit_head; while(u) { int32_t taken_cnt = u->dispatch_cb(u, layer); - if(taken_cnt < 0) { - break; - } - if(taken_cnt > 0) one_taken = true; + if(taken_cnt >= 0) render_running = true; u = u->next; } } } - return one_taken; - + return render_running; } void lv_draw_dispatch_wait_for_request(void) @@ -313,14 +323,14 @@ lv_draw_task_t * lv_draw_get_next_available_task(lv_layer_t * layer, lv_draw_tas lv_layer_t * lv_draw_layer_create(lv_layer_t * parent_layer, lv_color_format_t color_format, const lv_area_t * area) { lv_display_t * disp = _lv_refr_get_disp_refreshing(); - lv_layer_t * new_layer = lv_malloc(sizeof(lv_layer_t)); + lv_layer_t * new_layer = lv_malloc_zeroed(sizeof(lv_layer_t)); LV_ASSERT_MALLOC(new_layer); if(new_layer == NULL) return NULL; - lv_memzero(new_layer, sizeof(lv_layer_t)); new_layer->parent = parent_layer; new_layer->clip_area = *area; new_layer->buf_area = *area; + new_layer->buf_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), color_format); new_layer->color_format = color_format; if(disp->layer_head) { @@ -337,11 +347,12 @@ lv_layer_t * lv_draw_layer_create(lv_layer_t * parent_layer, lv_color_format_t c void * lv_draw_layer_alloc_buf(lv_layer_t * layer) { + int32_t w = lv_area_get_width(&layer->buf_area); + uint32_t stride = lv_draw_buf_width_to_stride(w, layer->color_format); + /*If the buffer of the layer is not allocated yet, allocate it now*/ if(layer->buf == NULL) { - int32_t w = lv_area_get_width(&layer->buf_area); int32_t h = lv_area_get_height(&layer->buf_area); - int32_t stride = lv_draw_buf_width_to_stride(w, layer->color_format); uint32_t layer_size_byte = h * stride; layer->buf_unaligned = lv_draw_buf_malloc(layer_size_byte, layer->color_format); @@ -356,7 +367,6 @@ void * lv_draw_layer_alloc_buf(lv_layer_t * layer) _draw_info.used_memory_for_layers_kb += kb; LV_LOG_INFO("Layer memory used: %d kB\n", _draw_info.used_memory_for_layers_kb); - if(lv_color_format_has_alpha(layer->color_format)) { lv_area_t a; a.x1 = 0; @@ -367,13 +377,16 @@ void * lv_draw_layer_alloc_buf(lv_layer_t * layer) } } + /*Set the stride also for static allocated buffers as well as for new dynamically allocated*/ + layer->buf_stride = stride; + + /*Make sure the buffer address is aligned in case of already allocated buffers*/ return lv_draw_buf_align(layer->buf, layer->color_format); } void * lv_draw_layer_go_to_xy(lv_layer_t * layer, int32_t x, int32_t y) { - uint32_t stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), layer->color_format); - return lv_draw_buf_go_to_xy(layer->buf, stride, layer->color_format, x, y); + return lv_draw_buf_go_to_xy(layer->buf, layer->buf_stride, layer->color_format, x, y); } @@ -407,4 +420,3 @@ static bool is_independent(lv_layer_t * layer, lv_draw_task_t * t_check) return true; } - diff --git a/project/gui/lvgl/src/draw/lv_draw.h b/project/gui/lvgl/src/draw/lv_draw.h index 52ada8118..6ee9a36d1 100644 --- a/project/gui/lvgl/src/draw/lv_draw.h +++ b/project/gui/lvgl/src/draw/lv_draw.h @@ -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 { @@ -116,9 +117,13 @@ typedef struct _lv_draw_unit_t { * A draw task should be assign only if the draw unit can draw it too * @param draw_unit pointer to the draw unit * @param layer pointer to a layer on which the draw task should be drawn - * @return >=0: The number of taken draw task - * -1: There where no available draw tasks at all. - * Also means to no call the dispatcher of the other draw units as there is no draw task to take + * @return >=0: The number of taken draw task: + * 0 means the task has not yet been completed. + * 1 means a new task has been accepted. + * -1: The draw unit wanted to work on a task but couldn't do that + * due to some errors (e.g. out of memory). + * It signals that LVGL should call the dispatcher later again + * to let draw unit try to start the rendering again. */ int32_t (*dispatch_cb)(struct _lv_draw_unit_t * draw_unit, struct _lv_layer_t * layer); @@ -129,8 +134,14 @@ typedef struct _lv_draw_unit_t { * @return */ int32_t (*evaluate_cb)(struct _lv_draw_unit_t * draw_unit, lv_draw_task_t * task); -} lv_draw_unit_t; + /** + * Called to delete draw unit. + * @param draw_unit + * @return + */ + int32_t (*delete_cb)(struct _lv_draw_unit_t * draw_unit); +} lv_draw_unit_t; typedef struct _lv_layer_t { @@ -190,6 +201,8 @@ typedef struct { void lv_draw_init(void); +void lv_draw_deinit(void); + /** * Allocate a new draw unit with the given size and appends it to the list of draw units * @param size the size to allocate. E.g. `sizeof(my_draw_unit_t)`, diff --git a/project/gui/lvgl/src/draw/lv_draw_buf.c b/project/gui/lvgl/src/draw/lv_draw_buf.c index 23b8565d8..7a0fd5fb1 100644 --- a/project/gui/lvgl/src/draw/lv_draw_buf.c +++ b/project/gui/lvgl/src/draw/lv_draw_buf.c @@ -11,7 +11,6 @@ #include "../stdlib/lv_string.h" #include "../core/lv_global.h" - /********************* * DEFINES *********************/ @@ -67,7 +66,6 @@ lv_draw_buf_handlers_t * lv_draw_buf_get_handlers(void) return &handlers; } - uint32_t lv_draw_buf_width_to_stride(uint32_t w, lv_color_format_t color_format) { if(handlers.width_to_stride_cb) return handlers.width_to_stride_cb(w, color_format); @@ -108,7 +106,6 @@ void lv_draw_buf_clear(void * buf, uint32_t w, uint32_t h, lv_color_format_t col if(handlers.buf_clear_cb) handlers.buf_clear_cb(buf, w, h, color_format, a); } - void lv_draw_buf_copy(void * dest_buf, uint32_t dest_w, uint32_t dest_h, const lv_area_t * dest_area_to_copy, void * src_buf, uint32_t src_w, uint32_t src_h, const lv_area_t * src_area_to_copy, lv_color_format_t color_format) @@ -122,7 +119,6 @@ void lv_draw_buf_copy(void * dest_buf, uint32_t dest_w, uint32_t dest_h, const l * STATIC FUNCTIONS **********************/ - static void * buf_malloc(size_t size_bytes, lv_color_format_t color_format) { LV_UNUSED(color_format); @@ -218,4 +214,3 @@ static void buf_copy(void * dest_buf, uint32_t dest_w, uint32_t dest_h, const lv src_bufc += src_stride; } } - diff --git a/project/gui/lvgl/src/draw/lv_draw_buf.h b/project/gui/lvgl/src/draw/lv_draw_buf.h index d4a134bf8..f9e8b95fe 100644 --- a/project/gui/lvgl/src/draw/lv_draw_buf.h +++ b/project/gui/lvgl/src/draw/lv_draw_buf.h @@ -76,7 +76,7 @@ lv_draw_buf_handlers_t * lv_draw_buf_get_handlers(void); /** * Allocate a buffer with the given size. It might allocate slightly larger buffer to fulfill the alignment requirements. * @param size the size to allocate in bytes - * @param color_format the color format of the buffer to allcoate + * @param color_format the color format of the buffer to allocate * @return the allocated buffer. * @note The returned value can be saved in draw_buf->buf * @note lv_draw_buf_align can be sued the align the returned pointer @@ -90,7 +90,7 @@ void * lv_draw_buf_malloc(size_t size_bytes, lv_color_format_t color_format); void lv_draw_buf_free(void * buf); /** - * Align the address of a buffer. The buffer needs to be large enough for the real data after alignement + * Align the address of a buffer. The buffer needs to be large enough for the real data after alignment * @param buf the data to align * @param color_format the color format of the buffer * @return the aligned buffer diff --git a/project/gui/lvgl/src/draw/lv_draw_image.c b/project/gui/lvgl/src/draw/lv_draw_image.c index 81943a74a..9f94a1517 100644 --- a/project/gui/lvgl/src/draw/lv_draw_image.c +++ b/project/gui/lvgl/src/draw/lv_draw_image.c @@ -48,7 +48,6 @@ void lv_draw_image_dsc_init(lv_draw_image_dsc_t * dsc) dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0; } - void lv_draw_layer(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords) { lv_draw_task_t * t = lv_draw_add_task(layer, coords); @@ -64,7 +63,6 @@ void lv_draw_layer(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv lv_draw_finalize_task_creation(layer, t); } - void lv_draw_image(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords) { if(dsc->src == NULL) { @@ -73,7 +71,6 @@ void lv_draw_image(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv } if(dsc->opa <= LV_OPA_MIN) return; - LV_PROFILER_BEGIN; lv_draw_image_dsc_t * new_image_dsc = lv_malloc(sizeof(*dsc)); @@ -129,4 +126,3 @@ lv_image_src_t lv_image_src_get_type(const void * src) /********************** * STATIC FUNCTIONS **********************/ - diff --git a/project/gui/lvgl/src/draw/lv_draw_image.h b/project/gui/lvgl/src/draw/lv_draw_image.h index 3d50419db..176d73e85 100644 --- a/project/gui/lvgl/src/draw/lv_draw_image.h +++ b/project/gui/lvgl/src/draw/lv_draw_image.h @@ -42,7 +42,6 @@ typedef struct _lv_draw_image_dsc_t { const void * src; lv_image_header_t header; - int32_t rotation; int32_t scale_x; int32_t scale_y; @@ -56,6 +55,7 @@ typedef struct _lv_draw_image_dsc_t { int32_t frame_id; uint16_t antialias : 1; + uint16_t tile : 1; lv_draw_image_sup_t * sup; } lv_draw_image_dsc_t; diff --git a/project/gui/lvgl/src/draw/lv_draw_label.c b/project/gui/lvgl/src/draw/lv_draw_label.c index 2263bb1c8..74071438f 100644 --- a/project/gui/lvgl/src/draw/lv_draw_label.c +++ b/project/gui/lvgl/src/draw/lv_draw_label.c @@ -60,7 +60,6 @@ void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc) dsc->bidi_dir = LV_BASE_DIR_LTR; } - void lv_draw_letter_dsc_init(lv_draw_glyph_dsc_t * dsc) { lv_memzero(dsc, sizeof(lv_draw_glyph_dsc_t)); @@ -91,8 +90,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(lv_layer_t * layer, const lv_draw_label lv_draw_finalize_task_creation(layer, t); } - - LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(lv_layer_t * layer, lv_draw_label_dsc_t * dsc, const lv_point_t * point, uint32_t unicode_letter) { @@ -307,8 +304,6 @@ void lv_draw_label_iterate_letters(lv_draw_unit_t * draw_unit, const lv_draw_lab } } - - if(sel_start != 0xFFFF && sel_end != 0xFFFF && logical_char_pos >= sel_start && logical_char_pos < sel_end) { draw_letter_dsc.color = dsc->sel_color; fill_dsc.color = dsc->sel_bg_color; @@ -318,7 +313,6 @@ void lv_draw_label_iterate_letters(lv_draw_unit_t * draw_unit, const lv_draw_lab draw_letter_dsc.color = dsc->color; } - draw_letter(draw_unit, &draw_letter_dsc, &pos, font, letter, cb); if(letter_w > 0) { @@ -359,7 +353,6 @@ void lv_draw_label_iterate_letters(lv_draw_unit_t * draw_unit, const lv_draw_lab LV_ASSERT_MEM_INTEGRITY(); } - /********************** * STATIC FUNCTIONS **********************/ @@ -423,4 +416,3 @@ static void draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, cb(draw_unit, dsc, NULL, NULL); LV_PROFILER_END; } - diff --git a/project/gui/lvgl/src/draw/lv_draw_label.h b/project/gui/lvgl/src/draw/lv_draw_label.h index 6682d157f..69869dee7 100644 --- a/project/gui/lvgl/src/draw/lv_draw_label.h +++ b/project/gui/lvgl/src/draw/lv_draw_label.h @@ -46,7 +46,6 @@ typedef struct _lv_draw_label_hint_t { int32_t coord_y; } lv_draw_label_hint_t; - typedef struct { lv_draw_dsc_base_t base; @@ -66,9 +65,11 @@ typedef struct { lv_text_align_t align; lv_text_flag_t flag; lv_text_decor_t decor : 3; - lv_blend_mode_t blend_mode: 3; -uint8_t text_local : - 1; /**< 1: malloc buffer and copy `text` there. 0: `text` is const and it's pointer will be valid during rendering*/ + lv_blend_mode_t blend_mode : 3; + /** + * < 1: malloc buffer and copy `text` there. + * 0: `text` is const and it's pointer will be valid during rendering.*/ + uint8_t text_local : 1; lv_draw_label_hint_t * hint; } lv_draw_label_dsc_t; @@ -89,7 +90,6 @@ typedef struct { lv_opa_t opa; } lv_draw_glyph_dsc_t; - typedef void(*lv_draw_letter_cb_t)(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, lv_draw_fill_dsc_t * fill_dsc, const lv_area_t * fill_area); diff --git a/project/gui/lvgl/src/draw/lv_draw_line.h b/project/gui/lvgl/src/draw/lv_draw_line.h index dcf22e684..773130cf3 100644 --- a/project/gui/lvgl/src/draw/lv_draw_line.h +++ b/project/gui/lvgl/src/draw/lv_draw_line.h @@ -58,7 +58,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc); */ void lv_draw_line(struct _lv_layer_t * layer, const lv_draw_line_dsc_t * dsc); - /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/draw/lv_draw_mask.h b/project/gui/lvgl/src/draw/lv_draw_mask.h index b8f431111..6c65e4f47 100644 --- a/project/gui/lvgl/src/draw/lv_draw_mask.h +++ b/project/gui/lvgl/src/draw/lv_draw_mask.h @@ -49,7 +49,6 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_mask_rect_dsc_init(lv_draw_mask_rect_dsc_t * */ void lv_draw_mask_rect(struct _lv_layer_t * layer, const lv_draw_mask_rect_dsc_t * dsc); - /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/draw/lv_draw_rect.c b/project/gui/lvgl/src/draw/lv_draw_rect.c index 4396ff7d7..682f4eca5 100644 --- a/project/gui/lvgl/src/draw/lv_draw_rect.c +++ b/project/gui/lvgl/src/draw/lv_draw_rect.c @@ -80,7 +80,6 @@ void lv_draw_bg_image_dsc_init(lv_draw_bg_image_dsc_t * dsc) dsc->opa = LV_OPA_COVER; } - void lv_draw_rect(lv_layer_t * layer, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) { @@ -174,7 +173,6 @@ void lv_draw_rect(lv_layer_t * layer, const lv_draw_rect_dsc_t * dsc, const lv_a t = lv_draw_add_task(layer, coords); - lv_image_src_t src_type = lv_image_src_get_type(dsc->bg_image_src); lv_result_t res = LV_RESULT_OK; lv_image_header_t header; @@ -243,7 +241,6 @@ void lv_draw_rect(lv_layer_t * layer, const lv_draw_rect_dsc_t * dsc, const lv_a LV_PROFILER_END; } - /********************** * STATIC FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/draw/lv_draw_rect.h b/project/gui/lvgl/src/draw/lv_draw_rect.h index b3fec02d4..2d1d6f53f 100644 --- a/project/gui/lvgl/src/draw/lv_draw_rect.h +++ b/project/gui/lvgl/src/draw/lv_draw_rect.h @@ -133,7 +133,6 @@ void lv_draw_box_shadow_dsc_init(lv_draw_box_shadow_dsc_t * dsc); void lv_draw_bg_image_dsc_init(lv_draw_bg_image_dsc_t * dsc); - /** * Draw a rectangle * @param layer pointer to a layer diff --git a/project/gui/lvgl/src/draw/lv_draw_vector.c b/project/gui/lvgl/src/draw/lv_draw_vector.c new file mode 100644 index 000000000..34d221eaf --- /dev/null +++ b/project/gui/lvgl/src/draw/lv_draw_vector.c @@ -0,0 +1,751 @@ +/** +* @file lv_draw_vector.c + * + */ + +/********************* +* INCLUDES + *********************/ +#include "lv_draw_vector.h" + +#if LV_USE_VECTOR_GRAPHIC + +#include "../misc/lv_ll.h" +#include "../stdlib/lv_string.h" +#include +#include + +/********************* +* DEFINES + *********************/ + +#ifndef M_PI + #define M_PI 3.1415926f +#endif + +#define CHECK_AND_RESIZE_PATH_CONTAINER(P, N) \ + do { \ + if ((lv_array_length(&(P)->ops) + (N)) > lv_array_capacity(&(P)->ops)) { \ + lv_array_resize(&(P)->ops, ((P)->ops.capacity << 1)); \ + } \ + if ((lv_array_length(&(P)->points) + (N)) > lv_array_capacity(&(P)->points)) { \ + lv_array_resize(&(P)->points, ((P)->points.capacity << 1)); \ + } \ + } while(0) + +/********************** +* TYPEDEFS + **********************/ + +typedef struct { + lv_vector_path_t * path; + lv_vector_draw_dsc_t dsc; +} _lv_vector_draw_task; + +/********************** +* STATIC PROTOTYPES + **********************/ + +static bool _is_identity_or_translation(const lv_matrix_t * matrix) +{ + return (matrix->m[0][0] == 1.0f && + matrix->m[0][1] == 0.0f && + matrix->m[1][0] == 0.0f && + matrix->m[1][1] == 1.0f && + matrix->m[2][0] == 0.0f && + matrix->m[2][1] == 0.0f && + matrix->m[2][2] == 1.0f); +} + +static void _multiply_matrix(lv_matrix_t * matrix, const lv_matrix_t * mul) +{ + // TODO: use NEON to optimize this function on ARM architecture. + lv_matrix_t tmp; + + for(int y = 0; y < 3; y++) { + for(int x = 0; x < 3; x++) { + tmp.m[y][x] = (matrix->m[y][0] * mul->m[0][x]) + + (matrix->m[y][1] * mul->m[1][x]) + + (matrix->m[y][2] * mul->m[2][x]); + } + } + + lv_memcpy(matrix, &tmp, sizeof(lv_matrix_t)); +} + +static void _copy_draw_dsc(lv_vector_draw_dsc_t * dst, const lv_vector_draw_dsc_t * src) +{ + dst->fill_dsc.style = src->fill_dsc.style; + dst->fill_dsc.color = src->fill_dsc.color; + dst->fill_dsc.opa = src->fill_dsc.opa; + dst->fill_dsc.fill_rule = src->fill_dsc.fill_rule; + dst->fill_dsc.gradient.style = src->fill_dsc.gradient.style; + dst->fill_dsc.gradient.cx = src->fill_dsc.gradient.cx; + dst->fill_dsc.gradient.cy = src->fill_dsc.gradient.cy; + dst->fill_dsc.gradient.cr = src->fill_dsc.gradient.cr; + dst->fill_dsc.gradient.spread = src->fill_dsc.gradient.spread; + lv_memcpy(&(dst->fill_dsc.gradient.grad), &(src->fill_dsc.gradient.grad), sizeof(lv_grad_dsc_t)); + lv_memcpy(&(dst->fill_dsc.img_dsc), &(src->fill_dsc.img_dsc), sizeof(lv_draw_image_dsc_t)); + lv_memcpy(&(dst->fill_dsc.matrix), &(src->fill_dsc.matrix), sizeof(lv_matrix_t)); + + dst->stroke_dsc.style = src->stroke_dsc.style; + dst->stroke_dsc.color = src->stroke_dsc.color; + dst->stroke_dsc.opa = src->stroke_dsc.opa; + dst->stroke_dsc.width = src->stroke_dsc.width; + dst->stroke_dsc.cap = src->stroke_dsc.cap; + dst->stroke_dsc.join = src->stroke_dsc.join; + dst->stroke_dsc.miter_limit = src->stroke_dsc.miter_limit; + lv_array_copy(&(dst->stroke_dsc.dash_pattern), &(src->stroke_dsc.dash_pattern)); + dst->stroke_dsc.gradient.style = src->stroke_dsc.gradient.style; + dst->stroke_dsc.gradient.cx = src->stroke_dsc.gradient.cx; + dst->stroke_dsc.gradient.cy = src->stroke_dsc.gradient.cy; + dst->stroke_dsc.gradient.cr = src->stroke_dsc.gradient.cr; + dst->stroke_dsc.gradient.spread = src->fill_dsc.gradient.spread; + lv_memcpy(&(dst->stroke_dsc.gradient.grad), &(src->stroke_dsc.gradient.grad), sizeof(lv_grad_dsc_t)); + lv_memcpy(&(dst->stroke_dsc.matrix), &(src->stroke_dsc.matrix), sizeof(lv_matrix_t)); + + dst->blend_mode = src->blend_mode; + lv_memcpy(&(dst->matrix), &(src->matrix), sizeof(lv_matrix_t)); + lv_area_copy(&(dst->scissor_area), &(src->scissor_area)); +} +/********************** +* GLOBAL FUNCTIONS + **********************/ + +/* matrix functions */ +void lv_matrix_identity(lv_matrix_t * matrix) +{ + matrix->m[0][0] = 1.0f; + matrix->m[0][1] = 0.0f; + matrix->m[0][2] = 0.0f; + matrix->m[1][0] = 0.0f; + matrix->m[1][1] = 1.0f; + matrix->m[1][2] = 0.0f; + matrix->m[2][0] = 0.0f; + matrix->m[2][1] = 0.0f; + matrix->m[2][2] = 1.0f; +} + +void lv_matrix_translate(lv_matrix_t * matrix, float dx, float dy) +{ + if(_is_identity_or_translation(matrix)) { + // optimization for matrix translation. + matrix->m[0][2] += dx; + matrix->m[1][2] += dy; + return; + } + + lv_matrix_t tlm = {{ + {1.0f, 0.0f, dx}, + {0.0f, 1.0f, dy}, + {0.0f, 0.0f, 1.0f}, + } + }; + + _multiply_matrix(matrix, &tlm); +} + +void lv_matrix_scale(lv_matrix_t * matrix, float scale_x, float scale_y) +{ + lv_matrix_t scm = {{ + {scale_x, 0.0f, 0.0f}, + {0.0f, scale_y, 0.0f}, + {0.0f, 0.0f, 1.0f}, + } + }; + + _multiply_matrix(matrix, &scm); +} + +void lv_matrix_rotate(lv_matrix_t * matrix, float degree) +{ + float radian = degree / 180.0f * (float)M_PI; + float cos_r = cosf(radian); + float sin_r = sinf(radian); + + lv_matrix_t rtm = {{ + {cos_r, -sin_r, 0.0f}, + {sin_r, cos_r, 0.0f}, + {0.0f, 0.0f, 1.0f}, + } + }; + + _multiply_matrix(matrix, &rtm); +} + +void lv_matrix_skew(lv_matrix_t * matrix, float skew_x, float skew_y) +{ + float rskew_x = skew_x / 180.0f * (float)M_PI; + float rskew_y = skew_y / 180.0f * (float)M_PI; + float tan_x = tanf(rskew_x); + float tan_y = tanf(rskew_y); + + lv_matrix_t skm = {{ + {1.0f, tan_x, 0.0f}, + {tan_y, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, + } + }; + + _multiply_matrix(matrix, &skm); +} + +void lv_matrix_multiply(lv_matrix_t * matrix, const lv_matrix_t * m) +{ + _multiply_matrix(matrix, m); +} + +/* path functions */ +lv_vector_path_t * lv_vector_path_create(lv_vector_path_quality_t quality) +{ + lv_vector_path_t * path = lv_malloc(sizeof(lv_vector_path_t)); + LV_ASSERT_MALLOC(path); + lv_memzero(path, sizeof(lv_vector_path_t)); + path->quality = quality; + LV_ARRAY_INIT_CAPACITY(&path->ops, 8, uint8_t); + LV_ARRAY_INIT_CAPACITY(&path->points, 8, lv_fpoint_t); + return path; +} + +void lv_vector_path_copy(lv_vector_path_t * target_path, const lv_vector_path_t * path) +{ + target_path->quality = path->quality; + lv_array_copy(&target_path->ops, &path->ops); + lv_array_copy(&target_path->points, &path->points); +} + +void lv_vector_path_clear(lv_vector_path_t * path) +{ + lv_array_clear(&path->ops); + lv_array_clear(&path->points); +} + +void lv_vector_path_delete(lv_vector_path_t * path) +{ + lv_array_destroy(&path->ops); + lv_array_destroy(&path->points); + lv_free(path); +} + +void lv_vector_path_move_to(lv_vector_path_t * path, const lv_fpoint_t * p) +{ + CHECK_AND_RESIZE_PATH_CONTAINER(path, 1); + + uint8_t op = LV_VECTOR_PATH_OP_MOVE_TO; + LV_ARRAY_APPEND_VALUE(&path->ops, op); + LV_ARRAY_APPEND(&path->points, p); +} + +void lv_vector_path_line_to(lv_vector_path_t * path, const lv_fpoint_t * p) +{ + if(lv_array_is_empty(&path->ops)) { + // first op must be move_to + return; + } + + CHECK_AND_RESIZE_PATH_CONTAINER(path, 1); + + uint8_t op = LV_VECTOR_PATH_OP_LINE_TO; + LV_ARRAY_APPEND_VALUE(&path->ops, op); + LV_ARRAY_APPEND(&path->points, p); +} + +void lv_vector_path_quad_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2) +{ + if(lv_array_is_empty(&path->ops)) { + // first op must be move_to + return; + } + + CHECK_AND_RESIZE_PATH_CONTAINER(path, 2); + + uint8_t op = LV_VECTOR_PATH_OP_QUAD_TO; + LV_ARRAY_APPEND_VALUE(&path->ops, op); + LV_ARRAY_APPEND(&path->points, p1); + LV_ARRAY_APPEND(&path->points, p2); +} + +void lv_vector_path_cubic_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2, + const lv_fpoint_t * p3) +{ + if(lv_array_is_empty(&path->ops)) { + // first op must be move_to + return; + } + + CHECK_AND_RESIZE_PATH_CONTAINER(path, 3); + + uint8_t op = LV_VECTOR_PATH_OP_CUBIC_TO; + LV_ARRAY_APPEND_VALUE(&path->ops, op); + LV_ARRAY_APPEND(&path->points, p1); + LV_ARRAY_APPEND(&path->points, p2); + LV_ARRAY_APPEND(&path->points, p3); +} + +void lv_vector_path_close(lv_vector_path_t * path) +{ + if(lv_array_is_empty(&path->ops)) { + // first op must be move_to + return; + } + + CHECK_AND_RESIZE_PATH_CONTAINER(path, 1); + + uint8_t op = LV_VECTOR_PATH_OP_CLOSE; + LV_ARRAY_APPEND_VALUE(&path->ops, op); +} + +void lv_vector_path_append_rect(lv_vector_path_t * path, const lv_area_t * rect, int32_t rx, int32_t ry) +{ + float x = rect->x1; + float y = rect->y1; + float w = (float)lv_area_get_width(rect); + float h = (float)lv_area_get_height(rect); + + float hw = w * 0.5f; + float hh = h * 0.5f; + + if(rx > hw) rx = (int32_t)hw; + if(ry > hh) ry = (int32_t)hh; + + if(rx == 0 && ry == 0) { + lv_fpoint_t pt = {x, y}; + lv_vector_path_move_to(path, &pt); + pt.x += w; + lv_vector_path_line_to(path, &pt); + pt.y += h; + lv_vector_path_line_to(path, &pt); + pt.x -= w; + lv_vector_path_line_to(path, &pt); + lv_vector_path_close(path); + } + else if(rx == (int32_t)hw && ry == (int32_t)hh) { + lv_fpoint_t pt = {x + w * 0.5f, y + h * 0.5f}; + lv_vector_path_append_circle(path, &pt, rx, ry); + } + else { + float hrx = rx * 0.5f; + float hry = ry * 0.5f; + lv_fpoint_t pt, pt2, pt3; + + pt.x = x + rx; + pt.y = y; + lv_vector_path_move_to(path, &pt); + + pt.x = x + w - rx; + pt.y = y; + lv_vector_path_line_to(path, &pt); + + pt.x = x + w - rx + hrx; + pt.y = y; + pt2.x = x + w; + pt2.y = y + ry - hry; + pt3.x = x + w; + pt3.y = y + ry; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + pt.x = x + w; + pt.y = y + h - ry; + lv_vector_path_line_to(path, &pt); + + pt.x = x + w; + pt.y = y + h - ry + hry; + pt2.x = x + w - rx + hrx; + pt2.y = y + h; + pt3.x = x + w - rx; + pt3.y = y + h; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + pt.x = x + rx; + pt.y = y + h; + lv_vector_path_line_to(path, &pt); + + pt.x = x + rx - hrx; + pt.y = y + h; + pt2.x = x; + pt2.y = y + h - ry + hry; + pt3.x = x; + pt3.y = y + h - ry; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + pt.x = x; + pt.y = y + ry; + lv_vector_path_line_to(path, &pt); + + pt.x = x; + pt.y = y + ry - hry; + pt2.x = x + rx - hrx; + pt2.y = y; + pt3.x = x + rx; + pt3.y = y; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + lv_vector_path_close(path); + } +} + +void lv_vector_path_append_circle(lv_vector_path_t * path, const lv_fpoint_t * c, int32_t rx, int32_t ry) +{ + float krx = rx * 0.552284f; + float kry = ry * 0.552284f; + float cx = c->x; + float cy = c->y; + + lv_fpoint_t pt, pt2, pt3; + pt.x = cx; + pt.y = cy - ry; + lv_vector_path_move_to(path, &pt); + + pt.x = cx + krx; + pt.y = cy - ry; + pt2.x = cx + rx; + pt2.y = cy - kry; + pt3.x = cx + rx; + pt3.y = cy; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + pt.x = cx + rx; + pt.y = cy + kry; + pt2.x = cx + krx; + pt2.y = cy + ry; + pt3.x = cx; + pt3.y = cy + ry; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + pt.x = cx - krx; + pt.y = cy + ry; + pt2.x = cx - rx; + pt2.y = cy + kry; + pt3.x = cx - rx; + pt3.y = cy; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + pt.x = cx - rx; + pt.y = cy - kry; + pt2.x = cx - krx; + pt2.y = cy - ry; + pt3.x = cx; + pt3.y = cy - ry; + lv_vector_path_cubic_to(path, &pt, &pt2, &pt3); + + lv_vector_path_close(path); +} + +void lv_vector_path_append_path(lv_vector_path_t * path, const lv_vector_path_t * subpath) +{ + uint32_t ops_size = lv_array_length(&path->ops); + uint32_t nops_size = lv_array_length(&subpath->ops); + uint32_t point_size = lv_array_length(&path->points); + uint32_t npoint_size = lv_array_length(&subpath->points); + + lv_array_resize(&path->ops, ops_size + nops_size); + uint8_t * start_ops = lv_array_get(&path->ops, ops_size - 1) + sizeof(lv_vector_path_op_t); + lv_memcpy(start_ops, lv_array_get(&subpath->ops, 0), nops_size * sizeof(lv_vector_path_op_t)); + path->ops.size = ops_size + nops_size; + + lv_array_resize(&path->points, point_size + npoint_size); + uint8_t * start_pt = lv_array_get(&path->points, point_size - 1) + sizeof(lv_fpoint_t); + lv_memcpy(start_pt, lv_array_get(&subpath->points, 0), npoint_size * sizeof(lv_fpoint_t)); + path->points.size = point_size + npoint_size; +} + +/* draw dsc functions */ + +lv_vector_dsc_t * lv_vector_dsc_create(lv_layer_t * layer) +{ + lv_vector_dsc_t * dsc = lv_malloc(sizeof(lv_vector_dsc_t)); + LV_ASSERT_MALLOC(dsc); + lv_memzero(dsc, sizeof(lv_vector_dsc_t)); + + dsc->layer = layer; + + lv_vector_fill_dsc_t * fill_dsc = &(dsc->current_dsc.fill_dsc); + fill_dsc->style = LV_VECTOR_DRAW_STYLE_SOLID; + fill_dsc->color = lv_color_to_32(lv_color_black(), 0xFF); + fill_dsc->opa = LV_OPA_COVER; + fill_dsc->fill_rule = LV_VECTOR_FILL_NONZERO; + lv_matrix_identity(&(fill_dsc->matrix)); // identity matrix + + lv_vector_stroke_dsc_t * stroke_dsc = &(dsc->current_dsc.stroke_dsc); + stroke_dsc->style = LV_VECTOR_DRAW_STYLE_SOLID; + stroke_dsc->color = lv_color_to_32(lv_color_black(), 0xFF); + stroke_dsc->opa = LV_OPA_0; // default no stroke + stroke_dsc->width = 1.0f; + stroke_dsc->cap = LV_VECTOR_STROKE_CAP_BUTT; + stroke_dsc->join = LV_VECTOR_STROKE_JOIN_MITER; + stroke_dsc->miter_limit = 4.0f; + lv_matrix_identity(&(stroke_dsc->matrix)); // identity matrix + + dsc->current_dsc.blend_mode = LV_VECTOR_BLEND_SRC_OVER; + dsc->current_dsc.scissor_area = layer->clip_area; + lv_matrix_identity(&(dsc->current_dsc.matrix)); // identity matrix + dsc->tasks.task_list = NULL; + return dsc; +} + +void lv_vector_dsc_delete(lv_vector_dsc_t * dsc) +{ + if(dsc->tasks.task_list) { + lv_ll_t * task_list = dsc->tasks.task_list; + _lv_vector_for_each_destroy_tasks(task_list, NULL, NULL); + dsc->tasks.task_list = NULL; + } + lv_array_destroy(&(dsc->current_dsc.stroke_dsc.dash_pattern)); + lv_free(dsc); +} + +void lv_vector_dsc_set_blend_mode(lv_vector_dsc_t * dsc, lv_vector_blend_t blend) +{ + dsc->current_dsc.blend_mode = blend; +} + +void lv_vector_dsc_set_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) +{ + lv_memcpy(&(dsc->current_dsc.matrix), matrix, sizeof(lv_matrix_t)); +} + +void lv_vector_dsc_set_fill_color(lv_vector_dsc_t * dsc, lv_color_t color) +{ + dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; + dsc->current_dsc.fill_dsc.color = lv_color_to_32(color, 0xFF); +} + +void lv_vector_dsc_set_fill_color32(lv_vector_dsc_t * dsc, lv_color32_t color) +{ + dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; + dsc->current_dsc.fill_dsc.color = color; +} + +void lv_vector_dsc_set_fill_opa(lv_vector_dsc_t * dsc, lv_opa_t opa) +{ + dsc->current_dsc.fill_dsc.opa = opa; +} + +void lv_vector_dsc_set_fill_rule(lv_vector_dsc_t * dsc, lv_vector_fill_t rule) +{ + dsc->current_dsc.fill_dsc.fill_rule = rule; +} + +void lv_vector_dsc_set_fill_image(lv_vector_dsc_t * dsc, const lv_draw_image_dsc_t * img_dsc) +{ + dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_PATTERN; + lv_memcpy(&(dsc->current_dsc.fill_dsc.img_dsc), img_dsc, sizeof(lv_draw_image_dsc_t)); +} + +void lv_vector_dsc_set_fill_linear_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, + lv_vector_gradient_spread_t spread) +{ + dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT; + dsc->current_dsc.fill_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_LINEAR; + dsc->current_dsc.fill_dsc.gradient.spread = spread; + lv_memcpy(&(dsc->current_dsc.fill_dsc.gradient.grad), grad, sizeof(lv_grad_dsc_t)); +} + +void lv_vector_dsc_set_fill_radial_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, float cx, float cy, + float radius, lv_vector_gradient_spread_t spread) +{ + dsc->current_dsc.fill_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT; + dsc->current_dsc.fill_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_RADIAL; + dsc->current_dsc.fill_dsc.gradient.cx = cx; + dsc->current_dsc.fill_dsc.gradient.cy = cy; + dsc->current_dsc.fill_dsc.gradient.cr = radius; + dsc->current_dsc.fill_dsc.gradient.spread = spread; + lv_memcpy(&(dsc->current_dsc.fill_dsc.gradient.grad), grad, sizeof(lv_grad_dsc_t)); +} + +void lv_vector_dsc_set_fill_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) +{ + lv_memcpy(&(dsc->current_dsc.fill_dsc.matrix), matrix, sizeof(lv_matrix_t)); +} + +void lv_vector_dsc_set_stroke_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix) +{ + lv_memcpy(&(dsc->current_dsc.stroke_dsc.matrix), matrix, sizeof(lv_matrix_t)); +} + +void lv_vector_dsc_set_stroke_color32(lv_vector_dsc_t * dsc, lv_color32_t color) +{ + dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; + dsc->current_dsc.stroke_dsc.color = color; +} + +void lv_vector_dsc_set_stroke_color(lv_vector_dsc_t * dsc, lv_color_t color) +{ + dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_SOLID; + dsc->current_dsc.stroke_dsc.color = lv_color_to_32(color, 0xFF); +} + +void lv_vector_dsc_set_stroke_opa(lv_vector_dsc_t * dsc, lv_opa_t opa) +{ + dsc->current_dsc.stroke_dsc.opa = opa; +} + +void lv_vector_dsc_set_stroke_width(lv_vector_dsc_t * dsc, float width) +{ + dsc->current_dsc.stroke_dsc.width = width; +} + +void lv_vector_dsc_set_stroke_dash(lv_vector_dsc_t * dsc, float * dash_pattern, uint16_t dash_count) +{ + lv_array_t * dash_array = &(dsc->current_dsc.stroke_dsc.dash_pattern); + if(dash_pattern) { + LV_ARRAY_INIT_CAPACITY(dash_array, dash_count, float); + for(uint16_t i = 0; i < dash_count; i++) { + LV_ARRAY_APPEND_VALUE(dash_array, dash_pattern[i]); + } + } + else { // clear dash + lv_array_clear(dash_array); + } +} + +void lv_vector_dsc_set_stroke_cap(lv_vector_dsc_t * dsc, lv_vector_stroke_cap_t cap) +{ + dsc->current_dsc.stroke_dsc.cap = cap; +} + +void lv_vector_dsc_set_stroke_join(lv_vector_dsc_t * dsc, lv_vector_stroke_join_t join) +{ + dsc->current_dsc.stroke_dsc.join = join; +} + +void lv_vector_dsc_set_stroke_miter_limit(lv_vector_dsc_t * dsc, uint16_t miter_limit) +{ + dsc->current_dsc.stroke_dsc.miter_limit = miter_limit; +} + +void lv_vector_dsc_set_stroke_linear_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, + lv_vector_gradient_spread_t spread) +{ + dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT; + dsc->current_dsc.stroke_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_LINEAR; + dsc->current_dsc.stroke_dsc.gradient.spread = spread; + lv_memcpy(&(dsc->current_dsc.stroke_dsc.gradient.grad), grad, sizeof(lv_grad_dsc_t)); +} + +void lv_vector_dsc_set_stroke_radial_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, float cx, float cy, + float radius, lv_vector_gradient_spread_t spread) +{ + dsc->current_dsc.stroke_dsc.style = LV_VECTOR_DRAW_STYLE_GRADIENT; + dsc->current_dsc.stroke_dsc.gradient.style = LV_VECTOR_GRADIENT_STYLE_RADIAL; + dsc->current_dsc.stroke_dsc.gradient.cx = cx; + dsc->current_dsc.stroke_dsc.gradient.cy = cy; + dsc->current_dsc.stroke_dsc.gradient.cr = radius; + dsc->current_dsc.stroke_dsc.gradient.spread = spread; + lv_memcpy(&(dsc->current_dsc.stroke_dsc.gradient.grad), grad, sizeof(lv_grad_dsc_t)); +} + +/* draw functions */ +void lv_vector_dsc_add_path(lv_vector_dsc_t * dsc, const lv_vector_path_t * path) +{ + lv_area_t rect; + if(!_lv_area_intersect(&rect, &(dsc->layer->clip_area), &(dsc->current_dsc.scissor_area))) { + return; + } + + if(dsc->current_dsc.fill_dsc.opa == 0 + && dsc->current_dsc.stroke_dsc.opa == 0) { + return; + } + + if(!dsc->tasks.task_list) { + dsc->tasks.task_list = lv_malloc(sizeof(lv_ll_t)); + LV_ASSERT_MALLOC(dsc->tasks.task_list); + _lv_ll_init(dsc->tasks.task_list, sizeof(_lv_vector_draw_task)); + } + + _lv_vector_draw_task * new_task = (_lv_vector_draw_task *)_lv_ll_ins_tail(dsc->tasks.task_list); + lv_memset(new_task, 0, sizeof(_lv_vector_draw_task)); + + new_task->path = lv_vector_path_create(0); + + _copy_draw_dsc(&(new_task->dsc), &(dsc->current_dsc)); + lv_vector_path_copy(new_task->path, path); + new_task->dsc.scissor_area = rect; +} + +void lv_vector_clear_area(lv_vector_dsc_t * dsc, const lv_area_t * rect) +{ + lv_area_t r; + if(!_lv_area_intersect(&r, &(dsc->layer->clip_area), &(dsc->current_dsc.scissor_area))) { + return; + } + + if(!dsc->tasks.task_list) { + dsc->tasks.task_list = lv_malloc(sizeof(lv_ll_t)); + LV_ASSERT_MALLOC(dsc->tasks.task_list); + _lv_ll_init(dsc->tasks.task_list, sizeof(_lv_vector_draw_task)); + } + + _lv_vector_draw_task * new_task = (_lv_vector_draw_task *)_lv_ll_ins_tail(dsc->tasks.task_list); + lv_memset(new_task, 0, sizeof(_lv_vector_draw_task)); + + new_task->dsc.fill_dsc.color = dsc->current_dsc.fill_dsc.color; + lv_area_copy(&(new_task->dsc.scissor_area), rect); +} + +void lv_draw_vector(lv_vector_dsc_t * dsc) +{ + if(!dsc->tasks.task_list) { + return; + } + + lv_layer_t * layer = dsc->layer; + + lv_draw_task_t * t = lv_draw_add_task(layer, &(layer->clip_area)); + t->type = LV_DRAW_TASK_TYPE_VECTOR; + t->draw_dsc = lv_malloc(sizeof(lv_draw_vector_task_dsc_t)); + lv_memcpy(t->draw_dsc, &(dsc->tasks), sizeof(lv_draw_vector_task_dsc_t)); + lv_draw_finalize_task_creation(layer, t); + dsc->tasks.task_list = NULL; +} + +/* draw dsc transform */ +void lv_vector_dsc_identity(lv_vector_dsc_t * dsc) +{ + lv_matrix_identity(&(dsc->current_dsc.matrix)); // identity matrix +} + +void lv_vector_dsc_scale(lv_vector_dsc_t * dsc, float scale_x, float scale_y) +{ + lv_matrix_scale(&(dsc->current_dsc.matrix), scale_x, scale_y); +} + +void lv_vector_dsc_rotate(lv_vector_dsc_t * dsc, float degree) +{ + lv_matrix_rotate(&(dsc->current_dsc.matrix), degree); +} + +void lv_vector_dsc_translate(lv_vector_dsc_t * dsc, float tx, float ty) +{ + lv_matrix_translate(&(dsc->current_dsc.matrix), tx, ty); +} + +void lv_vector_dsc_skew(lv_vector_dsc_t * dsc, float skew_x, float skew_y) +{ + lv_matrix_skew(&(dsc->current_dsc.matrix), skew_x, skew_y); +} + +void _lv_vector_for_each_destroy_tasks(lv_ll_t * task_list, vector_draw_task_cb cb, void * data) +{ + _lv_vector_draw_task * task = _lv_ll_get_head(task_list); + _lv_vector_draw_task * next_task = NULL; + + while(task != NULL) { + next_task = _lv_ll_get_next(task_list, task); + _lv_ll_remove(task_list, task); + + if(cb) { + cb(data ? data : NULL, task->path, &(task->dsc)); + } + + if(task->path) { + lv_vector_path_delete(task->path); + } + lv_array_destroy(&(task->dsc.stroke_dsc.dash_pattern)); + + lv_free(task); + task = next_task; + } + lv_free(task_list); +} +#endif /* LV_USE_VECTOR_GRAPHIC */ diff --git a/project/gui/lvgl/src/draw/lv_draw_vector.h b/project/gui/lvgl/src/draw/lv_draw_vector.h new file mode 100644 index 000000000..10a09d90d --- /dev/null +++ b/project/gui/lvgl/src/draw/lv_draw_vector.h @@ -0,0 +1,536 @@ +/** + * @file lv_draw_vector.h + * + */ + +#ifndef LV_DRAW_VECTOR_H +#define LV_DRAW_VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lv_draw.h" +#include "../misc/lv_array.h" + +#if LV_USE_VECTOR_GRAPHIC + +/********************** + * TYPEDEFS + **********************/ +enum { + LV_VECTOR_FILL_NONZERO = 0, + LV_VECTOR_FILL_EVENODD, +}; +typedef uint8_t lv_vector_fill_t; + +enum { + LV_VECTOR_STROKE_CAP_BUTT = 0, + LV_VECTOR_STROKE_CAP_SQUARE, + LV_VECTOR_STROKE_CAP_ROUND, +}; +typedef uint8_t lv_vector_stroke_cap_t; + +enum { + LV_VECTOR_STROKE_JOIN_MITER = 0, + LV_VECTOR_STROKE_JOIN_BEVEL, + LV_VECTOR_STROKE_JOIN_ROUND, +}; +typedef uint8_t lv_vector_stroke_join_t; + +enum { + LV_VECTOR_PATH_QUALITY_MEDIUM = 0, /* default*/ + LV_VECTOR_PATH_QUALITY_HIGH, + LV_VECTOR_PATH_QUALITY_LOW, +}; +typedef uint8_t lv_vector_path_quality_t; + +enum { + LV_VECTOR_BLEND_SRC_OVER = 0, + LV_VECTOR_BLEND_SRC_IN, + LV_VECTOR_BLEND_DST_OVER, + LV_VECTOR_BLEND_DST_IN, + LV_VECTOR_BLEND_SCREEN, + LV_VECTOR_BLEND_MULTIPLY, + LV_VECTOR_BLEND_NONE, + LV_VECTOR_BLEND_ADDITIVE, + LV_VECTOR_BLEND_SUBTRACTIVE, +}; +typedef uint8_t lv_vector_blend_t; + +enum { + LV_VECTOR_PATH_OP_MOVE_TO = 0, + LV_VECTOR_PATH_OP_LINE_TO, + LV_VECTOR_PATH_OP_QUAD_TO, + LV_VECTOR_PATH_OP_CUBIC_TO, + LV_VECTOR_PATH_OP_CLOSE, +}; +typedef uint8_t lv_vector_path_op_t; + +enum { + LV_VECTOR_DRAW_STYLE_SOLID = 0, + LV_VECTOR_DRAW_STYLE_PATTERN, + LV_VECTOR_DRAW_STYLE_GRADIENT, +}; +typedef uint8_t lv_vector_draw_style_t; + +enum { + LV_VECTOR_GRADIENT_SPREAD_PAD = 0, + LV_VECTOR_GRADIENT_SPREAD_REPEAT, + LV_VECTOR_GRADIENT_SPREAD_REFLECT, +}; +typedef uint8_t lv_vector_gradient_spread_t; + +enum { + LV_VECTOR_GRADIENT_STYLE_LINEAR = 0, + LV_VECTOR_GRADIENT_STYLE_RADIAL, +}; +typedef uint8_t lv_vector_gradient_style_t; + +typedef struct { + float x; + float y; +} lv_fpoint_t; + +typedef struct { + float m[3][3]; +} lv_matrix_t; + +typedef struct { + lv_vector_path_quality_t quality; + lv_array_t ops; + lv_array_t points; +} lv_vector_path_t; + +typedef struct { + lv_vector_gradient_style_t style; + lv_grad_dsc_t grad; + float cx; + float cy; + float cr; + lv_vector_gradient_spread_t spread; +} lv_vector_gradient_t; + +typedef struct { + lv_vector_draw_style_t style; + lv_color32_t color; + lv_opa_t opa; + lv_vector_fill_t fill_rule; + lv_draw_image_dsc_t img_dsc; + lv_vector_gradient_t gradient; + lv_matrix_t matrix; +} lv_vector_fill_dsc_t; + +typedef struct { + lv_vector_draw_style_t style; + lv_color32_t color; + lv_opa_t opa; + float width; + lv_array_t dash_pattern; + lv_vector_stroke_cap_t cap; + lv_vector_stroke_join_t join; + uint16_t miter_limit; + lv_vector_gradient_t gradient; + lv_matrix_t matrix; +} lv_vector_stroke_dsc_t; + +typedef struct { + lv_vector_fill_dsc_t fill_dsc; + lv_vector_stroke_dsc_t stroke_dsc; + lv_matrix_t matrix; + lv_vector_blend_t blend_mode; + lv_area_t scissor_area; +} lv_vector_draw_dsc_t; + +typedef struct { + lv_draw_dsc_base_t base; + lv_ll_t * task_list; // draw task list. +} lv_draw_vector_task_dsc_t; + +typedef struct { + lv_layer_t * layer; + lv_vector_draw_dsc_t current_dsc; + /* private data */ + lv_draw_vector_task_dsc_t tasks; +} lv_vector_dsc_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Set matrix to identity matrix + * @param matrix pointer to a matrix + */ +void lv_matrix_identity(lv_matrix_t * matrix); + +/** + * Translate the matrix to new position + * @param matrix pointer to a matrix + * @param tx the amount of translate in x direction + * @param tx the amount of translate in y direction + */ +void lv_matrix_translate(lv_matrix_t * matrix, float tx, float ty); + +/** + * Change the scale factor of the matrix + * @param matrix pointer to a matrix + * @param scale_x the scale factor for the X direction + * @param scale_y the scale factor for the Y direction + */ +void lv_matrix_scale(lv_matrix_t * matrix, float scale_x, float scale_y); + +/** + * Rotate the matrix with origin + * @param matrix pointer to a matrix + * @param degree angle to rotate + */ +void lv_matrix_rotate(lv_matrix_t * matrix, float degree); + +/** + * Change the skew factor of the matrix + * @param matrix pointer to a matrix + * @param skew_x the skew factor for x direction + * @param skew_y the skew factor for y direction + */ +void lv_matrix_skew(lv_matrix_t * matrix, float skew_x, float skew_y); + +/** + * Multiply two matrix and store the result to the first one + * @param matrix pointer to a matrix + * @param matrix2 pointer to another matrix + */ +void lv_matrix_multiply(lv_matrix_t * matrix, const lv_matrix_t * matrix2); + +/** + * Create a vector graphic path object + * @param quality the quality hint of path + * @return pointer to the created path object + */ +lv_vector_path_t * lv_vector_path_create(lv_vector_path_quality_t quality); + +/** + * Copy a path data to another + * @param target_path pointer to a path + * @param path pointer to source path + */ +void lv_vector_path_copy(lv_vector_path_t * target_path, const lv_vector_path_t * path); + +/** + * Clear path data + * @param path pointer to a path + */ +void lv_vector_path_clear(lv_vector_path_t * path); + +/** + * Delete the graphic path object + * @param path pointer to a path + */ +void lv_vector_path_delete(lv_vector_path_t * path); + +/** + * Begin a new sub path and set a point to path + * @param path pointer to a path + * @param p pointer to a `lv_fpoint_t` variable + */ +void lv_vector_path_move_to(lv_vector_path_t * path, const lv_fpoint_t * p); + +/** + * Add a line to the path from last point to the point + * @param path pointer to a path + * @param p pointer to a `lv_fpoint_t` variable + */ +void lv_vector_path_line_to(lv_vector_path_t * path, const lv_fpoint_t * p); + +/** + * Add a quadratic bezier line to the path from last point to the point + * @param path pointer to a path + * @param p1 pointer to a `lv_fpoint_t` variable for control point + * @param p2 pointer to a `lv_fpoint_t` variable for end point + */ +void lv_vector_path_quad_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2); + +/** + * Add a cubic bezier line to the path from last point to the point + * @param path pointer to a path + * @param p1 pointer to a `lv_fpoint_t` variable for first control point + * @param p2 pointer to a `lv_fpoint_t` variable for second control point + * @param p3 pointer to a `lv_fpoint_t` variable for end point + */ +void lv_vector_path_cubic_to(lv_vector_path_t * path, const lv_fpoint_t * p1, const lv_fpoint_t * p2, + const lv_fpoint_t * p3); + +/** + * Close the sub path + * @param path pointer to a path + */ +void lv_vector_path_close(lv_vector_path_t * path); + +/** + * Add a rectangle to the path + * @param path pointer to a path + * @param rect pointer to a `lv_area_t` variable + * @param rx the horizontal radius for rounded rectangle + * @param ry the vertical radius for rounded rectangle + */ +void lv_vector_path_append_rect(lv_vector_path_t * path, const lv_area_t * rect, int32_t rx, int32_t ry); + +/** + * Add a circle to the path + * @param path pointer to a path + * @param c pointer to a `lv_fpoint_t` variable for center of the circle + * @param rx the horizontal radius for circle + * @param ry the vertical radius for circle + */ +void lv_vector_path_append_circle(lv_vector_path_t * path, const lv_fpoint_t * c, int32_t rx, int32_t ry); + +/** + * Add an sub path to the path + * @param path pointer to a path + * @param subpath pointer to another path which will be added + */ +void lv_vector_path_append_path(lv_vector_path_t * path, const lv_vector_path_t * subpath); + +/** + * Create a vector graphic descriptor + * @param layer pointer to a layer + * @return pointer to the created descriptor + */ +lv_vector_dsc_t * lv_vector_dsc_create(lv_layer_t * layer); + +/** + * Delete the vector graphic descriptor + * @param dsc pointer to a vector graphic descriptor + */ +void lv_vector_dsc_delete(lv_vector_dsc_t * dsc); + +/** + * Set a matrix to current transformation matrix + * @param dsc pointer to a vector graphic descriptor + * @param matrix pointer to a matrix + */ +void lv_vector_dsc_set_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix); + +/** + * Set blend mode for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param blend the blend mode to be set in `lv_vector_blend_t` + */ +void lv_vector_dsc_set_blend_mode(lv_vector_dsc_t * dsc, lv_vector_blend_t blend); + +/** + * Set fill color for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param color the color to be set in lv_color32_t format + */ +void lv_vector_dsc_set_fill_color32(lv_vector_dsc_t * dsc, lv_color32_t color); + +/** + * Set fill color for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param color the color to be set in lv_color_t format + */ +void lv_vector_dsc_set_fill_color(lv_vector_dsc_t * dsc, lv_color_t color); + +/** + * Set fill opacity for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param opa the opacity to be set in lv_opa_t format + */ +void lv_vector_dsc_set_fill_opa(lv_vector_dsc_t * dsc, lv_opa_t opa); + +/** + * Set fill rule for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param rule the fill rule to be set in lv_vector_fill_t format + */ +void lv_vector_dsc_set_fill_rule(lv_vector_dsc_t * dsc, lv_vector_fill_t rule); + +/** + * Set fill image for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param img_dsc pointer to a `lv_draw_image_dsc_t` variable + */ +void lv_vector_dsc_set_fill_image(lv_vector_dsc_t * dsc, const lv_draw_image_dsc_t * img_dsc); + +/** + * Set fill linear gradient for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param grad pointer to a `lv_grad_dsc_t` variable + * @param spread the gradient spread to be set in lv_vector_gradient_spread_t format + */ +void lv_vector_dsc_set_fill_linear_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, + lv_vector_gradient_spread_t spread); + +/** + * Set fill radial gradient for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param grad pointer to a `lv_grad_dsc_t` variable + * @param cx the x for center of the circle + * @param cy the y for center of the circle + * @param radius the radius for circle + * @param spread the gradient spread to be set in lv_vector_gradient_spread_t format + */ +void lv_vector_dsc_set_fill_radial_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, float cx, float cy, + float radius, lv_vector_gradient_spread_t spread); + +/** + * Set a matrix to current fill transformation matrix + * @param dsc pointer to a vector graphic descriptor + * @param matrix pointer to a matrix + */ +void lv_vector_dsc_set_fill_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix); + +/** + * Set stroke color for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param color the color to be set in lv_color32_t format + */ +void lv_vector_dsc_set_stroke_color32(lv_vector_dsc_t * dsc, lv_color32_t color); + +/** + * Set stroke color for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param color the color to be set in lv_color_t format + */ +void lv_vector_dsc_set_stroke_color(lv_vector_dsc_t * dsc, lv_color_t color); + +/** + * Set stroke opacity for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param opa the opacity to be set in lv_opa_t format + */ +void lv_vector_dsc_set_stroke_opa(lv_vector_dsc_t * dsc, lv_opa_t opa); + +/** + * Set stroke line width for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param width the stroke line width + */ +void lv_vector_dsc_set_stroke_width(lv_vector_dsc_t * dsc, float width); + +/** + * Set stroke line dash pattern for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param dash_pattern an array of values that specify the segments of dash line + * @param dash_count the length of dash pattern array + */ +void lv_vector_dsc_set_stroke_dash(lv_vector_dsc_t * dsc, float * dash_pattern, uint16_t dash_count); + +/** + * Set stroke line cap style for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param cap the line cap to be set in lv_vector_stroke_cap_t format + */ +void lv_vector_dsc_set_stroke_cap(lv_vector_dsc_t * dsc, lv_vector_stroke_cap_t cap); + +/** + * Set stroke line join style for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param join the line join to be set in lv_vector_stroke_join_t format + */ +void lv_vector_dsc_set_stroke_join(lv_vector_dsc_t * dsc, lv_vector_stroke_join_t join); + +/** + * Set stroke miter limit for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param miter_limit the stroke miter_limit + */ +void lv_vector_dsc_set_stroke_miter_limit(lv_vector_dsc_t * dsc, uint16_t miter_limit); + +/** + * Set stroke linear gradient for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param grad pointer to a `lv_grad_dsc_t` variable + * @param spread the gradient spread to be set in lv_vector_gradient_spread_t format + */ +void lv_vector_dsc_set_stroke_linear_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, + lv_vector_gradient_spread_t spread); +/** + * Set stroke radial gradient for descriptor + * @param dsc pointer to a vector graphic descriptor + * @param grad pointer to a `lv_grad_dsc_t` variable + * @param cx the x for center of the circle + * @param cy the y for center of the circle + * @param radius the radius for circle + * @param spread the gradient spread to be set in lv_vector_gradient_spread_t format + */ +void lv_vector_dsc_set_stroke_radial_gradient(lv_vector_dsc_t * dsc, const lv_grad_dsc_t * grad, float cx, float cy, + float radius, lv_vector_gradient_spread_t spread); +/** + * Set a matrix to current stroke transformation matrix + * @param dsc pointer to a vector graphic descriptor + * @param matrix pointer to a matrix + */ +void lv_vector_dsc_set_stroke_transform(lv_vector_dsc_t * dsc, const lv_matrix_t * matrix); + +/** + * Set current transformation matrix to identity matrix + * @param dsc pointer to a vector graphic descriptor + */ +void lv_vector_dsc_identity(lv_vector_dsc_t * dsc); + +/** + * Change the scale factor of current transformation matrix + * @param dsc pointer to a vector graphic descriptor + * @param scale_x the scale factor for the X direction + * @param scale_y the scale factor for the Y direction + */ +void lv_vector_dsc_scale(lv_vector_dsc_t * dsc, float scale_x, float scale_y); + +/** + * Rotate current transformation matrix with origin + * @param dsc pointer to a vector graphic descriptor + * @param degree angle to rotate + */ +void lv_vector_dsc_rotate(lv_vector_dsc_t * dsc, float degree); + +/** + * Translate current transformation matrix to new position + * @param dsc pointer to a vector graphic descriptor + * @param tx the amount of translate in x direction + * @param tx the amount of translate in y direction + */ +void lv_vector_dsc_translate(lv_vector_dsc_t * dsc, float tx, float ty); + +/** + * Change the skew factor of current transformation matrix + * @param dsc pointer to a vector graphic descriptor + * @param skew_x the skew factor for x direction + * @param skew_y the skew factor for y direction + */ +void lv_vector_dsc_skew(lv_vector_dsc_t * dsc, float skew_x, float skew_y); + +/** + * Add a graphic path to the draw list + * @param dsc pointer to a vector graphic descriptor + * @param path pointer to a path + */ +void lv_vector_dsc_add_path(lv_vector_dsc_t * dsc, const lv_vector_path_t * path); + +/** + * Clear a rectangle area use current fill color + * @param dsc pointer to a vector graphic descriptor + * @param rect the area to clear in the buffer + */ +void lv_vector_clear_area(lv_vector_dsc_t * dsc, const lv_area_t * rect); + +/** + * Draw all the vector graphic paths + * @param dsc pointer to a vector graphic descriptor + */ +void lv_draw_vector(lv_vector_dsc_t * dsc); + +/* Traverser for task list */ +typedef void (*vector_draw_task_cb)(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc); +void _lv_vector_for_each_destroy_tasks(lv_ll_t * task_list, vector_draw_task_cb cb, void * data); +#endif /* LV_USE_VECTOR_GRAPHIC */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV_DRAW_VECTOR_H */ diff --git a/project/gui/lvgl/src/draw/lv_image_decoder.c b/project/gui/lvgl/src/draw/lv_image_decoder.c index 21b45fd88..d5471497b 100644 --- a/project/gui/lvgl/src/draw/lv_image_decoder.c +++ b/project/gui/lvgl/src/draw/lv_image_decoder.c @@ -74,6 +74,14 @@ void _lv_image_decoder_init(void) lv_image_decoder_set_close_cb(decoder, lv_image_decoder_built_in_close); } +/** + * Deinitialize the image decoder module + */ +void _lv_image_decoder_deinit(void) +{ + _lv_ll_clear(img_decoder_ll_p); +} + /** * Get information about an image. * Try the created image decoder one by one. Once one is able to get info that info will be used. @@ -338,14 +346,13 @@ static lv_image_decoder_built_in_data_t * get_decoder_data(lv_image_decoder_dsc_ { lv_image_decoder_built_in_data_t * data = dsc->user_data; if(data == NULL) { - data = lv_malloc(sizeof(lv_image_decoder_built_in_data_t)); + data = lv_malloc_zeroed(sizeof(lv_image_decoder_built_in_data_t)); LV_ASSERT_MALLOC(data); if(data == NULL) { LV_LOG_ERROR("out of memory"); return NULL; } - lv_memzero(data, sizeof(lv_image_decoder_built_in_data_t)); dsc->user_data = data; } @@ -581,7 +588,7 @@ lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_imag if(res != LV_RESULT_OK) { lv_fs_close(&f); - lv_memset(&decoder_data->f, 0, sizeof(decoder_data->f)); + lv_memzero(&decoder_data->f, sizeof(decoder_data->f)); } return res; @@ -744,7 +751,6 @@ lv_result_t lv_image_decoder_built_in_get_area(lv_image_decoder_t * decoder, lv_ return LV_RESULT_OK; } - if(cf == LV_COLOR_FORMAT_RGB565A8) { bpp = 16; /* RGB565 + A8 mask*/ uint32_t len = (w_px * bpp) / 8; /*map comes firstly*/ diff --git a/project/gui/lvgl/src/draw/lv_image_decoder.h b/project/gui/lvgl/src/draw/lv_image_decoder.h index 9dab56b60..4d966099c 100644 --- a/project/gui/lvgl/src/draw/lv_image_decoder.h +++ b/project/gui/lvgl/src/draw/lv_image_decoder.h @@ -97,7 +97,6 @@ typedef struct _lv_image_decoder_t { void * user_data; } lv_image_decoder_t; - /**Describe an image decoding session. Stores data about the decoding*/ typedef struct _lv_image_decoder_dsc_t { /**The decoder which was able to open the image source*/ @@ -146,6 +145,11 @@ typedef struct _lv_image_decoder_dsc_t { */ void _lv_image_decoder_init(void); +/** + * Deinitialize the image decoder module + */ +void _lv_image_decoder_deinit(void); + /** * Get information about an image. * Try the created image decoder one by one. Once one is able to get info that info will be used. diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c index 581d9ea0b..1170f6d58 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.c index 038f587ea..6a6eb1f0d 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ @@ -83,6 +82,11 @@ void lv_draw_pxp_init(void) #endif } +void lv_draw_pxp_deinit(void) +{ + lv_pxp_deinit(); +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.h b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.h index 156eb68e7..d164e291b 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.h +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_DRAW_PXP_H #define LV_DRAW_PXP_H @@ -53,6 +52,8 @@ void lv_draw_buf_pxp_init_handlers(void); void lv_draw_pxp_init(void); +void lv_draw_pxp_deinit(void); + void lv_draw_pxp_bg_img(lv_draw_unit_t * draw_unit, const lv_draw_bg_image_dsc_t * dsc, const lv_area_t * coords); diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c index 88e1ab8ab..9dada1ef2 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_bg_img.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c index 6112de60b..44fc1601a 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c index ec0431360..451aa0ad5 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c index ad7322d50..c933a5380 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c index ef813cb23..34dc71443 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.h b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.h index 227307173..b487e7ff9 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.h +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_cfg.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_PXP_CFG_H #define LV_PXP_CFG_H diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.c index 1632e719d..770e99c1f 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.h b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.h index c08d1c050..03a8fdc59 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.h +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_osa.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_PXP_OSA_H #define LV_PXP_OSA_H diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.c b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.c index 720edd036..164ca6f0e 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.c +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.h b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.h index d5eb9cadc..0a7c670e9 100644 --- a/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.h +++ b/project/gui/lvgl/src/draw/nxp/pxp/lv_pxp_utils.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_PXP_UTILS_H #define LV_PXP_UTILS_H diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_buf_vglite.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_buf_vglite.c index 91bfbb22c..c8b7b8bde 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_buf_vglite.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_buf_vglite.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.c index 405457242..c1b46153d 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ @@ -104,6 +103,10 @@ void lv_draw_vglite_init(void) #endif } +void lv_draw_vglite_deinit(void) +{ +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.h b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.h index c357f982a..c3d25a0a1 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.h +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_DRAW_VGLITE_H #define LV_DRAW_VGLITE_H @@ -66,6 +65,8 @@ void lv_draw_buf_vglite_init_handlers(void); void lv_draw_vglite_init(void); +void lv_draw_vglite_deinit(void); + void lv_draw_vglite_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, const lv_area_t * coords); diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c index 409c48500..8bc4b9c50 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ @@ -572,7 +571,7 @@ static void _vglite_draw_arc(const lv_point_t * center, const lv_area_t * clip_a /* path: max size = 16 cubic bezier (7 words each) */ int32_t arc_path[16 * 7]; - lv_memset(arc_path, 0, sizeof(arc_path)); + lv_memzero(arc_path, sizeof(arc_path)); /*** Init path ***/ int32_t width = dsc->width; /* inner arc radius = outer arc radius - width */ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c index 995331d53..b66af8336 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_bg_img.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_fill.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_fill.c index eef062c16..d2d452295 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_fill.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_fill.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ @@ -212,7 +211,7 @@ static void _vglite_draw_rect(const lv_area_t * coords, const lv_area_t * clip_a colors[i] = vglite_get_color(col32[i], true); } - lv_memset(&gradient, 0, sizeof(vg_lite_linear_gradient_t)); + lv_memzero(&gradient, sizeof(vg_lite_linear_gradient_t)); err = vg_lite_init_grad(&gradient); LV_ASSERT_MSG(err == VG_LITE_SUCCESS, "Init gradient failed"); diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_img.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_img.c index f5be3231c..58af218db 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_img.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_img.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_label.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_label.c index 73557b07d..edff907e1 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_label.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_label.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ @@ -38,7 +37,6 @@ static void _draw_vglite_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc, lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area); - /** * Draw letter (character bitmap blend) with optional color and opacity * @@ -175,7 +173,7 @@ static void _vglite_draw_letter(const lv_area_t * dest_area, mask_vgbuf.height = (int32_t)lv_area_get_height(mask_area); mask_vgbuf.stride = (int32_t)mask_stride; - lv_memset(&mask_vgbuf.yuv, 0, sizeof(mask_vgbuf.yuv)); + lv_memzero(&mask_vgbuf.yuv, sizeof(mask_vgbuf.yuv)); mask_vgbuf.memory = (void *)mask_buf; mask_vgbuf.address = (uint32_t)mask_vgbuf.memory; diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_layer.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_layer.c index eb5c1e029..683563d88 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_layer.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_layer.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_line.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_line.c index ea7c36e91..cae4bbd52 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_line.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_draw_vglite_line.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.c index baf8b26b0..4dc6217c3 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ @@ -97,7 +96,7 @@ void vglite_set_buf(vg_lite_buffer_t * vgbuf, void * buf, vgbuf->height = (int32_t)height; vgbuf->stride = (int32_t)stride; - lv_memset(&vgbuf->yuv, 0, sizeof(vgbuf->yuv)); + lv_memzero(&vgbuf->yuv, sizeof(vgbuf->yuv)); vgbuf->memory = buf; vgbuf->address = (uint32_t)vgbuf->memory; diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.h b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.h index 822ec44b5..97529a5d2 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.h +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_buf.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_VGLITE_BUF_H #define LV_VGLITE_BUF_H diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.c index 45f8083a4..8d41e8b60 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.h b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.h index f05e99e73..832cd86bb 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.h +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_matrix.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_VGLITE_MATRIX_H #define LV_VGLITE_MATRIX_H diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.c index ca163021b..271a0d295 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.h b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.h index 8ae82f379..6c92e50e6 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.h +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_path.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_VGLITE_PATH_H #define LV_VGLITE_PATH_H diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.c b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.c index 7c976baf3..330449059 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.c +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.c @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - /********************* * INCLUDES *********************/ diff --git a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.h b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.h index edaf6ea78..899743943 100644 --- a/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.h +++ b/project/gui/lvgl/src/draw/nxp/vglite/lv_vglite_utils.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT */ - #ifndef LV_VGLITE_UTILS_H #define LV_VGLITE_UTILS_H diff --git a/project/gui/lvgl/src/draw/sw/Makefile b/project/gui/lvgl/src/draw/sw/Makefile index 0b56c27a8..26ba51bd2 100644 --- a/project/gui/lvgl/src/draw/sw/Makefile +++ b/project/gui/lvgl/src/draw/sw/Makefile @@ -14,3 +14,4 @@ obj-y += lv_draw_sw_mask.o obj-y += lv_draw_sw_transform.o obj-y += lv_draw_sw_triangle.o obj-y += lv_draw_sw.o +obj-y += lv_draw_sw_vector.o diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw.c index 4907ec215..dbbf8111d 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw.c @@ -13,7 +13,15 @@ #include "lv_draw_sw.h" #include "../../display/lv_display_private.h" #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 + #else + #include "../../libs/thorvg/thorvg_capi.h" + #endif +#endif /********************* * DEFINES *********************/ @@ -33,14 +41,12 @@ static void execute_drawing(lv_draw_sw_unit_t * u); static int32_t lv_draw_sw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer); - -/********************** - * GLOBAL PROTOTYPES - **********************/ +static int32_t lv_draw_sw_delete(lv_draw_unit_t * draw_unit); /********************** * STATIC VARIABLES **********************/ +#define _draw_info LV_GLOBAL_DEFAULT()->draw_info /********************** * MACROS @@ -62,11 +68,46 @@ void lv_draw_sw_init(void) lv_draw_sw_unit_t * draw_sw_unit = lv_draw_create_unit(sizeof(lv_draw_sw_unit_t)); draw_sw_unit->base_unit.dispatch_cb = lv_draw_sw_dispatch; draw_sw_unit->idx = i; + draw_sw_unit->base_unit.delete_cb = LV_USE_OS ? lv_draw_sw_delete : NULL; #if LV_USE_OS 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 +} + +static int32_t lv_draw_sw_delete(lv_draw_unit_t * draw_unit) +{ +#if LV_USE_OS + lv_draw_sw_unit_t * draw_sw_unit = (lv_draw_sw_unit_t *) draw_unit; + + LV_LOG_INFO("cancel software rendering thread"); + draw_sw_unit->exit_status = true; + + if(draw_sw_unit->inited) { + lv_thread_sync_signal(&draw_sw_unit->sync); + } + + return lv_thread_delete(&draw_sw_unit->thread); +#else + LV_UNUSED(draw_unit); + return 0; +#endif } /********************** @@ -87,7 +128,6 @@ static int32_t lv_draw_sw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * laye void * buf = lv_draw_layer_alloc_buf(layer); if(buf == NULL) return -1; - t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; draw_sw_unit->base_unit.target_layer = layer; draw_sw_unit->base_unit.clip_area = &t->clip_area; @@ -95,7 +135,7 @@ static int32_t lv_draw_sw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * laye #if LV_USE_OS /*Let the render thread work*/ - lv_thread_sync_signal(&draw_sw_unit->sync); + if(draw_sw_unit->inited) lv_thread_sync_signal(&draw_sw_unit->sync); #else execute_drawing(draw_sw_unit); @@ -115,12 +155,21 @@ static void render_thread_cb(void * ptr) lv_draw_sw_unit_t * u = ptr; lv_thread_sync_init(&u->sync); + u->inited = true; while(1) { while(u->task_act == NULL) { + if(u->exit_status) { + break; + } lv_thread_sync_wait(&u->sync); } + if(u->exit_status) { + LV_LOG_INFO("ready to exit software rendering thread"); + break; + } + execute_drawing(u); /*Cleanup*/ @@ -130,6 +179,10 @@ static void render_thread_cb(void * ptr) /*The draw unit is free now. Request a new dispatching as it can get a new task*/ lv_draw_dispatch_request(); } + + u->inited = false; + lv_thread_sync_delete(&u->sync); + LV_LOG_INFO("exit software rendering thread"); } #endif @@ -171,6 +224,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; } @@ -182,8 +240,7 @@ static void execute_drawing(lv_draw_sw_unit_t * u) if(!_lv_area_intersect(&draw_area, &t->area, u->base_unit.clip_area)) return; int32_t idx = 0; - lv_display_t * disp = _lv_refr_get_disp_refreshing(); - lv_draw_unit_t * draw_unit_tmp = disp->draw_unit_head; + lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head; while(draw_unit_tmp != (lv_draw_unit_t *)u) { draw_unit_tmp = draw_unit_tmp->next; idx++; @@ -222,4 +279,37 @@ static void execute_drawing(lv_draw_sw_unit_t * u) } +void lv_draw_sw_rgb565_swap(void * buf, int32_t buf_size_px) +{ + uint32_t u32_cnt = buf_size_px / 2; + uint16_t * buf16 = buf; + uint32_t * buf32 = buf; + + while(u32_cnt >= 8) { + buf32[0] = ((uint32_t)(buf32[0] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[0] & 0x00ff00ff) << 8); + buf32[1] = ((uint32_t)(buf32[1] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[1] & 0x00ff00ff) << 8); + buf32[2] = ((uint32_t)(buf32[2] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[2] & 0x00ff00ff) << 8); + buf32[3] = ((uint32_t)(buf32[3] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[3] & 0x00ff00ff) << 8); + buf32[4] = ((uint32_t)(buf32[4] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[4] & 0x00ff00ff) << 8); + buf32[5] = ((uint32_t)(buf32[5] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[5] & 0x00ff00ff) << 8); + buf32[6] = ((uint32_t)(buf32[6] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[6] & 0x00ff00ff) << 8); + buf32[7] = ((uint32_t)(buf32[7] & 0xff00ff00) >> 8) + ((uint32_t)(buf32[7] & 0x00ff00ff) << 8); + buf32 += 8; + u32_cnt -= 8; + } + + while(u32_cnt) { + *buf32 = ((uint32_t)(*buf32 & 0xff00ff00) >> 8) + ((uint32_t)(*buf32 & 0x00ff00ff) << 8); + buf32++; + u32_cnt--; + } + + if(buf_size_px & 0x1) { + uint32_t e = buf_size_px - 1; + buf16[e] = ((buf16[e] & 0xff00) >> 8) + ((buf16[e] & 0x00ff) << 8); + } + + return; +} + #endif /*LV_USE_DRAW_SW*/ diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw.h b/project/gui/lvgl/src/draw/sw/lv_draw_sw.h index 97c31f6c2..a51c7df83 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw.h +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw.h @@ -21,6 +21,7 @@ extern "C" { #include "../../display/lv_display.h" #include "../../osal/lv_os.h" +#include "../../draw/lv_draw_vector.h" /********************* * DEFINES *********************/ @@ -35,6 +36,8 @@ typedef struct { #if LV_USE_OS lv_thread_sync_t sync; lv_thread_t thread; + volatile bool inited; + volatile bool exit_status; #endif uint32_t idx; } lv_draw_sw_unit_t; @@ -53,6 +56,8 @@ typedef struct { void lv_draw_sw_init(void); +void lv_draw_sw_deinit(void); + LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_image(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords); @@ -80,6 +85,18 @@ 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. + * Might be required if a 8bit parallel port or an SPI port send the bytes in the wrong order. + * The bytes will be swapped in place. + * @param buf_size_px number of pixels in the buffer + */ +void lv_draw_sw_rgb565_swap(void * buf, int32_t buf_size_px); + /*********************** * GLOBAL VARIABLES ***********************/ diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_arc.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_arc.c index b201a3a53..17a4926e3 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_arc.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_arc.c @@ -16,7 +16,6 @@ #include "../../stdlib/lv_string.h" #include "../lv_draw.h" - static void add_circle(const lv_opa_t * circle_mask, const lv_area_t * blend_area, const lv_area_t * circle_area, lv_opa_t * mask_buf, int32_t width); static void get_rounded_area(int16_t angle, int32_t radius, uint8_t thickness, lv_area_t * res_area); @@ -138,7 +137,6 @@ void lv_draw_sw_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, c blend_dsc.src_stride = decoder_dsc.header.stride; } - lv_opa_t * circle_mask = NULL; lv_area_t round_area_1; lv_area_t round_area_2; diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_border.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_border.c index 7a58ed958..c7f11919d 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_border.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_border.c @@ -22,7 +22,6 @@ *********************/ #define SPLIT_LIMIT 50 - /********************** * TYPEDEFS **********************/ @@ -36,7 +35,6 @@ static void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * ou static void draw_border_simple(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area, lv_color_t color, lv_opa_t opa); - /********************** * STATIC VARIABLES **********************/ @@ -100,7 +98,6 @@ void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_are lv_opa_t * mask_buf = lv_malloc(draw_area_w); blend_dsc.mask_buf = mask_buf; - void * mask_list[3] = {0}; /*Create mask for the inner mask*/ @@ -108,7 +105,6 @@ void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_are lv_draw_sw_mask_radius_init(&mask_rin_param, inner_area, rin, true); mask_list[0] = &mask_rin_param; - /*Create mask for the outer area*/ lv_draw_sw_mask_radius_param_t mask_rout_param; if(rout > 0) { @@ -144,7 +140,6 @@ void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_are split_hor = false; } - blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER; /*Draw the straight lines first if they are long enough*/ if(top_side && split_hor) { diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_box_shadow.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_box_shadow.c index 505059e05..f5ea57010 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_box_shadow.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_box_shadow.c @@ -40,7 +40,6 @@ LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coord LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(int32_t size, int32_t sw, uint16_t * sh_ups_buf); #endif /*LV_DRAW_SW_COMPLEX*/ - /********************** * STATIC VARIABLES **********************/ @@ -92,7 +91,6 @@ void lv_draw_sw_box_shadow(lv_draw_unit_t * draw_unit, const lv_draw_box_shadow_ short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area)); if(r_sh > short_side >> 1) r_sh = short_side >> 1; - /*Get how many pixels are affected by the blur on the corners*/ int32_t corner_size = dsc->width + r_sh; @@ -293,7 +291,6 @@ void lv_draw_sw_box_shadow(lv_draw_unit_t * draw_unit, const lv_draw_box_shadow_ blend_area.y2 = shadow_area.y2; blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1); - if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_unit->clip_area) && !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) { int32_t w = lv_area_get_width(&clip_area_sub); @@ -528,6 +525,8 @@ void lv_draw_sw_box_shadow(lv_draw_unit_t * draw_unit, const lv_draw_box_shadow_ blend_area.x2 = shadow_area.x2 - corner_size; blend_area.y1 = shadow_area.y1 + corner_size; blend_area.y2 = shadow_area.y2 - corner_size; + blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1); + blend_area.y2 = LV_MAX(blend_area.y2, h_half); blend_dsc.mask_buf = mask_buf; if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_unit->clip_area) && diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_fill.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_fill.c index 01aac052d..9274f25c1 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_fill.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_fill.c @@ -100,7 +100,6 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, blend_dsc.mask_area = &blend_area; blend_dsc.opa = LV_OPA_COVER; - /*Get gradient if appropriate*/ lv_grad_t * grad = lv_gradient_get(&dsc->grad, coords_bg_w, coords_bg_h); lv_opa_t * grad_opa_map = NULL; diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.c index 665158f53..0759d943c 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.c @@ -12,7 +12,6 @@ #include "../../misc/lv_types.h" #include "../../osal/lv_os.h" - /********************* * DEFINES *********************/ @@ -56,7 +55,6 @@ static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h) return item; } - /********************** * FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.h b/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.h index af7ea3718..19fa697b3 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.h +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_gradient.h @@ -25,7 +25,6 @@ extern "C" { #error LVGL needs at least 2 stops for gradients. Please increase the LV_GRADIENT_MAX_STOPS #endif - /********************** * TYPEDEFS **********************/ @@ -50,7 +49,6 @@ typedef struct _lv_gradient_cache_t { LV_ATTRIBUTE_FAST_MEM void lv_gradient_color_calculate(const lv_grad_dsc_t * dsc, int32_t range, int32_t frac, lv_grad_color_t * color_out, lv_opa_t * opa_out); - /** Get a gradient cache from the given parameters */ lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * gradient, int32_t w, int32_t h); diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_img.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_img.c index 16c8a4bb2..2e129f0ee 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_img.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_img.c @@ -18,6 +18,7 @@ #include "../../misc/lv_math.h" #include "../../misc/lv_color.h" #include "../../stdlib/lv_string.h" +#include "../../core/lv_global.h" /********************* * DEFINES @@ -31,12 +32,25 @@ /********************** * STATIC PROTOTYPES **********************/ -static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * draw_area, - const lv_image_decoder_dsc_t * src, lv_draw_image_sup_t * sup, const lv_area_t * img_coords); + +static void img_draw_normal(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords); + +static void img_draw_tiled(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords); + +static void img_decode_and_draw(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + lv_image_decoder_dsc_t * decoder_dsc, + const lv_area_t * img_area, const lv_area_t * clipped_img_area); + +static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup, + const lv_area_t * img_coords, const lv_area_t * clipped_img_area); /********************** * STATIC VARIABLES **********************/ +#define _draw_info LV_GLOBAL_DEFAULT()->draw_info /********************** * MACROS @@ -58,10 +72,9 @@ void lv_draw_sw_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dr img_dsc.header.w = lv_area_get_width(&layer_to_draw->buf_area); img_dsc.header.h = lv_area_get_height(&layer_to_draw->buf_area); img_dsc.header.cf = layer_to_draw->color_format; - img_dsc.header.stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer_to_draw->buf_area), - layer_to_draw->color_format); + img_dsc.header.stride = layer_to_draw->buf_stride; img_dsc.header.always_zero = 0; - img_dsc.data = lv_draw_buf_align(layer_to_draw->buf, layer_to_draw->color_format); + img_dsc.data = layer_to_draw->buf; lv_draw_image_dsc_t new_draw_dsc; lv_memcpy(&new_draw_dsc, draw_dsc, sizeof(lv_draw_image_dsc_t)); @@ -106,21 +119,24 @@ void lv_draw_sw_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dr #if LV_USE_PARALLEL_DRAW_DEBUG uint32_t idx = 0; - lv_display_t * disp = _lv_refr_get_disp_refreshing(); - lv_draw_unit_t * draw_unit_tmp = disp->draw_unit_head; + lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head; while(draw_unit_tmp != draw_unit) { draw_unit_tmp = draw_unit_tmp->next; idx++; } - lv_draw_rect_dsc_t rect_dsc; - lv_draw_rect_dsc_init(&rect_dsc); - rect_dsc.bg_color = lv_palette_main(idx % _LV_PALETTE_LAST); - rect_dsc.border_color = rect_dsc.bg_color; - rect_dsc.bg_opa = LV_OPA_10; - rect_dsc.border_opa = LV_OPA_100; - rect_dsc.border_width = 2; - lv_draw_sw_rect(draw_unit, &rect_dsc, &area_rot); + lv_draw_fill_dsc_t fill_dsc; + lv_draw_rect_dsc_init(&fill_dsc); + fill_dsc.color = lv_palette_main(idx % _LV_PALETTE_LAST); + fill_dsc.opa = LV_OPA_10; + lv_draw_sw_fill(draw_unit, &fill_dsc, &area_rot); + + lv_draw_border_dsc_t border_dsc; + lv_draw_border_dsc_init(&border_dsc); + border_dsc.color = lv_palette_main(idx % _LV_PALETTE_LAST); + border_dsc.opa = LV_OPA_100; + border_dsc.width = 2; + lv_draw_sw_border(draw_unit, &border_dsc, &area_rot); lv_point_t txt_size; lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE); @@ -131,9 +147,9 @@ void lv_draw_sw_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dr txt_area.y2 = draw_area.y2; txt_area.y1 = draw_area.y2 - txt_size.y + 1; - lv_draw_rect_dsc_init(&rect_dsc); - rect_dsc.bg_color = lv_color_black(); - lv_draw_sw_rect(draw_unit, &rect_dsc, &txt_area); + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_color_black(); + lv_draw_sw_fill(draw_unit, &fill_dsc, &txt_area); char buf[8]; lv_snprintf(buf, sizeof(buf), "%d", idx); @@ -145,29 +161,37 @@ void lv_draw_sw_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dr #endif } - - LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_image(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords) { - lv_area_t transformed_area; - lv_area_copy(&transformed_area, coords); + if(!draw_dsc->tile) { + img_draw_normal(draw_unit, draw_dsc, coords); + } + else { + img_draw_tiled(draw_unit, draw_dsc, coords); + } +} + +static void img_draw_normal(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords) +{ + lv_area_t draw_area; + lv_area_copy(&draw_area, coords); if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) { int32_t w = lv_area_get_width(coords); int32_t h = lv_area_get_height(coords); - _lv_image_buf_get_transformed_area(&transformed_area, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y, + _lv_image_buf_get_transformed_area(&draw_area, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y, &draw_dsc->pivot); - transformed_area.x1 += coords->x1; - transformed_area.y1 += coords->y1; - transformed_area.x2 += coords->x1; - transformed_area.y2 += coords->y1; + draw_area.x1 += coords->x1; + draw_area.y1 += coords->y1; + draw_area.x2 += coords->x1; + draw_area.y2 += coords->y1; } - lv_area_t draw_area; /*Common area of cilp_area and coords. The image is visible only here*/ - /*Out of mask. There is nothing to draw so the image is drawn successfully.*/ - if(!_lv_area_intersect(&draw_area, draw_unit->clip_area, &transformed_area)) { + lv_area_t clipped_img_area; + if(!_lv_area_intersect(&clipped_img_area, &draw_area, draw_unit->clip_area)) { return; } @@ -178,56 +202,100 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_image(lv_draw_unit_t * draw_unit, const lv return; } + img_decode_and_draw(draw_unit, draw_dsc, &decoder_dsc, coords, &clipped_img_area); + + lv_image_decoder_close(&decoder_dsc); +} + +static void img_draw_tiled(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords) +{ + lv_image_decoder_dsc_t decoder_dsc; + lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, draw_dsc->recolor, -1); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to open image"); + return; + } + + int32_t img_w = lv_area_get_width(coords); + int32_t img_h = lv_area_get_height(coords); + + lv_area_t tile_area = *coords; + int32_t tile_x_start = tile_area.x1; + + while(tile_area.y1 < draw_unit->clip_area->y2) { + while(tile_area.x1 < draw_unit->clip_area->x2) { + + lv_area_t clipped_img_area; + if(_lv_area_intersect(&clipped_img_area, &tile_area, draw_unit->clip_area)) { + img_decode_and_draw(draw_unit, draw_dsc, &decoder_dsc, &tile_area, &clipped_img_area); + } + + tile_area.x1 += img_w; + tile_area.x2 += img_w; + } + + tile_area.y1 += img_h; + tile_area.y2 += img_h; + tile_area.x1 = tile_x_start; + tile_area.x2 = tile_x_start + img_w - 1; + } + + lv_image_decoder_close(&decoder_dsc); +} + +static void img_decode_and_draw(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + lv_image_decoder_dsc_t * decoder_dsc, + const lv_area_t * img_area, const lv_area_t * clipped_img_area) +{ lv_draw_image_sup_t sup; sup.alpha_color = draw_dsc->recolor; - sup.palette = decoder_dsc.palette; - sup.palette_size = decoder_dsc.palette_size; + sup.palette = decoder_dsc->palette; + sup.palette_size = decoder_dsc->palette_size; /*The whole image is available, just draw it*/ - if(decoder_dsc.img_data) { - img_draw_core(draw_unit, draw_dsc, &draw_area, &decoder_dsc, - &sup, coords); + if(decoder_dsc->img_data) { + img_draw_core(draw_unit, draw_dsc, decoder_dsc, &sup, img_area, clipped_img_area); } - /*Draw line by line*/ + /*Draw in smaller pieces*/ else { - lv_area_t relative_full_area_to_decode = draw_area; - lv_area_move(&relative_full_area_to_decode, -coords->x1, -coords->y1); + lv_area_t relative_full_area_to_decode = *clipped_img_area; + lv_area_move(&relative_full_area_to_decode, -img_area->x1, -img_area->y1); lv_area_t relative_decoded_area; relative_decoded_area.x1 = LV_COORD_MIN; relative_decoded_area.y1 = LV_COORD_MIN; relative_decoded_area.x2 = LV_COORD_MIN; relative_decoded_area.y2 = LV_COORD_MIN; - res = LV_RESULT_OK; + lv_result_t res = LV_RESULT_OK; + while(res == LV_RESULT_OK) { - res = lv_image_decoder_get_area(&decoder_dsc, &relative_full_area_to_decode, &relative_decoded_area); + res = lv_image_decoder_get_area(decoder_dsc, &relative_full_area_to_decode, &relative_decoded_area); lv_area_t absolute_decoded_area = relative_decoded_area; - lv_area_move(&absolute_decoded_area, coords->x1, coords->y1); + lv_area_move(&absolute_decoded_area, img_area->x1, img_area->y1); if(res == LV_RESULT_OK) { /*Limit draw area to the current decoded area and draw the image*/ - lv_area_t draw_area_sub; - if(_lv_area_intersect(&draw_area_sub, &draw_area, &absolute_decoded_area)) { - img_draw_core(draw_unit, draw_dsc, &draw_area_sub, &decoder_dsc, - &sup, &absolute_decoded_area); + lv_area_t clipped_img_area_sub; + if(_lv_area_intersect(&clipped_img_area_sub, clipped_img_area, &absolute_decoded_area)) { + img_draw_core(draw_unit, draw_dsc, decoder_dsc, &sup, + &absolute_decoded_area, &clipped_img_area_sub); } } } } - - lv_image_decoder_close(&decoder_dsc); } - -static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * draw_area, - const lv_image_decoder_dsc_t * src, lv_draw_image_sup_t * sup, const lv_area_t * img_coords) +static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup, + const lv_area_t * img_coords, const lv_area_t * clipped_img_area) { bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE ? true : false; lv_draw_sw_blend_dsc_t blend_dsc; - const uint8_t * src_buf = src->img_data; - const lv_image_header_t * header = &src->header; + const uint8_t * src_buf = decoder_dsc->img_data; + const lv_image_header_t * header = &decoder_dsc->header; uint32_t img_stride = header->stride; lv_color_format_t cf = header->cf; @@ -276,7 +344,7 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t } /*In the other cases every pixel need to be checked one-by-one*/ else { - lv_area_t blend_area = *draw_area; + lv_area_t blend_area = *clipped_img_area; blend_dsc.blend_area = &blend_area; int32_t src_w = lv_area_get_width(img_coords); @@ -335,7 +403,6 @@ static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t blend_dsc.src_stride = lv_draw_buf_width_to_stride(blend_w, cf_final); } - while(blend_area.y1 <= y_last) { /*Apply transformations if any or separate the channels*/ lv_area_t relative_area; diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_line.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_line.c index 076a97b8e..d8fbd0042 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_line.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_line.c @@ -308,7 +308,6 @@ LV_ATTRIBUTE_FAST_MEM static void draw_line_skew(lv_draw_unit_t * draw_unit, con void * masks[5] = {&mask_left_param, & mask_right_param, NULL, NULL, NULL}; - if(flat) { if(xdiff > 0) { lv_draw_sw_mask_line_points_init(&mask_left_param, p1.x, p1.y - w_half0, p2.x, p2.y - w_half0, diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.c index 8af24e418..2439bcee0 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.c @@ -82,6 +82,10 @@ void lv_draw_sw_mask_init(void) lv_mutex_init(&circle_cache_mutex); } +void lv_draw_sw_mask_deinit(void) +{ + lv_mutex_delete(&circle_cache_mutex); +} LV_ATTRIBUTE_FAST_MEM lv_draw_sw_mask_res_t lv_draw_sw_mask_apply(void * masks[], lv_opa_t * mask_buf, int32_t abs_x, int32_t abs_y, @@ -379,9 +383,8 @@ void lv_draw_sw_mask_radius_init(lv_draw_sw_mask_radius_param_t * param, const l /*There is no unused entry. Allocate one temporarily*/ if(!entry) { - entry = lv_malloc(sizeof(_lv_draw_sw_mask_radius_circle_dsc_t)); + entry = lv_malloc_zeroed(sizeof(_lv_draw_sw_mask_radius_circle_dsc_t)); LV_ASSERT_MALLOC(entry); - lv_memzero(entry, sizeof(_lv_draw_sw_mask_radius_circle_dsc_t)); entry->life = -1; } else { @@ -895,8 +898,6 @@ LV_ATTRIBUTE_FAST_MEM static lv_draw_sw_mask_res_t lv_draw_mask_angle(lv_opa_t * } } - - LV_ATTRIBUTE_FAST_MEM static lv_draw_sw_mask_res_t lv_draw_mask_radius(lv_opa_t * mask_buf, int32_t abs_x, int32_t abs_y, int32_t len, lv_draw_sw_mask_radius_param_t * p) @@ -1152,8 +1153,7 @@ static void circ_calc_aa4(_lv_draw_sw_mask_radius_circle_dsc_t * c, int32_t radi } const size_t cir_xy_size = (radius + 1) * 2 * 2 * sizeof(int32_t); - int32_t * cir_x = lv_malloc(cir_xy_size); - lv_memset(cir_x, 0, cir_xy_size); + int32_t * cir_x = lv_malloc_zeroed(cir_xy_size); int32_t * cir_y = &cir_x[(radius + 1) * 2]; uint32_t y_8th_cnt = 0; @@ -1286,7 +1286,6 @@ static lv_opa_t * get_next_line(_lv_draw_sw_mask_radius_circle_dsc_t * c, int32_ return &c->cir_opa[c->opa_start_on_y[y]]; } - LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_t mask_new) { if(mask_new >= LV_OPA_MAX) return mask_act; @@ -1295,5 +1294,4 @@ LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_ return LV_UDIV255(mask_act * mask_new); } - #endif /*LV_DRAW_SW_COMPLEX*/ diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.h b/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.h index 9588108ea..9cde01683 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.h +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_mask.h @@ -10,7 +10,6 @@ extern "C" { #endif - /********************* * INCLUDES *********************/ @@ -155,7 +154,6 @@ typedef struct { _lv_draw_sw_mask_radius_circle_dsc_t * circle; } lv_draw_sw_mask_radius_param_t; - typedef struct { /*The first element must be the common descriptor*/ _lv_draw_sw_mask_common_dsc_t dsc; @@ -170,7 +168,6 @@ typedef struct { } lv_draw_sw_mask_fade_param_t; - typedef struct _lv_draw_sw_mask_map_param_t { /*The first element must be the common descriptor*/ _lv_draw_sw_mask_common_dsc_t dsc; @@ -181,13 +178,14 @@ typedef struct _lv_draw_sw_mask_map_param_t { } cfg; } lv_draw_sw_mask_map_param_t; - /********************** * GLOBAL PROTOTYPES **********************/ void lv_draw_sw_mask_init(void); +void lv_draw_sw_mask_deinit(void); + //! @cond Doxygen_Suppress /** diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_transform.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_transform.c index 0c72fa60f..051da4d51 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_transform.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_transform.c @@ -13,6 +13,7 @@ #include "../../misc/lv_area.h" #include "../../core/lv_refr.h" #include "../../misc/lv_color.h" +#include "../../stdlib/lv_string.h" /********************* * DEFINES @@ -54,7 +55,6 @@ static void transform_rgb888(const uint8_t * src, int32_t src_w, int32_t src_h, int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, int32_t x_end, uint8_t * dest_buf, bool aa, uint32_t px_size); - static void transform_argb8888(const uint8_t * src, int32_t src_w, int32_t src_h, int32_t src_stride, int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, int32_t x_end, uint8_t * dest_buf, bool aa); @@ -109,7 +109,6 @@ void lv_draw_sw_transform(lv_draw_unit_t * draw_unit, const lv_area_t * dest_are tr_dsc.pivot_x_256 = tr_dsc.pivot.x * 256; tr_dsc.pivot_y_256 = tr_dsc.pivot.y * 256; - int32_t dest_w = lv_area_get_width(dest_area); int32_t dest_h = lv_area_get_height(dest_area); @@ -289,15 +288,10 @@ static void transform_rgb888(const uint8_t * src, int32_t src_w, int32_t src_h, else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { dest_c32[x].alpha = (a * (0xFF - ys_fract)) >> 8; } - else { - dest_c32[x].alpha = 0x00; - } } } } -#include "../../stdlib/lv_string.h" - static void transform_argb8888(const uint8_t * src, int32_t src_w, int32_t src_h, int32_t src_stride, int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, int32_t x_end, uint8_t * dest_buf, bool aa) @@ -385,9 +379,6 @@ static void transform_argb8888(const uint8_t * src, int32_t src_w, int32_t src_h else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { dest_c32[x].alpha = (dest_c32[x].alpha * (0x7F - ys_fract)) >> 7; } - else { - dest_c32[x].alpha = 0x00; - } } } } @@ -444,7 +435,6 @@ static void transform_rgb565a8(const uint8_t * src, int32_t src_w, int32_t src_h src_tmp_u16 += (ys_int * src_stride) + xs_int; cbuf[x] = src_tmp_u16[0]; - if(aa && xs_int + x_next >= 0 && xs_int + x_next <= src_w - 1 && @@ -496,9 +486,6 @@ static void transform_rgb565a8(const uint8_t * src, int32_t src_w, int32_t src_h else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { abuf[x] = (a * (0xFF - ys_fract)) >> 8; } - else { - abuf[x] = 0x00; - } } } } @@ -573,14 +560,10 @@ static void transform_a8(const uint8_t * src, int32_t src_w, int32_t src_h, int3 else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { abuf[x] = (src_tmp[0] * (0xFF - ys_fract)) >> 8; } - else { - abuf[x] = 0x00; - } } } } - static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, int32_t * yout) { diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_triangle.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_triangle.c index 315f8a472..836dbcfb9 100644 --- a/project/gui/lvgl/src/draw/sw/lv_draw_sw_triangle.c +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_triangle.c @@ -206,5 +206,4 @@ static void point_swap(lv_point_t * p1, lv_point_t * p2) *p2 = tmp; } - #endif /*LV_USE_DRAW_SW*/ diff --git a/project/gui/lvgl/src/draw/sw/lv_draw_sw_vector.c b/project/gui/lvgl/src/draw/sw/lv_draw_sw_vector.c new file mode 100644 index 000000000..bf024fccc --- /dev/null +++ b/project/gui/lvgl/src/draw/sw/lv_draw_sw_vector.c @@ -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 +#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*/ diff --git a/project/gui/lvgl/src/layouts/lv_layout.c b/project/gui/lvgl/src/layouts/lv_layout.c index 2dd68d105..a14c1219f 100644 --- a/project/gui/lvgl/src/layouts/lv_layout.c +++ b/project/gui/lvgl/src/layouts/lv_layout.c @@ -50,6 +50,11 @@ void _lv_layout_init(void) #endif } +void _lv_layout_deinit(void) +{ + lv_free(layout_list_def); +} + uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data) { layout_list_def = lv_realloc(layout_list_def, (layout_cnt + 1) * sizeof(lv_layout_dsc_t)); @@ -71,4 +76,4 @@ void _lv_layout_apply(lv_obj_t * obj) /********************** * STATIC FUNCTIONS - **********************/ \ No newline at end of file + **********************/ diff --git a/project/gui/lvgl/src/layouts/lv_layout.h b/project/gui/lvgl/src/layouts/lv_layout.h index b72a5831d..0266d4386 100644 --- a/project/gui/lvgl/src/layouts/lv_layout.h +++ b/project/gui/lvgl/src/layouts/lv_layout.h @@ -15,7 +15,6 @@ extern "C" { *********************/ #include "../lv_conf_internal.h" - /********************* * DEFINES *********************/ @@ -32,7 +31,6 @@ typedef struct { void * user_data; } lv_layout_dsc_t; - typedef enum { LV_LAYOUT_NONE = 0, @@ -53,6 +51,8 @@ typedef enum { void _lv_layout_init(void); +void _lv_layout_deinit(void); + /** * Register a new layout * @param cb the layout update callback @@ -61,7 +61,6 @@ void _lv_layout_init(void); */ uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data); - /** * Update the layout of a widget * @param obj pointer to a widget @@ -80,7 +79,6 @@ void _lv_layout_apply(struct _lv_obj_t * obj); #include "grid/lv_grid.h" #endif /* LV_USE_GRID */ - #ifdef __cplusplus } /*extern "C"*/ #endif diff --git a/project/gui/lvgl/src/libs/Makefile b/project/gui/lvgl/src/libs/Makefile index 4fd1a990a..999ff0ed9 100644 --- a/project/gui/lvgl/src/libs/Makefile +++ b/project/gui/lvgl/src/libs/Makefile @@ -11,3 +11,4 @@ obj-y += tjpgd/ obj-y += tiny_ttf/ obj-y += libpng/ obj-y += libjpeg_turbo/ +obj-y += thorvg/ diff --git a/project/gui/lvgl/src/libs/barcode/lv_barcode.c b/project/gui/lvgl/src/libs/barcode/lv_barcode.c index 7bb96f18f..f832120a3 100644 --- a/project/gui/lvgl/src/libs/barcode/lv_barcode.c +++ b/project/gui/lvgl/src/libs/barcode/lv_barcode.c @@ -188,6 +188,7 @@ static void lv_barcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * ob barcode->light_color = lv_color_white(); barcode->scale = 1; barcode->direction = LV_DIR_HOR; + lv_image_set_align(obj, LV_IMAGE_ALIGN_TILE); } static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) diff --git a/project/gui/lvgl/src/libs/bmp/lv_bmp.c b/project/gui/lvgl/src/libs/bmp/lv_bmp.c index eb86bdfa5..fd03cadc3 100644 --- a/project/gui/lvgl/src/libs/bmp/lv_bmp.c +++ b/project/gui/lvgl/src/libs/bmp/lv_bmp.c @@ -59,6 +59,17 @@ void lv_bmp_init(void) lv_image_decoder_set_close_cb(dec, decoder_close); } +void lv_bmp_deinit(void) +{ + lv_image_decoder_t * dec = NULL; + while((dec = lv_image_decoder_get_next(dec)) != NULL) { + if(dec->info_cb == decoder_info) { + lv_image_decoder_delete(dec); + break; + } + } +} + /********************** * STATIC FUNCTIONS **********************/ @@ -123,7 +134,6 @@ static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/ } - /** * Open a PNG image and return the decided image * @param src can be file name or pointer to a C array @@ -178,7 +188,6 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d return LV_RESULT_INVALID; /*If not returned earlier then it failed*/ } - static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, const lv_area_t * full_area, lv_area_t * decoded_area) { @@ -196,7 +205,6 @@ static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decod decoded_area->y2++; } - if(decoded_area->y1 > full_area->y2) { return LV_RESULT_INVALID; } @@ -211,7 +219,6 @@ static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decod } } - /** * Free the allocated resources */ diff --git a/project/gui/lvgl/src/libs/bmp/lv_bmp.h b/project/gui/lvgl/src/libs/bmp/lv_bmp.h index 5577f5f65..a7dbe277b 100644 --- a/project/gui/lvgl/src/libs/bmp/lv_bmp.h +++ b/project/gui/lvgl/src/libs/bmp/lv_bmp.h @@ -28,6 +28,7 @@ extern "C" { * GLOBAL PROTOTYPES **********************/ void lv_bmp_init(void); +void lv_bmp_deinit(void); /********************** * MACROS diff --git a/project/gui/lvgl/src/libs/freetype/lv_freetype.c b/project/gui/lvgl/src/libs/freetype/lv_freetype.c index 87fe2ff85..ea883fea9 100644 --- a/project/gui/lvgl/src/libs/freetype/lv_freetype.c +++ b/project/gui/lvgl/src/libs/freetype/lv_freetype.c @@ -172,13 +172,12 @@ lv_font_t * lv_freetype_font_create(const char * pathname, uint16_t size, uint16 } size_t need_size = sizeof(lv_freetype_font_dsc_t) + sizeof(lv_font_t); - lv_freetype_font_dsc_t * dsc = lv_malloc(need_size); + lv_freetype_font_dsc_t * dsc = lv_malloc_zeroed(need_size); LV_ASSERT_MALLOC(dsc); if(!dsc) { LV_LOG_ERROR("malloc failed for lv_freetype_font_dsc"); return NULL; } - lv_memzero(dsc, need_size); dsc->font = (lv_font_t *)(((uint8_t *)dsc) + sizeof(lv_freetype_font_dsc_t)); diff --git a/project/gui/lvgl/src/libs/freetype/lv_ftsystem.c b/project/gui/lvgl/src/libs/freetype/lv_ftsystem.c index 18248ae74..ee0e836b6 100644 --- a/project/gui/lvgl/src/libs/freetype/lv_ftsystem.c +++ b/project/gui/lvgl/src/libs/freetype/lv_ftsystem.c @@ -155,7 +155,6 @@ FT_New_Memory(void) { FT_Memory memory; - memory = (FT_Memory)lv_malloc(sizeof(*memory)); if(memory) { memory->user = NULL; diff --git a/project/gui/lvgl/src/libs/thorvg/Makefile b/project/gui/lvgl/src/libs/thorvg/Makefile new file mode 100644 index 000000000..4041fc886 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/Makefile @@ -0,0 +1,34 @@ +obj-y += tvgAnimation.o +obj-y += tvgBezier.o +obj-y += tvgCanvas.o +obj-y += tvgCapi.o +obj-y += tvgCompressor.o +obj-y += tvgFill.o +obj-y += tvgInitializer.o +obj-y += tvgLoader.o +obj-y += tvgMath.o +obj-y += tvgPaint.o +obj-y += tvgPicture.o +obj-y += tvgRawLoader.o +obj-y += tvgRender.o +obj-y += tvgSaver.o +obj-y += tvgScene.o +obj-y += tvgShape.o +obj-y += tvgStr.o +obj-y += tvgSvgCssStyle.o +obj-y += tvgSvgLoader.o +obj-y += tvgSvgPath.o +obj-y += tvgSvgSceneBuilder.o +obj-y += tvgSvgUtil.o +obj-y += tvgSwCanvas.o +obj-y += tvgSwFill.o +obj-y += tvgSwImage.o +obj-y += tvgSwMath.o +obj-y += tvgSwMemPool.o +obj-y += tvgSwRaster.o +obj-y += tvgSwRenderer.o +obj-y += tvgSwRle.o +obj-y += tvgSwShape.o +obj-y += tvgSwStroke.o +obj-y += tvgTaskScheduler.o +obj-y += tvgXmlParser.o diff --git a/project/gui/lvgl/src/libs/thorvg/config.h b/project/gui/lvgl/src/libs/thorvg/config.h new file mode 100644 index 000000000..abeed9415 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/config.h @@ -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" + diff --git a/project/gui/lvgl/src/libs/thorvg/thorvg.h b/project/gui/lvgl/src/libs/thorvg/thorvg.h new file mode 100644 index 000000000..7f5a84b94 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/thorvg.h @@ -0,0 +1,1925 @@ +/*! + * @file thorvg.h + * + * The main APIs enabling the TVG initialization, preparation of the canvas and provisioning of its content: + * - drawing shapes: line, arc, curve, path, polygon... + * - drawing pictures: tvg, svg, png, jpg, bitmap... + * - drawing fillings: solid, linear and radial gradient... + * - drawing stroking: continuous stroking with arbitrary width, join, cap, dash styles. + * - drawing composition: blending, masking, path clipping... + * - drawing scene graph & affine transformation (translation, rotation, scale, ...) + * and finally drawing the canvas and TVG termination. + */ + +#include "../../lv_conf_internal.h" +#if LV_USE_THORVG_INTERNAL + + +#ifndef _THORVG_H_ +#define _THORVG_H_ + +#include +#include +#include +#include + +#ifdef TVG_API + #undef TVG_API +#endif + +#ifndef TVG_STATIC + #ifdef _WIN32 + #if TVG_BUILD + #define TVG_API __declspec(dllexport) + #else + #define TVG_API __declspec(dllimport) + #endif + #elif (defined(__SUNPRO_C) || defined(__SUNPRO_CC)) + #define TVG_API __global + #else + #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER) + #define TVG_API __attribute__ ((visibility("default"))) + #else + #define TVG_API + #endif + #endif +#else + #define TVG_API +#endif + +#ifdef TVG_DEPRECATED + #undef TVG_DEPRECATED +#endif + +#ifdef _WIN32 + #define TVG_DEPRECATED __declspec(deprecated) +#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) + #define TVG_DEPRECATED __attribute__ ((__deprecated__)) +#else + #define TVG_DEPRECATED +#endif + +#define _TVG_DECLARE_PRIVATE(A) \ + struct Impl; \ + Impl* pImpl; \ +protected: \ + A(const A&) = delete; \ + const A& operator=(const A&) = delete; \ + A() + +#define _TVG_DISABLE_CTOR(A) \ + A() = delete; \ + ~A() = delete + +#define _TVG_DECLARE_ACCESSOR(A) \ + friend A + +namespace tvg +{ + +class RenderMethod; +class Animation; + +/** + * @defgroup ThorVG ThorVG + * @brief ThorVG classes and enumerations providing C++ APIs. + */ + +/**@{*/ + +/** + * @brief Enumeration specifying the result from the APIs. + */ +enum class Result +{ + Success = 0, ///< The value returned in case of a correct request execution. + InvalidArguments, ///< The value returned in the event of a problem with the arguments given to the API - e.g. empty paths or null pointers. + InsufficientCondition, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist. + FailedAllocation, ///< The value returned in case of unsuccessful memory allocation. + MemoryCorruption, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting + NonSupport, ///< The value returned in case of choosing unsupported options. + Unknown ///< The value returned in all other cases. +}; + + +/** + * @brief Enumeration specifying the values of the path commands accepted by TVG. + * + * Not to be confused with the path commands from the svg path element (like M, L, Q, H and many others). + * TVG interprets all of them and translates to the ones from the PathCommand values. + */ +enum class PathCommand +{ + Close = 0, ///< Ends the current sub-path and connects it with its initial point. This command doesn't expect any points. + MoveTo, ///< Sets a new initial point of the sub-path and a new current point. This command expects 1 point: the starting position. + LineTo, ///< Draws a line from the current point to the given point and sets a new value of the current point. This command expects 1 point: the end-position of the line. + CubicTo ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point. This command expects 3 points: the 1st control-point, the 2nd control-point, the end-point of the curve. +}; + + +/** + * @brief Enumeration determining the ending type of a stroke in the open sub-paths. + */ +enum class StrokeCap +{ + Square = 0, ///< The stroke is extended in both end-points of a sub-path by a rectangle, with the width equal to the stroke width and the length equal to the half of the stroke width. For zero length sub-paths the square is rendered with the size of the stroke width. + Round, ///< The stroke is extended in both end-points of a sub-path by a half circle, with a radius equal to the half of a stroke width. For zero length sub-paths a full circle is rendered. + Butt ///< The stroke ends exactly at each of the two end-points of a sub-path. For zero length sub-paths no stroke is rendered. +}; + + +/** + * @brief Enumeration determining the style used at the corners of joined stroked path segments. + */ +enum class StrokeJoin +{ + Bevel = 0, ///< The outer corner of the joined path segments is bevelled at the join point. The triangular region of the corner is enclosed by a straight line between the outer corners of each stroke. + Round, ///< The outer corner of the joined path segments is rounded. The circular region is centered at the join point. + Miter ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style. +}; + + +/** + * @brief Enumeration specifying how to fill the area outside the gradient bounds. + */ +enum class FillSpread +{ + Pad = 0, ///< The remaining area is filled with the closest stop color. + Reflect, ///< The gradient pattern is reflected outside the gradient area until the expected region is filled. + Repeat ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled. +}; + + +/** + * @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape. + */ +enum class FillRule +{ + Winding = 0, ///< A line from the point to a location outside the shape is drawn. The intersections of the line with the path segment of the shape are counted. Starting from zero, if the path segment of the shape crosses the line clockwise, one is added, otherwise one is subtracted. If the resulting sum is non zero, the point is inside the shape. + EvenOdd ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape. +}; + + +/** + * @brief Enumeration indicating the method used in the composition of two objects - the target and the source. + * + * Notation: S(Source), T(Target), SA(Source Alpha), TA(Target Alpha) + * + * @see Paint::composite() + */ +enum class CompositeMethod +{ + None = 0, ///< No composition is applied. + ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. + AlphaMask, ///< Alpha Masking using the compositing target's pixels as an alpha value. + InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. + LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 + InvLumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the complement to the compositing target's pixels. + AddMask, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) @BETA_API + SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) @BETA_API + IntersectMask, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) @BETA_API + DifferenceMask ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) @BETA_API +}; + + +/** + * @brief Enumeration indicates the method used for blending paint. Please refer to the respective formulas for each method. + * + * Notation: S(source paint as the top layer), D(destination as the bottom layer), Sa(source paint alpha), Da(destination alpha) + * + * @see Paint::blend() + * + * @BETA_API + */ +enum class BlendMethod : uint8_t +{ + Normal = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D + Add, ///< Simply adds pixel values of one layer with the other. (S + D) + Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) + Multiply, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D) + Overlay, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) + Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) + Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) + SrcOver, ///< Replace the bottom layer with the top layer. + Darken, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D) + Lighten, ///< Only has the opposite action of Darken Only. max(S, D) + ColorDodge, ///< Divides the bottom layer by the inverted top layer. D / (255 - S) + ColorBurn, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S + HardLight, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) + SoftLight ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) +}; + + +/** + * @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed. + */ +enum class CanvasEngine +{ + Sw = (1 << 1), ///< CPU rasterizer. + Gl = (1 << 2), ///< OpenGL rasterizer. + Wg = (1 << 3), ///< WebGPU rasterizer. @BETA_API +}; + + +/** + * @brief A data structure representing a point in two-dimensional space. + */ +struct Point +{ + float x, y; +}; + + +/** + * @brief A data structure representing a three-dimensional matrix. + * + * The elements e11, e12, e21 and e22 represent the rotation matrix, including the scaling factor. + * The elements e13 and e23 determine the translation of the object along the x and y-axis, respectively. + * The elements e31 and e32 are set to 0, e33 is set to 1. + */ +struct Matrix +{ + float e11, e12, e13; + float e21, e22, e23; + float e31, e32, e33; +}; + + +/** + * @brief A data structure representing a texture mesh vertex + * + * @param pt The vertex coordinate + * @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0) + * + * @BETA_API + */ +struct Vertex +{ + Point pt; + Point uv; +}; + + +/** + * @brief A data structure representing a triange in a texture mesh + * + * @param vertex The three vertices that make up the polygon + * + * @BETA_API + */ +struct Polygon +{ + Vertex vertex[3]; +}; + + +/** + * @class Paint + * + * @brief An abstract class for managing graphical elements. + * + * A graphical element in TVG is any object composed into a Canvas. + * Paint represents such a graphical object and its behaviors such as duplication, transformation and composition. + * TVG recommends the user to regard a paint as a set of volatile commands. They can prepare a Paint and then request a Canvas to run them. + */ +class TVG_API Paint +{ +public: + virtual ~Paint(); + + /** + * @brief Sets the angle by which the object is rotated. + * + * The angle in measured clockwise from the horizontal axis. + * The rotational axis passes through the point on the object with zero coordinates. + * + * @param[in] degree The value of the angle in degrees. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result rotate(float degree) noexcept; + + /** + * @brief Sets the scale value of the object. + * + * @param[in] factor The value of the scaling factor. The default value is 1. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result scale(float factor) noexcept; + + /** + * @brief Sets the values by which the object is moved in a two-dimensional space. + * + * The origin of the coordinate system is in the upper left corner of the canvas. + * The horizontal and vertical axes point to the right and down, respectively. + * + * @param[in] x The value of the horizontal shift. + * @param[in] y The value of the vertical shift. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result translate(float x, float y) noexcept; + + /** + * @brief Sets the matrix of the affine transformation for the object. + * + * The augmented matrix of the transformation is expected to be given. + * + * @param[in] m The 3x3 augmented matrix. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result transform(const Matrix& m) noexcept; + + /** + * @brief Gets the matrix of the affine transformation of the object. + * + * The values of the matrix can be set by the transform() API, as well by the translate(), + * scale() and rotate(). In case no transformation was applied, the identity matrix is returned. + * + * @return The augmented transformation matrix. + * + * @since 0.4 + */ + Matrix transform() noexcept; + + /** + * @brief Sets the opacity of the object. + * + * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. + * + * @return Result::Success when succeed. + * + * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible. + * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath) + */ + Result opacity(uint8_t o) noexcept; + + /** + * @brief Sets the composition target object and the composition method. + * + * @param[in] target The paint of the target object. + * @param[in] method The method used to composite the source object with the target. + * + * @return Result::Success when succeed, Result::InvalidArguments otherwise. + */ + Result composite(std::unique_ptr target, CompositeMethod method) noexcept; + + /** + * @brief Sets the blending method for the paint object. + * + * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others. + * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations. + * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined. + * + * @param[in] method The blending method to be set. + * + * @return Result::Success when the blending method is successfully set. + * + * @BETA_API + */ + Result blend(BlendMethod method) const noexcept; + + /** + * @brief Gets the bounding box of the paint object before any transformation. + * + * @param[out] x The x coordinate of the upper left corner of the object. + * @param[out] y The y coordinate of the upper left corner of the object. + * @param[out] w The width of the object. + * @param[out] h The height of the object. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * + * @note The bounding box doesn't indicate the final rendered region. It's the smallest rectangle that encloses the object. + * @see Paint::bounds(float* x, float* y, float* w, float* h, bool transformed); + * @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead + */ + TVG_DEPRECATED Result bounds(float* x, float* y, float* w, float* h) const noexcept; + + /** + * @brief Gets the axis-aligned bounding box of the paint object. + * + * In case @p transform is @c true, all object's transformations are applied first, and then the bounding box is established. Otherwise, the bounding box is determined before any transformations. + * + * @param[out] x The x coordinate of the upper left corner of the object. + * @param[out] y The y coordinate of the upper left corner of the object. + * @param[out] w The width of the object. + * @param[out] h The height of the object. + * @param[in] transformed If @c true, the paint's transformations are taken into account, otherwise they aren't. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * + * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. + */ + Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept; + + /** + * @brief Duplicates the object. + * + * Creates a new object and sets its all properties as in the original object. + * + * @return The created object when succeed, @c nullptr otherwise. + */ + Paint* duplicate() const noexcept; + + /** + * @brief Gets the opacity value of the object. + * + * @return The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. + */ + uint8_t opacity() const noexcept; + + /** + * @brief Gets the composition target object and the composition method. + * + * @param[out] target The paint of the target object. + * + * @return The method used to composite the source object with the target. + * + * @since 0.5 + */ + CompositeMethod composite(const Paint** target) const noexcept; + + /** + * @brief Gets the blending method of the object. + * + * @return The blending method + * + * @BETA_API + */ + BlendMethod blend() const noexcept; + + /** + * @brief Return the unique id value of the paint instance. + * + * This method can be called for checking the current concrete instance type. + * + * @return The type id of the Paint instance. + */ + uint32_t identifier() const noexcept; + + _TVG_DECLARE_PRIVATE(Paint); +}; + + +/** + * @class Fill + * + * @brief An abstract class representing the gradient fill of the Shape object. + * + * It contains the information about the gradient colors and their arrangement + * inside the gradient bounds. The gradients bounds are defined in the LinearGradient + * or RadialGradient class, depending on the type of the gradient to be used. + * It specifies the gradient behavior in case the area defined by the gradient bounds + * is smaller than the area to be filled. + */ +class TVG_API Fill +{ +public: + /** + * @brief A data structure storing the information about the color and its relative position inside the gradient bounds. + */ + struct ColorStop + { + float offset; /**< The relative position of the color. */ + uint8_t r; /**< The red color channel value in the range [0 ~ 255]. */ + uint8_t g; /**< The green color channel value in the range [0 ~ 255]. */ + uint8_t b; /**< The blue color channel value in the range [0 ~ 255]. */ + uint8_t a; /**< The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. */ + }; + + virtual ~Fill(); + + /** + * @brief Sets the parameters of the colors of the gradient and their position. + * + * @param[in] colorStops An array of ColorStop data structure. + * @param[in] cnt The count of the @p colorStops array equal to the colors number used in the gradient. + * + * @return Result::Success when succeed. + */ + Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept; + + /** + * @brief Sets the FillSpread value, which specifies how to fill the area outside the gradient bounds. + * + * @param[in] s The FillSpread value. + * + * @return Result::Success when succeed. + */ + Result spread(FillSpread s) noexcept; + + /** + * @brief Sets the matrix of the affine transformation for the gradient fill. + * + * The augmented matrix of the transformation is expected to be given. + * + * @param[in] m The 3x3 augmented matrix. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result transform(const Matrix& m) noexcept; + + /** + * @brief Gets the parameters of the colors of the gradient, their position and number. + * + * @param[out] colorStops A pointer to the memory location, where the array of the gradient's ColorStop is stored. + * + * @return The number of colors used in the gradient. This value corresponds to the length of the @p colorStops array. + */ + uint32_t colorStops(const ColorStop** colorStops) const noexcept; + + /** + * @brief Gets the FillSpread value of the fill. + * + * @return The FillSpread value of this Fill. + */ + FillSpread spread() const noexcept; + + /** + * @brief Gets the matrix of the affine transformation of the gradient fill. + * + * In case no transformation was applied, the identity matrix is returned. + * + * @retval The augmented transformation matrix. + */ + Matrix transform() const noexcept; + + /** + * @brief Creates a copy of the Fill object. + * + * Return a newly created Fill object with the properties copied from the original. + * + * @return A copied Fill object when succeed, @c nullptr otherwise. + */ + Fill* duplicate() const noexcept; + + /** + * @brief Return the unique id value of the Fill instance. + * + * This method can be called for checking the current concrete instance type. + * + * @return The type id of the Fill instance. + */ + uint32_t identifier() const noexcept; + + _TVG_DECLARE_PRIVATE(Fill); +}; + + +/** + * @class Canvas + * + * @brief An abstract class for drawing graphical elements. + * + * A canvas is an entity responsible for drawing the target. It sets up the drawing engine and the buffer, which can be drawn on the screen. It also manages given Paint objects. + * + * @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical. + * @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases. + */ +class TVG_API Canvas +{ +public: + Canvas(RenderMethod*); + virtual ~Canvas(); + + /** + * @brief Sets the size of the container, where all the paints pushed into the Canvas are stored. + * + * If the number of objects pushed into the Canvas is known in advance, calling the function + * prevents multiple memory reallocation, thus improving the performance. + * + * @param[in] n The number of objects for which the memory is to be reserved. + * + * @return Result::Success when succeed. + */ + TVG_DEPRECATED Result reserve(uint32_t n) noexcept; + + /** + * @brief Returns the list of the paints that currently held by the Canvas. + * + * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. + * + * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). + * @see Canvas::sync() + * + * @BETA_API + */ + std::list& paints() noexcept; + + /** + * @brief Passes drawing elements to the Canvas using Paint objects. + * + * Only pushed paints in the canvas will be drawing targets. + * They are retained by the canvas until you call Canvas::clear(). + * + * @param[in] paint A Paint object to be drawn. + * + * @retval Result::Success When succeed. + * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument. + * @retval Result::InsufficientCondition An internal error. + * + * @note The rendering order of the paints is the same as the order as they were pushed into the canvas. Consider sorting the paints before pushing them if you intend to use layering. + * @see Canvas::paints() + * @see Canvas::clear() + */ + virtual Result push(std::unique_ptr paint) noexcept; + + /** + * @brief Clear the internal canvas resources that used for the drawing. + * + * This API sets the total number of paints pushed into the canvas to zero. + * Depending on the value of the @p free argument, the paints are either freed or retained. + * So if you need to update paint properties while maintaining the existing scene structure, you can set @p free = false. + * + * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * + * @see Canvas::push() + * @see Canvas::paints() + */ + virtual Result clear(bool free = true) noexcept; + + /** + * @brief Request the canvas to update the paint objects. + * + * If a @c nullptr is passed all paint objects retained by the Canvas are updated, + * otherwise only the paint to which the given @p paint points. + * + * @param[in] paint A pointer to the Paint object or @c nullptr. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * + * @note The Update behavior can be asynchronous if the assigned thread number is greater than zero. + */ + virtual Result update(Paint* paint = nullptr) noexcept; + + /** + * @brief Requests the canvas to draw the Paint objects. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * + * @note Drawing can be asynchronous if the assigned thread number is greater than zero. To guarantee the drawing is done, call sync() afterwards. + * @see Canvas::sync() + */ + virtual Result draw() noexcept; + + /** + * @brief Guarantees that drawing task is finished. + * + * The Canvas rendering can be performed asynchronously. To make sure that rendering is finished, + * the sync() must be called after the draw() regardless of threading. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @see Canvas::draw() + */ + virtual Result sync() noexcept; + + _TVG_DECLARE_PRIVATE(Canvas); +}; + + +/** + * @class LinearGradient + * + * @brief A class representing the linear gradient fill of the Shape object. + * + * Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds. + * The behavior outside the gradient bounds depends on the value specified in the spread API. + */ +class TVG_API LinearGradient final : public Fill +{ +public: + ~LinearGradient(); + + /** + * @brief Sets the linear gradient bounds. + * + * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing + * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking + * (@p x1, @p y1) and (@p x2, @p y2). + * + * @param[in] x1 The horizontal coordinate of the first point used to determine the gradient bounds. + * @param[in] y1 The vertical coordinate of the first point used to determine the gradient bounds. + * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds. + * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds. + * + * @return Result::Success when succeed. + * + * @note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered. + */ + Result linear(float x1, float y1, float x2, float y2) noexcept; + + /** + * @brief Gets the linear gradient bounds. + * + * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing + * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking + * (@p x1, @p y1) and (@p x2, @p y2). + * + * @param[out] x1 The horizontal coordinate of the first point used to determine the gradient bounds. + * @param[out] y1 The vertical coordinate of the first point used to determine the gradient bounds. + * @param[out] x2 The horizontal coordinate of the second point used to determine the gradient bounds. + * @param[out] y2 The vertical coordinate of the second point used to determine the gradient bounds. + * + * @return Result::Success when succeed. + */ + Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept; + + /** + * @brief Creates a new LinearGradient object. + * + * @return A new LinearGradient object. + */ + static std::unique_ptr gen() noexcept; + + /** + * @brief Return the unique id value of this class. + * + * This method can be referred for identifying the LinearGradient class type. + * + * @return The type id of the LinearGradient class. + */ + static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(LinearGradient); +}; + + +/** + * @class RadialGradient + * + * @brief A class representing the radial gradient fill of the Shape object. + * + */ +class TVG_API RadialGradient final : public Fill +{ +public: + ~RadialGradient(); + + /** + * @brief Sets the radial gradient bounds. + * + * The radial gradient bounds are defined as a circle centered in a given point (@p cx, @p cy) of a given radius. + * + * @param[in] cx The horizontal coordinate of the center of the bounding circle. + * @param[in] cy The vertical coordinate of the center of the bounding circle. + * @param[in] radius The radius of the bounding circle. + * + * @return Result::Success when succeed, Result::InvalidArguments in case the @p radius value is zero or less. + */ + Result radial(float cx, float cy, float radius) noexcept; + + /** + * @brief Gets the radial gradient bounds. + * + * The radial gradient bounds are defined as a circle centered in a given point (@p cx, @p cy) of a given radius. + * + * @param[out] cx The horizontal coordinate of the center of the bounding circle. + * @param[out] cy The vertical coordinate of the center of the bounding circle. + * @param[out] radius The radius of the bounding circle. + * + * @return Result::Success when succeed. + */ + Result radial(float* cx, float* cy, float* radius) const noexcept; + + /** + * @brief Creates a new RadialGradient object. + * + * @return A new RadialGradient object. + */ + static std::unique_ptr gen() noexcept; + + /** + * @brief Return the unique id value of this class. + * + * This method can be referred for identifying the RadialGradient class type. + * + * @return The type id of the RadialGradient class. + */ + static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(RadialGradient); +}; + + +/** + * @class Shape + * + * @brief A class representing two-dimensional figures and their properties. + * + * A shape has three major properties: shape outline, stroking, filling. The outline in the Shape is retained as the path. + * Path can be composed by accumulating primitive commands such as moveTo(), lineTo(), cubicTo(), or complete shape interfaces such as appendRect(), appendCircle(), etc. + * Path can consists of sub-paths. One sub-path is determined by a close command. + * + * The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders. + * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. + */ +class TVG_API Shape final : public Paint +{ +public: + ~Shape(); + + /** + * @brief Resets the properties of the shape path. + * + * The transformation matrix, the color, the fill and the stroke properties are retained. + * + * @return Result::Success when succeed. + * + * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect. + */ + Result reset() noexcept; + + /** + * @brief Sets the initial point of the sub-path. + * + * The value of the current point is set to the given point. + * + * @param[in] x The horizontal coordinate of the initial point of the sub-path. + * @param[in] y The vertical coordinate of the initial point of the sub-path. + * + * @return Result::Success when succeed. + */ + Result moveTo(float x, float y) noexcept; + + /** + * @brief Adds a new point to the sub-path, which results in drawing a line from the current point to the given end-point. + * + * The value of the current point is set to the given end-point. + * + * @param[in] x The horizontal coordinate of the end-point of the line. + * @param[in] y The vertical coordinate of the end-point of the line. + * + * @return Result::Success when succeed. + * + * @note In case this is the first command in the path, it corresponds to the moveTo() call. + */ + Result lineTo(float x, float y) noexcept; + + /** + * @brief Adds new points to the sub-path, which results in drawing a cubic Bezier curve starting + * at the current point and ending at the given end-point (@p x, @p y) using the control points (@p cx1, @p cy1) and (@p cx2, @p cy2). + * + * The value of the current point is set to the given end-point. + * + * @param[in] cx1 The horizontal coordinate of the 1st control point. + * @param[in] cy1 The vertical coordinate of the 1st control point. + * @param[in] cx2 The horizontal coordinate of the 2nd control point. + * @param[in] cy2 The vertical coordinate of the 2nd control point. + * @param[in] x The horizontal coordinate of the end-point of the curve. + * @param[in] y The vertical coordinate of the end-point of the curve. + * + * @return Result::Success when succeed. + * + * @note In case this is the first command in the path, no data from the path are rendered. + */ + Result cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept; + + /** + * @brief Closes the current sub-path by drawing a line from the current point to the initial point of the sub-path. + * + * The value of the current point is set to the initial point of the closed sub-path. + * + * @return Result::Success when succeed. + * + * @note In case the sub-path does not contain any points, this function has no effect. + */ + Result close() noexcept; + + /** + * @brief Appends a rectangle to the path. + * + * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments. + * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners. + * + * The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments. + * + * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path. + * + * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater + * than @p w/2 the current point is set to (@p x + @p w/2, @p y) + * + * @param[in] x The horizontal coordinate of the upper left corner of the rectangle. + * @param[in] y The vertical coordinate of the upper left corner of the rectangle. + * @param[in] w The width of the rectangle. + * @param[in] h The height of the rectangle. + * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. + * @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle. + * + * @return Result::Success when succeed. + * + * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse. + */ + Result appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0) noexcept; + + /** + * @brief Appends an ellipse to the path. + * + * The position of the ellipse is specified by the coordinates of its center - @p cx and @p cy arguments. + * + * The ellipse is treated as a new sub-path - it is not connected with the previous sub-path. + * + * The value of the current point is set to (@p cx, @p cy - @p ry). + * + * @param[in] cx The horizontal coordinate of the center of the ellipse. + * @param[in] cy The vertical coordinate of the center of the ellipse. + * @param[in] rx The x-axis radius of the ellipse. + * @param[in] ry The y-axis radius of the ellipse. + * + * @return Result::Success when succeed. + */ + Result appendCircle(float cx, float cy, float rx, float ry) noexcept; + + /** + * @brief Appends a circular arc to the path. + * + * The arc is treated as a new sub-path - it is not connected with the previous sub-path. + * The current point value is set to the end-point of the arc in case @p pie is @c false, and to the center of the arc otherwise. + * + * @param[in] cx The horizontal coordinate of the center of the arc. + * @param[in] cy The vertical coordinate of the center of the arc. + * @param[in] radius The radius of the arc. + * @param[in] startAngle The start angle of the arc given in degrees, measured counter-clockwise from the horizontal line. + * @param[in] sweep The central angle of the arc given in degrees, measured counter-clockwise from @p startAngle. + * @param[in] pie Specifies whether to draw radii from the arc's center to both of its end-point - drawn if @c true. + * + * @return Result::Success when succeed. + * + * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius). + */ + Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept; + + /** + * @brief Appends a given sub-path to the path. + * + * The current point value is set to the last point from the sub-path. + * For each command from the @p cmds array, an appropriate number of points in @p pts array should be specified. + * If the number of points in the @p pts array is different than the number required by the @p cmds array, the shape with this sub-path will not be displayed on the screen. + * + * @param[in] cmds The array of the commands in the sub-path. + * @param[in] cmdCnt The number of the sub-path's commands. + * @param[in] pts The array of the two-dimensional points. + * @param[in] ptsCnt The number of the points in the @p pts array. + * + * @return Result::Success when succeed, Result::InvalidArguments otherwise. + * + * @note The interface is designed for optimal path setting if the caller has a completed path commands already. + */ + Result appendPath(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept; + + /** + * @brief Sets the stroke width for all of the figures from the path. + * + * @param[in] width The width of the stroke. The default value is 0. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result stroke(float width) noexcept; + + /** + * @brief Sets the color of the stroke for all of the figures from the path. + * + * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; + + /** + * @brief Sets the gradient fill of the stroke for all of the figures from the path. + * + * @param[in] f The gradient fill. + * + * @retval Result::Success When succeed. + * @retval Result::FailedAllocation An internal error with a memory allocation for an object to be filled. + * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument. + */ + Result stroke(std::unique_ptr f) noexcept; + + /** + * @brief Sets the dash pattern of the stroke. + * + * @param[in] dashPattern The array of consecutive pair values of the dash length and the gap length. + * @param[in] cnt The length of the @p dashPattern array. + * + * @retval Result::Success When succeed. + * @retval Result::FailedAllocation An internal error with a memory allocation for an object to be dashed. + * @retval Result::InvalidArguments In case @p dashPattern is @c nullptr and @p cnt > 0, @p cnt is zero, any of the dash pattern values is zero or less. + * + * @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt. + * @warning @p cnt must be greater than 1 if the dash pattern is valid. + */ + Result stroke(const float* dashPattern, uint32_t cnt) noexcept; + + /** + * @brief Sets the cap style of the stroke in the open sub-paths. + * + * @param[in] cap The cap style value. The default value is @c StrokeCap::Square. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result stroke(StrokeCap cap) noexcept; + + /** + * @brief Sets the join style for stroked path segments. + * + * The join style is used for joining the two line segment while stroking the path. + * + * @param[in] join The join style value. The default value is @c StrokeJoin::Bevel. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + Result stroke(StrokeJoin join) noexcept; + + + /** + * @brief Sets the stroke miterlimit. + * + * @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4. + * + * @return Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise. + * + * @since 0.11 + */ + Result strokeMiterlimit(float miterlimit) noexcept; + + /** + * @brief Sets the solid color for all of the figures from the path. + * + * The parts of the shape defined as inner are colored. + * + * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. + * + * @return Result::Success when succeed. + * + * @note Either a solid color or a gradient fill is applied, depending on what was set as last. + * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath) + */ + Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; + + /** + * @brief Sets the gradient fill for all of the figures from the path. + * + * The parts of the shape defined as inner are filled. + * + * @param[in] f The unique pointer to the gradient fill. + * + * @return Result::Success when succeed, Result::MemoryCorruption otherwise. + * + * @note Either a solid color or a gradient fill is applied, depending on what was set as last. + */ + Result fill(std::unique_ptr f) noexcept; + + /** + * @brief Sets the fill rule for the Shape object. + * + * @param[in] r The fill rule value. The default value is @c FillRule::Winding. + * + * @return Result::Success when succeed. + */ + Result fill(FillRule r) noexcept; + + + /** + * @brief Sets the rendering order of the stroke and the fill. + * + * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * + * @since 0.10 + */ + Result order(bool strokeFirst) noexcept; + + + /** + * @brief Gets the commands data of the path. + * + * @param[out] cmds The pointer to the array of the commands from the path. + * + * @return The length of the @p cmds array when succeed, zero otherwise. + */ + uint32_t pathCommands(const PathCommand** cmds) const noexcept; + + /** + * @brief Gets the points values of the path. + * + * @param[out] pts The pointer to the array of the two-dimensional points from the path. + * + * @return The length of the @p pts array when succeed, zero otherwise. + */ + uint32_t pathCoords(const Point** pts) const noexcept; + + /** + * @brief Gets the pointer to the gradient fill of the shape. + * + * @return The pointer to the gradient fill of the stroke when succeed, @c nullptr in case no fill was set. + */ + const Fill* fill() const noexcept; + + /** + * @brief Gets the solid color of the shape. + * + * @param[out] r The red color channel value in the range [0 ~ 255]. + * @param[out] g The green color channel value in the range [0 ~ 255]. + * @param[out] b The blue color channel value in the range [0 ~ 255]. + * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. + * + * @return Result::Success when succeed. + */ + Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; + + /** + * @brief Gets the fill rule value. + * + * @return The fill rule value of the shape. + */ + FillRule fillRule() const noexcept; + + /** + * @brief Gets the stroke width. + * + * @return The stroke width value when succeed, zero if no stroke was set. + */ + float strokeWidth() const noexcept; + + /** + * @brief Gets the color of the shape's stroke. + * + * @param[out] r The red color channel value in the range [0 ~ 255]. + * @param[out] g The green color channel value in the range [0 ~ 255]. + * @param[out] b The blue color channel value in the range [0 ~ 255]. + * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + */ + Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; + + /** + * @brief Gets the pointer to the gradient fill of the stroke. + * + * @return The pointer to the gradient fill of the stroke when succeed, @c nullptr otherwise. + */ + const Fill* strokeFill() const noexcept; + + /** + * @brief Gets the dash pattern of the stroke. + * + * @param[out] dashPattern The pointer to the memory, where the dash pattern array is stored. + * + * @return The length of the @p dashPattern array. + */ + uint32_t strokeDash(const float** dashPattern) const noexcept; + + /** + * @brief Gets the cap style used for stroking the path. + * + * @return The cap style value of the stroke. + */ + StrokeCap strokeCap() const noexcept; + + /** + * @brief Gets the join style value used for stroking the path. + * + * @return The join style value of the stroke. + */ + StrokeJoin strokeJoin() const noexcept; + + /** + * @brief Gets the stroke miterlimit. + * + * @return The stroke miterlimit value when succeed, 4 if no stroke was set. + * + * @since 0.11 + */ + float strokeMiterlimit() const noexcept; + + /** + * @brief Creates a new Shape object. + * + * @return A new Shape object. + */ + static std::unique_ptr gen() noexcept; + + /** + * @brief Return the unique id value of this class. + * + * This method can be referred for identifying the Shape class type. + * + * @return The type id of the Shape class. + */ + static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(Shape); +}; + + +/** + * @class Picture + * + * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg, lottie(json) and etc. + * Besides the methods inherited from the Paint, it provides methods to load & draw images on the canvas. + * + * @note Supported formats are depended on the available TVG loaders. + * @note See Animation class if the picture data is animatable. + */ +class TVG_API Picture final : public Paint +{ +public: + ~Picture(); + + /** + * @brief Loads a picture data directly from a file. + * + * @param[in] path A path to the picture file. + * + * @retval Result::Success When succeed. + * @retval Result::InvalidArguments In case the @p path is invalid. + * @retval Result::NonSupport When trying to load a file with an unknown extension. + * @retval Result::Unknown If an error occurs at a later stage. + * + * @note The Load behavior can be asynchronous if the assigned thread number is greater than zero. + * @see Initializer::init() + */ + Result load(const std::string& path) noexcept; + + /** + * @brief Loads a picture data from a memory block of a given size. + * + * @param[in] data A pointer to a memory location where the content of the picture file is stored. + * @param[in] size The size in bytes of the memory occupied by the @p data. + * @param[in] copy Decides whether the data should be copied into the engine local buffer. + * + * @retval Result::Success When succeed. + * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less. + * @retval Result::NonSupport When trying to load a file with an unknown extension. + * @retval Result::Unknown If an error occurs at a later stage. + * + * @warning: you have responsibility to release the @p data memory if the @p copy is true + * @deprecated Use load(const char* data, uint32_t size, const std::string& mimeType, bool copy) instead. + * @see Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept + */ + TVG_DEPRECATED Result load(const char* data, uint32_t size, bool copy = false) noexcept; + + /** + * @brief Loads a picture data from a memory block of a given size. + * + * @param[in] data A pointer to a memory location where the content of the picture file is stored. + * @param[in] size The size in bytes of the memory occupied by the @p data. + * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one. + * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. + * + * @retval Result::Success When succeed. + * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less. + * @retval Result::NonSupport When trying to load a file with an unknown extension. + * @retval Result::Unknown If an error occurs at a later stage. + * + * @warning: It's the user responsibility to release the @p data memory if the @p copy is @c true. + * + * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. + * @since 0.5 + */ + Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept; + + /** + * @brief Resizes the picture content to the given width and height. + * + * The picture content is resized while keeping the default size aspect ratio. + * The scaling factor is established for each of dimensions and the smaller value is applied to both of them. + * + * @param[in] w A new width of the image in pixels. + * @param[in] h A new height of the image in pixels. + * + * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + */ + Result size(float w, float h) noexcept; + + /** + * @brief Gets the size of the image. + * + * @param[out] w The width of the image in pixels. + * @param[out] h The height of the image in pixels. + * + * @return Result::Success when succeed. + */ + Result size(float* w, float* h) const noexcept; + + /** + * @brief Loads a raw data from a memory block with a given size. + * + * @retval Result::Success When succeed, Result::InsufficientCondition otherwise. + * @retval Result::FailedAllocation An internal error possibly with memory allocation. + * + * @since 0.9 + */ + Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept; + + /** + * @brief Sets or removes the triangle mesh to deform the image. + * + * If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the + * image data will be used as the texture. + * + * If @p triangles is @c nullptr, or @p triangleCnt is 0, the mesh will be removed. + * + * Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support. + * mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh. + * + * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh. + * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh. + * + * @retval Result::Success When succeed. + * @retval Result::Unknown If fails + * + * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect. + * @warning Please do not use it, this API is not official one. It could be modified in the next version. + * + * @BETA_API + */ + Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept; + + /** + * @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh. + * + * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh. + * + * @return uint32_t The number of polygons in the array. + * + * @note Modifying the triangles returned by this method will modify them directly within the mesh. + * @warning Please do not use it, this API is not official one. It could be modified in the next version. + * + * @BETA_API + */ + uint32_t mesh(const Polygon** triangles) const noexcept; + + /** + * @brief Creates a new Picture object. + * + * @return A new Picture object. + */ + static std::unique_ptr gen() noexcept; + + /** + * @brief Return the unique id value of this class. + * + * This method can be referred for identifying the Picture class type. + * + * @return The type id of the Picture class. + */ + static uint32_t identifier() noexcept; + + _TVG_DECLARE_ACCESSOR(Animation); + _TVG_DECLARE_PRIVATE(Picture); +}; + + +/** + * @class Scene + * + * @brief A class to composite children paints. + * + * As the traditional graphics rendering method, TVG also enables scene-graph mechanism. + * This feature supports an array function for managing the multiple paints as one group paint. + * + * As a group, the scene can be transformed, made translucent and composited with other target paints, + * its children will be affected by the scene world. + */ +class TVG_API Scene final : public Paint +{ +public: + ~Scene(); + + /** + * @brief Passes drawing elements to the Scene using Paint objects. + * + * Only the paints pushed into the scene will be the drawn targets. + * The paints are retained by the scene until Scene::clear() is called. + * + * @param[in] paint A Paint object to be drawn. + * + * @return Result::Success when succeed, Result::MemoryCorruption otherwise. + * + * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. + * @see Scene::paints() + * @see Scene::clear() + */ + Result push(std::unique_ptr paint) noexcept; + + /** + * @brief Sets the size of the container, where all the paints pushed into the Scene are stored. + * + * If the number of objects pushed into the scene is known in advance, calling the function + * prevents multiple memory reallocation, thus improving the performance. + * + * @param[in] size The number of objects for which the memory is to be reserved. + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + */ + TVG_DEPRECATED Result reserve(uint32_t size) noexcept; + + /** + * @brief Returns the list of the paints that currently held by the Scene. + * + * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. + * + * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync(). + * @see Canvas::sync() + * @see Scene::push() + * @see Scene::clear() + * + * @BETA_API + */ + std::list& paints() noexcept; + + /** + * @brief Sets the total number of the paints pushed into the scene to be zero. + * Depending on the value of the @p free argument, the paints are freed or not. + * + * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not. + * + * @return Result::Success when succeed + * + * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended. + * + * @since 0.2 + */ + Result clear(bool free = true) noexcept; + + /** + * @brief Creates a new Scene object. + * + * @return A new Scene object. + */ + static std::unique_ptr gen() noexcept; + + /** + * @brief Return the unique id value of this class. + * + * This method can be referred for identifying the Scene class type. + * + * @return The type id of the Scene class. + */ + static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(Scene); +}; + + +/** + * @class SwCanvas + * + * @brief A class for the rendering graphical elements with a software raster engine. + */ +class TVG_API SwCanvas final : public Canvas +{ +public: + ~SwCanvas(); + + /** + * @brief Enumeration specifying the methods of combining the 8-bit color channels into 32-bit color. + */ + enum Colorspace + { + ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. (a << 24 | b << 16 | g << 8 | r) + ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. (a << 24 | r << 16 | g << 8 | b) + ABGR8888S, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. + ARGB8888S, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. + }; + + /** + * @brief Enumeration specifying the methods of Memory Pool behavior policy. + * @since 0.4 + */ + enum MempoolPolicy + { + Default = 0, ///< Default behavior that ThorVG is designed to. + Shareable, ///< Memory Pool is shared among the SwCanvases. + Individual ///< Allocate designated memory pool that is only used by current instance. + }; + + /** + * @brief Sets the target buffer for the rasterization. + * + * The buffer of a desirable size should be allocated and owned by the caller. + * + * @param[in] buffer A pointer to a memory block of the size @p stride x @p h, where the raster data are stored. + * @param[in] stride The stride of the raster image - greater than or equal to @p w. + * @param[in] w The width of the raster image. + * @param[in] h The height of the raster image. + * @param[in] cs The value specifying the way the 32-bits colors should be read/written. + * + * @retval Result::Success When succeed. + * @retval Result::MemoryCorruption When casting in the internal function implementation failed. + * @retval Result::InvalidArguments In case no valid pointer is provided or the width, or the height or the stride is zero. + * @retval Result::NonSupport In case the software engine is not supported. + * + * @warning Do not access @p buffer during Canvas::draw() - Canvas::sync(). It should not be accessed while TVG is writing on it. + */ + Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept; + + /** + * @brief Set sw engine memory pool behavior policy. + * + * Basically ThorVG draws a lot of shapes, it allocates/deallocates a few chunk of memory + * while processing rendering. It internally uses one shared memory pool + * which can be reused among the canvases in order to avoid memory overhead. + * + * Thus ThorVG suggests using a memory pool policy to satisfy user demands, + * if it needs to guarantee the thread-safety of the internal data access. + * + * @param[in] policy The method specifying the Memory Pool behavior. The default value is @c MempoolPolicy::Default. + * + * @retval Result::Success When succeed. + * @retval Result::InsufficientCondition If the canvas contains some paints already. + * @retval Result::NonSupport In case the software engine is not supported. + * + * @note When @c policy is set as @c MempoolPolicy::Individual, the current instance of canvas uses its own individual + * memory data, which is not shared with others. This is necessary when the canvas is accessed on a worker-thread. + * + * @warning It's not allowed after pushing any paints. + * + * @since 0.4 + */ + Result mempool(MempoolPolicy policy) noexcept; + + /** + * @brief Creates a new SwCanvas object. + * @return A new SwCanvas object. + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(SwCanvas); +}; + + +/** + * @class GlCanvas + * + * @brief A class for the rendering graphic elements with a GL raster engine. + * + * @warning Please do not use it. This class is not fully supported yet. + * + * @BETA_API + */ +class TVG_API GlCanvas final : public Canvas +{ +public: + ~GlCanvas(); + + /** + * @brief Sets the target buffer for the rasterization. + * + * @warning Please do not use it, this API is not official one. It could be modified in the next version. + * + * @BETA_API + */ + Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept; + + /** + * @brief Creates a new GlCanvas object. + * + * @return A new GlCanvas object. + * + * @BETA_API + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(GlCanvas); +}; + + +/** + * @class WgCanvas + * + * @brief A class for the rendering graphic elements with a WebGPU raster engine. + * + * @warning Please do not use it. This class is not fully supported yet. + * + * @BETA_API + */ +class TVG_API WgCanvas final : public Canvas +{ +public: + ~WgCanvas(); + + /** + * @brief Sets the target window for the rasterization. + * + * @warning Please do not use it, this API is not official one. It could be modified in the next version. + * + * @BETA_API + */ + Result target(void* window, uint32_t w, uint32_t h) noexcept; + + /** + * @brief Creates a new WgCanvas object. + * + * @return A new WgCanvas object. + * + * @BETA_API + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(WgCanvas); +}; + + +/** + * @class Initializer + * + * @brief A class that enables initialization and termination of the TVG engines. + */ +class TVG_API Initializer final +{ +public: + /** + * @brief Initializes TVG engines. + * + * TVG requires the running-engine environment. + * TVG runs its own task-scheduler for parallelizing rendering tasks efficiently. + * You can indicate the number of threads, the count of which is designated @p threads. + * In the initialization step, TVG will generate/spawn the threads as set by @p threads count. + * + * @param[in] engine The engine types to initialize. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed. + * @param[in] threads The number of additional threads. Zero indicates only the main thread is to be used. + * + * @retval Result::Success When succeed. + * @retval Result::FailedAllocation An internal error possibly with memory allocation. + * @retval Result::InvalidArguments If unknown engine type chosen. + * @retval Result::NonSupport In case the engine type is not supported on the system. + * @retval Result::Unknown Others. + * + * @note The Initializer keeps track of the number of times it was called. Threads count is fixed at the first init() call. + * @see Initializer::term() + */ + static Result init(CanvasEngine engine, uint32_t threads) noexcept; + + /** + * @brief Terminates TVG engines. + * + * @param[in] engine The engine types to terminate. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed + * + * @retval Result::Success When succeed. + * @retval Result::InsufficientCondition In case there is nothing to be terminated. + * @retval Result::InvalidArguments If unknown engine type chosen. + * @retval Result::NonSupport In case the engine type is not supported on the system. + * @retval Result::Unknown Others. + * + * @note Initializer does own reference counting for multiple calls. + * @see Initializer::init() + */ + static Result term(CanvasEngine engine) noexcept; + + _TVG_DISABLE_CTOR(Initializer); +}; + + +/** + * @class Animation + * + * @brief The Animation class enables manipulation of animatable images. + * + * This class supports the display and control of animation frames. + * + * @BETA_API + */ + +class TVG_API Animation +{ +public: + ~Animation(); + + /** + * @brief Specifies the current frame in the animation. + * + * @param[in] no The index of the animation frame to be displayed. The index should be less than the totalFrame(). + * + * @retval Result::Success Successfully set the frame. + * @retval Result::InsufficientCondition No animatable data loaded from the Picture. + * @retval Result::NonSupport The Picture data does not support animations. + * + * @see totalFrame() + * + * @BETA_API + */ + Result frame(uint32_t no) noexcept; + + /** + * @brief Retrieves a picture instance associated with this animation instance. + * + * This function provides access to the picture instance that can be used to load animation formats, such as Lottie(json). + * After setting up the picture, it can be pushed to the designated canvas, enabling control over animation frames + * with this Animation instance. + * + * @return A picture instance that is tied to this animation. + * + * @warning The picture instance is owned by Animation. It should not be deleted manually. + * + * @BETA_API + */ + Picture* picture() const noexcept; + + /** + * @brief Retrieves the current frame number of the animation. + * + * @return The current frame number of the animation, between 0 and totalFrame() - 1. + * + * @note If the Picture is not properly configured, this function will return 0. + * + * @see Animation::frame(uint32_t no) + * @see Animation::totalFrame() + * + * @BETA_API + */ + uint32_t curFrame() const noexcept; + + /** + * @brief Retrieves the total number of frames in the animation. + * + * @return The total number of frames in the animation. + * + * @note Frame numbering starts from 0. + * @note If the Picture is not properly configured, this function will return 0. + * + * @BETA_API + */ + uint32_t totalFrame() const noexcept; + + /** + * @brief Retrieves the duration of the animation in seconds. + * + * @return The duration of the animation in seconds. + * + * @note If the Picture is not properly configured, this function will return 0. + * + * @BETA_API + */ + float duration() const noexcept; + + /** + * @brief Creates a new Animation object. + * + * @return A new Animation object. + * + * @BETA_API + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(Animation); +}; + + +/** + * @class Saver + * + * @brief A class for exporting a paint object into a specified file, from which to recover the paint data later. + * + * ThorVG provides a feature for exporting & importing paint data. The Saver role is to export the paint data to a file. + * It's useful when you need to save the composed scene or image from a paint object and recreate it later. + * + * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment. + * If it doesn't support the file format, the save() method returns the @c Result::NonSuppport result. + * + * Once you export a paint to the file successfully, you can recreate it using the Picture class. + * + * @see Picture::load() + * + * @since 0.5 + */ +class TVG_API Saver final +{ +public: + ~Saver(); + + /** + * @brief Exports the given @p paint data to the given @p path + * + * If the saver module supports any compression mechanism, it will optimize the data size. + * This might affect the encoding/decoding time in some cases. You can turn off the compression + * if you wish to optimize for speed. + * + * @param[in] paint The paint to be saved with all its associated properties. + * @param[in] path A path to the file, in which the paint data is to be saved. + * @param[in] compress If @c true then compress data if possible. + * + * @retval Result::Success When succeed. + * @retval Result::InsufficientCondition If currently saving other resources. + * @retval Result::NonSupport When trying to save a file with an unknown extension or in an unsupported format. + * @retval Result::MemoryCorruption An internal error. + * @retval Result::Unknown In case an empty paint is to be saved. + * + * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards. + * @see Saver::sync() + * + * @since 0.5 + */ + Result save(std::unique_ptr paint, const std::string& path, bool compress = true) noexcept; + + /** + * @brief Guarantees that the saving task is finished. + * + * The behavior of the Saver works on a sync/async basis, depending on the threading setting of the Initializer. + * Thus, if you wish to have a benefit of it, you must call sync() after the save() in the proper delayed time. + * Otherwise, you can call sync() immediately. + * + * @retval Result::Success when succeed. + * @retval Result::InsufficientCondition otherwise. + * + * @note The asynchronous tasking is dependent on the Saver module implementation. + * @see Saver::save() + * + * @since 0.5 + */ + Result sync() noexcept; + + /** + * @brief Creates a new Saver object. + * + * @return A new Saver object. + * + * @since 0.5 + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(Saver); +}; + + +/** + * @class Accessor + * + * @brief The Accessor is a utility class to debug the Scene structure by traversing the scene-tree. + * + * The Accessor helps you search specific nodes to read the property information, figure out the structure of the scene tree and its size. + * + * @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure. + * + * @since 0.10 + */ +class TVG_API Accessor final +{ +public: + ~Accessor(); + + /** + * @brief Set the access function for traversing the Picture scene tree nodes. + * + * @param[in] picture The picture node to traverse the internal scene-tree. + * @param[in] func The callback function calling for every paint nodes of the Picture. + * + * @return Return the given @p picture instance. + * + * @note The bitmap based picture might not have the scene-tree. + */ + std::unique_ptr set(std::unique_ptr picture, std::function func) noexcept; + + /** + * @brief Creates a new Accessor object. + * + * @return A new Accessor object. + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(Accessor); +}; + + +/** + * @brief The cast() function is a utility function used to cast a 'Paint' to type 'T'. + * @since 0.11 + */ +template +std::unique_ptr cast(Paint* paint) +{ + return std::unique_ptr(static_cast(paint)); +} + + +/** + * @brief The cast() function is a utility function used to cast a 'Fill' to type 'T'. + * @since 0.11 + */ +template +std::unique_ptr cast(Fill* fill) +{ + return std::unique_ptr(static_cast(fill)); +} + + +/** @}*/ + +} //namespace + +#endif //_THORVG_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/thorvg_capi.h b/project/gui/lvgl/src/libs/thorvg/thorvg_capi.h new file mode 100644 index 000000000..1f31cb71d --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/thorvg_capi.h @@ -0,0 +1,2334 @@ +/*! +* \file thorvg_capi.h +* +* \brief The module provides C bindings for the ThorVG library. +* Please refer to src/examples/Capi.cpp to find the thorvg_capi usage examples. +* +* The thorvg_capi module allows to implement the ThorVG client and provides +* the following functionalities: +* - drawing shapes: line, arc, curve, polygon, circle, user-defined, ... +* - filling: solid, linear and radial gradient +* - scene graph & affine transformation (translation, rotation, scale, ...) +* - stroking: width, join, cap, dash +* - composition: blending, masking, path clipping +* - pictures: SVG, PNG, JPG, bitmap +* +*/ + +#include "../../lv_conf_internal.h" +#if LV_USE_THORVG_INTERNAL + +#include "../../lv_conf_internal.h" +#if LV_USE_THORVG_INTERNAL + +#ifndef __THORVG_CAPI_H__ +#define __THORVG_CAPI_H__ + +#include +#include + +#ifdef TVG_API + #undef TVG_API +#endif + +#ifndef TVG_STATIC + #ifdef _WIN32 + #if TVG_BUILD + #define TVG_API __declspec(dllexport) + #else + #define TVG_API __declspec(dllimport) + #endif + #elif (defined(__SUNPRO_C) || defined(__SUNPRO_CC)) + #define TVG_API __global + #else + #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER) + #define TVG_API __attribute__ ((visibility("default"))) + #else + #define TVG_API + #endif + #endif +#else + #define TVG_API +#endif + +#ifdef TVG_DEPRECATED + #undef TVG_DEPRECATED +#endif + +#ifdef _WIN32 + #define TVG_DEPRECATED __declspec(deprecated) +#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) + #define TVG_DEPRECATED __attribute__ ((__deprecated__)) +#else + #define TVG_DEPRECATED +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* \defgroup ThorVG_CAPI ThorVG_CAPI +* \brief ThorVG C language binding APIs. +* +* \{ +*/ + + +/** +* \brief A structure responsible for managing and drawing graphical elements. +* +* It sets up the target buffer, which can be drawn on the screen. It stores the Tvg_Paint objects (Shape, Scene, Picture). +*/ +typedef struct _Tvg_Canvas Tvg_Canvas; + + +/** +* \brief A structure representing a graphical element. +* +* \warning The TvgPaint objects can not be shared between Canvases. +*/ +typedef struct _Tvg_Paint Tvg_Paint; + + +/** +* \brief A structure representing a gradient fill of a Tvg_Paint object. +*/ +typedef struct _Tvg_Gradient Tvg_Gradient; + + +/** +* \brief A structure representing an object that enables to save a Tvg_Paint object into a file. +*/ +typedef struct _Tvg_Saver Tvg_Saver; + +/** +* \brief A structure representing an animation controller object. (BETA_API) +*/ +typedef struct _Tvg_Animation Tvg_Animation; + + +/** +* \brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed. +* +* \ingroup ThorVGCapi_Initializer +*/ +typedef enum { + TVG_ENGINE_SW = (1 << 1), ///< CPU rasterizer. + TVG_ENGINE_GL = (1 << 2) ///< OpenGL rasterizer. +} Tvg_Engine; + + +/** + * \brief Enumeration specifying the result from the APIs. + */ +typedef enum { + TVG_RESULT_SUCCESS = 0, ///< The value returned in case of a correct request execution. + TVG_RESULT_INVALID_ARGUMENT, ///< The value returned in the event of a problem with the arguments given to the API - e.g. empty paths or null pointers. + TVG_RESULT_INSUFFICIENT_CONDITION, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist. + TVG_RESULT_FAILED_ALLOCATION, ///< The value returned in case of unsuccessful memory allocation. + TVG_RESULT_MEMORY_CORRUPTION, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting + TVG_RESULT_NOT_SUPPORTED, ///< The value returned in case of choosing unsupported options. + TVG_RESULT_UNKNOWN ///< The value returned in all other cases. +} Tvg_Result; + + +/** + * \brief Enumeration indicating the method used in the composition of two objects - the target and the source. + * + * \ingroup ThorVGCapi_Paint + */ +typedef enum { + TVG_COMPOSITE_METHOD_NONE = 0, ///< No composition is applied. + TVG_COMPOSITE_METHOD_CLIP_PATH, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. + TVG_COMPOSITE_METHOD_ALPHA_MASK, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which intersects with the target is visible. + TVG_COMPOSITE_METHOD_INVERSE_ALPHA_MASK, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which is not covered by the target is visible. + TVG_COMPOSITE_METHOD_LUMA_MASK, ///< The source pixels are converted to grayscale (luma value) and alpha blended with the target. As a result, only the part of the source which intersects with the target is visible. \since 0.9 + TVG_COMPOSITE_METHOD_INVERSE_LUMA_MASK ///< The source pixels are converted to grayscale (luma value) and complement to the target's pixels are alpha blended. As a result, only the part of the source which is not covered by the target is visible. \BETA_API +} Tvg_Composite_Method; + +/** + * @brief Enumeration indicates the method used for blending paint. Please refer to the respective formulas for each method. + * + * \ingroup ThorVGCapi_Paint + * + * @BETA_API + */ +typedef enum { + TVG_BLEND_METHOD_NORMAL = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D + TVG_BLEND_METHOD_ADD, ///< Simply adds pixel values of one layer with the other. (S + D) + TVG_BLEND_METHOD_SCREEN, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D) + TVG_BLEND_METHOD_MULTIPLY, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D) + TVG_BLEND_METHOD_OVERLAY, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) + TVG_BLEND_METHOD_DIFFERENCE, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S) + TVG_BLEND_METHOD_EXCLUSION, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d) + TVG_BLEND_METHOD_SRCOVER, ///< Replace the bottom layer with the top layer. + TVG_BLEND_METHOD_DARKEN, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D) + TVG_BLEND_METHOD_LIGHTEN, ///< Only has the opposite action of Darken Only. max(S, D) + TVG_BLEND_METHOD_COLORDODGE, ///< Divides the bottom layer by the inverted top layer. D / (255 - S) + TVG_BLEND_METHOD_COLORBURN, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S + TVG_BLEND_METHOD_HARDLIGHT, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D) + TVG_BLEND_METHOD_SOFTLIGHT ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D) +} Tvg_Blend_Method; + + +/** + * \brief Enumeration indicating the ThorVG class type. + * + * \ingroup ThorVGCapi_Paint + * + * \since 0.9 + */ +typedef enum { + TVG_IDENTIFIER_UNDEF = 0, ///< Undefined type. + TVG_IDENTIFIER_SHAPE, ///< A shape type paint. + TVG_IDENTIFIER_SCENE, ///< A scene type paint. + TVG_IDENTIFIER_PICTURE, ///< A picture type paint. + TVG_IDENTIFIER_LINEAR_GRAD, ///< A linear gradient type. + TVG_IDENTIFIER_RADIAL_GRAD ///< A radial gradient type. +} Tvg_Identifier; + + +/** + * \addtogroup ThorVGCapi_Shape + * \{ + */ + +/** + * \brief Enumeration specifying the values of the path commands accepted by TVG. + * + * Not to be confused with the path commands from the svg path element (like M, L, Q, H and many others). + * TVG interprets all of them and translates to the ones from the PathCommand values. + */ +typedef enum { + TVG_PATH_COMMAND_CLOSE = 0, ///< Ends the current sub-path and connects it with its initial point - corresponds to Z command in the svg path commands. + TVG_PATH_COMMAND_MOVE_TO, ///< Sets a new initial point of the sub-path and a new current point - corresponds to M command in the svg path commands. + TVG_PATH_COMMAND_LINE_TO, ///< Draws a line from the current point to the given point and sets a new value of the current point - corresponds to L command in the svg path commands. + TVG_PATH_COMMAND_CUBIC_TO ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point - corresponds to C command in the svg path commands. +} Tvg_Path_Command; + + +/** + * \brief Enumeration determining the ending type of a stroke in the open sub-paths. + */ +typedef enum { + TVG_STROKE_CAP_SQUARE = 0, ///< The stroke is extended in both endpoints of a sub-path by a rectangle, with the width equal to the stroke width and the length equal to the half of the stroke width. For zero length sub-paths the square is rendered with the size of the stroke width. + TVG_STROKE_CAP_ROUND, ///< The stroke is extended in both endpoints of a sub-path by a half circle, with a radius equal to the half of a stroke width. For zero length sub-paths a full circle is rendered. + TVG_STROKE_CAP_BUTT ///< The stroke ends exactly at each of the two endpoints of a sub-path. For zero length sub-paths no stroke is rendered. +} Tvg_Stroke_Cap; + + +/** + * \brief Enumeration specifying how to fill the area outside the gradient bounds. + */ +typedef enum { + TVG_STROKE_JOIN_BEVEL = 0, ///< The outer corner of the joined path segments is bevelled at the join point. The triangular region of the corner is enclosed by a straight line between the outer corners of each stroke. + TVG_STROKE_JOIN_ROUND, ///< The outer corner of the joined path segments is rounded. The circular region is centered at the join point. + TVG_STROKE_JOIN_MITER ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style. +} Tvg_Stroke_Join; + + +/** + * \brief Enumeration specifying how to fill the area outside the gradient bounds. + */ +typedef enum { + TVG_STROKE_FILL_PAD = 0, ///< The remaining area is filled with the closest stop color. + TVG_STROKE_FILL_REFLECT, ///< The gradient pattern is reflected outside the gradient area until the expected region is filled. + TVG_STROKE_FILL_REPEAT ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled. +} Tvg_Stroke_Fill; + + +/** + * \brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape. + */ +typedef enum { + TVG_FILL_RULE_WINDING = 0, ///< A line from the point to a location outside the shape is drawn. The intersections of the line with the path segment of the shape are counted. Starting from zero, if the path segment of the shape crosses the line clockwise, one is added, otherwise one is subtracted. If the resulting sum is non zero, the point is inside the shape. + TVG_FILL_RULE_EVEN_ODD ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape. +} Tvg_Fill_Rule; + +/** \} */ // end addtogroup ThorVGCapi_Shape + + +/*! +* \addtogroup ThorVGCapi_Gradient +* \{ +*/ + +/*! +* \brief A data structure storing the information about the color and its relative position inside the gradient bounds. +*/ +typedef struct +{ + float offset; /**< The relative position of the color. */ + uint8_t r; /**< The red color channel value in the range [0 ~ 255]. */ + uint8_t g; /**< The green color channel value in the range [0 ~ 255]. */ + uint8_t b; /**< The blue color channel value in the range [0 ~ 255]. */ + uint8_t a; /**< The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. */ +} Tvg_Color_Stop; + +/** \} */ // end addtogroup ThorVGCapi_Gradient + + +/** + * \brief A data structure representing a point in two-dimensional space. + */ +typedef struct +{ + float x, y; +} Tvg_Point; + + +/** + * \brief A data structure representing a three-dimensional matrix. + * + * The elements e11, e12, e21 and e22 represent the rotation matrix, including the scaling factor. + * The elements e13 and e23 determine the translation of the object along the x and y-axis, respectively. + * The elements e31 and e32 are set to 0, e33 is set to 1. + */ +typedef struct +{ + float e11, e12, e13; + float e21, e22, e23; + float e31, e32, e33; +} Tvg_Matrix; + + +/** +* \defgroup ThorVGCapi_Initializer Initializer +* \brief A module enabling initialization and termination of the TVG engines. +* +* \{ +*/ + +/************************************************************************/ +/* Engine API */ +/************************************************************************/ +/*! +* \brief Initializes TVG engines. +* +* TVG requires the running-engine environment. +* TVG runs its own task-scheduler for parallelizing rendering tasks efficiently. +* You can indicate the number of threads, the count of which is designated @p threads. +* In the initialization step, TVG will generate/spawn the threads as set by @p threads count. +* +* \code +* tvg_engine_init(TVG_ENGINE_SW, 0); //Initialize software renderer and use the main thread only +* \endcode +* +* \param[in] engine_method The engine types to initialize. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed. +* - TVG_ENGINE_SW: CPU rasterizer +* - TVG_ENGINE_GL: OpenGL rasterizer (not supported yet) +* \param[in] threads The number of additional threads used to perform rendering. Zero indicates only the main thread is to be used. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error possibly with memory allocation. +* \retval TVG_RESULT_INVALID_ARGUMENT Unknown engine type. +* \retval TVG_RESULT_NOT_SUPPORTED Unsupported engine type. +* \retval TVG_RESULT_UNKNOWN Other error. +* +* \note The Initializer keeps track of the number of times it was called. Threads count is fixed at the first init() call. +* \see tvg_engine_term() +* \see Tvg_Engine +*/ +TVG_API Tvg_Result tvg_engine_init(Tvg_Engine engine_method, unsigned threads); + + +/*! +* \brief Terminates TVG engines. +* +* It should be called in case of termination of the TVG client with the same engine types as were passed when tvg_engine_init() was called. +* +* \code +* tvg_engine_init(TVG_ENGINE_SW, 0); +* //define canvas and shapes, update shapes, general rendering calls +* tvg_engine_term(TVG_ENGINE_SW); +* \endcode +* +* \param engine_method The engine types to terminate. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed +* - TVG_ENGINE_SW: CPU rasterizer +* - TVG_ENGINE_GL: OpenGL rasterizer (not supported yet) +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION Nothing to be terminated. +* \retval TVG_RESULT_INVALID_ARGUMENT Unknown engine type. +* \retval TVG_RESULT_NOT_SUPPORTED Unsupported engine type. +* \retval TVG_RESULT_UNKNOWN An internal error. +* +* \see tvg_engine_init() +* \see Tvg_Engine +*/ +TVG_API Tvg_Result tvg_engine_term(Tvg_Engine engine_method); + + +/** \} */ // end defgroup ThorVGCapi_Initializer + + +/** +* \defgroup ThorVGCapi_Canvas Canvas +* \brief A module for managing and drawing graphical elements. +* +* A canvas is an entity responsible for drawing the target. It sets up the drawing engine and the buffer, which can be drawn on the screen. It also manages given Paint objects. +* +* \note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical. +* \warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases. +\{ +*/ + + +/** +* \defgroup ThorVGCapi_SwCanvas SwCanvas +* \ingroup ThorVGCapi_Canvas +* +* \brief A module for rendering the graphical elements using the software engine. +* +* \{ +*/ + +/************************************************************************/ +/* SwCanvas API */ +/************************************************************************/ + +/** + * \brief Enumeration specifying the methods of Memory Pool behavior policy. + */ +typedef enum { + TVG_MEMPOOL_POLICY_DEFAULT = 0, ///< Default behavior that ThorVG is designed to. + TVG_MEMPOOL_POLICY_SHAREABLE, ///< Memory Pool is shared among canvases. + TVG_MEMPOOL_POLICY_INDIVIDUAL ///< Allocate designated memory pool that is used only by the current canvas instance. +} Tvg_Mempool_Policy; + + +/** + * \brief Enumeration specifying the methods of combining the 8-bit color channels into 32-bit color. + */ +typedef enum { + TVG_COLORSPACE_ABGR8888 = 0, ///< The 8-bit color channels are combined into 32-bit color in the order: alpha, blue, green, red. + TVG_COLORSPACE_ARGB8888 ///< The 8-bit color channels are combined into 32-bit color in the order: alpha, red, green, blue. +} Tvg_Colorspace; + + +/*! +* \brief Creates a Canvas object. +* +* \code +* Tvg_Canvas *canvas = NULL; +* +* tvg_engine_init(TVG_ENGINE_SW, 4); +* canvas = tvg_swcanvas_create(); +* +* //set up the canvas buffer +* uint32_t *buffer = NULL; +* buffer = (uint32_t*) malloc(sizeof(uint32_t) * 100 * 100); +* if (!buffer) return; +* +* tvg_swcanvas_set_target(canvas, buffer, 100, 100, 100, TVG_COLORSPACE_ARGB8888); +* +* //set up paints and add them into the canvas before drawing it +* +* tvg_canvas_destroy(canvas); +* tvg_engine_term(TVG_ENGINE_SW); +* \endcode +* +* \return A new Tvg_Canvas object. +*/ +TVG_API Tvg_Canvas* tvg_swcanvas_create(); + + +/*! +* \brief Sets the buffer used in the rasterization process and defines the used colorspace. +* +* For optimisation reasons TVG does not allocate memory for the output buffer on its own. +* The buffer of a desirable size should be allocated and owned by the caller. +* +* \param[in] canvas The Tvg_Canvas object managing the @p buffer. +* \param[in] buffer A pointer to the allocated memory block of the size @p stride x @p h. +* \param[in] stride The stride of the raster image - in most cases same value as @p w. +* \param[in] w The width of the raster image. +* \param[in] h The height of the raster image. +* \param[in] cs The colorspace value defining the way the 32-bits colors should be read/written. +* - TVG_COLORSPACE_ABGR8888 +* - TVG_COLORSPACE_ARGB8888 +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_MEMORY_CORRUPTION Casting in the internal function implementation failed. +* \retval TVG_RESULT_INVALID_ARGUMENTS An invalid canvas or buffer pointer passed or one of the @p stride, @p w or @p h being zero. +* \retval TVG_RESULT_NOT_SUPPORTED The software engine is not supported. +* +* \warning Do not access @p buffer during tvg_canvas_draw() - tvg_canvas_sync(). It should not be accessed while TVG is writing on it. +* +* \see Tvg_Colorspace +*/ +TVG_API Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas* canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Tvg_Colorspace cs); + + +/*! +* \brief Sets the software engine memory pool behavior policy. +* +* ThorVG draws a lot of shapes, it allocates/deallocates a few chunk of memory +* while processing rendering. It internally uses one shared memory pool +* which can be reused among the canvases in order to avoid memory overhead. +* +* Thus ThorVG suggests using a memory pool policy to satisfy user demands, +* if it needs to guarantee the thread-safety of the internal data access. +* +* \param[in] canvas The Tvg_Canvas object of which the Memory Pool behavior is to be specified. +* \param[in] policy The method specifying the Memory Pool behavior. The default value is @c TVG_MEMPOOL_POLICY_DEFAULT. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENTS An invalid canvas pointer passed. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION The canvas contains some paints already. +* \retval TVG_RESULT_NOT_SUPPORTED The software engine is not supported. +* +* \note When @c policy is set as @c TVG_MEMPOOL_POLICY_INDIVIDUAL, the current instance of canvas uses its own individual +* memory data, which is not shared with others. This is necessary when the canvas is accessed on a worker-thread. +* +* \warning It's not allowed after pushing any paints. +*/ +TVG_API Tvg_Result tvg_swcanvas_set_mempool(Tvg_Canvas* canvas, Tvg_Mempool_Policy policy); + +/** \} */ // end defgroup ThorVGCapi_SwCanvas + + +/************************************************************************/ +/* Common Canvas API */ +/************************************************************************/ +/*! +* \brief Clears the canvas internal data, releases all paints stored by the canvas and destroys the canvas object itself. +* +* \code +* static Tvg_Canvas *canvas = NULL; +* static uint32_t *buffer = NULL; +* +* static void _init() { +* canvas = tvg_swcanvas_create(); +* buffer = (uint32_t*) malloc(sizeof(uint32_t) * 100 * 100); +* tvg_swcanvas_set_target(canvas, buffer, 100, 100, 100, TVG_COLORSPACE_ARGB8888); +* } +* +* //a task called from main function in a loop +* static void _job(const int cmd) { +* //define a valid rectangle shape +* switch (cmd) { +* case CMD_EXIT: return 0; +* case CMD_ADD_RECT: +* tvg_canvas_push(canvas, rect); +* break; +* case CMD_DEL_RECT: +* tvg_paint_del(rect); +* //now to safely delete Tvg_Canvas, tvg_canvas_clear() API have to be used +* break; +* default: +* break; +* } +* } +* +* int main(int argc, char **argv) { +* int cmd = 0; +* int stop = 1; +* +* tvg_engine_init(TVG_ENGINE_SW, 4); +* +* while (stop) { +* //wait for a command e.g. from a console +* stop = _job(cmd); +* } +* tvg_canvas_clear(canvas, false); +* tvg_canvas_destroy(canvas); +* tvg_engine_term(TVG_ENGINE_SW); +* return 0; +* } +* +* tvg_canvas_destroy(canvas); +* tvg_engine_term(TVG_ENGINE_SW) +* \endcode +* +* \param[in] canvas The Tvg_Canvas object to be destroyed. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer to the Tvg_Canvas object is passed. +* +* \note If the paints from the canvas should not be released, the tvg_canvas_clear() with a @c free argument value set to @c false should be called. +* Please be aware that in such a case TVG is not responsible for the paints release anymore and it has to be done manually in order to avoid memory leaks. +* +* \see tvg_paint_del(), tvg_canvas_clear() +*/ +TVG_API Tvg_Result tvg_canvas_destroy(Tvg_Canvas* canvas); + + +/*! +* \brief Inserts a drawing element into the canvas using a Tvg_Paint object. +* +* \param[in] canvas The Tvg_Canvas object managing the @p paint. +* \param[in] paint The Tvg_Paint object to be drawn. +* +* Only the paints pushed into the canvas will be drawing targets. +* They are retained by the canvas until you call tvg_canvas_clear(). +* If you know the number of the pushed objects in advance, please call tvg_canvas_reserve(). +* +* \return Tvg_Result return values: +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An internal error. +* +* \note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. +* \see tvg_canvas_clear() +*/ +TVG_API Tvg_Result tvg_canvas_push(Tvg_Canvas* canvas, Tvg_Paint* paint); + + +/*! +* \brief Reserves a memory block where the objects pushed into a canvas are stored. +* +* If the number of Tvg_Paints to be stored in a canvas is known in advance, calling this function reduces the multiple +* memory allocations thus improves the performance. +* +* \code +* Tvg_Canvas *canvas = NULL; +* +* tvg_engine_init(TVG_ENGINE_SW, 4); +* canvas = tvg_swcanvas_create(); +* +* uint32_t *buffer = NULL; +* buffer = (uint32_t*) malloc(sizeof(uint32_t) * 100 * 100); +* if (!buffer) return; +* +* tvg_swcanvas_set_target(canvas, buffer, 100, 100, 100, TVG_COLORSPACE_ARGB8888); +* +* tvg_canvas_destroy(canvas); +* tvg_engine_term(TVG_ENGINE_SW) +* \endcode +* +* \param[in] canvas The Tvg_Canvas object managing the reserved memory. +* \param[in] n The number of objects for which the memory is to be reserved. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with memory allocation. +*/ +TVG_DEPRECATED TVG_API Tvg_Result tvg_canvas_reserve(Tvg_Canvas* canvas, uint32_t n); + + +/*! +* \brief Sets the total number of the paints pushed into the canvas to be zero. +* Tvg_Paint objects stored in the canvas are released if @p free is set to @c true, otherwise the memory is not deallocated and +* all paints should be released manually in order to avoid memory leaks. +* +* \param[in] canvas The Tvg_Canvas object to be cleared. +* \param[in] free If @c true the memory occupied by paints is deallocated, otherwise it is not. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An internal error. +* +* \warning Please use the @p free argument only when you know how it works, otherwise it's not recommended. +* +* \see tvg_canvas_destroy() +*/ +TVG_API Tvg_Result tvg_canvas_clear(Tvg_Canvas* canvas, bool free); + + +/*! +* \brief Updates all paints in a canvas. +* +* Should be called before drawing in order to prepare paints for the rendering. +* +* \code +* //A frame drawing example. Thread safety and events implementation is skipped to show only TVG code. +* +* static Tvg_Canvas *canvas = NULL; +* static Tvg_Paint *rect = NULL; +* +* int _frame_render(void) { +* tvg_canvas_update(canvas); +* tvg_canvas_draw(canvas); +* tvg_canvas_sync(canvas); +* } +* +* //event handler from your code or third party library +* void _event_handler(event *event_data) { +* if (!event_data) return NULL; +* switch(event_data.type) { +* case EVENT_RECT_ADD: +* if (!rect) { +* tvg_shape_append_rect(rect, 10, 10, 50, 50, 0, 0); +* tvg_shape_set_stroke_width(rect, 1.0f); +* tvg_shape_set_stroke_color(rect, 255, 0, 0, 255); +* tvg_canvas_push(canvas, rect); +* } +* break; +* case EVENT_RECT_MOVE: +* if (rect) tvg_paint_translate(rect, 10.0, 10.0); +* break; +* default: +* break; +* } +* } +* +* int main(int argc, char **argv) { +* //example handler from your code or third party lib +* event_handler_add(handler, _event_handler); +* +* //create frame rendering process which calls _frame_render() function. +* app_loop_begin(_frame_render); +* app_loop_finish(); +* cleanup(); +* } +* \endcode +* +* \param[in] canvas The Tvg_Canvas object to be updated. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An internal error. +* +* \see tvg_canvas_update_paint() +*/ +TVG_API Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas); + + +/*! +* \brief Updates the given Tvg_Paint object from the canvas before the rendering. +* +* If a client application using the TVG library does not update the entire canvas with tvg_canvas_update() in the frame +* rendering process, Tvg_Paint objects previously added to the canvas should be updated manually with this function. +* +* \param[in] canvas The Tvg_Canvas object to which the @p paint belongs. +* \param[in] paint The Tvg_Paint object to be updated. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. +* +* \see tvg_canvas_update() +*/ +TVG_API Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint); + + +/*! +* \brief Requests the canvas to draw the Tvg_Paint objects. +* +* All paints from the given canvas will be rasterized to the buffer. +* +* \param[in] canvas The Tvg_Canvas object containing elements to be drawn. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An internal error. +* +* \note Drawing can be asynchronous based on the assigned thread number. To guarantee the drawing is done, call tvg_canvas_sync() afterwards. +* \see tvg_canvas_sync() +*/ +TVG_API Tvg_Result tvg_canvas_draw(Tvg_Canvas* canvas); + + +/*! +* \brief Guarantees that the drawing process is finished. +* +* Since the canvas rendering can be performed asynchronously, it should be called after the tvg_canvas_draw(). +* +* \param[in] canvas The Tvg_Canvas object containing elements which were drawn. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An internal error. +* +* \see tvg_canvas_draw() +*/ +TVG_API Tvg_Result tvg_canvas_sync(Tvg_Canvas* canvas); + + +/** \} */ // end defgroup ThorVGCapi_Canvas + + +/** +* \defgroup ThorVGCapi_Paint Paint +* \brief A module for managing graphical elements. It enables duplication, transformation and composition. +* +* \{ +*/ + +/************************************************************************/ +/* Paint API */ +/************************************************************************/ +/*! +* \brief Releases the given Tvg_Paint object. +* +* \code +* //example of cleanup function +* Tvg_Paint *rect = NULL; //rectangle shape added in other function +* +* //rectangle delete API +* int rectangle_delete(void) { +* if (rect) tvg_paint_del(rect); +* rect = NULL; +* } +* +* int cleanup(void) { +* tvg_canvas_clear(canvas, false); +* tvg_canvas_destroy(canvas); +* canvas = NULL; +* } +* \endcode +* +* \param[in] paint The Tvg_Paint object to be released. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \warning If this function is used, tvg_canvas_clear() with the @c free argument value set to @c false should be used in order to avoid unexpected behaviours. +* +* \see tvg_canvas_clear(), tvg_canvas_destroy() +*/ +TVG_API Tvg_Result tvg_paint_del(Tvg_Paint* paint); + + +/*! +* \brief Scales the given Tvg_Paint object by the given factor. +* +* \param[in] paint The Tvg_Paint object to be scaled. +* \param[in] factor The value of the scaling factor. The default value is 1. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with memory allocation. +*/ +TVG_API Tvg_Result tvg_paint_scale(Tvg_Paint* paint, float factor); + + +/*! +* \brief Rotates the given Tvg_Paint by the given angle. +* +* The angle in measured clockwise from the horizontal axis. +* The rotational axis passes through the point on the object with zero coordinates. +* +* \param[in] paint The Tvg_Paint object to be rotated. +* \param[in] degree The value of the rotation angle in degrees. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with memory allocation. +*/ +TVG_API Tvg_Result tvg_paint_rotate(Tvg_Paint* paint, float degree); + + +/*! +* \brief Moves the given Tvg_Paint in a two-dimensional space. +* +* The origin of the coordinate system is in the upper left corner of the canvas. +* The horizontal and vertical axes point to the right and down, respectively. +* +* \param[in] paint The Tvg_Paint object to be shifted. +* \param[in] x The value of the horizontal shift. +* \param[in] y The value of the vertical shift. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with memory allocation. +*/ +TVG_API Tvg_Result tvg_paint_translate(Tvg_Paint* paint, float x, float y); + + +/*! +* \brief Transforms the given Tvg_Paint using the augmented transformation matrix. +* +* The augmented matrix of the transformation is expected to be given. +* +* \param[in] paint The Tvg_Paint object to be transformed. +* \param[in] m The 3x3 augmented matrix. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with memory allocation. +*/ +TVG_API Tvg_Result tvg_paint_set_transform(Tvg_Paint* paint, const Tvg_Matrix* m); + + +/*! +* \brief Gets the matrix of the affine transformation of the given Tvg_Paint object. +* +* In case no transformation was applied, the identity matrix is returned. +* +* \param[in] paint The Tvg_Paint object of which to get the transformation matrix. +* \param[out] m The 3x3 augmented matrix. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. +*/ +TVG_API Tvg_Result tvg_paint_get_transform(Tvg_Paint* paint, Tvg_Matrix* m); + + +/*! +* \brief Sets the opacity of the given Tvg_Paint. +* +* \param[in] paint The Tvg_Paint object of which the opacity value is to be set. +* \param[in] opacity The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note Setting the opacity with this API may require multiple renderings using a composition. It is recommended to avoid changing the opacity if possible. +*/ +TVG_API Tvg_Result tvg_paint_set_opacity(Tvg_Paint* paint, uint8_t opacity); + + +/*! +* \brief Gets the opacity of the given Tvg_Paint. +* +* \param[in] paint The Tvg_Paint object of which to get the opacity value. +* \param[out] opacity The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. +*/ +TVG_API Tvg_Result tvg_paint_get_opacity(const Tvg_Paint* paint, uint8_t* opacity); + + +/*! +* \brief Duplicates the given Tvg_Paint object. +* +* Creates a new object and sets its all properties as in the original object. +* +* \param[in] paint The Tvg_Paint object to be copied. +* +* \return A copied Tvg_Paint object if succeed, @c nullptr otherwise. +*/ +TVG_API Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint); + + +/*! +* \brief Gets the axis-aligned bounding box of the Tvg_Paint object. +* +* \param[in] paint The Tvg_Paint object of which to get the bounds. +* \param[out] x The x coordinate of the upper left corner of the object. +* \param[out] y The y coordinate of the upper left corner of the object. +* \param[out] w The width of the object. +* \param[out] h The height of the object. +* \param[in] transformed If @c true, the transformation of the paint is taken into account, otherwise it isn't. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION Other errors. +* +* \note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. +*/ +TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* y, float* w, float* h, bool transformed); + + +/*! +* \brief Sets the composition target object and the composition method. +* +* \param[in] paint The source object of the composition. +* \param[in] target The target object of the composition. +* \param[in] method The method used to composite the source object with the target. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid @p paint or @p target object or the @p method equal to TVG_COMPOSITE_METHOD_NONE. +*/ +TVG_API Tvg_Result tvg_paint_set_composite_method(Tvg_Paint* paint, Tvg_Paint* target, Tvg_Composite_Method method); + + +/** +* \brief Gets the composition target object and the composition method. +* +* \param[in] paint The source object of the composition. +* \param[out] target The target object of the composition. +* \param[out] method The method used to composite the source object with the target. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. +*/ +TVG_API Tvg_Result tvg_paint_get_composite_method(const Tvg_Paint* paint, const Tvg_Paint** target, Tvg_Composite_Method* method); + + +/** +* \brief Gets the unique id value of the paint instance indicating the instance type. +* +* \param[in] paint The Tvg_Paint object of which to get the identifier value. +* \param[out] identifier The unique identifier of the paint instance type. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. +* +* \since 0.9 +*/ +TVG_API Tvg_Result tvg_paint_get_identifier(const Tvg_Paint* paint, Tvg_Identifier* identifier); + + +/** + * @brief Sets the blending method for the paint object. + * + * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others. + * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations. + * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined. + * + * \param[in] paint The Tvg_Paint object of which to get the identifier value. + * \param[in] method The blending method to be set. + * + * \return Tvg_Result enumeration. + * \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. + * + * @BETA_API + */ +TVG_API Tvg_Result tvg_paint_set_blend_method(const Tvg_Paint* paint, Tvg_Blend_Method method); + + +/** + * @brief Gets the blending method for the paint object. + * + * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others. + * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations. + * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined. + * + * \param[in] paint The Tvg_Paint object of which to get the identifier value. + * \param[out] method The blending method of the paint. + * + * \return Tvg_Result enumeration. + * \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. + * + * @BETA_API + */ +TVG_API Tvg_Result tvg_paint_get_blend_method(const Tvg_Paint* paint, Tvg_Blend_Method* method); + + +/** \} */ // end defgroup ThorVGCapi_Paint + +/** +* \defgroup ThorVGCapi_Shape Shape +* +* \brief A module for managing two-dimensional figures and their properties. +* +* A shape has three major properties: shape outline, stroking, filling. The outline in the shape is retained as the path. +* Path can be composed by accumulating primitive commands such as tvg_shape_move_to(), tvg_shape_line_to(), tvg_shape_cubic_to() or complete shape interfaces such as tvg_shape_append_rect(), tvg_shape_append_circle(), etc. +* Path can consists of sub-paths. One sub-path is determined by a close command. +* +* The stroke of a shape is an optional property in case the shape needs to be represented with/without the outline borders. +* It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. +* +* \{ +*/ + +/************************************************************************/ +/* Shape API */ +/************************************************************************/ +/*! +* \brief Creates a new shape object. +* +* \return A new shape object. +*/ +TVG_API Tvg_Paint* tvg_shape_new(); + + +/*! +* \brief Resets the shape path properties. +* +* The color, the fill and the stroke properties are retained. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note The memory, where the path data is stored, is not deallocated at this stage for caching effect. +*/ +TVG_API Tvg_Result tvg_shape_reset(Tvg_Paint* paint); + + +/*! +* \brief Sets the initial point of the sub-path. +* +* The value of the current point is set to the given point. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] x The horizontal coordinate of the initial point of the sub-path. +* \param[in] y The vertical coordinate of the initial point of the sub-path. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +*/ +TVG_API Tvg_Result tvg_shape_move_to(Tvg_Paint* paint, float x, float y); + + +/*! +* \brief Adds a new point to the sub-path, which results in drawing a line from the current point to the given end-point. +* +* The value of the current point is set to the given end-point. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] x The horizontal coordinate of the end-point of the line. +* \param[in] y The vertical coordinate of the end-point of the line. + +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note In case this is the first command in the path, it corresponds to the tvg_shape_move_to() call. +*/ +TVG_API Tvg_Result tvg_shape_line_to(Tvg_Paint* paint, float x, float y); + + +/*! +* \brief Adds new points to the sub-path, which results in drawing a cubic Bezier curve. +* +* The Bezier curve starts at the current point and ends at the given end-point (@p x, @p y). Two control points (@p cx1, @p cy1) and (@p cx2, @p cy2) are used to determine the shape of the curve. +* The value of the current point is set to the given end-point. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] cx1 The horizontal coordinate of the 1st control point. +* \param[in] cy1 The vertical coordinate of the 1st control point. +* \param[in] cx2 The horizontal coordinate of the 2nd control point. +* \param[in] cy2 The vertical coordinate of the 2nd control point. +* \param[in] x The horizontal coordinate of the endpoint of the curve. +* \param[in] y The vertical coordinate of the endpoint of the curve. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note In case this is the first command in the path, no data from the path are rendered. +*/ +TVG_API Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1, float cx2, float cy2, float x, float y); + + +/*! +* \brief Closes the current sub-path by drawing a line from the current point to the initial point of the sub-path. +* +* The value of the current point is set to the initial point of the closed sub-path. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note In case the sub-path does not contain any points, this function has no effect. +*/ +TVG_API Tvg_Result tvg_shape_close(Tvg_Paint* paint); + + +/*! +* \brief Appends a rectangle to the path. +* +* The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments. +* The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners. +* +* The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments. +* +* The rectangle is treated as a new sub-path - it is not connected with the previous sub-path. +* +* The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater +* than @p w/2 the current point is set to (@p x + @p w/2, @p y) +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] x The horizontal coordinate of the upper left corner of the rectangle. +* \param[in] y The vertical coordinate of the upper left corner of the rectangle. +* \param[in] w The width of the rectangle. +* \param[in] h The height of the rectangle. +* \param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. +* \param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +& \note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse. +*/ +TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry); + + +/*! +* \brief Appends an ellipse to the path. +* +* The position of the ellipse is specified by the coordinates of its center - @p cx and @p cy arguments. +* +* The ellipse is treated as a new sub-path - it is not connected with the previous sub-path. +* +* The value of the current point is set to (@p cx, @p cy - @p ry). +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] cx The horizontal coordinate of the center of the ellipse. +* \param[in] cy The vertical coordinate of the center of the ellipse. +* \param[in] rx The x-axis radius of the ellipse. +* \param[in] ry The y-axis radius of the ellipse. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +*/ +TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry); + + +/*! +* \brief Appends a circular arc to the path. +* +* The arc is treated as a new sub-path - it is not connected with the previous sub-path. +* The current point value is set to the end-point of the arc in case @p pie is @c false, and to the center of the arc otherwise. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] cx The horizontal coordinate of the center of the arc. +* \param[in] cy The vertical coordinate of the center of the arc. +* \param[in] radius The radius of the arc. +* \param[in] startAngle The start angle of the arc given in degrees, measured counter-clockwise from the horizontal line. +* \param[in] sweep The central angle of the arc given in degrees, measured counter-clockwise from @p startAngle. +* \param[in] pie Specifies whether to draw radii from the arc's center to both of its end-point - drawn if @c true. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note Setting @p sweep value greater than 360 degrees, is equivalent to calling tvg_shape_append_circle(paint, cx, cy, radius, radius). +*/ +TVG_API Tvg_Result tvg_shape_append_arc(Tvg_Paint* paint, float cx, float cy, float radius, float startAngle, float sweep, uint8_t pie); + + +/*! +* \brief Appends a given sub-path to the path. +* +* The current point value is set to the last point from the sub-path. +* For each command from the @p cmds array, an appropriate number of points in @p pts array should be specified. +* If the number of points in the @p pts array is different than the number required by the @p cmds array, the shape with this sub-path will not be displayed on the screen. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] cmds The array of the commands in the sub-path. +* \param[in] cmdCnt The length of the @p cmds array. +* \param[in] pts The array of the two-dimensional points. +* \param[in] ptsCnt The length of the @p pts array. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument or @p cmdCnt or @p ptsCnt equal to zero. +*/ +TVG_API Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt); + + +/*! +* \brief Gets the points values of the path. +* +* The function does not allocate any data, it operates on internal memory. There is no need to free the @p pts array. +* +* \code +* Tvg_Shape *shape = tvg_shape_new(); +* Tvg_Point *coords = NULL; +* uint32_t len = 0; +* +* tvg_shape_append_circle(shape, 10, 10, 50, 50); +* tvg_shape_get_path_coords(shape, (const Tvg_Point**)&coords, &len); +* //TVG approximates a circle by four Bezier curves. In the example above the coords array stores their coordinates. +* \endcode +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] pts The pointer to the array of the two-dimensional points from the path. +* \param[out] cnt The length of the @p pts array. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +*/ +TVG_API Tvg_Result tvg_shape_get_path_coords(const Tvg_Paint* paint, const Tvg_Point** pts, uint32_t* cnt); + + +/*! +* \brief Gets the commands data of the path. +* +* The function does not allocate any data. There is no need to free the @p cmds array. +* +* \code +* Tvg_Shape *shape = tvg_shape_new(); +* Tvg_Path_Command *cmds = NULL; +* uint32_t len = 0; +* +* tvg_shape_append_circle(shape, 10, 10, 50, 50); +* tvg_shape_get_path_commands(shape, (const Tvg_Path_Command**)&cmds, &len); +* //TVG approximates a circle by four Bezier curves. In the example above the cmds array stores the commands of the path data. +* \endcode +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] cmds The pointer to the array of the commands from the path. +* \param[out] cnt The length of the @p cmds array. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +*/ +TVG_API Tvg_Result tvg_shape_get_path_commands(const Tvg_Paint* paint, const Tvg_Path_Command** cmds, uint32_t* cnt); + + +/*! +* \brief Sets the stroke width for all of the figures from the @p paint. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] width The width of the stroke. The default value is 0. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width); + + +/*! +* \brief Gets the shape's stroke width. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] width The stroke width. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_width(const Tvg_Paint* paint, float* width); + + +/*! +* \brief Sets the shape's stroke color. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. +* \param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. +* \param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. +* \param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* +* \note Either a solid color or a gradient fill is applied, depending on what was set as last. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a); + + +/*! +* \brief Gets the shape's stroke color. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] r The red color channel value in the range [0 ~ 255]. The default value is 0. +* \param[out] g The green color channel value in the range [0 ~ 255]. The default value is 0. +* \param[out] b The blue color channel value in the range [0 ~ 255]. The default value is 0. +* \param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION No stroke was set. +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint* paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a); + + +/*! +* \brief Sets the linear gradient fill of the stroke for all of the figures from the path. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] grad The linear gradient fill. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* \retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer. +* +* \note Either a solid color or a gradient fill is applied, depending on what was set as last. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* grad); + + +/*! +* \brief Sets the radial gradient fill of the stroke for all of the figures from the path. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] grad The radial gradient fill. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* \retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer. +* +* \note Either a solid color or a gradient fill is applied, depending on what was set as last. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* grad); + + +/*! +* \brief Gets the gradient fill of the shape's stroke. +* +* The function does not allocate any memory. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] grad The gradient fill. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint* paint, Tvg_Gradient** grad); + + +/*! +* \brief Sets the shape's stroke dash pattern. +* +* \code +* //dash pattern examples +* float dashPattern[2] = {20, 10}; // -- -- -- +* float dashPattern[2] = {40, 20}; // ---- ---- ---- +* float dashPattern[4] = {10, 20, 30, 40} // - --- - --- +* \endcode +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] dashPattern The array of consecutive pair values of the dash length and the gap length. +* \param[in] cnt The size of the @p dashPattern array. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument and @p cnt > 0, the given length of the array is less than two or any of the @p dashPattern values is zero or less. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* +* \note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt); + + +/*! +* \brief Gets the dash pattern of the stroke. +* +* The function does not allocate any memory. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] dashPattern The array of consecutive pair values of the dash length and the gap length. +* \param[out] cnt The size of the @p dashPattern array. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_dash(const Tvg_Paint* paint, const float** dashPattern, uint32_t* cnt); + + +/*! +* \brief Sets the cap style used for stroking the path. +* +* The cap style specifies the shape to be used at the end of the open stroked sub-paths. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] cap The cap style value. The default value is @c TVG_STROKE_CAP_SQUARE. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint* paint, Tvg_Stroke_Cap cap); + + +/*! +* \brief Gets the stroke cap style used for stroking the path. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] cap The cap style value. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_cap(const Tvg_Paint* paint, Tvg_Stroke_Cap* cap); + + +/*! +* \brief Sets the join style for stroked path segments. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] join The join style value. The default value is @c TVG_STROKE_JOIN_BEVEL. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join join); + + +/*! +* \brief The function gets the stroke join method +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] join The join style value. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_join(const Tvg_Paint* paint, Tvg_Stroke_Join* join); + + +/*! +* \brief Sets the stroke miterlimit. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join when the @c TVG_STROKE_JOIN_MITER join style is set. The default value is 4. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_NOT_SUPPORTED Unsupported value. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* +* \since 0.11 +*/ +TVG_API Tvg_Result tvg_shape_set_stroke_miterlimit(Tvg_Paint* paint, float miterlimit); + + +/*! +* \brief The function gets the stroke miterlimit. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] miterlimit The stroke miterlimit. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +* +* \since 0.11 +*/ +TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint* paint, float* miterlimit); + + +/*! +* \brief Sets the shape's solid color. +* +* The parts of the shape defined as inner are colored. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. +* \param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. +* \param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. +* \param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* +* \note Either a solid color or a gradient fill is applied, depending on what was set as last. +* \see tvg_shape_set_fill_rule() +*/ +TVG_API Tvg_Result tvg_shape_set_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a); + + +/*! +* \brief Gets the shape's solid color. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] r The red color channel value in the range [0 ~ 255]. The default value is 0. +* \param[out] g The green color channel value in the range [0 ~ 255]. The default value is 0. +* \param[out] b The blue color channel value in the range [0 ~ 255]. The default value is 0. +* \param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +*/ +TVG_API Tvg_Result tvg_shape_get_fill_color(const Tvg_Paint* paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a); + + +/*! +* \brief Sets the shape's fill rule. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] rule The fill rule value. The default value is @c TVG_FILL_RULE_WINDING. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +*/ +TVG_API Tvg_Result tvg_shape_set_fill_rule(Tvg_Paint* paint, Tvg_Fill_Rule rule); + + +/*! +* \brief Gets the shape's fill rule. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] rule shape's fill rule +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_fill_rule(const Tvg_Paint* paint, Tvg_Fill_Rule* rule); + + +/*! +* \brief Sets the rendering order of the stroke and the fill. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* +* \since 0.10 +*/ +TVG_API Tvg_Result tvg_shape_set_paint_order(Tvg_Paint* paint, bool strokeFirst); + + +/*! +* \brief Sets the linear gradient fill for all of the figures from the path. +* +* The parts of the shape defined as inner are filled. +* +* \code +* Tvg_Gradient* grad = tvg_linear_gradient_new(); +* tvg_linear_gradient_set(grad, 700, 700, 800, 800); +* Tvg_Color_Stop color_stops[4] = +* { +* {0.0 , 0, 0, 0, 255}, +* {0.25, 255, 0, 0, 255}, +* {0.5 , 0, 255, 0, 255}, +* {1.0 , 0, 0, 255, 255} +* }; +* tvg_gradient_set_color_stops(grad, color_stops, 4); +* tvg_shape_set_linear_gradient(shape, grad); +* \endcode +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] grad The linear gradient fill. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer. +* +* \note Either a solid color or a gradient fill is applied, depending on what was set as last. +* \see tvg_shape_set_fill_rule() +*/ +TVG_API Tvg_Result tvg_shape_set_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* grad); + + +/*! +* \brief Sets the radial gradient fill for all of the figures from the path. +* +* The parts of the shape defined as inner are filled. +* +* \code +* Tvg_Gradient* grad = tvg_radial_gradient_new(); +* tvg_radial_gradient_set(grad, 550, 550, 50); +* Tvg_Color_Stop color_stops[4] = +* { +* {0.0 , 0, 0, 0, 255}, +* {0.25, 255, 0, 0, 255}, +* {0.5 , 0, 255, 0, 255}, +* {1.0 , 0, 0, 255, 255} +* }; +* tvg_gradient_set_color_stops(grad, color_stops, 4); +* tvg_shape_set_radial_gradient(shape, grad); +* \endcode +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[in] grad The radial gradient fill. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_MEMORY_CORRUPTION An invalid Tvg_Gradient pointer. +* +* \note Either a solid color or a gradient fill is applied, depending on what was set as last. +* \see tvg_shape_set_fill_rule() +*/ +TVG_API Tvg_Result tvg_shape_set_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* grad); + + +/*! +* \brief Gets the gradient fill of the shape. +* +* The function does not allocate any data. +* +* \param[in] paint A Tvg_Paint pointer to the shape object. +* \param[out] grad The gradient fill. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument. +*/ +TVG_API Tvg_Result tvg_shape_get_gradient(const Tvg_Paint* paint, Tvg_Gradient** grad); + + +/** \} */ // end defgroup ThorVGCapi_Shape + + +/** +* \defgroup ThorVGCapi_Gradient Gradient +* \brief A module managing the gradient fill of objects. +* +* The module enables to set and to get the gradient colors and their arrangement inside the gradient bounds, +* to specify the gradient bounds and the gradient behavior in case the area defined by the gradient bounds +* is smaller than the area to be filled. +* +* \{ +*/ + +/************************************************************************/ +/* Gradient API */ +/************************************************************************/ +/*! +* \brief Creates a new linear gradient object. +* +* \code +* Tvg_Paint* shape = tvg_shape_new(); +* tvg_shape_append_rect(shape, 700, 700, 100, 100, 20, 20); +* Tvg_Gradient* grad = tvg_linear_gradient_new(); +* tvg_linear_gradient_set(grad, 700, 700, 800, 800); +* Tvg_Color_Stop color_stops[2] = +* { +* {0.0, 0, 0, 0, 255}, +* {1.0, 0, 255, 0, 255}, +* }; +* tvg_gradient_set_color_stops(grad, color_stops, 2); +* tvg_shape_set_linear_gradient(shape, grad); +* \endcode +* +* \return A new linear gradient object. +*/ +TVG_API Tvg_Gradient* tvg_linear_gradient_new(); + + +/*! +* \brief Creates a new radial gradient object. +* +* \code +* Tvg_Paint* shape = tvg_shape_new(); +* tvg_shape_append_rect(shape, 700, 700, 100, 100, 20, 20); +* Tvg_Gradient* grad = tvg_radial_gradient_new(); +* tvg_radial_gradient_set(grad, 550, 550, 50); +* Tvg_Color_Stop color_stops[2] = +* { +* {0.0, 0, 0, 0, 255}, +* {1.0, 0, 255, 0, 255}, +* }; +* tvg_gradient_set_color_stops(grad, color_stops, 2); +* tvg_shape_set_radial_gradient(shape, grad); +* \endcode +* +* \return A new radial gradient object. +*/ +TVG_API Tvg_Gradient* tvg_radial_gradient_new(); + + +/*! +* \brief Sets the linear gradient bounds. +* +* The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing +* the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking +* (@p x1, @p y1) and (@p x2, @p y2). +* +* \param[in] grad The Tvg_Gradient object of which bounds are to be set. +* @param[in] x1 The horizontal coordinate of the first point used to determine the gradient bounds. +* @param[in] y1 The vertical coordinate of the first point used to determine the gradient bounds. +* @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds. +* @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. +* +* \note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered. +*/ +TVG_API Tvg_Result tvg_linear_gradient_set(Tvg_Gradient* grad, float x1, float y1, float x2, float y2); + + +/*! +* \brief Gets the linear gradient bounds. +* +* The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing +* the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking +* (@p x1, @p y1) and (@p x2, @p y2). +* +* \param[in] grad The Tvg_Gradient object of which to get the bounds. +* \param[out] x1 The horizontal coordinate of the first point used to determine the gradient bounds. +* \param[out] y1 The vertical coordinate of the first point used to determine the gradient bounds. +* \param[out] x2 The horizontal coordinate of the second point used to determine the gradient bounds. +* \param[out] y2 The vertical coordinate of the second point used to determine the gradient bounds. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. +*/ +TVG_API Tvg_Result tvg_linear_gradient_get(Tvg_Gradient* grad, float* x1, float* y1, float* x2, float* y2); + + +/*! +* \brief Sets the radial gradient bounds. +* +* The radial gradient bounds are defined as a circle centered in a given point (@p cx, @p cy) of a given radius. +* +* \param[in] grad The Tvg_Gradient object of which bounds are to be set. +* \param[in] cx The horizontal coordinate of the center of the bounding circle. +* \param[in] cy The vertical coordinate of the center of the bounding circle. +* \param[in] radius The radius of the bounding circle. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer or the @p radius value less than zero. +*/ +TVG_API Tvg_Result tvg_radial_gradient_set(Tvg_Gradient* grad, float cx, float cy, float radius); + + +/*! +* \brief The function gets radial gradient center point ant radius +* +* \param[in] grad The Tvg_Gradient object of which bounds are to be set. +* \param[out] cx The horizontal coordinate of the center of the bounding circle. +* \param[out] cy The vertical coordinate of the center of the bounding circle. +* \param[out] radius The radius of the bounding circle. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. +*/ +TVG_API Tvg_Result tvg_radial_gradient_get(Tvg_Gradient* grad, float* cx, float* cy, float* radius); + + +/*! +* \brief Sets the parameters of the colors of the gradient and their position. +* +* \param[in] grad The Tvg_Gradient object of which the color information is to be set. +* \param[in] color_stop An array of Tvg_Color_Stop data structure. +* \param[in] cnt The size of the @p color_stop array equal to the colors number used in the gradient. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. +*/ +TVG_API Tvg_Result tvg_gradient_set_color_stops(Tvg_Gradient* grad, const Tvg_Color_Stop* color_stop, uint32_t cnt); + + +/*! +* \brief Gets the parameters of the colors of the gradient, their position and number +* +* The function does not allocate any memory. +* +* \param[in] grad The Tvg_Gradient object of which to get the color information. +* \param[out] color_stop An array of Tvg_Color_Stop data structure. +* \param[out] cnt The size of the @p color_stop array equal to the colors number used in the gradient. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +*/ +TVG_API Tvg_Result tvg_gradient_get_color_stops(const Tvg_Gradient* grad, const Tvg_Color_Stop** color_stop, uint32_t* cnt); + + +/*! +* \brief Sets the Tvg_Stroke_Fill value, which specifies how to fill the area outside the gradient bounds. +* +* \param[in] grad The Tvg_Gradient object. +* \param[in] spread The FillSpread value. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. +*/ +TVG_API Tvg_Result tvg_gradient_set_spread(Tvg_Gradient* grad, const Tvg_Stroke_Fill spread); + + +/*! +* \brief Gets the FillSpread value of the gradient object. +* +* \param[in] grad The Tvg_Gradient object. +* \param[out] spread The FillSpread value. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +*/ +TVG_API Tvg_Result tvg_gradient_get_spread(const Tvg_Gradient* grad, Tvg_Stroke_Fill* spread); + + +/*! +* \brief Sets the matrix of the affine transformation for the gradient object. +* +* The augmented matrix of the transformation is expected to be given. +* +* \param[in] grad The Tvg_Gradient object to be transformed. +* \param[in] m The 3x3 augmented matrix. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +*/ +TVG_API Tvg_Result tvg_gradient_set_transform(Tvg_Gradient* grad, const Tvg_Matrix* m); + + +/*! +* \brief Gets the matrix of the affine transformation of the gradient object. +* +* In case no transformation was applied, the identity matrix is set. +* +* \param[in] grad The Tvg_Gradient object of which to get the transformation matrix. +* \param[out] m The 3x3 augmented matrix. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr is passed as the argument. +*/ +TVG_API Tvg_Result tvg_gradient_get_transform(const Tvg_Gradient* grad, Tvg_Matrix* m); + +/** +* \brief Gets the unique id value of the gradient instance indicating the instance type. +* +* \param[in] grad The Tvg_Gradient object of which to get the identifier value. +* \param[out] identifier The unique identifier of the gradient instance type. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. +* +* \since 0.9 +*/ +TVG_API Tvg_Result tvg_gradient_get_identifier(const Tvg_Gradient* grad, Tvg_Identifier* identifier); + + +/*! +* \brief Duplicates the given Tvg_Gradient object. +* +* Creates a new object and sets its all properties as in the original object. +* +* \param[in] grad The Tvg_Gradient object to be copied. +* +* \return A copied Tvg_Gradient object if succeed, @c nullptr otherwise. +*/ +TVG_API Tvg_Gradient* tvg_gradient_duplicate(Tvg_Gradient* grad); + + +/*! +* \brief Deletes the given gradient object. +* +* \param[in] grad The gradient object to be deleted. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer. +*/ +TVG_API Tvg_Result tvg_gradient_del(Tvg_Gradient* grad); + + +/** \} */ // end defgroup ThorVGCapi_Gradient + + +/** +* \defgroup ThorVGCapi_Picture Picture +* +* \brief A module enabling to create and to load an image in one of the supported formats: svg, png, jpg, lottie and raw. +* +* +* \{ +*/ + +/************************************************************************/ +/* Picture API */ +/************************************************************************/ +/*! +* \brief Creates a new picture object. +* +* \return A new picture object. +*/ +TVG_API Tvg_Paint* tvg_picture_new(); + + +/*! +* \brief Loads a picture data directly from a file. +* +* \param[in] paint A Tvg_Paint pointer to the picture object. +* \param[in] path The absolute path to the image file. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer or an empty @p path. +* \retval TVG_RESULT_NOT_SUPPORTED A file with an unknown extension. +* \retval TVG_RESULT_UNKNOWN An error at a later stage. +*/ +TVG_API Tvg_Result tvg_picture_load(Tvg_Paint* paint, const char* path); + + +/*! +* \brief Loads a picture data from a memory block of a given size. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer or no data are provided or the @p width or @p height value is zero or less. +* \retval TVG_RESULT_FAILED_ALLOCATION A problem with memory allocation occurs. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An error occurs at a later stage. +* +* \since 0.9 +*/ +TVG_API Tvg_Result tvg_picture_load_raw(Tvg_Paint* paint, uint32_t *data, uint32_t w, uint32_t h, bool copy); + + +/*! +* \brief Loads a picture data from a memory block of a given size. +* +* \param[in] paint A Tvg_Paint pointer to the picture object. +* \param[in] data A pointer to a memory location where the content of the picture file is stored. +* \param[in] size The size in bytes of the memory occupied by the @p data. +* \param[in] mimetype Mimetype or extension of data such as "jpg", "jpeg", "svg", "svg+xml", "lottie", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one. +* \param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument or the @p size is zero or less. +* \retval TVG_RESULT_NOT_SUPPORTED A file with an unknown extension. +* \retval TVG_RESULT_UNKNOWN An error at a later stage. +* +* \warning: It's the user responsibility to release the @p data memory if the @p copy is @c true. +*/ +TVG_API Tvg_Result tvg_picture_load_data(Tvg_Paint* paint, const char *data, uint32_t size, const char *mimetype, bool copy); + + +/*! +* \brief Resizes the picture content to the given width and height. +* +* The picture content is resized while keeping the default size aspect ratio. +* The scaling factor is established for each of dimensions and the smaller value is applied to both of them. +* +* \param[in] paint A Tvg_Paint pointer to the picture object. +* \param[in] w A new width of the image in pixels. +* \param[in] h A new height of the image in pixels. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION An internal error. +*/ +TVG_API Tvg_Result tvg_picture_set_size(Tvg_Paint* paint, float w, float h); + + +/*! +* \brief Gets the size of the loaded picture. +* +* \param[in] paint A Tvg_Paint pointer to the picture object. +* \param[out] w A width of the image in pixels. +* \param[out] h A height of the image in pixels. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +*/ +TVG_API Tvg_Result tvg_picture_get_size(const Tvg_Paint* paint, float* w, float* h); + + +/** \} */ // end defgroup ThorVGCapi_Picture + + +/** +* \defgroup ThorVGCapi_Scene Scene +* \brief A module managing the multiple paints as one group paint. +* +* As a group, scene can be transformed, translucent, composited with other target paints, +* its children will be affected by the scene world. +* +* \{ +*/ + +/************************************************************************/ +/* Scene API */ +/************************************************************************/ +/*! +* \brief Creates a new scene object. +* +* A scene object is used to group many paints into one object, which can be manipulated using TVG APIs. +* +* \return A new scene object. +*/ +TVG_API Tvg_Paint* tvg_scene_new(); + + +/*! +* \brief Sets the size of the container, where all the paints pushed into the scene are stored. +* +* If the number of objects pushed into the scene is known in advance, calling the function +* prevents multiple memory reallocation, thus improving the performance. +* +* \param[in] scene A Tvg_Paint pointer to the scene object. +* \param[in] size The number of objects for which the memory is to be reserved. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. +*/ +TVG_DEPRECATED TVG_API Tvg_Result tvg_scene_reserve(Tvg_Paint* scene, uint32_t size); + + +/*! +* \brief Passes drawing elements to the scene using Tvg_Paint objects. +* +* Only the paints pushed into the scene will be the drawn targets. +* The paints are retained by the scene until the tvg_scene_clear() is called. +* If you know the number of pushed objects in advance, please call tvg_scene_reserve(). +* +* \param[in] scene A Tvg_Paint pointer to the scene object. +* \param[in] paint A graphical object to be drawn. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +* \retval TVG_RESULT_MEMORY_CORRUPTION An internal error. +* +* \note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. +*/ +TVG_API Tvg_Result tvg_scene_push(Tvg_Paint* scene, Tvg_Paint* paint); + + +/*! +* \brief Clears a Tvg_Scene objects from pushed paints. +* +* Tvg_Paint objects stored in the scene are released if @p free is set to @c true, otherwise the memory is not deallocated and +* all paints should be released manually in order to avoid memory leaks. +* +* \param[in] scene The Tvg_Scene object to be cleared. +* \param[in] free If @c true the memory occupied by paints is deallocated, otherwise it is not. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Canvas pointer. +* +* \warning Please use the @p free argument only when you know how it works, otherwise it's not recommended. +*/ +TVG_API Tvg_Result tvg_scene_clear(Tvg_Paint* scene, bool free); + +/** \} */ // end defgroup ThorVGCapi_Scene + + +/** +* \defgroup ThorVGCapi_Saver Saver +* \brief A module for exporting a paint object into a specified file. +* +* The module enables to save the composed scene and/or image from a paint object. +* Once it's successfully exported to a file, it can be recreated using the Picture module. +* +* \{ +*/ + +/************************************************************************/ +/* Saver API */ +/************************************************************************/ +/*! +* \brief Creates a new Tvg_Saver object. +* +* \return A new Tvg_Saver object. +*/ +TVG_API Tvg_Saver* tvg_saver_new(); + + +/*! +* \brief Exports the given @p paint data to the given @p path +* +* If the saver module supports any compression mechanism, it will optimize the data size. +* This might affect the encoding/decoding time in some cases. You can turn off the compression +* if you wish to optimize for speed. +* +* \param[in] saver The Tvg_Saver object connected with the saving task. +* \param[in] paint The paint to be saved with all its associated properties. +* \param[in] path A path to the file, in which the paint data is to be saved. +* \param[in] compress If @c true then compress data if possible. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION Currently saving other resources. +* \retval TVG_RESULT_NOT_SUPPORTED Trying to save a file with an unknown extension or in an unsupported format. +* \retval TVG_RESULT_MEMORY_CORRUPTION An internal error. +* \retval TVG_RESULT_UNKNOWN An empty paint is to be saved. +* +* \note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call tvg_saver_sync() afterwards. +* \see tvg_saver_sync() +*/ +TVG_API Tvg_Result tvg_saver_save(Tvg_Saver* saver, Tvg_Paint* paint, const char* path, bool compress); + + +/*! +* \brief Guarantees that the saving task is finished. +* +* The behavior of the Saver module works on a sync/async basis, depending on the threading setting of the Initializer. +* Thus, if you wish to have a benefit of it, you must call tvg_saver_sync() after the tvg_saver_save() in the proper delayed time. +* Otherwise, you can call tvg_saver_sync() immediately. +* +* \param[in] saver The Tvg_Saver object connected with the saving task. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT A @c nullptr passed as the argument. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION No saving task is running. +* +* \note The asynchronous tasking is dependent on the Saver module implementation. +* \see tvg_saver_save() +*/ +TVG_API Tvg_Result tvg_saver_sync(Tvg_Saver* saver); + + +/*! +* \brief Deletes the given Tvg_Saver object. +* +* \param[in] saver The Tvg_Saver object to be deleted. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Saver pointer. +*/ +TVG_API Tvg_Result tvg_saver_del(Tvg_Saver* saver); + + +/** \} */ // end defgroup ThorVGCapi_Saver + + +/** +* \defgroup ThorVGCapi_Animation Animation +* \brief A module for manipulation of animatable images. +* +* The module supports the display and control of animation frames. +* +* \{ +*/ + +/************************************************************************/ +/* Animation API */ +/************************************************************************/ + +/*! +* \brief Creates a new Animation object. (BETA_API) +* +* \return Tvg_Animation A new Tvg_Animation object. +*/ +TVG_API Tvg_Animation* tvg_animation_new(); + + +/*! +* \brief Specifies the current frame in the animation. (BETA_API) +* +* \param[in] animation A Tvg_Animation pointer to the animation object. +* \param[in] no The index of the animation frame to be displayed. The index should be less than the tvg_animatio_total_frame(). +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION No animatable data loaded from the Picture. +* \retval TVG_RESULT_NOT_SUPPORTED The picture data does not support animations. +* +* \see tvg_animation_get_total_frame() +*/ +TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation* animation, uint32_t no); + + +/*! +* \brief Retrieves a picture instance associated with this animation instance. (BETA_API) +* +* This function provides access to the picture instance that can be used to load animation formats, such as Lottie(json). +* After setting up the picture, it can be pushed to the designated canvas, enabling control over animation frames +* with this Animation instance. +* +* \param[in] animation A Tvg_Animation pointer to the animation object. +* +* \return A picture instance that is tied to this animation. +* +* \warning The picture instance is owned by Animation. It should not be deleted manually. +*/ +TVG_API Tvg_Paint* tvg_animation_get_picture(Tvg_Animation* animation); + + +/*! +* \brief Retrieves the current frame number of the animation. (BETA_API) +* +* \param[in] animation A Tvg_Animation pointer to the animation object. +* \param[in] no The current frame number of the animation, between 0 and totalFrame() - 1. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer or @p no +* +* \see tvg_animation_get_total_frame() +* \see tvg_animation_set_frame() +*/ +TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, uint32_t* no); + + +/*! +* \brief Retrieves the total number of frames in the animation. (BETA_API) +* +* \param[in] animation A Tvg_Animation pointer to the animation object. +* \param[in] cnt The total number of frames in the animation. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer or @p cnt. +* +* \note Frame numbering starts from 0. +* \note If the Picture is not properly configured, this function will return 0. +*/ +TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation* animation, uint32_t* cnt); + + +/*! +* \brief Retrieves the duration of the animation in seconds. (BETA_API) +* +* \param[in] animation A Tvg_Animation pointer to the animation object. +* \param[in] duration The duration of the animation in seconds. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer or @p duration. +* +* \note If the Picture is not properly configured, this function will return 0. +*/ +TVG_API Tvg_Result tvg_animation_get_duration(Tvg_Animation* animation, float* duration); + + +/*! +* \brief Deletes the given Tvg_Animation object. +* +* \param[in] animation The Tvg_Animation object to be deleted. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_SUCCESS Succeed. +* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. +*/ +TVG_API Tvg_Result tvg_animation_del(Tvg_Animation* animation); + + +/** \} */ // end defgroup ThorVG_CAPI + + +#ifdef __cplusplus +} +#endif + +#endif //_THORVG_CAPI_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgAnimation.cpp b/project/gui/lvgl/src/libs/thorvg/tvgAnimation.cpp new file mode 100644 index 000000000..ae5e8f94e --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgAnimation.cpp @@ -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(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(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(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(loader)->duration(); +} + + +unique_ptr Animation::gen() noexcept +{ + return unique_ptr(new Animation); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgArray.h b/project/gui/lvgl/src/libs/thorvg/tvgArray.h new file mode 100644 index 000000000..41accc13b --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgArray.h @@ -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 +#include + +namespace tvg +{ + +template +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(realloc(data, sizeof(T) * reserved)); + } + data[count++] = element; + } + + void push(Array& 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(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 + void sort() + { + qsort(data, 0, static_cast(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 + 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(arr, low, i - 1); + qsort(arr, i + 1, high); + } + } +}; + +} + +#endif //_TVG_ARRAY_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgBezier.cpp b/project/gui/lvgl/src/libs/thorvg/tvgBezier.cpp new file mode 100644 index 000000000..e00fc1855 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgBezier.cpp @@ -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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgBezier.h b/project/gui/lvgl/src/libs/thorvg/tvgBezier.h new file mode 100644 index 000000000..24f70f336 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgBezier.h @@ -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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgCanvas.cpp b/project/gui/lvgl/src/libs/thorvg/tvgCanvas.cpp new file mode 100644 index 000000000..4da0e5e12 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgCanvas.cpp @@ -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& Canvas::paints() noexcept +{ + return pImpl->paints; +} + + +Result Canvas::push(unique_ptr 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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgCanvas.h b/project/gui/lvgl/src/libs/thorvg/tvgCanvas.h new file mode 100644 index 000000000..852f7db43 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgCanvas.h @@ -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 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) + { + //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 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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgCapi.cpp b/project/gui/lvgl/src/libs/thorvg/tvgCapi.cpp new file mode 100644 index 000000000..e8affb53d --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgCapi.cpp @@ -0,0 +1,784 @@ +/* + * 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 +#include +#include "thorvg_capi.h" + +using namespace std; +using namespace tvg; + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* Engine API */ +/************************************************************************/ + +TVG_API Tvg_Result tvg_engine_init(Tvg_Engine engine_method, unsigned threads) +{ + return (Tvg_Result) Initializer::init(CanvasEngine(engine_method), threads); +} + + +TVG_API Tvg_Result tvg_engine_term(Tvg_Engine engine_method) +{ + return (Tvg_Result) Initializer::term(CanvasEngine(engine_method)); +} + + +/************************************************************************/ +/* Canvas API */ +/************************************************************************/ + +TVG_API Tvg_Canvas* tvg_swcanvas_create() +{ + return (Tvg_Canvas*) SwCanvas::gen().release(); +} + + +TVG_API Tvg_Result tvg_canvas_destroy(Tvg_Canvas* canvas) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + delete(reinterpret_cast(canvas)); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_swcanvas_set_mempool(Tvg_Canvas* canvas, Tvg_Mempool_Policy policy) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->mempool(static_cast(policy)); +} + + +TVG_API Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas* canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Tvg_Colorspace cs) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->target(buffer, stride, w, h, static_cast(cs)); +} + + +TVG_API Tvg_Result tvg_canvas_push(Tvg_Canvas* canvas, Tvg_Paint* paint) +{ + if (!canvas || !paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->push(unique_ptr((Paint*)paint)); +} + + +TVG_API Tvg_Result tvg_canvas_reserve(Tvg_Canvas* canvas, uint32_t n) +{ + return TVG_RESULT_NOT_SUPPORTED; +} + + +TVG_API Tvg_Result tvg_canvas_clear(Tvg_Canvas* canvas, bool free) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->clear(free); +} + + +TVG_API Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->update(nullptr); +} + + +TVG_API Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint) +{ + if (!canvas || !paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->update((Paint*) paint); +} + + +TVG_API Tvg_Result tvg_canvas_draw(Tvg_Canvas* canvas) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->draw(); +} + + +TVG_API Tvg_Result tvg_canvas_sync(Tvg_Canvas* canvas) +{ + if (!canvas) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(canvas)->sync(); +} + + +/************************************************************************/ +/* Paint API */ +/************************************************************************/ + +TVG_API Tvg_Result tvg_paint_del(Tvg_Paint* paint) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + delete(reinterpret_cast(paint)); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_paint_scale(Tvg_Paint* paint, float factor) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->scale(factor); +} + + +TVG_API Tvg_Result tvg_paint_rotate(Tvg_Paint* paint, float degree) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->rotate(degree); +} + + +TVG_API Tvg_Result tvg_paint_translate(Tvg_Paint* paint, float x, float y) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->translate(x, y); +} + + +TVG_API Tvg_Result tvg_paint_set_transform(Tvg_Paint* paint, const Tvg_Matrix* m) +{ + if (!paint || !m) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->transform(*(reinterpret_cast(m))); +} + + +TVG_API Tvg_Result tvg_paint_get_transform(Tvg_Paint* paint, Tvg_Matrix* m) +{ + if (!paint || !m) return TVG_RESULT_INVALID_ARGUMENT; + *reinterpret_cast(m) = reinterpret_cast(paint)->transform(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint) +{ + if (!paint) return nullptr; + return (Tvg_Paint*) reinterpret_cast(paint)->duplicate(); +} + + +TVG_API Tvg_Result tvg_paint_set_opacity(Tvg_Paint* paint, uint8_t opacity) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->opacity(opacity); +} + + +TVG_API Tvg_Result tvg_paint_get_opacity(const Tvg_Paint* paint, uint8_t* opacity) +{ + if (!paint || !opacity) return TVG_RESULT_INVALID_ARGUMENT; + *opacity = reinterpret_cast(paint)->opacity(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* y, float* w, float* h, bool transformed) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->bounds(x, y, w, h, transformed); +} + + +TVG_API Tvg_Result tvg_paint_set_composite_method(Tvg_Paint* paint, Tvg_Paint* target, Tvg_Composite_Method method) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->composite(unique_ptr((Paint*)(target)), (CompositeMethod)method); +} + + +TVG_API Tvg_Result tvg_paint_get_composite_method(const Tvg_Paint* paint, const Tvg_Paint** target, Tvg_Composite_Method* method) +{ + if (!paint || !target || !method) return TVG_RESULT_INVALID_ARGUMENT; + *reinterpret_cast(method) = reinterpret_cast(paint)->composite(reinterpret_cast(target)); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_paint_set_blend_method(const Tvg_Paint* paint, Tvg_Blend_Method method) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->blend((BlendMethod)method); +} + + +TVG_API Tvg_Result tvg_paint_get_blend_method(const Tvg_Paint* paint, Tvg_Blend_Method* method) +{ + if (!paint || !method) return TVG_RESULT_INVALID_ARGUMENT; + *method = static_cast(reinterpret_cast(paint)->blend()); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_paint_get_identifier(const Tvg_Paint* paint, Tvg_Identifier* identifier) +{ + if (!paint || !identifier) return TVG_RESULT_INVALID_ARGUMENT; + *identifier = static_cast(reinterpret_cast(paint)->identifier()); + return TVG_RESULT_SUCCESS; +} + +/************************************************************************/ +/* Shape API */ +/************************************************************************/ + +TVG_API Tvg_Paint* tvg_shape_new() +{ + return (Tvg_Paint*) Shape::gen().release(); +} + + +TVG_API Tvg_Result tvg_shape_reset(Tvg_Paint* paint) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->reset(); +} + + +TVG_API Tvg_Result tvg_shape_move_to(Tvg_Paint* paint, float x, float y) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->moveTo(x, y); +} + + +TVG_API Tvg_Result tvg_shape_line_to(Tvg_Paint* paint, float x, float y) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->lineTo(x, y); +} + + +TVG_API Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1, float cx2, float cy2, float x, float y) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->cubicTo(cx1, cy1, cx2, cy2, x, y); +} + + +TVG_API Tvg_Result tvg_shape_close(Tvg_Paint* paint) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->close(); +} + + +TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->appendRect(x, y, w, h, rx, ry); +} + + +TVG_API Tvg_Result tvg_shape_append_arc(Tvg_Paint* paint, float cx, float cy, float radius, float startAngle, float sweep, uint8_t pie) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->appendArc(cx, cy, radius, startAngle, sweep, pie); +} + + +TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->appendCircle(cx, cy, rx, ry); +} + + +TVG_API Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->appendPath((const PathCommand*)cmds, cmdCnt, (const Point*)pts, ptsCnt); +} + + +TVG_API Tvg_Result tvg_shape_get_path_coords(const Tvg_Paint* paint, const Tvg_Point** pts, uint32_t* cnt) +{ + if (!paint || !pts || !cnt) return TVG_RESULT_INVALID_ARGUMENT; + *cnt = reinterpret_cast(paint)->pathCoords((const Point**)pts); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_get_path_commands(const Tvg_Paint* paint, const Tvg_Path_Command** cmds, uint32_t* cnt) +{ + if (!paint || !cmds || !cnt) return TVG_RESULT_INVALID_ARGUMENT; + *cnt = reinterpret_cast(paint)->pathCommands((const PathCommand**)cmds); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke(width); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_width(const Tvg_Paint* paint, float* width) +{ + if (!paint || !width) return TVG_RESULT_INVALID_ARGUMENT; + *width = reinterpret_cast(paint)->strokeWidth(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke(r, g, b, a); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint* paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->strokeColor(r, g, b, a); +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke(unique_ptr((LinearGradient*)(gradient))); +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke(unique_ptr((RadialGradient*)(gradient))); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint* paint, Tvg_Gradient** gradient) +{ + if (!paint || !gradient) return TVG_RESULT_INVALID_ARGUMENT; + *gradient = (Tvg_Gradient*)(reinterpret_cast(paint)->strokeFill()); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke(dashPattern, cnt); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_dash(const Tvg_Paint* paint, const float** dashPattern, uint32_t* cnt) +{ + if (!paint || !cnt || !dashPattern) return TVG_RESULT_INVALID_ARGUMENT; + *cnt = reinterpret_cast(paint)->strokeDash(dashPattern); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint* paint, Tvg_Stroke_Cap cap) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke((StrokeCap)cap); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_cap(const Tvg_Paint* paint, Tvg_Stroke_Cap* cap) +{ + if (!paint || !cap) return TVG_RESULT_INVALID_ARGUMENT; + *cap = (Tvg_Stroke_Cap) reinterpret_cast(paint)->strokeCap(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join join) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->stroke((StrokeJoin)join); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_join(const Tvg_Paint* paint, Tvg_Stroke_Join* join) +{ + if (!paint || !join) return TVG_RESULT_INVALID_ARGUMENT; + *join = (Tvg_Stroke_Join) reinterpret_cast(paint)->strokeJoin(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_stroke_miterlimit(Tvg_Paint* paint, float ml) +{ + if (ml < 0.0f) return TVG_RESULT_NOT_SUPPORTED; + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->strokeMiterlimit(ml); +} + + +TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint* paint, float* ml) +{ + if (!paint || !ml) return TVG_RESULT_INVALID_ARGUMENT; + *ml = reinterpret_cast(paint)->strokeMiterlimit(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->fill(r, g, b, a); +} + + +TVG_API Tvg_Result tvg_shape_get_fill_color(const Tvg_Paint* paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->fillColor(r, g, b, a); +} + + +TVG_API Tvg_Result tvg_shape_set_fill_rule(Tvg_Paint* paint, Tvg_Fill_Rule rule) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->fill((FillRule)rule); +} + + +TVG_API Tvg_Result tvg_shape_get_fill_rule(const Tvg_Paint* paint, Tvg_Fill_Rule* rule) +{ + if (!paint || !rule) return TVG_RESULT_INVALID_ARGUMENT; + *rule = (Tvg_Fill_Rule) reinterpret_cast(paint)->fillRule(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_shape_set_paint_order(Tvg_Paint* paint, bool strokeFirst) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->order(strokeFirst); +} + + +TVG_API Tvg_Result tvg_shape_set_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->fill(unique_ptr((LinearGradient*)(gradient))); +} + + +TVG_API Tvg_Result tvg_shape_set_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->fill(unique_ptr((RadialGradient*)(gradient))); +} + + +TVG_API Tvg_Result tvg_shape_get_gradient(const Tvg_Paint* paint, Tvg_Gradient** gradient) +{ + if (!paint || !gradient) return TVG_RESULT_INVALID_ARGUMENT; + *gradient = (Tvg_Gradient*)(reinterpret_cast(paint)->fill()); + return TVG_RESULT_SUCCESS; +} + +/************************************************************************/ +/* Picture API */ +/************************************************************************/ + +TVG_API Tvg_Paint* tvg_picture_new() +{ + return (Tvg_Paint*) Picture::gen().release(); +} + + +TVG_API Tvg_Result tvg_picture_load(Tvg_Paint* paint, const char* path) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->load(path); +} + + +TVG_API Tvg_Result tvg_picture_load_raw(Tvg_Paint* paint, uint32_t *data, uint32_t w, uint32_t h, bool copy) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->load(data, w, h, copy); +} + + +TVG_API Tvg_Result tvg_picture_load_data(Tvg_Paint* paint, const char *data, uint32_t size, const char *mimetype, bool copy) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->load(data, size, mimetype ? mimetype : "", copy); +} + + +TVG_API Tvg_Result tvg_picture_set_size(Tvg_Paint* paint, float w, float h) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->size(w, h); +} + + +TVG_API Tvg_Result tvg_picture_get_size(const Tvg_Paint* paint, float* w, float* h) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->size(w, h); +} + + +/************************************************************************/ +/* Gradient API */ +/************************************************************************/ + +TVG_API Tvg_Gradient* tvg_linear_gradient_new() +{ + return (Tvg_Gradient*)LinearGradient::gen().release(); +} + + +TVG_API Tvg_Gradient* tvg_radial_gradient_new() +{ + return (Tvg_Gradient*)RadialGradient::gen().release(); +} + + +TVG_API Tvg_Gradient* tvg_gradient_duplicate(Tvg_Gradient* grad) +{ + if (!grad) return nullptr; + return (Tvg_Gradient*) reinterpret_cast(grad)->duplicate(); +} + + +TVG_API Tvg_Result tvg_gradient_del(Tvg_Gradient* grad) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + delete(reinterpret_cast(grad)); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_linear_gradient_set(Tvg_Gradient* grad, float x1, float y1, float x2, float y2) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->linear(x1, y1, x2, y2); +} + + +TVG_API Tvg_Result tvg_linear_gradient_get(Tvg_Gradient* grad, float* x1, float* y1, float* x2, float* y2) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->linear(x1, y1, x2, y2); +} + + +TVG_API Tvg_Result tvg_radial_gradient_set(Tvg_Gradient* grad, float cx, float cy, float radius) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->radial(cx, cy, radius); +} + + +TVG_API Tvg_Result tvg_radial_gradient_get(Tvg_Gradient* grad, float* cx, float* cy, float* radius) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->radial(cx, cy, radius); +} + + +TVG_API Tvg_Result tvg_gradient_set_color_stops(Tvg_Gradient* grad, const Tvg_Color_Stop* color_stop, uint32_t cnt) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->colorStops(reinterpret_cast(color_stop), cnt); +} + + +TVG_API Tvg_Result tvg_gradient_get_color_stops(const Tvg_Gradient* grad, const Tvg_Color_Stop** color_stop, uint32_t* cnt) +{ + if (!grad || !color_stop || !cnt) return TVG_RESULT_INVALID_ARGUMENT; + *cnt = reinterpret_cast(grad)->colorStops(reinterpret_cast(color_stop)); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_gradient_set_spread(Tvg_Gradient* grad, const Tvg_Stroke_Fill spread) +{ + if (!grad) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->spread((FillSpread)spread); +} + + +TVG_API Tvg_Result tvg_gradient_get_spread(const Tvg_Gradient* grad, Tvg_Stroke_Fill* spread) +{ + if (!grad || !spread) return TVG_RESULT_INVALID_ARGUMENT; + *spread = (Tvg_Stroke_Fill) reinterpret_cast(grad)->spread(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_gradient_set_transform(Tvg_Gradient* grad, const Tvg_Matrix* m) +{ + if (!grad || !m) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(grad)->transform(*(reinterpret_cast(m))); +} + + +TVG_API Tvg_Result tvg_gradient_get_transform(const Tvg_Gradient* grad, Tvg_Matrix* m) +{ + if (!grad || !m) return TVG_RESULT_INVALID_ARGUMENT; + *reinterpret_cast(m) = reinterpret_cast(const_cast(grad))->transform(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_gradient_get_identifier(const Tvg_Gradient* grad, Tvg_Identifier* identifier) +{ + if (!grad || !identifier) return TVG_RESULT_INVALID_ARGUMENT; + *identifier = static_cast(reinterpret_cast(grad)->identifier()); + return TVG_RESULT_SUCCESS; +} + +/************************************************************************/ +/* Scene API */ +/************************************************************************/ + +TVG_API Tvg_Paint* tvg_scene_new() +{ + return (Tvg_Paint*) Scene::gen().release(); +} + + +TVG_API Tvg_Result tvg_scene_reserve(Tvg_Paint* scene, uint32_t size) +{ + return TVG_RESULT_NOT_SUPPORTED; +} + + +TVG_API Tvg_Result tvg_scene_push(Tvg_Paint* scene, Tvg_Paint* paint) +{ + if (!scene || !paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(scene)->push(unique_ptr((Paint*)paint)); +} + + +TVG_API Tvg_Result tvg_scene_clear(Tvg_Paint* scene, bool free) +{ + if (!scene) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(scene)->clear(free); +} + + +/************************************************************************/ +/* Saver API */ +/************************************************************************/ + +TVG_API Tvg_Saver* tvg_saver_new() +{ + return (Tvg_Saver*) Saver::gen().release(); +} + + +TVG_API Tvg_Result tvg_saver_save(Tvg_Saver* saver, Tvg_Paint* paint, const char* path, bool compress) +{ + if (!saver || !paint || !path) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(saver)->save(unique_ptr((Paint*)paint), path, compress); +} + + +TVG_API Tvg_Result tvg_saver_sync(Tvg_Saver* saver) +{ + if (!saver) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(saver)->sync(); +} + + +TVG_API Tvg_Result tvg_saver_del(Tvg_Saver* saver) +{ + if (!saver) return TVG_RESULT_INVALID_ARGUMENT; + delete(reinterpret_cast(saver)); + return TVG_RESULT_SUCCESS; +} + + +/************************************************************************/ +/* Animation API */ +/************************************************************************/ + +TVG_API Tvg_Animation* tvg_animation_new() +{ + return (Tvg_Animation*) Animation::gen().release(); +} + + +TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation* animation, uint32_t no) +{ + return TVG_RESULT_INVALID_ARGUMENT; +// if (!animation) return TVG_RESULT_INVALID_ARGUMENT; +// return (Tvg_Result) reinterpret_cast(animation)->frame(no); +} + + +TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, uint32_t* no) +{ + if (!animation || !no) return TVG_RESULT_INVALID_ARGUMENT; + *no = reinterpret_cast(animation)->curFrame(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation* animation, uint32_t* cnt) +{ + if (!animation || !cnt) return TVG_RESULT_INVALID_ARGUMENT; + *cnt = reinterpret_cast(animation)->totalFrame(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Paint* tvg_animation_get_picture(Tvg_Animation* animation) +{ + if (!animation) return nullptr; + return (Tvg_Paint*) reinterpret_cast(animation)->picture(); +} + + +TVG_API Tvg_Result tvg_animation_get_duration(Tvg_Animation* animation, float* duration) +{ + if (!animation || !duration) return TVG_RESULT_INVALID_ARGUMENT; + *duration = reinterpret_cast(animation)->duration(); + return TVG_RESULT_SUCCESS; +} + + +TVG_API Tvg_Result tvg_animation_del(Tvg_Animation* animation) +{ + if (!animation) return TVG_RESULT_INVALID_ARGUMENT; + delete(reinterpret_cast(animation)); + return TVG_RESULT_SUCCESS; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgCommon.h b/project/gui/lvgl/src/libs/thorvg/tvgCommon.h new file mode 100644 index 000000000..d70681170 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgCommon.h @@ -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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgCompressor.cpp b/project/gui/lvgl/src/libs/thorvg/tvgCompressor.cpp new file mode 100644 index 000000000..20941712f --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgCompressor.cpp @@ -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 +#include +#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(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(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(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(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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgCompressor.h b/project/gui/lvgl/src/libs/thorvg/tvgCompressor.h new file mode 100644 index 000000000..6d9dfce1b --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgCompressor.h @@ -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 + +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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgFill.cpp b/project/gui/lvgl/src/libs/thorvg/tvgFill.cpp new file mode 100644 index 000000000..152883445 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgFill.cpp @@ -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(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(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(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::gen() noexcept +{ + return unique_ptr(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(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::gen() noexcept +{ + return unique_ptr(new LinearGradient); +} + + +uint32_t LinearGradient::identifier() noexcept +{ + return TVG_CLASS_ID_LINEAR; +} + + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgFill.h b/project/gui/lvgl/src/libs/thorvg/tvgFill.h new file mode 100644 index 000000000..159540398 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgFill.h @@ -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 +#include +#include "tvgCommon.h" + +template +struct DuplicateMethod +{ + virtual ~DuplicateMethod() {} + virtual T* duplicate() = 0; +}; + +template +struct FillDup : DuplicateMethod +{ + 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* dup = nullptr; + uint8_t id; + + ~Impl() + { + delete(dup); + free(colorStops); + free(transform); + } + + void method(DuplicateMethod* 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(malloc(sizeof(ColorStop) * cnt)); + memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt); + if (transform) { + ret->pImpl->transform = static_cast(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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgFrameModule.h b/project/gui/lvgl/src/libs/thorvg/tvgFrameModule.h new file mode 100644 index 000000000..726b84c84 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgFrameModule.h @@ -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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgInitializer.cpp b/project/gui/lvgl/src/libs/thorvg/tvgInitializer.cpp new file mode 100644 index 000000000..779d97bcd --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgInitializer.cpp @@ -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 +#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(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(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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgIteratorAccessor.h b/project/gui/lvgl/src/libs/thorvg/tvgIteratorAccessor.h new file mode 100644 index 000000000..ac7015fe4 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgIteratorAccessor.h @@ -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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgLoadModule.h b/project/gui/lvgl/src/libs/thorvg/tvgLoadModule.h new file mode 100644 index 000000000..7ccf94850 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgLoadModule.h @@ -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 bitmap() { return nullptr; } + virtual unique_ptr paint() { return nullptr; } +}; + +} + +#endif //_TVG_LOAD_MODULE_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgLoader.cpp b/project/gui/lvgl/src/libs/thorvg/tvgLoader.cpp new file mode 100644 index 000000000..3a2cb32a4 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgLoader.cpp @@ -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 LoaderMgr::loader(const string& path, bool* invalid) +{ + *invalid = false; + + if (auto loader = _findByPath(path)) { + if (loader->open(path)) return shared_ptr(loader); + else delete(loader); + *invalid = true; + } + return nullptr; +} + + +shared_ptr 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(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(FileType::Unknown); i++) { + auto loader = _find(static_cast(i)); + if (loader) { + if (loader->open(data, size, copy)) return shared_ptr(loader); + else delete(loader); + } + } + } + return nullptr; +} + + +shared_ptr 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(loader); + else delete(loader); + + return nullptr; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgLoader.h b/project/gui/lvgl/src/libs/thorvg/tvgLoader.h new file mode 100644 index 000000000..7acdc943a --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgLoader.h @@ -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 loader(const string& path, bool* invalid); + static shared_ptr loader(const char* data, uint32_t size, const string& mimeType, bool copy); + static shared_ptr loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy); +}; + +#endif //_TVG_LOADER_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgMath.cpp b/project/gui/lvgl/src/libs/thorvg/tvgMath.cpp new file mode 100644 index 000000000..cf43490df --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgMath.cpp @@ -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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgMath.h b/project/gui/lvgl/src/libs/thorvg/tvgMath.h new file mode 100644 index 000000000..3f48e3cac --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgMath.h @@ -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 +#include +#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 +static inline T mathLerp(const T &start, const T &end, float t) +{ + return static_cast(start + (end - start) * t); +} + + +#endif //_TVG_MATH_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgPaint.cpp b/project/gui/lvgl/src/libs/thorvg/tvgPaint.cpp new file mode 100644 index 000000000..50ba3e6cc --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgPaint.cpp @@ -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(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(v1.x); + viewport.y = static_cast(v1.y); + viewport.w = static_cast(ceil(v2.x - viewport.x)); + viewport.h = static_cast(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& 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(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(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 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 */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgPaint.h b/project/gui/lvgl/src/libs/thorvg/tvgPaint.h new file mode 100644 index 000000000..ea5878d85 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgPaint.h @@ -0,0 +1,222 @@ +/* + * 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_PAINT_H_ +#define _TVG_PAINT_H_ + +#include "tvgRender.h" +#include "tvgMath.h" + +namespace tvg +{ + enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1}; + + struct Iterator + { + virtual ~Iterator() {} + virtual const Paint* next() = 0; + virtual uint32_t count() = 0; + virtual void begin() = 0; + }; + + struct StrategyMethod + { + virtual ~StrategyMethod() {} + + virtual bool dispose(RenderMethod& renderer) = 0; //return true if the deletion is allowed. + virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has. + virtual bool render(RenderMethod& renderer) = 0; + virtual bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0; + virtual RenderRegion bounds(RenderMethod& renderer) const = 0; + virtual Paint* duplicate() = 0; + virtual Iterator* iterator() = 0; + }; + + struct Composite + { + Paint* target; + Paint* source; + CompositeMethod method; + }; + + struct Paint::Impl + { + StrategyMethod* smethod = nullptr; + RenderTransform* rTransform = nullptr; + Composite* compData = nullptr; + BlendMethod blendMethod = BlendMethod::Normal; //uint8_t + uint8_t renderFlag = RenderUpdateFlag::None; + uint8_t ctxFlag = ContextFlag::Invalid; + uint8_t id; + uint8_t opacity = 255; + uint8_t refCnt = 0; + + ~Impl() + { + if (compData) { + delete(compData->target); + free(compData); + } + delete(smethod); + delete(rTransform); + } + + uint8_t ref() + { + if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!"); + return (++refCnt); + } + + uint8_t unref() + { + if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!"); + return (--refCnt); + } + + void method(StrategyMethod* method) + { + smethod = method; + } + + bool transform(const Matrix& m) + { + if (!rTransform) { + if (mathIdentity(&m)) return true; + rTransform = new RenderTransform(); + if (!rTransform) return false; + } + rTransform->override(m); + renderFlag |= RenderUpdateFlag::Transform; + + return true; + } + + Matrix* transform() + { + if (rTransform) { + rTransform->update(); + return &rTransform->m; + } + return nullptr; + } + + RenderRegion bounds(RenderMethod& renderer) const + { + return smethod->bounds(renderer); + } + + bool dispose(RenderMethod& renderer) + { + if (compData) compData->target->pImpl->dispose(renderer); + return smethod->dispose(renderer); + } + + Iterator* iterator() + { + return smethod->iterator(); + } + + bool composite(Paint* source, Paint* target, CompositeMethod method) + { + //Invalid case + if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false; + + if (compData) { + delete(compData->target); + //Reset scenario + if (!target && method == CompositeMethod::None) { + free(compData); + compData = nullptr; + return true; + } + } else { + if (!target && method == CompositeMethod::None) return true; + compData = static_cast(calloc(1, sizeof(Composite))); + } + compData->target = target; + compData->source = source; + compData->method = method; + return true; + } + + bool rotate(float degree); + bool scale(float factor); + bool translate(float x, float y); + bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); + RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); + bool render(RenderMethod& renderer); + Paint* duplicate(); + }; + + + template + struct PaintMethod : StrategyMethod + { + T* inst = nullptr; + + PaintMethod(T* _inst) : inst(_inst) {} + ~PaintMethod() {} + + bool bounds(float* x, float* y, float* w, float* h, bool stroking) override + { + return inst->bounds(x, y, w, h, stroking); + } + + RenderRegion bounds(RenderMethod& renderer) const override + { + return inst->bounds(renderer); + } + + bool dispose(RenderMethod& renderer) override + { + return inst->dispose(renderer); + } + + RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag renderFlag, bool clipper) override + { + return inst->update(renderer, transform, clips, opacity, renderFlag, clipper); + } + + bool render(RenderMethod& renderer) override + { + return inst->render(renderer); + } + + Paint* duplicate() override + { + return inst->duplicate(); + } + + Iterator* iterator() override + { + return inst->iterator(); + } + }; +} + +#endif //_TVG_PAINT_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgPicture.cpp b/project/gui/lvgl/src/libs/thorvg/tvgPicture.cpp new file mode 100644 index 000000000..e58d68ea3 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgPicture.cpp @@ -0,0 +1,153 @@ +/* + * 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 "tvgPicture.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +RenderUpdateFlag Picture::Impl::load() +{ + if (loader) { + if (!paint) { + if (auto p = loader->paint()) { + paint = p.release(); + loader->close(); + if (w != loader->w || h != loader->h) { + if (!resizing) { + w = loader->w; + h = loader->h; + } + loader->resize(paint, w, h); + resizing = false; + } + if (paint) return RenderUpdateFlag::None; + } + } else loader->sync(); + + if (!surface) { + if ((surface = loader->bitmap().release())) { + loader->close(); + return RenderUpdateFlag::Image; + } + } + } + return RenderUpdateFlag::None; +} + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Picture::Picture() : pImpl(new Impl(this)) +{ + Paint::pImpl->id = TVG_CLASS_ID_PICTURE; + Paint::pImpl->method(new PaintMethod(pImpl)); +} + + +Picture::~Picture() +{ + delete(pImpl); +} + + +unique_ptr Picture::gen() noexcept +{ + return unique_ptr(new Picture); +} + + +uint32_t Picture::identifier() noexcept +{ + return TVG_CLASS_ID_PICTURE; +} + + +Result Picture::load(const std::string& path) noexcept +{ + if (path.empty()) return Result::InvalidArguments; + + return pImpl->load(path); +} + + +Result Picture::load(const char* data, uint32_t size, const string& mimeType, bool copy) noexcept +{ + if (!data || size <= 0) return Result::InvalidArguments; + + return pImpl->load(data, size, mimeType, copy); +} + + +TVG_DEPRECATED Result Picture::load(const char* data, uint32_t size, bool copy) noexcept +{ + return load(data, size, "", copy); +} + + +Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept +{ + if (!data || w <= 0 || h <= 0) return Result::InvalidArguments; + + return pImpl->load(data, w, h, copy); +} + + +Result Picture::size(float w, float h) noexcept +{ + if (pImpl->size(w, h)) return Result::Success; + return Result::InsufficientCondition; +} + + +Result Picture::size(float* w, float* h) const noexcept +{ + if (!pImpl->loader) return Result::InsufficientCondition; + if (w) *w = pImpl->w; + if (h) *h = pImpl->h; + return Result::Success; +} + + +Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept +{ + if (!triangles && triangleCnt > 0) return Result::InvalidArguments; + if (triangles && triangleCnt == 0) return Result::InvalidArguments; + + pImpl->mesh(triangles, triangleCnt); + return Result::Success; +} + + +uint32_t Picture::mesh(const Polygon** triangles) const noexcept +{ + if (triangles) *triangles = pImpl->rm.triangles; + return pImpl->rm.triangleCnt; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgPicture.h b/project/gui/lvgl/src/libs/thorvg/tvgPicture.h new file mode 100644 index 000000000..a9a52a98f --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgPicture.h @@ -0,0 +1,317 @@ +/* + * 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_PICTURE_IMPL_H_ +#define _TVG_PICTURE_IMPL_H_ + +#include +#include "tvgPaint.h" +#include "tvgLoader.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct PictureIterator : Iterator +{ + Paint* paint = nullptr; + Paint* ptr = nullptr; + + PictureIterator(Paint* p) : paint(p) {} + + const Paint* next() override + { + if (!ptr) ptr = paint; + else ptr = nullptr; + return ptr; + } + + uint32_t count() override + { + if (paint) return 1; + else return 0; + } + + void begin() override + { + ptr = nullptr; + } +}; + + +struct Picture::Impl +{ + shared_ptr loader = nullptr; + + Paint* paint = nullptr; //vector picture uses + Surface* surface = nullptr; //bitmap picture uses + RenderData rd = nullptr; //engine data + float w = 0, h = 0; + RenderMesh rm; //mesh data + Picture* picture = nullptr; + bool resizing = false; + bool needComp = false; //need composition + + Impl(Picture* p) : picture(p) + { + } + + ~Impl() + { + delete(paint); + delete(surface); + } + + bool dispose(RenderMethod& renderer) + { + if (paint) paint->pImpl->dispose(renderer); + else if (surface) renderer.dispose(rd); + rd = nullptr; + return true; + } + + RenderTransform resizeTransform(const RenderTransform* pTransform) + { + //Overriding Transformation by the desired image size + auto sx = w / loader->w; + auto sy = h / loader->h; + auto scale = sx < sy ? sx : sy; + + RenderTransform tmp; + tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1}; + + if (!pTransform) return tmp; + else return RenderTransform(pTransform, &tmp); + } + + bool needComposition(uint8_t opacity) + { + //In this case, paint(scene) would try composition itself. + if (opacity < 255) return false; + + //Composition test + const Paint* target; + auto method = picture->composite(&target); + if (!target || method == tvg::CompositeMethod::ClipPath) return false; + if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; + + return true; + } + + RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + { + auto flag = load(); + + if (surface) { + auto transform = resizeTransform(pTransform); + rd = renderer.prepare(surface, &rm, rd, &transform, clips, opacity, static_cast(pFlag | flag)); + } else if (paint) { + if (resizing) { + loader->resize(paint, w, h); + resizing = false; + } + needComp = needComposition(opacity) ? true : false; + rd = paint->pImpl->update(renderer, pTransform, clips, opacity, static_cast(pFlag | flag), clipper); + } + return rd; + } + + bool render(RenderMethod &renderer) + { + bool ret = false; + if (surface) return renderer.renderImage(rd); + else if (paint) { + Compositor* cmp = nullptr; + if (needComp) { + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); + renderer.beginComposite(cmp, CompositeMethod::None, 255); + } + ret = paint->pImpl->render(renderer); + if (cmp) renderer.endComposite(cmp); + } + return ret; + } + + bool size(float w, float h) + { + this->w = w; + this->h = h; + resizing = true; + return true; + } + + bool bounds(float* x, float* y, float* w, float* h, bool stroking) + { + if (rm.triangleCnt > 0) { + auto triangles = rm.triangles; + auto min = triangles[0].vertex[0].pt; + auto max = triangles[0].vertex[0].pt; + + for (uint32_t i = 0; i < rm.triangleCnt; ++i) { + if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; + else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; + if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; + else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y; + + if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x; + else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x; + if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y; + else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y; + + if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x; + else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x; + if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; + else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; + } + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + } else { + if (x) *x = 0; + if (y) *y = 0; + if (w) *w = this->w; + if (h) *h = this->h; + } + return true; + } + + RenderRegion bounds(RenderMethod& renderer) + { + if (rd) return renderer.region(rd); + if (paint) return paint->pImpl->bounds(renderer); + return {0, 0, 0, 0}; + } + + Result load(const string& path) + { + if (paint || surface) return Result::InsufficientCondition; + if (loader) loader->close(); + bool invalid; //Invalid Path + loader = LoaderMgr::loader(path, &invalid); + if (!loader) { + if (invalid) return Result::InvalidArguments; + return Result::NonSupport; + } + if (!loader->read()) return Result::Unknown; + w = loader->w; + h = loader->h; + return Result::Success; + } + + Result load(const char* data, uint32_t size, const string& mimeType, bool copy) + { + if (paint || surface) return Result::InsufficientCondition; + if (loader) loader->close(); + loader = LoaderMgr::loader(data, size, mimeType, copy); + if (!loader) return Result::NonSupport; + if (!loader->read()) return Result::Unknown; + w = loader->w; + h = loader->h; + return Result::Success; + } + + Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) + { + if (paint || surface) return Result::InsufficientCondition; + if (loader) loader->close(); + loader = LoaderMgr::loader(data, w, h, copy); + if (!loader) return Result::FailedAllocation; + this->w = loader->w; + this->h = loader->h; + return Result::Success; + } + + void mesh(const Polygon* triangles, const uint32_t triangleCnt) + { + if (triangles && triangleCnt > 0) { + this->rm.triangleCnt = triangleCnt; + this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt); + memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt); + } else { + free(this->rm.triangles); + this->rm.triangles = nullptr; + this->rm.triangleCnt = 0; + } + } + + Paint* duplicate() + { + load(); + + auto ret = Picture::gen(); + + auto dup = ret.get()->pImpl; + if (paint) dup->paint = paint->duplicate(); + + dup->loader = loader; + if (surface) { + dup->surface = new Surface; + *dup->surface = *surface; + //TODO: A dupilcation is not a proxy... it needs copy of the pixel data? + dup->surface->owner = false; + } + dup->w = w; + dup->h = h; + dup->resizing = resizing; + + if (rm.triangleCnt > 0) { + dup->rm.triangleCnt = rm.triangleCnt; + dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt); + memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt); + } + + return ret.release(); + } + + Iterator* iterator() + { + load(); + return new PictureIterator(paint); + } + + uint32_t* data(uint32_t* w, uint32_t* h) + { + //Try it, If not loaded yet. + load(); + + if (loader) { + if (w) *w = static_cast(loader->w); + if (h) *h = static_cast(loader->h); + } else { + if (w) *w = 0; + if (h) *h = 0; + } + if (surface) return surface->buf32; + else return nullptr; + } + + RenderUpdateFlag load(); +}; + +#endif //_TVG_PICTURE_IMPL_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgRawLoader.cpp b/project/gui/lvgl/src/libs/thorvg/tvgRawLoader.cpp new file mode 100644 index 000000000..b20c7fda8 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgRawLoader.cpp @@ -0,0 +1,101 @@ +/* + * 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 +#include +#include "tvgLoader.h" +#include "tvgRawLoader.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +RawLoader::~RawLoader() +{ + if (copy && content) { + free((void*)content); + content = nullptr; + } +} + + +bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) +{ + if (!data || w == 0 || h == 0) return false; + + this->w = (float)w; + this->h = (float)h; + this->copy = copy; + + if (copy) { + content = (uint32_t*)malloc(sizeof(uint32_t) * w * h); + if (!content) return false; + memcpy((void*)content, data, sizeof(uint32_t) * w * h); + } + else content = const_cast(data); + + cs = ColorSpace::ARGB8888; + + return true; +} + + +bool RawLoader::read() +{ + return true; +} + + +bool RawLoader::close() +{ + return true; +} + + +unique_ptr RawLoader::bitmap() +{ + if (!content) return nullptr; + + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; + surface->buf32 = content; + surface->stride = static_cast(w); + surface->w = static_cast(w); + surface->h = static_cast(h); + surface->cs = cs; + surface->channelSize = sizeof(uint32_t); + surface->premultiplied = false; + surface->owner = true; + + return unique_ptr(surface); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgRawLoader.h b/project/gui/lvgl/src/libs/thorvg/tvgRawLoader.h new file mode 100644 index 000000000..bc3851f55 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgRawLoader.h @@ -0,0 +1,49 @@ +/* + * 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_RAW_LOADER_H_ +#define _TVG_RAW_LOADER_H_ + +class RawLoader : public LoadModule +{ +public: + uint32_t* content = nullptr; + bool copy = false; + + ~RawLoader(); + + using LoadModule::open; + bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) override; + bool read() override; + bool close() override; + + unique_ptr bitmap() override; +}; + + +#endif //_TVG_RAW_LOADER_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgRender.cpp b/project/gui/lvgl/src/libs/thorvg/tvgRender.cpp new file mode 100644 index 000000000..9994c8cbb --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgRender.cpp @@ -0,0 +1,75 @@ +/* + * 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 "tvgRender.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +void RenderTransform::override(const Matrix& m) +{ + this->m = m; + overriding = true; +} + + +bool RenderTransform::update() +{ + if (overriding) return true; + + //Init Status + if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false; + + mathIdentity(&m); + + mathScale(&m, scale, scale); + + if (!mathZero(degree)) mathRotate(&m, degree); + + mathTranslate(&m, x, y); + + return true; +} + + +RenderTransform::RenderTransform() +{ +} + + +RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs) +{ + m = mathMultiply(&lhs->m, &rhs->m); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgRender.h b/project/gui/lvgl/src/libs/thorvg/tvgRender.h new file mode 100644 index 000000000..d95c37cc0 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgRender.h @@ -0,0 +1,342 @@ +/* + * 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_RENDER_H_ +#define _TVG_RENDER_H_ + +#include "tvgCommon.h" +#include "tvgArray.h" + +namespace tvg +{ + +using RenderData = void*; +using pixel_t = uint32_t; + +enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255}; + +struct Surface; + +enum ColorSpace +{ + ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. + ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. + ABGR8888S, //The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. + ARGB8888S, //The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. + Grayscale8, //One single channel data. + Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace. +}; + +struct Surface +{ + union { + pixel_t* data; //system based data pointer + uint32_t* buf32; //for explicit 32bits channels + uint8_t* buf8; //for explicit 8bits grayscale + }; + uint32_t stride; + uint32_t w, h; + ColorSpace cs; + uint8_t channelSize; + + bool premultiplied; //Alpha-premultiplied + bool owner; //Only owner could modify the buffer +}; + +struct Compositor +{ + CompositeMethod method; + uint8_t opacity; +}; + +struct RenderMesh +{ + Polygon* triangles = nullptr; + uint32_t triangleCnt = 0; + + ~RenderMesh() + { + free(triangles); + } +}; + +struct RenderRegion +{ + int32_t x, y, w, h; + + void intersect(const RenderRegion& rhs) + { + auto x1 = x + w; + auto y1 = y + h; + auto x2 = rhs.x + rhs.w; + auto y2 = rhs.y + rhs.h; + + x = (x > rhs.x) ? x : rhs.x; + y = (y > rhs.y) ? y : rhs.y; + w = ((x1 < x2) ? x1 : x2) - x; + h = ((y1 < y2) ? y1 : y2) - y; + + if (w < 0) w = 0; + if (h < 0) h = 0; + } + + void add(const RenderRegion& rhs) + { + if (rhs.x < x) { + w += (x - rhs.x); + x = rhs.x; + } + if (rhs.y < y) { + h += (y - rhs.y); + y = rhs.y; + } + if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x; + if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y; + } +}; + +struct RenderTransform +{ + Matrix m; //3x3 Matrix Elements + float x = 0.0f; + float y = 0.0f; + float degree = 0.0f; //rotation degree + float scale = 1.0f; //scale factor + bool overriding = false; //user transform? + + bool update(); + void override(const Matrix& m); + + RenderTransform(); + RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs); +}; + +struct RenderStroke +{ + float width = 0.0f; + uint8_t color[4] = {0, 0, 0, 0}; + Fill *fill = nullptr; + float* dashPattern = nullptr; + uint32_t dashCnt = 0; + float dashOffset = 0.0f; + StrokeCap cap = StrokeCap::Square; + StrokeJoin join = StrokeJoin::Bevel; + float miterlimit = 4.0f; + bool strokeFirst = false; + + struct { + float begin = 0.0f; + float end = 1.0f; + } trim; + + ~RenderStroke() + { + free(dashPattern); + delete(fill); + } +}; + +struct RenderShape +{ + struct + { + Array cmds; + Array pts; + } path; + + Fill *fill = nullptr; + RenderStroke *stroke = nullptr; + uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a + FillRule rule = FillRule::Winding; + + ~RenderShape() + { + delete(fill); + delete(stroke); + } + + void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const + { + if (r) *r = color[0]; + if (g) *g = color[1]; + if (b) *b = color[2]; + if (a) *a = color[3]; + } + + float strokeWidth() const + { + if (!stroke) return 0; + return stroke->width; + } + + bool strokeTrim() const + { + if (!stroke) return false; + if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false; + if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false; + return true; + } + + bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const + { + if (!stroke) return false; + + if (r) *r = stroke->color[0]; + if (g) *g = stroke->color[1]; + if (b) *b = stroke->color[2]; + if (a) *a = stroke->color[3]; + + return true; + } + + const Fill* strokeFill() const + { + if (!stroke) return nullptr; + return stroke->fill; + } + + uint32_t strokeDash(const float** dashPattern, float* offset) const + { + if (!stroke) return 0; + if (dashPattern) *dashPattern = stroke->dashPattern; + if (offset) *offset = stroke->dashOffset; + return stroke->dashCnt; + } + + StrokeCap strokeCap() const + { + if (!stroke) return StrokeCap::Square; + return stroke->cap; + } + + StrokeJoin strokeJoin() const + { + if (!stroke) return StrokeJoin::Bevel; + return stroke->join; + } + + float strokeMiterlimit() const + { + if (!stroke) return 4.0f; + + return stroke->miterlimit;; + } +}; + +class RenderMethod +{ +public: + virtual ~RenderMethod() {} + virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; + virtual RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; + virtual bool preRender() = 0; + virtual bool renderShape(RenderData data) = 0; + virtual bool renderImage(RenderData data) = 0; + virtual bool postRender() = 0; + virtual bool dispose(RenderData data) = 0; + virtual RenderRegion region(RenderData data) = 0; + virtual RenderRegion viewport() = 0; + virtual bool viewport(const RenderRegion& vp) = 0; + virtual bool blend(BlendMethod method) = 0; + virtual ColorSpace colorSpace() = 0; + + virtual bool clear() = 0; + virtual bool sync() = 0; + + virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0; + virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0; + virtual bool endComposite(Compositor* cmp) = 0; +}; + +static inline bool MASK_REGION_MERGING(CompositeMethod method) +{ + switch(method) { + case CompositeMethod::AlphaMask: + case CompositeMethod::InvAlphaMask: + case CompositeMethod::LumaMask: + case CompositeMethod::InvLumaMask: + case CompositeMethod::SubtractMask: + case CompositeMethod::IntersectMask: + return false; + //these might expand the rendering region + case CompositeMethod::AddMask: + case CompositeMethod::DifferenceMask: + return true; + default: + TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method); + return false; + } +} + +static inline uint8_t CHANNEL_SIZE(ColorSpace cs) +{ + switch(cs) { + case ColorSpace::ABGR8888: + case ColorSpace::ABGR8888S: + case ColorSpace::ARGB8888: + case ColorSpace::ARGB8888S: + return sizeof(uint32_t); + case ColorSpace::Grayscale8: + return sizeof(uint8_t); + case ColorSpace::Unsupported: + default: + TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs); + return 0; + } +} + +static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, CompositeMethod method) +{ + switch(method) { + case CompositeMethod::AlphaMask: + case CompositeMethod::InvAlphaMask: + case CompositeMethod::AddMask: + case CompositeMethod::DifferenceMask: + case CompositeMethod::SubtractMask: + case CompositeMethod::IntersectMask: + return ColorSpace::Grayscale8; + //TODO: Optimize Luma/InvLuma colorspace to Grayscale8 + case CompositeMethod::LumaMask: + case CompositeMethod::InvLumaMask: + return renderer.colorSpace(); + default: + TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method); + return ColorSpace::Unsupported; + } +} + +static inline uint8_t MULTIPLY(uint8_t c, uint8_t a) +{ + return (((c) * (a) + 0xff) >> 8); +} + + +} + +#endif //_TVG_RENDER_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSaveModule.h b/project/gui/lvgl/src/libs/thorvg/tvgSaveModule.h new file mode 100644 index 000000000..cff1a81db --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSaveModule.h @@ -0,0 +1,48 @@ +/* + * 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_SAVE_MODULE_H_ +#define _TVG_SAVE_MODULE_H_ + +#include "tvgIteratorAccessor.h" + +namespace tvg +{ + +class SaveModule +{ +public: + virtual ~SaveModule() {} + + virtual bool save(Paint* paint, const string& path, bool compress) = 0; + virtual bool close() = 0; +}; + +} + +#endif //_TVG_SAVE_MODULE_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSaver.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSaver.cpp new file mode 100644 index 000000000..4fd9848e3 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSaver.cpp @@ -0,0 +1,147 @@ +/* + * 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 "tvgCommon.h" +#include "tvgSaveModule.h" + +#ifdef THORVG_TVG_SAVER_SUPPORT + #include "tvgTvgSaver.h" +#endif + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct Saver::Impl +{ + SaveModule* saveModule = nullptr; + ~Impl() + { + delete(saveModule); + } +}; + + +static SaveModule* _find(FileType type) +{ + switch(type) { + case FileType::Tvg: { +#ifdef THORVG_TVG_SAVER_SUPPORT + return new TvgSaver; +#endif + break; + } + default: { + break; + } + } + +#ifdef THORVG_LOG_ENABLED + const char *format; + switch(type) { + case FileType::Tvg: { + format = "TVG"; + break; + } + default: { + format = "???"; + break; + } + } + TVGLOG("SAVER", "%s format is not supported", format); +#endif + return nullptr; +} + + +static SaveModule* _find(const string& path) +{ + auto ext = path.substr(path.find_last_of(".") + 1); + if (!ext.compare("tvg")) { + return _find(FileType::Tvg); + } + return nullptr; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Saver::Saver() : pImpl(new Impl()) +{ +} + + +Saver::~Saver() +{ + delete(pImpl); +} + + +Result Saver::save(std::unique_ptr paint, const string& path, bool compress) noexcept +{ + auto p = paint.release(); + if (!p) return Result::MemoryCorruption; + + //Already on saving an other resource. + if (pImpl->saveModule) { + delete(p); + return Result::InsufficientCondition; + } + + if (auto saveModule = _find(path)) { + if (saveModule->save(p, path, compress)) { + pImpl->saveModule = saveModule; + return Result::Success; + } else { + delete(p); + delete(saveModule); + return Result::Unknown; + } + } + delete(p); + return Result::NonSupport; +} + + +Result Saver::sync() noexcept +{ + if (!pImpl->saveModule) return Result::InsufficientCondition; + pImpl->saveModule->close(); + delete(pImpl->saveModule); + pImpl->saveModule = nullptr; + + return Result::Success; +} + + +unique_ptr Saver::gen() noexcept +{ + return unique_ptr(new Saver); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgScene.cpp b/project/gui/lvgl/src/libs/thorvg/tvgScene.cpp new file mode 100644 index 000000000..122cd5354 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgScene.cpp @@ -0,0 +1,88 @@ +/* + * 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 "tvgScene.h" + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Scene::Scene() : pImpl(new Impl(this)) +{ + Paint::pImpl->id = TVG_CLASS_ID_SCENE; + Paint::pImpl->method(new PaintMethod(pImpl)); +} + + +Scene::~Scene() +{ + delete(pImpl); +} + + +unique_ptr Scene::gen() noexcept +{ + return unique_ptr(new Scene); +} + + +uint32_t Scene::identifier() noexcept +{ + return TVG_CLASS_ID_SCENE; +} + + +Result Scene::push(unique_ptr paint) noexcept +{ + auto p = paint.release(); + if (!p) return Result::MemoryCorruption; + PP(p)->ref(); + pImpl->paints.push_back(p); + + return Result::Success; +} + + +Result Scene::reserve(TVG_UNUSED uint32_t size) noexcept +{ + return Result::NonSupport; +} + + +Result Scene::clear(bool free) noexcept +{ + pImpl->clear(free); + + return Result::Success; +} + + +list& Scene::paints() noexcept +{ + return pImpl->paints; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgScene.h b/project/gui/lvgl/src/libs/thorvg/tvgScene.h new file mode 100644 index 000000000..f626e993e --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgScene.h @@ -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 + +#ifndef _TVG_SCENE_IMPL_H_ +#define _TVG_SCENE_IMPL_H_ + +#include +#include "tvgPaint.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct SceneIterator : Iterator +{ + list* paints; + list::iterator itr; + + SceneIterator(list* p) : paints(p) + { + begin(); + } + + const Paint* next() override + { + if (itr == paints->end()) return nullptr; + auto paint = *itr; + ++itr; + return paint; + } + + uint32_t count() override + { + return paints->size(); + } + + void begin() override + { + itr = paints->begin(); + } +}; + +struct Scene::Impl +{ + list paints; + RenderMethod* renderer = nullptr; //keep it for explicit clear + RenderData rd = nullptr; + Scene* scene = nullptr; + uint8_t opacity; //for composition + bool needComp; //composite or not + + Impl(Scene* s) : scene(s) + { + } + + ~Impl() + { + for (auto paint : paints) { + if (paint->pImpl->unref() == 0) delete(paint); + } + } + + bool dispose(RenderMethod& renderer) + { + for (auto paint : paints) { + paint->pImpl->dispose(renderer); + } + + renderer.dispose(rd); + this->renderer = nullptr; + this->rd = nullptr; + + return true; + } + + bool needComposition(uint8_t opacity) + { + if (opacity == 0 || paints.empty()) return false; + + //Masking may require composition (even if opacity == 255) + auto compMethod = scene->composite(nullptr); + if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true; + + //Blending may require composition (even if opacity == 255) + if (scene->blend() != BlendMethod::Normal) return true; + + //Half translucent requires intermediate composition. + if (opacity == 255) return false; + + //If scene has several children or only scene, it may require composition. + //OPTIMIZE: the bitmap type of the picture would not need the composition. + //OPTIMIZE: a single paint of a scene would not need the composition. + if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false; + + return true; + } + + RenderData update(RenderMethod &renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) + { + if ((needComp = needComposition(opacity))) { + /* Overriding opacity value. If this scene is half-translucent, + It must do intermeidate composition with that opacity value. */ + this->opacity = opacity; + opacity = 255; + } + + this->renderer = &renderer; + + if (clipper) { + Array rds; + rds.reserve(paints.size()); + for (auto paint : paints) { + rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true)); + } + rd = renderer.prepare(rds, rd, transform, clips, opacity, flag); + return rd; + } else { + for (auto paint : paints) { + paint->pImpl->update(renderer, transform, clips, opacity, flag, false); + } + return nullptr; + } + } + + bool render(RenderMethod& renderer) + { + Compositor* cmp = nullptr; + + if (needComp) { + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); + renderer.beginComposite(cmp, CompositeMethod::None, opacity); + } + + for (auto paint : paints) { + if (!paint->pImpl->render(renderer)) return false; + } + + if (cmp) renderer.endComposite(cmp); + + return true; + } + + RenderRegion bounds(RenderMethod& renderer) const + { + if (paints.empty()) return {0, 0, 0, 0}; + + int32_t x1 = INT32_MAX; + int32_t y1 = INT32_MAX; + int32_t x2 = 0; + int32_t y2 = 0; + + for (auto paint : paints) { + auto region = paint->pImpl->bounds(renderer); + + //Merge regions + if (region.x < x1) x1 = region.x; + if (x2 < region.x + region.w) x2 = (region.x + region.w); + if (region.y < y1) y1 = region.y; + if (y2 < region.y + region.h) y2 = (region.y + region.h); + } + + return {x1, y1, (x2 - x1), (y2 - y1)}; + } + + bool bounds(float* px, float* py, float* pw, float* ph, bool stroking) + { + if (paints.empty()) return false; + + auto x1 = FLT_MAX; + auto y1 = FLT_MAX; + auto x2 = -FLT_MAX; + auto y2 = -FLT_MAX; + + for (auto paint : paints) { + auto x = FLT_MAX; + auto y = FLT_MAX; + auto w = 0.0f; + auto h = 0.0f; + + if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue; + + //Merge regions + if (x < x1) x1 = x; + if (x2 < x + w) x2 = (x + w); + if (y < y1) y1 = y; + if (y2 < y + h) y2 = (y + h); + } + + if (px) *px = x1; + if (py) *py = y1; + if (pw) *pw = (x2 - x1); + if (ph) *ph = (y2 - y1); + + return true; + } + + Paint* duplicate() + { + auto ret = Scene::gen(); + + auto dup = ret.get()->pImpl; + + for (auto paint : paints) { + auto cdup = paint->duplicate(); + P(cdup)->ref(); + dup->paints.push_back(cdup); + } + + return ret.release(); + } + + void clear(bool free) + { + auto dispose = renderer ? true : false; + + for (auto paint : paints) { + if (dispose) free &= P(paint)->dispose(*renderer); + if (P(paint)->unref() == 0 && free) delete(paint); + } + paints.clear(); + renderer = nullptr; + } + + Iterator* iterator() + { + return new SceneIterator(&paints); + } +}; + +#endif //_TVG_SCENE_IMPL_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgShape.cpp b/project/gui/lvgl/src/libs/thorvg/tvgShape.cpp new file mode 100644 index 000000000..e43c07f2f --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgShape.cpp @@ -0,0 +1,416 @@ +/* + * 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 "tvgShape.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ +constexpr auto PATH_KAPPA = 0.552284f; + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Shape :: Shape() : pImpl(new Impl(this)) +{ + Paint::pImpl->id = TVG_CLASS_ID_SHAPE; + Paint::pImpl->method(new PaintMethod(pImpl)); +} + + +Shape :: ~Shape() +{ + delete(pImpl); +} + + +unique_ptr Shape::gen() noexcept +{ + return unique_ptr(new Shape); +} + + +uint32_t Shape::identifier() noexcept +{ + return TVG_CLASS_ID_SHAPE; +} + + +Result Shape::reset() noexcept +{ + pImpl->rs.path.cmds.clear(); + pImpl->rs.path.pts.clear(); + + pImpl->flag = RenderUpdateFlag::Path; + + return Result::Success; +} + + +uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept +{ + if (!cmds) return 0; + + *cmds = pImpl->rs.path.cmds.data; + return pImpl->rs.path.cmds.count; +} + + +uint32_t Shape::pathCoords(const Point** pts) const noexcept +{ + if (!pts) return 0; + + *pts = pImpl->rs.path.pts.data; + return pImpl->rs.path.pts.count; +} + + +Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept +{ + if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments; + + pImpl->grow(cmdCnt, ptsCnt); + pImpl->append(cmds, cmdCnt, pts, ptsCnt); + + return Result::Success; +} + + +Result Shape::moveTo(float x, float y) noexcept +{ + pImpl->moveTo(x, y); + + return Result::Success; +} + + +Result Shape::lineTo(float x, float y) noexcept +{ + pImpl->lineTo(x, y); + + return Result::Success; +} + + +Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept +{ + pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y); + + return Result::Success; +} + + +Result Shape::close() noexcept +{ + pImpl->close(); + + return Result::Success; +} + + +Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept +{ + auto rxKappa = rx * PATH_KAPPA; + auto ryKappa = ry * PATH_KAPPA; + + pImpl->grow(6, 13); + pImpl->moveTo(cx, cy - ry); + pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); + pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); + pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); + pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); + pImpl->close(); + + return Result::Success; +} + +Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept +{ + //just circle + if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius); + + startAngle = (startAngle * MATH_PI) / 180.0f; + sweep = sweep * MATH_PI / 180.0f; + + auto nCurves = ceil(fabsf(sweep / MATH_PI2)); + auto sweepSign = (sweep < 0 ? -1 : 1); + auto fract = fmodf(sweep, MATH_PI2); + fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract; + + //Start from here + Point start = {radius * cosf(startAngle), radius * sinf(startAngle)}; + + if (pie) { + pImpl->moveTo(cx, cy); + pImpl->lineTo(start.x + cx, start.y + cy); + } else { + pImpl->moveTo(start.x + cx, start.y + cy); + } + + for (int i = 0; i < nCurves; ++i) { + auto endAngle = startAngle + ((i != nCurves - 1) ? float(M_PI_2) * sweepSign : fract); + Point end = {radius * cosf(endAngle), radius * sinf(endAngle)}; + + //variables needed to calculate bezier control points + + //get bezier control points using article: + //(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479) + auto ax = start.x; + auto ay = start.y; + auto bx = end.x; + auto by = end.y; + auto q1 = ax * ax + ay * ay; + auto q2 = ax * bx + ay * by + q1; + auto k2 = (4.0f/3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx)); + + start = end; //Next start point is the current end point + + end.x += cx; + end.y += cy; + + Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy}; + Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy}; + + pImpl->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y); + + startAngle = endAngle; + } + + if (pie) pImpl->close(); + + return Result::Success; +} + + +Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept +{ + auto halfW = w * 0.5f; + auto halfH = h * 0.5f; + + //clamping cornerRadius by minimum size + if (rx > halfW) rx = halfW; + if (ry > halfH) ry = halfH; + + //rectangle + if (rx == 0 && ry == 0) { + pImpl->grow(5, 4); + pImpl->moveTo(x, y); + pImpl->lineTo(x + w, y); + pImpl->lineTo(x + w, y + h); + pImpl->lineTo(x, y + h); + pImpl->close(); + //circle + } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) { + return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry); + } else { + auto hrx = rx * 0.5f; + auto hry = ry * 0.5f; + pImpl->grow(10, 17); + pImpl->moveTo(x + rx, y); + pImpl->lineTo(x + w - rx, y); + pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); + pImpl->lineTo(x + w, y + h - ry); + pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); + pImpl->lineTo(x + rx, y + h); + pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); + pImpl->lineTo(x, y + ry); + pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); + pImpl->close(); + } + + return Result::Success; +} + + +Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept +{ + if (pImpl->rs.fill) { + delete(pImpl->rs.fill); + pImpl->rs.fill = nullptr; + pImpl->flag |= RenderUpdateFlag::Gradient; + } + + if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success; + + pImpl->rs.color[0] = r; + pImpl->rs.color[1] = g; + pImpl->rs.color[2] = b; + pImpl->rs.color[3] = a; + pImpl->flag |= RenderUpdateFlag::Color; + + return Result::Success; +} + + +Result Shape::fill(unique_ptr f) noexcept +{ + auto p = f.release(); + if (!p) return Result::MemoryCorruption; + + if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill); + pImpl->rs.fill = p; + pImpl->flag |= RenderUpdateFlag::Gradient; + + return Result::Success; +} + + +Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept +{ + pImpl->rs.fillColor(r, g, b, a); + + return Result::Success; +} + + +const Fill* Shape::fill() const noexcept +{ + return pImpl->rs.fill; +} + + +Result Shape::order(bool strokeFirst) noexcept +{ + if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation; + + return Result::Success; +} + + +Result Shape::stroke(float width) noexcept +{ + if (!pImpl->strokeWidth(width)) return Result::FailedAllocation; + + return Result::Success; +} + + +float Shape::strokeWidth() const noexcept +{ + return pImpl->rs.strokeWidth(); +} + + +Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept +{ + if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation; + + return Result::Success; +} + + +Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept +{ + if (!pImpl->rs.strokeColor(r, g, b, a)) return Result::InsufficientCondition; + + return Result::Success; +} + + +Result Shape::stroke(unique_ptr f) noexcept +{ + return pImpl->strokeFill(std::move(f)); +} + + +const Fill* Shape::strokeFill() const noexcept +{ + return pImpl->rs.strokeFill(); +} + + +Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept +{ + return pImpl->strokeDash(dashPattern, cnt, 0); +} + + +uint32_t Shape::strokeDash(const float** dashPattern) const noexcept +{ + return pImpl->rs.strokeDash(dashPattern, nullptr); +} + + +Result Shape::stroke(StrokeCap cap) noexcept +{ + if (!pImpl->strokeCap(cap)) return Result::FailedAllocation; + + return Result::Success; +} + + +Result Shape::stroke(StrokeJoin join) noexcept +{ + if (!pImpl->strokeJoin(join)) return Result::FailedAllocation; + + return Result::Success; +} + +Result Shape::strokeMiterlimit(float miterlimit) noexcept +{ + // https://www.w3.org/TR/SVG2/painting.html#LineJoin + // - A negative value for stroke-miterlimit must be treated as an illegal value. + if (miterlimit < 0.0f) return Result::NonSupport; + // TODO Find out a reasonable max value. + if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation; + + return Result::Success; +} + + +StrokeCap Shape::strokeCap() const noexcept +{ + return pImpl->rs.strokeCap(); +} + + +StrokeJoin Shape::strokeJoin() const noexcept +{ + return pImpl->rs.strokeJoin(); +} + +float Shape::strokeMiterlimit() const noexcept +{ + return pImpl->rs.strokeMiterlimit(); +} + + +Result Shape::fill(FillRule r) noexcept +{ + pImpl->rs.rule = r; + + return Result::Success; +} + + +FillRule Shape::fillRule() const noexcept +{ + return pImpl->rs.rule; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgShape.h b/project/gui/lvgl/src/libs/thorvg/tvgShape.h new file mode 100644 index 000000000..161932d11 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgShape.h @@ -0,0 +1,396 @@ +/* + * 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_SHAPE_IMPL_H_ +#define _TVG_SHAPE_IMPL_H_ + +#include +#include "tvgMath.h" +#include "tvgPaint.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct Shape::Impl +{ + RenderShape rs; //shape data + RenderData rd = nullptr; //engine data + Shape* shape; + uint8_t flag = RenderUpdateFlag::None; + uint8_t opacity; //for composition + bool needComp; //composite or not + + Impl(Shape* s) : shape(s) + { + } + + bool dispose(RenderMethod& renderer) + { + renderer.dispose(rd); + rd = nullptr; + return true; + } + + bool render(RenderMethod& renderer) + { + Compositor* cmp = nullptr; + bool ret; + + if (needComp) { + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); + renderer.beginComposite(cmp, CompositeMethod::None, opacity); + } + ret = renderer.renderShape(rd); + if (cmp) renderer.endComposite(cmp); + return ret; + } + + bool needComposition(uint8_t opacity) + { + if (opacity == 0) return false; + + //Shape composition is only necessary when stroking & fill are valid. + if (!rs.stroke || rs.stroke->width < FLT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false; + if (!rs.fill && rs.color[3] == 0) return false; + + //translucent fill & stroke + if (opacity < 255) return true; + + //Composition test + const Paint* target; + auto method = shape->composite(&target); + if (!target || method == tvg::CompositeMethod::ClipPath) return false; + if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; + + return true; + } + + RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + { + if ((needComp = needComposition(opacity))) { + /* Overriding opacity value. If this scene is half-translucent, + It must do intermeidate composition with that opacity value. */ + this->opacity = opacity; + opacity = 255; + } + + rd = renderer.prepare(rs, rd, transform, clips, opacity, static_cast(pFlag | flag), clipper); + flag = RenderUpdateFlag::None; + return rd; + } + + RenderRegion bounds(RenderMethod& renderer) + { + return renderer.region(rd); + } + + bool bounds(float* x, float* y, float* w, float* h, bool stroking) + { + //Path bounding size + if (rs.path.pts.count > 0 ) { + auto pts = rs.path.pts.data; + Point min = { pts->x, pts->y }; + Point max = { pts->x, pts->y }; + + for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) { + if (pts2->x < min.x) min.x = pts2->x; + if (pts2->y < min.y) min.y = pts2->y; + if (pts2->x > max.x) max.x = pts2->x; + if (pts2->y > max.y) max.y = pts2->y; + } + + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + } + + //Stroke feathering + if (stroking && rs.stroke) { + if (x) *x -= rs.stroke->width * 0.5f; + if (y) *y -= rs.stroke->width * 0.5f; + if (w) *w += rs.stroke->width; + if (h) *h += rs.stroke->width; + } + return rs.path.pts.count > 0 ? true : false; + } + + void reserveCmd(uint32_t cmdCnt) + { + rs.path.cmds.reserve(cmdCnt); + } + + void reservePts(uint32_t ptsCnt) + { + rs.path.pts.reserve(ptsCnt); + } + + void grow(uint32_t cmdCnt, uint32_t ptsCnt) + { + rs.path.cmds.grow(cmdCnt); + rs.path.pts.grow(ptsCnt); + } + + void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) + { + memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt); + memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt); + rs.path.cmds.count += cmdCnt; + rs.path.pts.count += ptsCnt; + + flag |= RenderUpdateFlag::Path; + } + + void moveTo(float x, float y) + { + rs.path.cmds.push(PathCommand::MoveTo); + rs.path.pts.push({x, y}); + + flag |= RenderUpdateFlag::Path; + } + + void lineTo(float x, float y) + { + rs.path.cmds.push(PathCommand::LineTo); + rs.path.pts.push({x, y}); + + flag |= RenderUpdateFlag::Path; + } + + void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) + { + rs.path.cmds.push(PathCommand::CubicTo); + rs.path.pts.push({cx1, cy1}); + rs.path.pts.push({cx2, cy2}); + rs.path.pts.push({x, y}); + + flag |= RenderUpdateFlag::Path; + } + + void close() + { + //Don't close multiple times. + if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return; + + rs.path.cmds.push(PathCommand::Close); + + flag |= RenderUpdateFlag::Path; + } + + bool strokeWidth(float width) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->width = width; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + bool strokeTrim(float begin, float end) + { + if (!rs.stroke) { + if (begin == 0.0f && end == 1.0f) return true; + rs.stroke = new RenderStroke(); + } + + if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true; + + rs.stroke->trim.begin = begin; + rs.stroke->trim.end = end; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + bool strokeCap(StrokeCap cap) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->cap = cap; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + bool strokeJoin(StrokeJoin join) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->join = join; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + bool strokeMiterlimit(float miterlimit) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->miterlimit = miterlimit; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->fill) { + delete(rs.stroke->fill); + rs.stroke->fill = nullptr; + flag |= RenderUpdateFlag::GradientStroke; + } + + rs.stroke->color[0] = r; + rs.stroke->color[1] = g; + rs.stroke->color[2] = b; + rs.stroke->color[3] = a; + + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + Result strokeFill(unique_ptr f) + { + auto p = f.release(); + if (!p) return Result::MemoryCorruption; + + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill); + rs.stroke->fill = p; + + flag |= RenderUpdateFlag::Stroke; + flag |= RenderUpdateFlag::GradientStroke; + + return Result::Success; + } + + Result strokeDash(const float* pattern, uint32_t cnt, float offset) + { + if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) { + return Result::InvalidArguments; + } + + for (uint32_t i = 0; i < cnt; i++) { + if (pattern[i] < FLT_EPSILON) return Result::InvalidArguments; + } + + //Reset dash + if (!pattern && cnt == 0) { + free(rs.stroke->dashPattern); + rs.stroke->dashPattern = nullptr; + } else { + if (!rs.stroke) rs.stroke = new RenderStroke(); + if (rs.stroke->dashCnt != cnt) { + free(rs.stroke->dashPattern); + rs.stroke->dashPattern = nullptr; + } + if (!rs.stroke->dashPattern) { + rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * cnt)); + if (!rs.stroke->dashPattern) return Result::FailedAllocation; + } + for (uint32_t i = 0; i < cnt; ++i) { + rs.stroke->dashPattern[i] = pattern[i]; + } + } + rs.stroke->dashCnt = cnt; + rs.stroke->dashOffset = offset; + flag |= RenderUpdateFlag::Stroke; + + return Result::Success; + } + + bool strokeFirst() + { + if (!rs.stroke) return true; + return rs.stroke->strokeFirst; + } + + bool strokeFirst(bool strokeFirst) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->strokeFirst = strokeFirst; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + + void update(RenderUpdateFlag flag) + { + this->flag |= flag; + } + + Paint* duplicate() + { + auto ret = Shape::gen(); + + auto dup = ret.get()->pImpl; + dup->rs.rule = rs.rule; + + //Color + memcpy(dup->rs.color, rs.color, sizeof(rs.color)); + dup->flag = RenderUpdateFlag::Color; + + //Path + if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) { + dup->rs.path.cmds = rs.path.cmds; + dup->rs.path.pts = rs.path.pts; + dup->flag |= RenderUpdateFlag::Path; + } + + //Stroke + if (rs.stroke) { + dup->rs.stroke = new RenderStroke(); + *dup->rs.stroke = *rs.stroke; + memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); + if (rs.stroke->dashCnt > 0) { + dup->rs.stroke->dashPattern = static_cast(malloc(sizeof(float) * rs.stroke->dashCnt)); + memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt); + } + if (rs.stroke->fill) { + dup->rs.stroke->fill = rs.stroke->fill->duplicate(); + dup->flag |= RenderUpdateFlag::GradientStroke; + } + dup->flag |= RenderUpdateFlag::Stroke; + } + + //Fill + if (rs.fill) { + dup->rs.fill = rs.fill->duplicate(); + dup->flag |= RenderUpdateFlag::Gradient; + } + + return ret.release(); + } + + Iterator* iterator() + { + return nullptr; + } +}; + +#endif //_TVG_SHAPE_IMPL_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgStr.cpp b/project/gui/lvgl/src/libs/thorvg/tvgStr.cpp new file mode 100644 index 000000000..25630c7e9 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgStr.cpp @@ -0,0 +1,245 @@ +/* + * 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 "config.h" +#include +#include +#include "tvgMath.h" +#include "tvgStr.h" + + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static inline bool _floatExact(float a, float b) +{ + return memcmp(&a, &b, sizeof(float)) == 0; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +namespace tvg { + +/* + * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160 + * + * src should be one of the following form : + * + * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits] + * [whitespace] [sign] {INF | INFINITY} + * [whitespace] [sign] NAN [sequence] + * + * No hexadecimal form supported + * no sequence supported after NAN + */ +float strToFloat(const char *nPtr, char **endPtr) +{ + if (endPtr) *endPtr = (char *) (nPtr); + if (!nPtr) return 0.0f; + + auto a = nPtr; + auto iter = nPtr; + auto val = 0.0f; + unsigned long long integerPart = 0; + int minus = 1; + + //ignore leading whitespaces + while (isspace(*iter)) iter++; + + //signed or not + if (*iter == '-') { + minus = -1; + iter++; + } else if (*iter == '+') { + iter++; + } + + if (tolower(*iter) == 'i') { + if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3; + else goto error; + + if (tolower(*(iter)) == 'i') { + if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && + (tolower(*(iter + 4)) == 'y')) + iter += 5; + else goto error; + } + if (endPtr) *endPtr = (char *) (iter); + return (minus == -1) ? -INFINITY : INFINITY; + } + + if (tolower(*iter) == 'n') { + if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3; + else goto error; + + if (endPtr) *endPtr = (char *) (iter); + return (minus == -1) ? -NAN : NAN; + } + + //Optional: integer part before dot + if (isdigit(*iter)) { + for (; isdigit(*iter); iter++) { + integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0'); + } + a = iter; + } else if (*iter != '.') { + goto success; + } + + val = static_cast(integerPart); + + //Optional: decimal part after dot + if (*iter == '.') { + unsigned long long decimalPart = 0; + unsigned long long pow10 = 1; + int count = 0; + + iter++; + + if (isdigit(*iter)) { + for (; isdigit(*iter); iter++, count++) { + if (count < 19) { + decimalPart = decimalPart * 10ULL + +static_cast(*iter - '0'); + pow10 *= 10ULL; + } + } + } else if (isspace(*iter)) { //skip if there is a space after the dot. + a = iter; + goto success; + } + + val += static_cast(decimalPart) / static_cast(pow10); + a = iter; + } + + //Optional: exponent + if (*iter == 'e' || *iter == 'E') { + ++iter; + + //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em + if ((*iter == 'm') || (*iter == 'M')) { + //TODO: We don't support font em unit now, but has to multiply val * font size later... + a = iter + 1; + goto success; + } + + //signed or not + int minus_e = 1; + + if (*iter == '-') { + minus_e = -1; + ++iter; + } else if (*iter == '+') { + iter++; + } + + unsigned int exponentPart = 0; + + if (isdigit(*iter)) { + while (*iter == '0') iter++; + for (; isdigit(*iter); iter++) { + exponentPart = exponentPart * 10U + static_cast(*iter - '0'); + } + } else if (!isdigit(*(a - 1))) { + a = nPtr; + goto success; + } else if (*iter == 0) { + goto success; + } + + //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast(exponentPart)) <= -308)) { + if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast(exponentPart)) <= -38)) { + //val *= 1.0e-308f; + val *= 1.0e-38f; + a = iter; + goto success; + } + + a = iter; + auto scale = 1.0f; + + while (exponentPart >= 8U) { + scale *= (float)1E8; + exponentPart -= 8U; + } + while (exponentPart > 0U) { + scale *= 10.0f; + exponentPart--; + } + val = (minus_e == -1) ? (val / scale) : (val * scale); + } else if ((iter > nPtr) && !isdigit(*(iter - 1))) { + a = nPtr; + goto success; + } + +success: + if (endPtr) *endPtr = (char *)(a); + return minus * val; + +error: + if (endPtr) *endPtr = (char *)(nPtr); + return 0.0f; +} + + +int str2int(const char* str, size_t n) +{ + int ret = 0; + for(size_t i = 0; i < n; ++i) { + ret = ret * 10 + (str[i] - '0'); + } + return ret; +} + +char* strDuplicate(const char *str, size_t n) +{ + auto len = strlen(str); + if (len < n) n = len; + + auto ret = (char *) malloc(n + 1); + if (!ret) return nullptr; + ret[n] = '\0'; + + return (char *) memcpy(ret, str, n); +} + +char* strDirname(const char* path) +{ + const char *ptr = strrchr(path, '/'); +#ifdef _WIN32 + if (ptr) ptr = strrchr(ptr + 1, '\\'); +#endif + int len = int(ptr + 1 - path); // +1 to include '/' + return strDuplicate(path, len); +} + +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgStr.h b/project/gui/lvgl/src/libs/thorvg/tvgStr.h new file mode 100644 index 000000000..eb664e2f5 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgStr.h @@ -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_STR_H_ +#define _TVG_STR_H_ + +#include + +namespace tvg +{ + +float strToFloat(const char *nPtr, char **endPtr); //convert to float +int str2int(const char* str, size_t n); //convert to integer +char* strDuplicate(const char *str, size_t n); //copy the string +char* strDirname(const char* path); //return the full directory name + +} +#endif //_TVG_STR_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.cpp new file mode 100644 index 000000000..89e45d401 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2022 - 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 "tvgSvgCssStyle.h" + +#include + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag) +{ + if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) { + return true; + } + return false; +} + +static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) +{ + if (from == nullptr) return; + //Copy the properties of 'from' only if they were explicitly set (not the default ones). + if ((from->curColorSet && !(to->flags & SvgStyleFlags::Color)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) { + to->color = from->color; + to->curColorSet = true; + to->flags = (to->flags | SvgStyleFlags::Color); + if (from->flagsImportance & SvgStyleFlags::Color) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color); + } + } + //Fill + if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) { + to->fill.paint.color = from->fill.paint.color; + to->fill.paint.none = from->fill.paint.none; + to->fill.paint.curColor = from->fill.paint.curColor; + if (from->fill.paint.url) { + if (to->fill.paint.url) free(to->fill.paint.url); + to->fill.paint.url = strdup(from->fill.paint.url); + } + to->fill.flags = (to->fill.flags | SvgFillFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Fill); + if (from->flagsImportance & SvgStyleFlags::Fill) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill); + } + } + if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) { + to->fill.opacity = from->fill.opacity; + to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::FillOpacity); + if (from->flagsImportance & SvgStyleFlags::FillOpacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity); + } + } + if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) { + to->fill.fillRule = from->fill.fillRule; + to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule); + to->flags = (to->flags | SvgStyleFlags::FillRule); + if (from->flagsImportance & SvgStyleFlags::FillRule) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule); + } + } + //Stroke + if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) { + to->stroke.paint.color = from->stroke.paint.color; + to->stroke.paint.none = from->stroke.paint.none; + to->stroke.paint.curColor = from->stroke.paint.curColor; + if (from->stroke.paint.url) { + if (to->stroke.paint.url) free(to->stroke.paint.url); + to->stroke.paint.url = strdup(from->stroke.paint.url); + } + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Stroke); + if (from->flagsImportance & SvgStyleFlags::Stroke) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke); + } + } + if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) { + to->stroke.opacity = from->stroke.opacity; + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::StrokeOpacity); + if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity); + } + } + if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) { + to->stroke.width = from->stroke.width; + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width); + to->flags = (to->flags | SvgStyleFlags::StrokeWidth); + if (from->flagsImportance & SvgStyleFlags::StrokeWidth) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth); + } + } + if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) { + if (from->stroke.dash.array.count > 0) { + to->stroke.dash.array.clear(); + to->stroke.dash.array.reserve(from->stroke.dash.array.count); + for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { + to->stroke.dash.array.push(from->stroke.dash.array[i]); + } + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash); + to->flags = (to->flags | SvgStyleFlags::StrokeDashArray); + if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray); + } + } + } + if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) { + to->stroke.cap = from->stroke.cap; + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap); + to->flags = (to->flags | SvgStyleFlags::StrokeLineCap); + if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap); + } + } + if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) { + to->stroke.join = from->stroke.join; + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join); + to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); + if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin); + } + } + //Opacity + //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity' + if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) { + to->opacity = from->opacity; + to->flags = (to->flags | SvgStyleFlags::Opacity); + if (from->flagsImportance & SvgStyleFlags::Opacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity); + } + } +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +void cssCopyStyleAttr(SvgNode* to, const SvgNode* from) +{ + //Copy matrix attribute + if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) { + to->transform = (Matrix*)malloc(sizeof(Matrix)); + if (to->transform) { + *to->transform = *from->transform; + to->style->flags = (to->style->flags | SvgStyleFlags::Transform); + } + } + //Copy style attribute + _copyStyle(to->style, from->style); + + if (from->style->clipPath.url) { + if (to->style->clipPath.url) free(to->style->clipPath.url); + to->style->clipPath.url = strdup(from->style->clipPath.url); + } + if (from->style->mask.url) { + if (to->style->mask.url) free(to->style->mask.url); + to->style->mask.url = strdup(from->style->mask.url); + } +} + + +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type) +{ + if (!style) return nullptr; + + auto child = style->child.data; + for (uint32_t i = 0; i < style->child.count; ++i, ++child) { + if ((*child)->type == type) { + if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + } + } + return nullptr; +} + + +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title) +{ + if (!style || !title) return nullptr; + + auto child = style->child.data; + for (uint32_t i = 0; i < style->child.count; ++i, ++child) { + if ((*child)->type == SvgNodeType::CssStyle) { + if ((*child)->id && !strcmp((*child)->id, title)) return (*child); + } + } + return nullptr; +} + + +void cssUpdateStyle(SvgNode* doc, SvgNode* style) +{ + if (doc->child.count > 0) { + auto child = doc->child.data; + for (uint32_t i = 0; i < doc->child.count; ++i, ++child) { + if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) { + cssCopyStyleAttr(*child, cssNode); + } + cssUpdateStyle(*child, style); + } + } +} + + +void cssApplyStyleToPostponeds(Array& postponeds, SvgNode* style) +{ + for (uint32_t i = 0; i < postponeds.count; ++i) { + auto nodeIdPair = postponeds[i]; + + //css styling: tag.name has higher priority than .name + if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) { + cssCopyStyleAttr(nodeIdPair.node, cssNode); + } + if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id)) { + cssCopyStyleAttr(nodeIdPair.node, cssNode); + } + } +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.h b/project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.h new file mode 100644 index 000000000..d718ede64 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgCssStyle.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 - 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_SVG_CSS_STYLE_H_ +#define _TVG_SVG_CSS_STYLE_H_ + +#include "tvgSvgLoaderCommon.h" + +void cssCopyStyleAttr(SvgNode* to, const SvgNode* from); +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type); +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title); +void cssUpdateStyle(SvgNode* doc, SvgNode* style); +void cssApplyStyleToPostponeds(Array& postponeds, SvgNode* style); + +#endif //_TVG_SVG_CSS_STYLE_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.cpp new file mode 100644 index 000000000..efca81245 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.cpp @@ -0,0 +1,3759 @@ +/* + * 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 + +/* + * Copyright notice for the EFL: + + * Copyright (C) EFL developers (see AUTHORS) + + * All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include "tvgLoader.h" +#include "tvgXmlParser.h" +#include "tvgSvgLoader.h" +#include "tvgSvgSceneBuilder.h" +#include "tvgStr.h" +#include "tvgSvgCssStyle.h" +#include "tvgMath.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +/* + * According to: https://www.w3.org/TR/SVG2/coords.html#Units + * and: https://www.w3.org/TR/css-values-4/#absolute-lengths + */ +#define PX_PER_IN 96 //1 in = 96 px +#define PX_PER_PC 16 //1 pc = 1/6 in -> PX_PER_IN/6 +#define PX_PER_PT 1.333333f //1 pt = 1/72 in -> PX_PER_IN/72 +#define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4 +#define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54 + +typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data); +typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func); +typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength); + +static char* _skipSpace(const char* str, const char* end) +{ + while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) { + ++str; + } + return (char*) str; +} + + +static char* _copyId(const char* str) +{ + if (!str) return nullptr; + + return strdup(str); +} + + +static const char* _skipComma(const char* content) +{ + content = _skipSpace(content, nullptr); + if (*content == ',') return content + 1; + return content; +} + + +static bool _parseNumber(const char** content, float* number) +{ + char* end = nullptr; + + *number = strToFloat(*content, &end); + //If the start of string is not number + if ((*content) == end) return false; + //Skip comma if any + *content = _skipComma(end); + + return true; +} + + +static constexpr struct +{ + AspectRatioAlign align; + const char* tag; +} alignTags[] = { + { AspectRatioAlign::XMinYMin, "xMinYMin" }, + { AspectRatioAlign::XMidYMin, "xMidYMin" }, + { AspectRatioAlign::XMaxYMin, "xMaxYMin" }, + { AspectRatioAlign::XMinYMid, "xMinYMid" }, + { AspectRatioAlign::XMidYMid, "xMidYMid" }, + { AspectRatioAlign::XMaxYMid, "xMaxYMid" }, + { AspectRatioAlign::XMinYMax, "xMinYMax" }, + { AspectRatioAlign::XMidYMax, "xMidYMax" }, + { AspectRatioAlign::XMaxYMax, "xMaxYMax" }, +}; + + +static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice) +{ + if (!strcmp(*content, "none")) { + *align = AspectRatioAlign::None; + return; + } + + for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) { + if (!strncmp(*content, alignTags[i].tag, 8)) { + *align = alignTags[i].align; + *content += 8; + *content = _skipSpace(*content, nullptr); + break; + } + } + + if (!strcmp(*content, "meet")) { + *meetOrSlice = AspectRatioMeetOrSlice::Meet; + } else if (!strcmp(*content, "slice")) { + *meetOrSlice = AspectRatioMeetOrSlice::Slice; + } +} + + +/** + * According to https://www.w3.org/TR/SVG/coords.html#Units + */ +static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type) +{ + float parsedValue = strToFloat(str, nullptr); + + if (strstr(str, "cm")) parsedValue *= PX_PER_CM; + else if (strstr(str, "mm")) parsedValue *= PX_PER_MM; + else if (strstr(str, "pt")) parsedValue *= PX_PER_PT; + else if (strstr(str, "pc")) parsedValue *= PX_PER_PC; + else if (strstr(str, "in")) parsedValue *= PX_PER_IN; + else if (strstr(str, "%")) { + if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h; + else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w; + else //if other then it's radius + { + float max = svgParse->global.w; + if (max < svgParse->global.h) + max = svgParse->global.h; + parsedValue = (parsedValue / 100.0f) * max; + } + } + //TODO: Implement 'em', 'ex' attributes + + return parsedValue; +} + + +static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage) +{ + char* end = nullptr; + + float parsedValue = strToFloat(str, &end); + isPercentage = false; + + if (strstr(str, "%")) { + parsedValue = parsedValue / 100.0f; + isPercentage = true; + } + else if (strstr(str, "cm")) parsedValue *= PX_PER_CM; + else if (strstr(str, "mm")) parsedValue *= PX_PER_MM; + else if (strstr(str, "pt")) parsedValue *= PX_PER_PT; + else if (strstr(str, "pc")) parsedValue *= PX_PER_PC; + else if (strstr(str, "in")) parsedValue *= PX_PER_IN; + //TODO: Implement 'em', 'ex' attributes + + return parsedValue; +} + + +static float _toOffset(const char* str) +{ + char* end = nullptr; + auto strEnd = str + strlen(str); + + float parsedValue = strToFloat(str, &end); + + end = _skipSpace(end, nullptr); + auto ptr = strstr(str, "%"); + + if (ptr) { + parsedValue = parsedValue / 100.0f; + if (end != ptr || (end + 1) != strEnd) return 0; + } else if (end != strEnd) return 0; + + return parsedValue; +} + + +static int _toOpacity(const char* str) +{ + char* end = nullptr; + float opacity = strToFloat(str, &end); + + if (end) { + if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f); + else if (*end == '\0') return lrint(opacity * 255); + } + return 255; +} + + +static SvgMaskType _toMaskType(const char* str) +{ + if (!strcmp(str, "Alpha")) return SvgMaskType::Alpha; + + return SvgMaskType::Luminance; +} + + +//The default rendering order: fill, stroke, markers +//If any is omitted, will be rendered in its default order after the specified ones. +static bool _toPaintOrder(const char* str) +{ + uint8_t position = 1; + uint8_t strokePosition = 0; + uint8_t fillPosition = 0; + + while (*str != '\0') { + str = _skipSpace(str, nullptr); + if (!strncmp(str, "fill", 4)) { + fillPosition = position++; + str += 4; + } else if (!strncmp(str, "stroke", 6)) { + strokePosition = position++; + str += 6; + } else if (!strncmp(str, "markers", 7)) { + str += 7; + } else { + return _toPaintOrder("fill stroke"); + } + } + + if (fillPosition == 0) fillPosition = position++; + if (strokePosition == 0) strokePosition = position++; + + return fillPosition < strokePosition; +} + + +#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \ + static Type _to##Name1(const char* str) \ + { \ + unsigned int i; \ + \ + for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \ + if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \ + } \ + return Default; \ + } + + +/* parse the line cap used during stroking a path. + * Value: butt | round | square | inherit + * Initial: butt + * https://www.w3.org/TR/SVG/painting.html + */ +static constexpr struct +{ + StrokeCap lineCap; + const char* tag; +} lineCapTags[] = { + { StrokeCap::Butt, "butt" }, + { StrokeCap::Round, "round" }, + { StrokeCap::Square, "square" } +}; + + +_PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt) + + +/* parse the line join used during stroking a path. + * Value: miter | round | bevel | inherit + * Initial: miter + * https://www.w3.org/TR/SVG/painting.html + */ +static constexpr struct +{ + StrokeJoin lineJoin; + const char* tag; +} lineJoinTags[] = { + { StrokeJoin::Miter, "miter" }, + { StrokeJoin::Round, "round" }, + { StrokeJoin::Bevel, "bevel" } +}; + + +_PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter) + + +/* parse the fill rule used during filling a path. + * Value: nonzero | evenodd | inherit + * Initial: nonzero + * https://www.w3.org/TR/SVG/painting.html + */ +static constexpr struct +{ + FillRule fillRule; + const char* tag; +} fillRuleTags[] = { + { FillRule::EvenOdd, "evenodd" } +}; + + +_PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::Winding) + + +/* parse the dash pattern used during stroking a path. + * Value: none | | inherit + * Initial: none + * https://www.w3.org/TR/SVG/painting.html + */ +static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash) +{ + if (!strncmp(str, "none", 4)) return; + + char *end = nullptr; + + while (*str) { + str = _skipComma(str); + float parsedValue = strToFloat(str, &end); + if (str == end) break; + if (parsedValue <= 0.0f) break; + if (*end == '%') { + ++end; + //Refers to the diagonal length of the viewport. + //https://www.w3.org/TR/SVG2/coords.html#Units + parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f); + } + (*dash).array.push(parsedValue); + str = end; + } + //If dash array size is 1, it means that dash and gap size are the same. + if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]); +} + + +static char* _idFromUrl(const char* url) +{ + url = _skipSpace(url, nullptr); + if ((*url) == '(') { + ++url; + url = _skipSpace(url, nullptr); + } + + if ((*url) == '\'') ++url; + if ((*url) == '#') ++url; + + int i = 0; + while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i; + + return strDuplicate(url, i); +} + + +static unsigned char _parseColor(const char* value, char** end) +{ + float r; + + r = strToFloat(value, end); + *end = _skipSpace(*end, nullptr); + if (**end == '%') { + r = 255 * r / 100; + (*end)++; + } + *end = _skipSpace(*end, nullptr); + + if (r < 0 || r > 255) { + *end = nullptr; + return 0; + } + + return lrint(r); +} + + +static constexpr struct +{ + const char* name; + unsigned int value; +} colors[] = { + { "aliceblue", 0xfff0f8ff }, + { "antiquewhite", 0xfffaebd7 }, + { "aqua", 0xff00ffff }, + { "aquamarine", 0xff7fffd4 }, + { "azure", 0xfff0ffff }, + { "beige", 0xfff5f5dc }, + { "bisque", 0xffffe4c4 }, + { "black", 0xff000000 }, + { "blanchedalmond", 0xffffebcd }, + { "blue", 0xff0000ff }, + { "blueviolet", 0xff8a2be2 }, + { "brown", 0xffa52a2a }, + { "burlywood", 0xffdeb887 }, + { "cadetblue", 0xff5f9ea0 }, + { "chartreuse", 0xff7fff00 }, + { "chocolate", 0xffd2691e }, + { "coral", 0xffff7f50 }, + { "cornflowerblue", 0xff6495ed }, + { "cornsilk", 0xfffff8dc }, + { "crimson", 0xffdc143c }, + { "cyan", 0xff00ffff }, + { "darkblue", 0xff00008b }, + { "darkcyan", 0xff008b8b }, + { "darkgoldenrod", 0xffb8860b }, + { "darkgray", 0xffa9a9a9 }, + { "darkgrey", 0xffa9a9a9 }, + { "darkgreen", 0xff006400 }, + { "darkkhaki", 0xffbdb76b }, + { "darkmagenta", 0xff8b008b }, + { "darkolivegreen", 0xff556b2f }, + { "darkorange", 0xffff8c00 }, + { "darkorchid", 0xff9932cc }, + { "darkred", 0xff8b0000 }, + { "darksalmon", 0xffe9967a }, + { "darkseagreen", 0xff8fbc8f }, + { "darkslateblue", 0xff483d8b }, + { "darkslategray", 0xff2f4f4f }, + { "darkslategrey", 0xff2f4f4f }, + { "darkturquoise", 0xff00ced1 }, + { "darkviolet", 0xff9400d3 }, + { "deeppink", 0xffff1493 }, + { "deepskyblue", 0xff00bfff }, + { "dimgray", 0xff696969 }, + { "dimgrey", 0xff696969 }, + { "dodgerblue", 0xff1e90ff }, + { "firebrick", 0xffb22222 }, + { "floralwhite", 0xfffffaf0 }, + { "forestgreen", 0xff228b22 }, + { "fuchsia", 0xffff00ff }, + { "gainsboro", 0xffdcdcdc }, + { "ghostwhite", 0xfff8f8ff }, + { "gold", 0xffffd700 }, + { "goldenrod", 0xffdaa520 }, + { "gray", 0xff808080 }, + { "grey", 0xff808080 }, + { "green", 0xff008000 }, + { "greenyellow", 0xffadff2f }, + { "honeydew", 0xfff0fff0 }, + { "hotpink", 0xffff69b4 }, + { "indianred", 0xffcd5c5c }, + { "indigo", 0xff4b0082 }, + { "ivory", 0xfffffff0 }, + { "khaki", 0xfff0e68c }, + { "lavender", 0xffe6e6fa }, + { "lavenderblush", 0xfffff0f5 }, + { "lawngreen", 0xff7cfc00 }, + { "lemonchiffon", 0xfffffacd }, + { "lightblue", 0xffadd8e6 }, + { "lightcoral", 0xfff08080 }, + { "lightcyan", 0xffe0ffff }, + { "lightgoldenrodyellow", 0xfffafad2 }, + { "lightgray", 0xffd3d3d3 }, + { "lightgrey", 0xffd3d3d3 }, + { "lightgreen", 0xff90ee90 }, + { "lightpink", 0xffffb6c1 }, + { "lightsalmon", 0xffffa07a }, + { "lightseagreen", 0xff20b2aa }, + { "lightskyblue", 0xff87cefa }, + { "lightslategray", 0xff778899 }, + { "lightslategrey", 0xff778899 }, + { "lightsteelblue", 0xffb0c4de }, + { "lightyellow", 0xffffffe0 }, + { "lime", 0xff00ff00 }, + { "limegreen", 0xff32cd32 }, + { "linen", 0xfffaf0e6 }, + { "magenta", 0xffff00ff }, + { "maroon", 0xff800000 }, + { "mediumaquamarine", 0xff66cdaa }, + { "mediumblue", 0xff0000cd }, + { "mediumorchid", 0xffba55d3 }, + { "mediumpurple", 0xff9370d8 }, + { "mediumseagreen", 0xff3cb371 }, + { "mediumslateblue", 0xff7b68ee }, + { "mediumspringgreen", 0xff00fa9a }, + { "mediumturquoise", 0xff48d1cc }, + { "mediumvioletred", 0xffc71585 }, + { "midnightblue", 0xff191970 }, + { "mintcream", 0xfff5fffa }, + { "mistyrose", 0xffffe4e1 }, + { "moccasin", 0xffffe4b5 }, + { "navajowhite", 0xffffdead }, + { "navy", 0xff000080 }, + { "oldlace", 0xfffdf5e6 }, + { "olive", 0xff808000 }, + { "olivedrab", 0xff6b8e23 }, + { "orange", 0xffffa500 }, + { "orangered", 0xffff4500 }, + { "orchid", 0xffda70d6 }, + { "palegoldenrod", 0xffeee8aa }, + { "palegreen", 0xff98fb98 }, + { "paleturquoise", 0xffafeeee }, + { "palevioletred", 0xffd87093 }, + { "papayawhip", 0xffffefd5 }, + { "peachpuff", 0xffffdab9 }, + { "peru", 0xffcd853f }, + { "pink", 0xffffc0cb }, + { "plum", 0xffdda0dd }, + { "powderblue", 0xffb0e0e6 }, + { "purple", 0xff800080 }, + { "red", 0xffff0000 }, + { "rosybrown", 0xffbc8f8f }, + { "royalblue", 0xff4169e1 }, + { "saddlebrown", 0xff8b4513 }, + { "salmon", 0xfffa8072 }, + { "sandybrown", 0xfff4a460 }, + { "seagreen", 0xff2e8b57 }, + { "seashell", 0xfffff5ee }, + { "sienna", 0xffa0522d }, + { "silver", 0xffc0c0c0 }, + { "skyblue", 0xff87ceeb }, + { "slateblue", 0xff6a5acd }, + { "slategray", 0xff708090 }, + { "slategrey", 0xff708090 }, + { "snow", 0xfffffafa }, + { "springgreen", 0xff00ff7f }, + { "steelblue", 0xff4682b4 }, + { "tan", 0xffd2b48c }, + { "teal", 0xff008080 }, + { "thistle", 0xffd8bfd8 }, + { "tomato", 0xffff6347 }, + { "turquoise", 0xff40e0d0 }, + { "violet", 0xffee82ee }, + { "wheat", 0xfff5deb3 }, + { "white", 0xffffffff }, + { "whitesmoke", 0xfff5f5f5 }, + { "yellow", 0xffffff00 }, + { "yellowgreen", 0xff9acd32 } +}; + + +static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref) +{ + unsigned int len = strlen(str); + char *red, *green, *blue; + unsigned char tr, tg, tb; + + if (len == 4 && str[0] == '#') { + //Case for "#456" should be interprete as "#445566" + if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) { + char tmp[3] = { '\0', '\0', '\0' }; + tmp[0] = str[1]; + tmp[1] = str[1]; + *r = strtol(tmp, nullptr, 16); + tmp[0] = str[2]; + tmp[1] = str[2]; + *g = strtol(tmp, nullptr, 16); + tmp[0] = str[3]; + tmp[1] = str[3]; + *b = strtol(tmp, nullptr, 16); + } + } else if (len == 7 && str[0] == '#') { + if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) { + char tmp[3] = { '\0', '\0', '\0' }; + tmp[0] = str[1]; + tmp[1] = str[2]; + *r = strtol(tmp, nullptr, 16); + tmp[0] = str[3]; + tmp[1] = str[4]; + *g = strtol(tmp, nullptr, 16); + tmp[0] = str[5]; + tmp[1] = str[6]; + *b = strtol(tmp, nullptr, 16); + } + } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') { + tr = _parseColor(str + 4, &red); + if (red && *red == ',') { + tg = _parseColor(red + 1, &green); + if (green && *green == ',') { + tb = _parseColor(green + 1, &blue); + if (blue && blue[0] == ')' && blue[1] == '\0') { + *r = tr; + *g = tg; + *b = tb; + } + } + } + } else if (ref && len >= 3 && !strncmp(str, "url", 3)) { + if (*ref) free(*ref); + *ref = _idFromUrl((const char*)(str + 3)); + } else { + //Handle named color + for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) { + if (!strcasecmp(colors[i].name, str)) { + *r = (((uint8_t*)(&(colors[i].value)))[2]); + *g = (((uint8_t*)(&(colors[i].value)))[1]); + *b = (((uint8_t*)(&(colors[i].value)))[0]); + return; + } + } + } +} + + +static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len) +{ + int count = 0; + char* end = nullptr; + + str = _skipSpace(str, nullptr); + while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) { + points[count++] = strToFloat(str, &end); + str = end; + str = _skipSpace(str, nullptr); + if (*str == ',') ++str; + //Eat the rest of space + str = _skipSpace(str, nullptr); + } + *ptCount = count; + return str; +} + + +enum class MatrixState { + Unknown, + Matrix, + Translate, + Rotate, + Scale, + SkewX, + SkewY +}; + + +#define MATRIX_DEF(Name, Value) \ + { \ +#Name, sizeof(#Name), Value \ + } + + +static constexpr struct +{ + const char* tag; + int sz; + MatrixState state; +} matrixTags[] = { + MATRIX_DEF(matrix, MatrixState::Matrix), + MATRIX_DEF(translate, MatrixState::Translate), + MATRIX_DEF(rotate, MatrixState::Rotate), + MATRIX_DEF(scale, MatrixState::Scale), + MATRIX_DEF(skewX, MatrixState::SkewX), + MATRIX_DEF(skewY, MatrixState::SkewY) +}; + + +static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst) +{ + auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31); + auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32); + auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33); + + auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31); + auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32); + auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33); + + auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31); + auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32); + auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33); + + dst->e11 = a11; + dst->e12 = a12; + dst->e13 = a13; + dst->e21 = a21; + dst->e22 = a22; + dst->e23 = a23; + dst->e31 = a31; + dst->e32 = a32; + dst->e33 = a33; +} + + +/* parse transform attribute + * https://www.w3.org/TR/SVG/coords.html#TransformAttribute + */ +static Matrix* _parseTransformationMatrix(const char* value) +{ + const int POINT_CNT = 8; + + auto matrix = (Matrix*)malloc(sizeof(Matrix)); + if (!matrix) return nullptr; + *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + + float points[POINT_CNT]; + int ptCount = 0; + char* str = (char*)value; + char* end = str + strlen(str); + + while (str < end) { + auto state = MatrixState::Unknown; + + if (isspace(*str) || (*str == ',')) { + ++str; + continue; + } + for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) { + if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) { + state = matrixTags[i].state; + str += (matrixTags[i].sz - 1); + break; + } + } + if (state == MatrixState::Unknown) goto error; + + str = _skipSpace(str, end); + if (*str != '(') goto error; + ++str; + str = _parseNumbersArray(str, points, &ptCount, POINT_CNT); + if (*str != ')') goto error; + ++str; + + if (state == MatrixState::Matrix) { + if (ptCount != 6) goto error; + Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1}; + _matrixCompose(matrix, &tmp, matrix); + } else if (state == MatrixState::Translate) { + if (ptCount == 1) { + Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1}; + _matrixCompose(matrix, &tmp, matrix); + } else if (ptCount == 2) { + Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1}; + _matrixCompose(matrix, &tmp, matrix); + } else goto error; + } else if (state == MatrixState::Rotate) { + //Transform to signed. + points[0] = fmod(points[0], (float)360); + if (points[0] < 0) points[0] += 360; + auto c = cosf(points[0] * (float)(M_PI / 180.0)); + auto s = sinf(points[0] * (float)(M_PI / 180.0)); + if (ptCount == 1) { + Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; + _matrixCompose(matrix, &tmp, matrix); + } else if (ptCount == 3) { + Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 }; + _matrixCompose(matrix, &tmp, matrix); + tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; + _matrixCompose(matrix, &tmp, matrix); + tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 }; + _matrixCompose(matrix, &tmp, matrix); + } else { + goto error; + } + } else if (state == MatrixState::Scale) { + if (ptCount < 1 || ptCount > 2) goto error; + auto sx = points[0]; + auto sy = sx; + if (ptCount == 2) sy = (float)points[1]; + Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; + _matrixCompose(matrix, &tmp, matrix); + } + } + return matrix; +error: + if (matrix) free(matrix); + return nullptr; +} + + +#define LENGTH_DEF(Name, Value) \ + { \ +#Name, sizeof(#Name), Value \ + } + + +static void _postpone(Array& nodes, SvgNode *node, char* id) +{ + nodes.push({node, id}); +} + + +/* +// TODO - remove? +static constexpr struct +{ + const char* tag; + int sz; + SvgLengthType type; +} lengthTags[] = { + LENGTH_DEF(%, SvgLengthType::Percent), + LENGTH_DEF(px, SvgLengthType::Px), + LENGTH_DEF(pc, SvgLengthType::Pc), + LENGTH_DEF(pt, SvgLengthType::Pt), + LENGTH_DEF(mm, SvgLengthType::Mm), + LENGTH_DEF(cm, SvgLengthType::Cm), + LENGTH_DEF(in, SvgLengthType::In) +}; + +static float _parseLength(const char* str, SvgLengthType* type) +{ + float value; + int sz = strlen(str); + + *type = SvgLengthType::Px; + for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) { + if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type; + } + value = svgUtilStrtof(str, nullptr); + return value; +} +*/ + +static bool _parseStyleAttr(void* data, const char* key, const char* value); +static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style); + + +static bool _attrParseSvgNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgDocNode* doc = &(node->node.doc); + + if (!strcmp(key, "width")) { + doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); + if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent); + } else { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width); + } + } else if (!strcmp(key, "height")) { + doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); + if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent); + } else { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height); + } + } else if (!strcmp(key, "viewBox")) { + if (_parseNumber(&value, &doc->vx)) { + if (_parseNumber(&value, &doc->vy)) { + if (_parseNumber(&value, &doc->vw)) { + if (_parseNumber(&value, &doc->vh)) { + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox); + loader->svgParse->global.h = doc->vh; + } + loader->svgParse->global.w = doc->vw; + } + loader->svgParse->global.y = doc->vy; + } + loader->svgParse->global.x = doc->vx; + } + if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) { + doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox); + TVGLOG("SVG", "Negative values of the width and/or height - the attribute invalidated."); + } + if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { + loader->svgParse->global.x = loader->svgParse->global.y = 0.0f; + loader->svgParse->global.w = loader->svgParse->global.h = 1.0f; + } + } else if (!strcmp(key, "preserveAspectRatio")) { + _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); +#ifdef THORVG_LOG_ENABLED + } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLT_EPSILON) { + TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value); +#endif + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +//https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint +static void _handlePaintAttr(SvgPaint* paint, const char* value) +{ + if (!strcmp(value, "none")) { + //No paint property + paint->none = true; + return; + } + paint->none = false; + if (!strcmp(value, "currentColor")) { + paint->curColor = true; + return; + } + _toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url); +} + + +static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + SvgStyleProperty* style = node->style; + style->curColorSet = true; + _toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr); +} + + +static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + SvgStyleProperty* style = node->style; + style->fill.flags = (style->fill.flags | SvgFillFlags::Paint); + _handlePaintAttr(&style->fill.paint, value); +} + + +static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + SvgStyleProperty* style = node->style; + style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint); + _handlePaintAttr(&style->stroke.paint, value); +} + + +static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity); + node->style->stroke.opacity = _toOpacity(value); +} + +static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash); + _parseDashArray(loader, value, &node->style->stroke.dash); +} + +static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset); + node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); +} + +static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width); + node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); +} + + +static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap); + node->style->stroke.cap = _toLineCap(value); +} + + +static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join); + node->style->stroke.join = _toLineJoin(value); +} + +static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value) +{ + char* end = nullptr; + const float miterlimit = strToFloat(value, &end); + + // https://www.w3.org/TR/SVG2/painting.html#LineJoin + // - A negative value for stroke-miterlimit must be treated as an illegal value. + if (miterlimit < 0.0f) { + TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.", + node->style->stroke.miterlimit, miterlimit); + return; + } + + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit); + node->style->stroke.miterlimit = miterlimit; +} + +static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule); + node->style->fill.fillRule = _toFillRule(value); +} + + +static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->opacity = _toOpacity(value); +} + + +static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity); + node->style->fill.opacity = _toOpacity(value); +} + + +static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->transform = _parseTransformationMatrix(value); +} + + +static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + SvgStyleProperty* style = node->style; + int len = strlen(value); + if (len >= 3 && !strncmp(value, "url", 3)) { + if (style->clipPath.url) free(style->clipPath.url); + style->clipPath.url = _idFromUrl((const char*)(value + 3)); + } +} + + +static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + SvgStyleProperty* style = node->style; + int len = strlen(value); + if (len >= 3 && !strncmp(value, "url", 3)) { + if (style->mask.url) free(style->mask.url); + style->mask.url = _idFromUrl((const char*)(value + 3)); + } +} + + +static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->node.mask.type = _toMaskType(value); +} + + +static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + //TODO : The display attribute can have various values as well as "none". + // The default is "inline" which means visible and "none" means invisible. + // Depending on the type of node, additional functionality may be required. + // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display + if (!strcmp(value, "none")) node->display = false; + else node->display = true; +} + + +static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) +{ + node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder); + node->style->paintOrder = _toPaintOrder(value); +} + + +static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value) +{ + auto cssClass = &node->style->cssClass; + + if (*cssClass && value) free(*cssClass); + *cssClass = _copyId(value); + + bool cssClassFound = false; + + //css styling: tag.name has higher priority than .name + if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass, node->type)) { + cssClassFound = true; + cssCopyStyleAttr(node, cssNode); + } + if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass)) { + cssClassFound = true; + cssCopyStyleAttr(node, cssNode); + } + + if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass); +} + + +typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value); + +#define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag } + + +static constexpr struct +{ + const char* tag; + int sz; + styleMethod tagHandler; + SvgStyleFlags flag; +} styleTags[] = { + STYLE_DEF(color, Color, SvgStyleFlags::Color), + STYLE_DEF(fill, Fill, SvgStyleFlags::Fill), + STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule), + STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity), + STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity), + STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke), + STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth), + STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin), + STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit), + STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap), + STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity), + STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray), + STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset), + STYLE_DEF(transform, Transform, SvgStyleFlags::Transform), + STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath), + STYLE_DEF(mask, Mask, SvgStyleFlags::Mask), + STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType), + STYLE_DEF(display, Display, SvgStyleFlags::Display), + STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder) +}; + + +static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + int sz; + if (!key || !value) return false; + + //Trim the white space + key = _skipSpace(key, nullptr); + value = _skipSpace(value, nullptr); + + sz = strlen(key); + for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) { + if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) { + bool importance = false; + if (auto ptr = strstr(value, "!important")) { + size_t size = ptr - value; + while (size > 0 && isspace(value[size - 1])) { + size--; + } + value = strDuplicate(value, size); + importance = true; + } + if (style) { + if (importance || !(node->style->flagsImportance & styleTags[i].flag)) { + styleTags[i].tagHandler(loader, node, value); + node->style->flags = (node->style->flags | styleTags[i].flag); + } + } else if (!(node->style->flags & styleTags[i].flag)) { + styleTags[i].tagHandler(loader, node, value); + } + if (importance) { + node->style->flagsImportance = (node->style->flags | styleTags[i].flag); + free(const_cast(value)); + } + return true; + } + } + + return false; +} + + +static bool _parseStyleAttr(void* data, const char* key, const char* value) +{ + return _parseStyleAttr(data, key, value, true); +} + + +/* parse g node + * https://www.w3.org/TR/SVG/struct.html#Groups + */ +static bool _attrParseGNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + + if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "transform")) { + node->transform = _parseTransformationMatrix(value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +/* parse clipPath node + * https://www.w3.org/TR/SVG/struct.html#Groups + */ +static bool _attrParseClipPathNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgClipNode* clip = &(node->node.clip); + + if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "transform")) { + node->transform = _parseTransformationMatrix(value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "clipPathUnits")) { + if (!strcmp(value, "objectBoundingBox")) clip->userSpace = false; + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static bool _attrParseMaskNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgMaskNode* mask = &(node->node.mask); + + if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "transform")) { + node->transform = _parseTransformationMatrix(value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "maskContentUnits")) { + if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false; + } else if (!strcmp(key, "mask-type")) { + mask->type = _toMaskType(value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static bool _attrParseCssStyleNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + + if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static bool _attrParseSymbolNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgSymbolNode* symbol = &(node->node.symbol); + + if (!strcmp(key, "viewBox")) { + if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false; + if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false; + symbol->hasViewBox = true; + } else if (!strcmp(key, "width")) { + symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); + symbol->hasWidth = true; + } else if (!strcmp(key, "height")) { + symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); + symbol->hasHeight = true; + } else if (!strcmp(key, "preserveAspectRatio")) { + _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice); + } else if (!strcmp(key, "overflow")) { + if (!strcmp(value, "visible")) symbol->overflowVisible = true; + } else { + return _attrParseGNode(data, key, value); + } + + return true; +} + + +static SvgNode* _createNode(SvgNode* parent, SvgNodeType type) +{ + SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode)); + + if (!node) return nullptr; + + //Default fill property + node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty)); + + if (!node->style) { + free(node); + return nullptr; + } + + //Update the default value of stroke and fill + //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint + node->style->fill.paint.none = false; + //Default fill opacity is 1 + node->style->fill.opacity = 255; + node->style->opacity = 255; + //Default current color is not set + node->style->fill.paint.curColor = false; + node->style->curColorSet = false; + //Default fill rule is nonzero + node->style->fill.fillRule = FillRule::Winding; + + //Default stroke is none + node->style->stroke.paint.none = true; + //Default stroke opacity is 1 + node->style->stroke.opacity = 255; + //Default stroke current color is not set + node->style->stroke.paint.curColor = false; + //Default stroke width is 1 + node->style->stroke.width = 1; + //Default line cap is butt + node->style->stroke.cap = StrokeCap::Butt; + //Default line join is miter + node->style->stroke.join = StrokeJoin::Miter; + node->style->stroke.miterlimit = 4.0f; + node->style->stroke.scale = 1.0; + + node->style->paintOrder = _toPaintOrder("fill stroke"); + + //Default display is true("inline"). + node->display = true; + + node->parent = parent; + node->type = type; + + if (parent) parent->child.push(node); + return node; +} + + +static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func) +{ + if (loader->def && loader->doc->node.doc.defs) return loader->def; + SvgNode* node = _createNode(nullptr, SvgNodeType::Defs); + + loader->def = node; + loader->doc->node.doc.defs = node; + return node; +} + + +static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::G); + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParseGNode, loader); + return loader->svgParse->node; +} + + +static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Doc); + if (!loader->svgParse->node) return nullptr; + SvgDocNode* doc = &(loader->svgParse->node->node.doc); + + loader->svgParse->global.w = 1.0f; + loader->svgParse->global.h = 1.0f; + + doc->align = AspectRatioAlign::XMidYMid; + doc->meetOrSlice = AspectRatioMeetOrSlice::Meet; + doc->viewFlag = SvgViewFlag::None; + func(buf, bufLength, _attrParseSvgNode, loader); + + if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { + if (doc->viewFlag & SvgViewFlag::Width) { + loader->svgParse->global.w = doc->w; + } + if (doc->viewFlag & SvgViewFlag::Height) { + loader->svgParse->global.h = doc->h; + } + } + return loader->svgParse->node; +} + + +static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Mask); + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->node.mask.userSpace = true; + loader->svgParse->node->node.mask.type = SvgMaskType::Luminance; + + func(buf, bufLength, _attrParseMaskNode, loader); + + return loader->svgParse->node; +} + + +static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath); + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->display = false; + loader->svgParse->node->node.clip.userSpace = true; + + func(buf, bufLength, _attrParseClipPathNode, loader); + + return loader->svgParse->node; +} + + +static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::CssStyle); + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParseCssStyleNode, loader); + + return loader->svgParse->node; +} + + +static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol); + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->display = false; + loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid; + loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet; + loader->svgParse->node->node.symbol.overflowVisible = false; + + loader->svgParse->node->node.symbol.hasViewBox = false; + loader->svgParse->node->node.symbol.hasWidth = false; + loader->svgParse->node->node.symbol.hasHeight = false; + loader->svgParse->node->node.symbol.vx = 0.0f; + loader->svgParse->node->node.symbol.vy = 0.0f; + + func(buf, bufLength, _attrParseSymbolNode, loader); + + return loader->svgParse->node; +} + + +static bool _attrParsePathNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgPathNode* path = &(node->node.path); + + if (!strcmp(key, "d")) { + if (path->path) free(path->path); + //Temporary: need to copy + path->path = _copyId(value); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Path); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParsePathNode, loader); + + return loader->svgParse->node; +} + + +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} circleTags[] = { + {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)}, + {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)}, + {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)} +}; + + +/* parse the attributes for a circle element. + * https://www.w3.org/TR/SVG/shapes.html#CircleElement + */ +static bool _attrParseCircleNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgCircleNode* circle = &(node->node.circle); + unsigned char* array; + int sz = strlen(key); + + array = (unsigned char*)circle; + for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) { + if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) { + *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type); + return true; + } + } + + if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Circle); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParseCircleNode, loader); + return loader->svgParse->node; +} + + +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} ellipseTags[] = { + {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)}, + {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)}, + {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)}, + {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)} +}; + + +/* parse the attributes for an ellipse element. + * https://www.w3.org/TR/SVG/shapes.html#EllipseElement + */ +static bool _attrParseEllipseNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgEllipseNode* ellipse = &(node->node.ellipse); + unsigned char* array; + int sz = strlen(key); + + array = (unsigned char*)ellipse; + for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) { + if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) { + *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type); + return true; + } + } + + if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParseEllipseNode, loader); + return loader->svgParse->node; +} + + +static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon) +{ + float num; + while (_parseNumber(&str, &num)) polygon->pts.push(num); + return true; +} + + +/* parse the attributes for a polygon element. + * https://www.w3.org/TR/SVG/shapes.html#PolylineElement + */ +static bool _attrParsePolygonNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgPolygonNode* polygon = nullptr; + + if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon); + else polygon = &(node->node.polyline); + + if (!strcmp(key, "points")) { + return _attrParsePolygonPoints(value, polygon); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParsePolygonNode, loader); + return loader->svgParse->node; +} + + +static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParsePolygonNode, loader); + return loader->svgParse->node; +} + +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} rectTags[] = { + {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)}, + {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)}, + {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)}, + {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}, + {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)}, + {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)} +}; + + +/* parse the attributes for a rect element. + * https://www.w3.org/TR/SVG/shapes.html#RectElement + */ +static bool _attrParseRectNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgRectNode* rect = &(node->node.rect); + unsigned char* array; + bool ret = true; + int sz = strlen(key); + + array = (unsigned char*)rect; + for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) { + if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) { + *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type); + + //Case if only rx or ry is declared + if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true; + if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true; + + if ((rect->rx >= FLT_EPSILON) && (rect->ry < FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx; + if ((rect->ry >= FLT_EPSILON) && (rect->rx < FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry; + return ret; + } + } + + if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "style")) { + ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else { + ret = _parseStyleAttr(loader, key, value, false); + } + + return ret; +} + + +static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Rect); + + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false; + + func(buf, bufLength, _attrParseRectNode, loader); + return loader->svgParse->node; +} + + +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} lineTags[] = { + {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)}, + {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)}, + {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)}, + {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)} +}; + + +/* parse the attributes for a line element. + * https://www.w3.org/TR/SVG/shapes.html#LineElement + */ +static bool _attrParseLineNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgLineNode* line = &(node->node.line); + unsigned char* array; + int sz = strlen(key); + + array = (unsigned char*)line; + for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) { + if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) { + *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type); + return true; + } + } + + if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else { + return _parseStyleAttr(loader, key, value, false); + } + return true; +} + + +static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Line); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParseLineNode, loader); + return loader->svgParse->node; +} + + +static char* _idFromHref(const char* href) +{ + href = _skipSpace(href, nullptr); + if ((*href) == '#') href++; + return strdup(href); +} + + +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} imageTags[] = { + {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)}, + {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)}, + {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)}, + {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}, +}; + + +/* parse the attributes for a image element. + * https://www.w3.org/TR/SVG/embedded.html#ImageElement + */ +static bool _attrParseImageNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode* node = loader->svgParse->node; + SvgImageNode* image = &(node->node.image); + unsigned char* array; + int sz = strlen(key); + + array = (unsigned char*)image; + for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) { + if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) { + *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type); + return true; + } + } + + if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { + if (image->href && value) free(image->href); + image->href = _idFromHref(value); + } else if (!strcmp(key, "id")) { + if (node->id && value) free(node->id); + node->id = _copyId(value); + } else if (!strcmp(key, "class")) { + _handleCssClassAttr(loader, node, value); + } else if (!strcmp(key, "style")) { + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); + } else if (!strcmp(key, "clip-path")) { + _handleClipPathAttr(loader, node, value); + } else if (!strcmp(key, "mask")) { + _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "transform")) { + node->transform = _parseTransformationMatrix(value); + } else { + return _parseStyleAttr(loader, key, value); + } + return true; +} + + +static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Image); + + if (!loader->svgParse->node) return nullptr; + + func(buf, bufLength, _attrParseImageNode, loader); + return loader->svgParse->node; +} + + +static SvgNode* _getDefsNode(SvgNode* node) +{ + if (!node) return nullptr; + + while (node->parent != nullptr) { + node = node->parent; + } + + if (node->type == SvgNodeType::Doc) return node->node.doc.defs; + if (node->type == SvgNodeType::Defs) return node; + + return nullptr; +} + + +static SvgNode* _findNodeById(SvgNode *node, const char* id) +{ + if (!node) return nullptr; + + SvgNode* result = nullptr; + if (node->id && !strcmp(node->id, id)) return node; + + if (node->child.count > 0) { + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + result = _findNodeById(*child, id); + if (result) break; + } + } + return result; +} + + +static constexpr struct +{ + const char* tag; + SvgParserLengthType type; + int sz; + size_t offset; +} useTags[] = { + {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)}, + {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)}, + {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)}, + {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)} +}; + + +static void _cloneNode(SvgNode* from, SvgNode* parent, int depth); +static bool _attrParseUseNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgNode *defs, *nodeFrom, *node = loader->svgParse->node; + char* id; + + SvgUseNode* use = &(node->node.use); + int sz = strlen(key); + unsigned char* array = (unsigned char*)use; + for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) { + if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) { + *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type); + + if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true; + else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true; + + return true; + } + } + + if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { + id = _idFromHref(value); + defs = _getDefsNode(node); + nodeFrom = _findNodeById(defs, id); + if (nodeFrom) { + _cloneNode(nodeFrom, node, 0); + if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom; + free(id); + } else { + //some svg export software include element at the end of the file + //if so the 'from' element won't be found now and we have to repeat finding + //after the whole file is parsed + _postpone(loader->cloneNodes, node, id); + } + } else { + return _attrParseGNode(data, key, value); + } + return true; +} + + +static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func) +{ + loader->svgParse->node = _createNode(parent, SvgNodeType::Use); + + if (!loader->svgParse->node) return nullptr; + + loader->svgParse->node->node.use.isWidthSet = false; + loader->svgParse->node->node.use.isHeightSet = false; + + func(buf, bufLength, _attrParseUseNode, loader); + return loader->svgParse->node; +} + + +//TODO: Implement 'text' primitive +static constexpr struct +{ + const char* tag; + int sz; + FactoryMethod tagHandler; +} graphicsTags[] = { + {"use", sizeof("use"), _createUseNode}, + {"circle", sizeof("circle"), _createCircleNode}, + {"ellipse", sizeof("ellipse"), _createEllipseNode}, + {"path", sizeof("path"), _createPathNode}, + {"polygon", sizeof("polygon"), _createPolygonNode}, + {"rect", sizeof("rect"), _createRectNode}, + {"polyline", sizeof("polyline"), _createPolylineNode}, + {"line", sizeof("line"), _createLineNode}, + {"image", sizeof("image"), _createImageNode} +}; + + +static constexpr struct +{ + const char* tag; + int sz; + FactoryMethod tagHandler; +} groupTags[] = { + {"defs", sizeof("defs"), _createDefsNode}, + {"g", sizeof("g"), _createGNode}, + {"svg", sizeof("svg"), _createSvgNode}, + {"mask", sizeof("mask"), _createMaskNode}, + {"clipPath", sizeof("clipPath"), _createClipPathNode}, + {"style", sizeof("style"), _createCssStyleNode}, + {"symbol", sizeof("symbol"), _createSymbolNode} +}; + + +#define FIND_FACTORY(Short_Name, Tags_Array) \ + static FactoryMethod \ + _find##Short_Name##Factory(const char* name) \ + { \ + unsigned int i; \ + int sz = strlen(name); \ + \ + for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \ + if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \ + return Tags_Array[i].tagHandler; \ + } \ + } \ + return nullptr; \ + } + +FIND_FACTORY(Group, groupTags) +FIND_FACTORY(Graphics, graphicsTags) + + +FillSpread _parseSpreadValue(const char* value) +{ + auto spread = FillSpread::Pad; + + if (!strcmp(value, "reflect")) { + spread = FillSpread::Reflect; + } else if (!strcmp(value, "repeat")) { + spread = FillSpread::Repeat; + } + + return spread; +} + + +static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage); + if (!loader->svgParse->gradient.parsedFx) { + radial->fx = radial->cx; + radial->isFxPercentage = radial->isCxPercentage; + } +} + + +static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage); + if (!loader->svgParse->gradient.parsedFy) { + radial->fy = radial->cy; + radial->isFyPercentage = radial->isCyPercentage; + } +} + + +static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage); + loader->svgParse->gradient.parsedFx = true; +} + + +static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage); + loader->svgParse->gradient.parsedFy = true; +} + + +static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage); +} + + +static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value) +{ + radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage); +} + + +static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w; +} + + +static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h; +} + + +static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w; +} + + +static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h; +} + + +static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html + if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); +} + + +static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html + if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0)); +} + + +static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isCxPercentage) { + if (userSpace) radial->cx /= loader->svgParse->global.w; + else radial->cx *= loader->svgParse->global.w; + } +} + + +static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isCyPercentage) { + if (userSpace) radial->cy /= loader->svgParse->global.h; + else radial->cy *= loader->svgParse->global.h; + } +} + + +static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isFxPercentage) { + if (userSpace) radial->fx /= loader->svgParse->global.w; + else radial->fx *= loader->svgParse->global.w; + } +} + + +static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isFyPercentage) { + if (userSpace) radial->fy /= loader->svgParse->global.h; + else radial->fy *= loader->svgParse->global.h; + } +} + + +static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isFrPercentage) { + if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); + else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); + } +} + + +static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace) +{ + if (!radial->isRPercentage) { + if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); + else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0); + } +} + + +static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->cx = from->radial->cx; + to->radial->isCxPercentage = from->radial->isCxPercentage; + to->flags = (to->flags | SvgGradientFlags::Cx); +} + + +static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->cy = from->radial->cy; + to->radial->isCyPercentage = from->radial->isCyPercentage; + to->flags = (to->flags | SvgGradientFlags::Cy); +} + + +static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->fx = from->radial->fx; + to->radial->isFxPercentage = from->radial->isFxPercentage; + to->flags = (to->flags | SvgGradientFlags::Fx); +} + + +static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->fy = from->radial->fy; + to->radial->isFyPercentage = from->radial->isFyPercentage; + to->flags = (to->flags | SvgGradientFlags::Fy); +} + + +static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->fr = from->radial->fr; + to->radial->isFrPercentage = from->radial->isFrPercentage; + to->flags = (to->flags | SvgGradientFlags::Fr); +} + + +static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->radial->r = from->radial->r; + to->radial->isRPercentage = from->radial->isRPercentage; + to->flags = (to->flags | SvgGradientFlags::R); +} + + +typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value); +typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from); +typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace); + + +#define RADIAL_DEF(Name, Name1, Flag) \ + { \ +#Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \ + } + + +static constexpr struct +{ + const char* tag; + int sz; + radialMethod tagHandler; + radialInheritMethod tagInheritHandler; + radialMethodRecalc tagRecalc; + radialMethodRecalc tagInheritedRecalc; + SvgGradientFlags flag; +} radialTags[] = { + RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx), + RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy), + RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx), + RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy), + RADIAL_DEF(r, R, SvgGradientFlags::R), + RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr) +}; + + +static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgStyleGradient* grad = loader->svgParse->styleGrad; + SvgRadialGradient* radial = grad->radial; + int sz = strlen(key); + + for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { + if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) { + radialTags[i].tagHandler(loader, radial, value); + grad->flags = (grad->flags | radialTags[i].flag); + return true; + } + } + + if (!strcmp(key, "id")) { + if (grad->id && value) free(grad->id); + grad->id = _copyId(value); + } else if (!strcmp(key, "spreadMethod")) { + grad->spread = _parseSpreadValue(value); + grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); + } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { + if (grad->ref && value) free(grad->ref); + grad->ref = _idFromHref(value); + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; + grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); + } else if (!strcmp(key, "gradientTransform")) { + grad->transform = _parseTransformationMatrix(value); + } else { + return false; + } + + return true; +} + + +static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength) +{ + auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); + loader->svgParse->styleGrad = grad; + + grad->flags = SvgGradientFlags::None; + grad->type = SvgGradientType::Radial; + grad->userSpace = false; + grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient)); + if (!grad->radial) { + grad->clear(); + free(grad); + return nullptr; + } + /** + * Default values of gradient transformed into global percentage + */ + grad->radial->cx = 0.5f; + grad->radial->cy = 0.5f; + grad->radial->fx = 0.5f; + grad->radial->fy = 0.5f; + grad->radial->r = 0.5f; + grad->radial->isCxPercentage = true; + grad->radial->isCyPercentage = true; + grad->radial->isFxPercentage = true; + grad->radial->isFyPercentage = true; + grad->radial->isRPercentage = true; + grad->radial->isFrPercentage = true; + + loader->svgParse->gradient.parsedFx = false; + loader->svgParse->gradient.parsedFy = false; + simpleXmlParseAttributes(buf, bufLength, + _attrParseRadialGradientNode, loader); + + for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { + radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace); + } + + return loader->svgParse->styleGrad; +} + + +static bool _attrParseStopsStyle(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + auto stop = &loader->svgParse->gradStop; + + if (!strcmp(key, "stop-opacity")) { + stop->a = _toOpacity(value); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); + } else if (!strcmp(key, "stop-color")) { + _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); + } else { + return false; + } + + return true; +} + + +static bool _attrParseStops(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + auto stop = &loader->svgParse->gradStop; + + if (!strcmp(key, "offset")) { + stop->offset = _toOffset(value); + } else if (!strcmp(key, "stop-opacity")) { + if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) { + stop->a = _toOpacity(value); + } + } else if (!strcmp(key, "stop-color")) { + if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { + _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); + } + } else if (!strcmp(key, "style")) { + simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data); + } else { + return false; + } + + return true; +} + + +static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) +{ + linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage); +} + + +static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) +{ + linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage); +} + + +static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) +{ + linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage); +} + + +static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value) +{ + linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage); +} + + +static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w; +} + + +static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h; +} + + +static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w; +} + + +static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h; +} + + +static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isX1Percentage) { + if (userSpace) linear->x1 /= loader->svgParse->global.w; + else linear->x1 *= loader->svgParse->global.w; + } +} + + +static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isX2Percentage) { + if (userSpace) linear->x2 /= loader->svgParse->global.w; + else linear->x2 *= loader->svgParse->global.w; + } +} + + +static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isY1Percentage) { + if (userSpace) linear->y1 /= loader->svgParse->global.h; + else linear->y1 *= loader->svgParse->global.h; + } +} + + +static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace) +{ + if (!linear->isY2Percentage) { + if (userSpace) linear->y2 /= loader->svgParse->global.h; + else linear->y2 *= loader->svgParse->global.h; + } +} + + +static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->x1 = from->linear->x1; + to->linear->isX1Percentage = from->linear->isX1Percentage; + to->flags = (to->flags | SvgGradientFlags::X1); +} + + +static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->x2 = from->linear->x2; + to->linear->isX2Percentage = from->linear->isX2Percentage; + to->flags = (to->flags | SvgGradientFlags::X2); +} + + +static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->y1 = from->linear->y1; + to->linear->isY1Percentage = from->linear->isY1Percentage; + to->flags = (to->flags | SvgGradientFlags::Y1); +} + + +static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from) +{ + to->linear->y2 = from->linear->y2; + to->linear->isY2Percentage = from->linear->isY2Percentage; + to->flags = (to->flags | SvgGradientFlags::Y2); +} + + +typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value); +typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from); +typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace); + + +#define LINEAR_DEF(Name, Name1, Flag) \ + { \ +#Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \ + } + + +static constexpr struct +{ + const char* tag; + int sz; + Linear_Method tagHandler; + Linear_Inherit_Method tagInheritHandler; + Linear_Method_Recalc tagRecalc; + Linear_Method_Recalc tagInheritedRecalc; + SvgGradientFlags flag; +} linear_tags[] = { + LINEAR_DEF(x1, X1, SvgGradientFlags::X1), + LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1), + LINEAR_DEF(x2, X2, SvgGradientFlags::X2), + LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2) +}; + + +static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + SvgStyleGradient* grad = loader->svgParse->styleGrad; + SvgLinearGradient* linear = grad->linear; + int sz = strlen(key); + + for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { + if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) { + linear_tags[i].tagHandler(loader, linear, value); + grad->flags = (grad->flags | linear_tags[i].flag); + return true; + } + } + + if (!strcmp(key, "id")) { + if (grad->id && value) free(grad->id); + grad->id = _copyId(value); + } else if (!strcmp(key, "spreadMethod")) { + grad->spread = _parseSpreadValue(value); + grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); + } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { + if (grad->ref && value) free(grad->ref); + grad->ref = _idFromHref(value); + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; + grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); + } else if (!strcmp(key, "gradientTransform")) { + grad->transform = _parseTransformationMatrix(value); + } else { + return false; + } + + return true; +} + + +static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength) +{ + auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); + loader->svgParse->styleGrad = grad; + + grad->flags = SvgGradientFlags::None; + grad->type = SvgGradientType::Linear; + grad->userSpace = false; + grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient)); + if (!grad->linear) { + grad->clear(); + free(grad); + return nullptr; + } + /** + * Default value of x2 is 100% - transformed to the global percentage + */ + grad->linear->x2 = 1.0f; + grad->linear->isX2Percentage = true; + + simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader); + + for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { + linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace); + } + + return loader->svgParse->styleGrad; +} + + +#define GRADIENT_DEF(Name, Name1) \ + { \ +#Name, sizeof(#Name), _create##Name1 \ + } + + +/** + * In the case when the gradients lengths are given as numbers (not percentages) + * in the current user coordinate system, they are recalculated into percentages + * related to the canvas width and height. + */ +static constexpr struct +{ + const char* tag; + int sz; + GradientFactoryMethod tagHandler; +} gradientTags[] = { + GRADIENT_DEF(linearGradient, LinearGradient), + GRADIENT_DEF(radialGradient, RadialGradient) +}; + + +static GradientFactoryMethod _findGradientFactory(const char* name) +{ + int sz = strlen(name); + + for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) { + if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) { + return gradientTags[i].tagHandler; + } + } + return nullptr; +} + + +static void _cloneGradStops(Array& dst, const Array& src) +{ + for (uint32_t i = 0; i < src.count; ++i) { + dst.push(src[i]); + } +} + + +static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from) +{ + if (!to || !from) return; + + if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) { + to->spread = from->spread; + to->flags = (to->flags | SvgGradientFlags::SpreadMethod); + } + bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits); + if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) { + to->userSpace = from->userSpace; + to->flags = (to->flags | SvgGradientFlags::GradientUnits); + } + + if (!to->transform && from->transform) { + to->transform = (Matrix*)malloc(sizeof(Matrix)); + if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix)); + } + + if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) { + for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { + bool coordSet = to->flags & linear_tags[i].flag; + if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) { + linear_tags[i].tagInheritHandler(to, from); + } + + //GradUnits not set directly, coord set + if (!gradUnitSet && coordSet) { + linear_tags[i].tagRecalc(loader, to->linear, to->userSpace); + } + //GradUnits set, coord not set directly + if (to->userSpace == from->userSpace) continue; + if (gradUnitSet && !coordSet) { + linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace); + } + } + } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) { + for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { + bool coordSet = (to->flags & radialTags[i].flag); + if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) { + radialTags[i].tagInheritHandler(to, from); + } + + //GradUnits not set directly, coord set + if (!gradUnitSet && coordSet) { + radialTags[i].tagRecalc(loader, to->radial, to->userSpace); + } + //GradUnits set, coord not set directly + if (to->userSpace == from->userSpace) continue; + if (gradUnitSet && !coordSet) { + radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace); + } + } + } + + if (to->stops.empty()) _cloneGradStops(to->stops, from->stops); +} + + +static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from) +{ + if (!from) return nullptr; + + auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient))); + if (!grad) return nullptr; + + grad->type = from->type; + grad->id = from->id ? _copyId(from->id) : nullptr; + grad->ref = from->ref ? _copyId(from->ref) : nullptr; + grad->spread = from->spread; + grad->userSpace = from->userSpace; + grad->flags = from->flags; + + if (from->transform) { + grad->transform = (Matrix*)calloc(1, sizeof(Matrix)); + if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix)); + } + + if (grad->type == SvgGradientType::Linear) { + grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient)); + if (!grad->linear) goto error_grad_alloc; + memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient)); + } else if (grad->type == SvgGradientType::Radial) { + grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient)); + if (!grad->radial) goto error_grad_alloc; + memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient)); + } + + _cloneGradStops(grad->stops, from->stops); + + return grad; + + error_grad_alloc: + if (grad) { + grad->clear(); + free(grad); + } + return nullptr; +} + + +static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent) +{ + if (parent == nullptr) return; + //Inherit the property of parent if not present in child. + if (!child->curColorSet) { + child->color = parent->color; + child->curColorSet = parent->curColorSet; + } + if (!(child->flags & SvgStyleFlags::PaintOrder)) { + child->paintOrder = parent->paintOrder; + } + //Fill + if (!(child->fill.flags & SvgFillFlags::Paint)) { + child->fill.paint.color = parent->fill.paint.color; + child->fill.paint.none = parent->fill.paint.none; + child->fill.paint.curColor = parent->fill.paint.curColor; + if (parent->fill.paint.url) { + if (child->fill.paint.url) free(child->fill.paint.url); + child->fill.paint.url = _copyId(parent->fill.paint.url); + } + } + if (!(child->fill.flags & SvgFillFlags::Opacity)) { + child->fill.opacity = parent->fill.opacity; + } + if (!(child->fill.flags & SvgFillFlags::FillRule)) { + child->fill.fillRule = parent->fill.fillRule; + } + //Stroke + if (!(child->stroke.flags & SvgStrokeFlags::Paint)) { + child->stroke.paint.color = parent->stroke.paint.color; + child->stroke.paint.none = parent->stroke.paint.none; + child->stroke.paint.curColor = parent->stroke.paint.curColor; + if (parent->stroke.paint.url) { + if (child->stroke.paint.url) free(child->stroke.paint.url); + child->stroke.paint.url = _copyId(parent->stroke.paint.url); + } + } + if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) { + child->stroke.opacity = parent->stroke.opacity; + } + if (!(child->stroke.flags & SvgStrokeFlags::Width)) { + child->stroke.width = parent->stroke.width; + } + if (!(child->stroke.flags & SvgStrokeFlags::Dash)) { + if (parent->stroke.dash.array.count > 0) { + child->stroke.dash.array.clear(); + child->stroke.dash.array.reserve(parent->stroke.dash.array.count); + for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) { + child->stroke.dash.array.push(parent->stroke.dash.array[i]); + } + } + } + if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) { + child->stroke.dash.offset = parent->stroke.dash.offset; + } + if (!(child->stroke.flags & SvgStrokeFlags::Cap)) { + child->stroke.cap = parent->stroke.cap; + } + if (!(child->stroke.flags & SvgStrokeFlags::Join)) { + child->stroke.join = parent->stroke.join; + } + if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) { + child->stroke.miterlimit = parent->stroke.miterlimit; + } +} + + +static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) +{ + if (from == nullptr) return; + //Copy the properties of 'from' only if they were explicitly set (not the default ones). + if (from->curColorSet) { + to->color = from->color; + to->curColorSet = true; + } + if (from->flags & SvgStyleFlags::PaintOrder) { + to->paintOrder = from->paintOrder; + } + //Fill + to->fill.flags = (to->fill.flags | from->fill.flags); + if (from->fill.flags & SvgFillFlags::Paint) { + to->fill.paint.color = from->fill.paint.color; + to->fill.paint.none = from->fill.paint.none; + to->fill.paint.curColor = from->fill.paint.curColor; + if (from->fill.paint.url) { + if (to->fill.paint.url) free(to->fill.paint.url); + to->fill.paint.url = _copyId(from->fill.paint.url); + } + } + if (from->fill.flags & SvgFillFlags::Opacity) { + to->fill.opacity = from->fill.opacity; + } + if (from->fill.flags & SvgFillFlags::FillRule) { + to->fill.fillRule = from->fill.fillRule; + } + //Stroke + to->stroke.flags = (to->stroke.flags | from->stroke.flags); + if (from->stroke.flags & SvgStrokeFlags::Paint) { + to->stroke.paint.color = from->stroke.paint.color; + to->stroke.paint.none = from->stroke.paint.none; + to->stroke.paint.curColor = from->stroke.paint.curColor; + if (from->stroke.paint.url) { + if (to->stroke.paint.url) free(to->stroke.paint.url); + to->stroke.paint.url = _copyId(from->stroke.paint.url); + } + } + if (from->stroke.flags & SvgStrokeFlags::Opacity) { + to->stroke.opacity = from->stroke.opacity; + } + if (from->stroke.flags & SvgStrokeFlags::Width) { + to->stroke.width = from->stroke.width; + } + if (from->stroke.flags & SvgStrokeFlags::Dash) { + if (from->stroke.dash.array.count > 0) { + to->stroke.dash.array.clear(); + to->stroke.dash.array.reserve(from->stroke.dash.array.count); + for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { + to->stroke.dash.array.push(from->stroke.dash.array[i]); + } + } + } + if (from->stroke.flags & SvgStrokeFlags::DashOffset) { + to->stroke.dash.offset = from->stroke.dash.offset; + } + if (from->stroke.flags & SvgStrokeFlags::Cap) { + to->stroke.cap = from->stroke.cap; + } + if (from->stroke.flags & SvgStrokeFlags::Join) { + to->stroke.join = from->stroke.join; + } + if (from->stroke.flags & SvgStrokeFlags::Miterlimit) { + to->stroke.miterlimit = from->stroke.miterlimit; + } +} + + +static void _copyAttr(SvgNode* to, const SvgNode* from) +{ + //Copy matrix attribute + if (from->transform) { + to->transform = (Matrix*)malloc(sizeof(Matrix)); + if (to->transform) *to->transform = *from->transform; + } + //Copy style attribute + _styleCopy(to->style, from->style); + to->style->flags = (to->style->flags | from->style->flags); + if (from->style->clipPath.url) { + if (to->style->clipPath.url) free(to->style->clipPath.url); + to->style->clipPath.url = strdup(from->style->clipPath.url); + } + if (from->style->mask.url) { + if (to->style->mask.url) free(to->style->mask.url); + to->style->mask.url = strdup(from->style->mask.url); + } + + //Copy node attribute + switch (from->type) { + case SvgNodeType::Circle: { + to->node.circle.cx = from->node.circle.cx; + to->node.circle.cy = from->node.circle.cy; + to->node.circle.r = from->node.circle.r; + break; + } + case SvgNodeType::Ellipse: { + to->node.ellipse.cx = from->node.ellipse.cx; + to->node.ellipse.cy = from->node.ellipse.cy; + to->node.ellipse.rx = from->node.ellipse.rx; + to->node.ellipse.ry = from->node.ellipse.ry; + break; + } + case SvgNodeType::Rect: { + to->node.rect.x = from->node.rect.x; + to->node.rect.y = from->node.rect.y; + to->node.rect.w = from->node.rect.w; + to->node.rect.h = from->node.rect.h; + to->node.rect.rx = from->node.rect.rx; + to->node.rect.ry = from->node.rect.ry; + to->node.rect.hasRx = from->node.rect.hasRx; + to->node.rect.hasRy = from->node.rect.hasRy; + break; + } + case SvgNodeType::Line: { + to->node.line.x1 = from->node.line.x1; + to->node.line.y1 = from->node.line.y1; + to->node.line.x2 = from->node.line.x2; + to->node.line.y2 = from->node.line.y2; + break; + } + case SvgNodeType::Path: { + if (from->node.path.path) { + if (to->node.path.path) free(to->node.path.path); + to->node.path.path = strdup(from->node.path.path); + } + break; + } + case SvgNodeType::Polygon: { + if ((to->node.polygon.pts.count = from->node.polygon.pts.count)) { + to->node.polygon.pts = from->node.polygon.pts; + } + break; + } + case SvgNodeType::Polyline: { + if ((to->node.polyline.pts.count = from->node.polyline.pts.count)) { + to->node.polyline.pts = from->node.polyline.pts; + } + break; + } + case SvgNodeType::Image: { + to->node.image.x = from->node.image.x; + to->node.image.y = from->node.image.y; + to->node.image.w = from->node.image.w; + to->node.image.h = from->node.image.h; + if (from->node.image.href) { + if (to->node.image.href) free(to->node.image.href); + to->node.image.href = strdup(from->node.image.href); + } + break; + } + case SvgNodeType::Use: { + to->node.use.x = from->node.use.x; + to->node.use.y = from->node.use.y; + to->node.use.w = from->node.use.w; + to->node.use.h = from->node.use.h; + to->node.use.isWidthSet = from->node.use.isWidthSet; + to->node.use.isHeightSet = from->node.use.isHeightSet; + to->node.use.symbol = from->node.use.symbol; + break; + } + default: { + break; + } + } +} + + +static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) +{ + /* Exception handling: Prevent invalid SVG data input. + The size is the arbitrary value, we need an experimental size. */ + if (depth == 8192) { + TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); + return; + } + + SvgNode* newNode; + if (!from || !parent || from == parent) return; + + newNode = _createNode(parent, from->type); + if (!newNode) return; + + _styleInherit(newNode->style, parent->style); + _copyAttr(newNode, from); + + auto child = from->child.data; + for (uint32_t i = 0; i < from->child.count; ++i, ++child) { + _cloneNode(*child, newNode, depth + 1); + } +} + + +static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) +{ + for (uint32_t i = 0; i < cloneNodes->count; ++i) { + auto nodeIdPair = (*cloneNodes)[i]; + auto defs = _getDefsNode(nodeIdPair.node); + auto nodeFrom = _findNodeById(defs, nodeIdPair.id); + if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id); + _cloneNode(nodeFrom, nodeIdPair.node, 0); + if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) { + nodeIdPair.node->node.use.symbol = nodeFrom; + } + free(nodeIdPair.id); + } +} + + +static constexpr struct +{ + const char* tag; + size_t sz; +} popArray[] = { + {"g", sizeof("g")}, + {"svg", sizeof("svg")}, + {"defs", sizeof("defs")}, + {"mask", sizeof("mask")}, + {"clipPath", sizeof("clipPath")}, + {"style", sizeof("style")}, + {"symbol", sizeof("symbol")} +}; + + +static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content) +{ + content = _skipSpace(content, nullptr); + + for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) { + if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) { + loader->stack.pop(); + break; + } + } + + loader->level--; +} + + +static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty) +{ + const char* attrs = nullptr; + int attrsLength = 0; + int sz = length; + char tagName[20] = ""; + FactoryMethod method; + GradientFactoryMethod gradientMethod; + SvgNode *node = nullptr, *parent = nullptr; + loader->level++; + attrs = simpleXmlFindAttributesTag(content, length); + + if (!attrs) { + //Parse the empty tag + attrs = content; + while ((attrs != nullptr) && *attrs != '>') attrs++; + if (empty) attrs--; + } + + if (attrs) { + //Find out the tag name starting from content till sz length + sz = attrs - content; + while ((sz > 0) && (isspace(content[sz - 1]))) sz--; + if ((unsigned)sz >= sizeof(tagName)) return; + strncpy(tagName, content, sz); + tagName[sz] = '\0'; + attrsLength = length - sz; + } + + if ((method = _findGroupFactory(tagName))) { + //Group + if (empty) return; + if (!loader->doc) { + if (strcmp(tagName, "svg")) return; //Not a valid svg document + node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); + loader->doc = node; + } else { + if (!strcmp(tagName, "svg")) return; //Already loaded (SvgNodeType::Doc) tag + if (loader->stack.count > 0) parent = loader->stack.last(); + else parent = loader->doc; + if (!strcmp(tagName, "style")) { + // TODO: For now only the first style node is saved. After the css id selector + // is introduced this if condition shouldin't be necessary any more + if (!loader->cssStyle) { + node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); + loader->cssStyle = node; + loader->doc->node.doc.style = node; + loader->style = true; + } + } else { + node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); + } + } + + if (!node) return; + if (node->type != SvgNodeType::Defs || !empty) { + loader->stack.push(node); + } + } else if ((method = _findGraphicsFactory(tagName))) { + if (loader->stack.count > 0) parent = loader->stack.last(); + else parent = loader->doc; + node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); + } else if ((gradientMethod = _findGradientFactory(tagName))) { + SvgStyleGradient* gradient; + gradient = gradientMethod(loader, attrs, attrsLength); + //FIXME: The current parsing structure does not distinguish end tags. + // There is no way to know if the currently parsed gradient is in defs. + // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs. + // But finally, the loader has a gradient style list regardless of defs. + // This is only to support this when multiple gradients are declared, even if no defs are declared. + // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs + if (loader->def && loader->doc->node.doc.defs) { + loader->def->node.defs.gradients.push(gradient); + } else { + loader->gradients.push(gradient); + } + loader->latestGradient = gradient; + } else if (!strcmp(tagName, "stop")) { + if (!loader->latestGradient) { + TVGLOG("SVG", "Stop element is used outside of the Gradient element"); + return; + } + /* default value for opacity */ + loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255}; + loader->svgParse->flags = SvgStopStyleFlags::StopDefault; + simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader); + loader->latestGradient->stops.push(loader->svgParse->gradStop); + } else if (!isIgnoreUnsupportedLogElements(tagName)) { + TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName); + } +} + + +static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length) +{ + char* tag; + char* name; + const char* attrs = nullptr; + unsigned int attrsLength = 0; + + FactoryMethod method; + GradientFactoryMethod gradientMethod; + SvgNode *node = nullptr; + + while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) { + if ((method = _findGroupFactory(tag))) { + if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); + } else if ((method = _findGraphicsFactory(tag))) { + if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); + } else if ((gradientMethod = _findGradientFactory(tag))) { + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); + } else if (!strcmp(tag, "stop")) { + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); + } else if (!strcmp(tag, "all")) { + if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); + } else if (!isIgnoreUnsupportedLogElements(tag)) { + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); + } + + length -= next - content; + content = next; + + free(tag); + free(name); + } + loader->style = false; +} + + +static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + + switch (type) { + case SimpleXMLType::Open: { + _svgLoaderParserXmlOpen(loader, content, length, false); + break; + } + case SimpleXMLType::OpenEmpty: { + _svgLoaderParserXmlOpen(loader, content, length, true); + break; + } + case SimpleXMLType::Close: { + _svgLoaderParserXmlClose(loader, content); + break; + } + case SimpleXMLType::Data: + case SimpleXMLType::CData: { + if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length); + break; + } + case SimpleXMLType::DoctypeChild: { + break; + } + case SimpleXMLType::Ignored: + case SimpleXMLType::Comment: + case SimpleXMLType::Doctype: { + break; + } + default: { + break; + } + } + + return true; +} + + +static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node) +{ +#ifdef THORVG_LOG_ENABLED + auto type = simpleXmlNodeTypeToString(node->type); + + if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type); + if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type); + if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type); + + switch (node->type) { + case SvgNodeType::Path: { + if (!node->node.path.path) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type); + break; + } + case SvgNodeType::Ellipse: { + if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type); + break; + } + case SvgNodeType::Polygon: + case SvgNodeType::Polyline: { + if (node->node.polygon.pts.count < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type); + break; + } + case SvgNodeType::Circle: { + if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type); + break; + } + case SvgNodeType::Rect: { + if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type); + break; + } + case SvgNodeType::Line: { + if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type); + break; + } + default: break; + } +#endif +} + + +static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle) +{ + _styleInherit(node->style, parentStyle); + _inefficientNodeCheck(node); + + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + _updateStyle(*child, node->style); + } +} + + +static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array* gradients, const char* id) +{ + SvgStyleGradient* result = nullptr; + + auto gradList = gradients->data; + + for (uint32_t i = 0; i < gradients->count; ++i) { + if ((*gradList)->id && !strcmp((*gradList)->id, id)) { + result = _cloneGradient(*gradList); + break; + } + ++gradList; + } + + if (result && result->ref) { + gradList = gradients->data; + for (uint32_t i = 0; i < gradients->count; ++i) { + if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) { + _inheritGradient(loader, result, *gradList); + break; + } + ++gradList; + } + } + + return result; +} + + +static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array* gradients) +{ + if (node->child.count > 0) { + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + _updateGradient(loader, *child, gradients); + } + } else { + if (node->style->fill.paint.url) { + auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url); + if (newGrad) { + if (node->style->fill.paint.gradient) { + node->style->fill.paint.gradient->clear(); + free(node->style->fill.paint.gradient); + } + node->style->fill.paint.gradient = newGrad; + } + } + if (node->style->stroke.paint.url) { + auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url); + if (newGrad) { + if (node->style->stroke.paint.gradient) { + node->style->stroke.paint.gradient->clear(); + free(node->style->stroke.paint.gradient); + } + node->style->stroke.paint.gradient = newGrad; + } + } + } +} + + +static void _updateComposite(SvgNode* node, SvgNode* root) +{ + if (node->style->clipPath.url && !node->style->clipPath.node) { + SvgNode* findResult = _findNodeById(root, node->style->clipPath.url); + if (findResult) node->style->clipPath.node = findResult; + } + if (node->style->mask.url && !node->style->mask.node) { + SvgNode* findResult = _findNodeById(root, node->style->mask.url); + if (findResult) node->style->mask.node = findResult; + } + if (node->child.count > 0) { + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + _updateComposite(*child, root); + } + } +} + + +static void _freeNodeStyle(SvgStyleProperty* style) +{ + if (!style) return; + + //style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode. + free(style->clipPath.url); + free(style->mask.url); + free(style->cssClass); + + if (style->fill.paint.gradient) { + style->fill.paint.gradient->clear(); + free(style->fill.paint.gradient); + } + if (style->stroke.paint.gradient) { + style->stroke.paint.gradient->clear(); + free(style->stroke.paint.gradient); + } + free(style->fill.paint.url); + free(style->stroke.paint.url); + style->stroke.dash.array.reset(); + free(style); +} + + +static void _freeNode(SvgNode* node) +{ + if (!node) return; + + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + _freeNode(*child); + } + node->child.reset(); + + free(node->id); + free(node->transform); + _freeNodeStyle(node->style); + switch (node->type) { + case SvgNodeType::Path: { + free(node->node.path.path); + break; + } + case SvgNodeType::Polygon: { + free(node->node.polygon.pts.data); + break; + } + case SvgNodeType::Polyline: { + free(node->node.polyline.pts.data); + break; + } + case SvgNodeType::Doc: { + _freeNode(node->node.doc.defs); + _freeNode(node->node.doc.style); + break; + } + case SvgNodeType::Defs: { + auto gradients = node->node.defs.gradients.data; + for (size_t i = 0; i < node->node.defs.gradients.count; ++i) { + (*gradients)->clear(); + free(*gradients); + ++gradients; + } + node->node.defs.gradients.reset(); + break; + } + case SvgNodeType::Image: { + free(node->node.image.href); + break; + } + default: { + break; + } + } + free(node); +} + + +static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length) +{ + const char* attrs = nullptr; + int sz = length; + char tagName[20] = ""; + FactoryMethod method; + SvgNode *node = nullptr; + int attrsLength = 0; + loader->level++; + attrs = simpleXmlFindAttributesTag(content, length); + + if (!attrs) { + //Parse the empty tag + attrs = content; + while ((attrs != nullptr) && *attrs != '>') attrs++; + } + + if (attrs) { + sz = attrs - content; + while ((sz > 0) && (isspace(content[sz - 1]))) sz--; + if ((unsigned)sz >= sizeof(tagName)) return false; + strncpy(tagName, content, sz); + tagName[sz] = '\0'; + attrsLength = length - sz; + } + + if ((method = _findGroupFactory(tagName))) { + if (!loader->doc) { + if (strcmp(tagName, "svg")) return true; //Not a valid svg document + node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes); + loader->doc = node; + loader->stack.push(node); + return false; + } + } + return true; +} + + +static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length) +{ + SvgLoaderData* loader = (SvgLoaderData*)data; + bool res = true;; + + switch (type) { + case SimpleXMLType::Open: + case SimpleXMLType::OpenEmpty: { + //If 'res' is false, it means tag is found. + res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length); + break; + } + default: { + break; + } + } + + return res; +} + + +void SvgLoader::clear(bool all) +{ + //flush out the intermediate data + free(loaderData.svgParse); + loaderData.svgParse = nullptr; + + for (auto gradient = loaderData.gradients.data; gradient < loaderData.gradients.end(); ++gradient) { + (*gradient)->clear(); + free(*gradient); + } + loaderData.gradients.reset(); + + _freeNode(loaderData.doc); + loaderData.doc = nullptr; + loaderData.stack.reset(); + + if (!all) return; + + for (auto p = loaderData.images.data; p < loaderData.images.end(); ++p) { + free(*p); + } + loaderData.images.reset(); + + if (copy) free((char*)content); + size = 0; + content = nullptr; + copy = false; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +SvgLoader::SvgLoader() +{ +} + + +SvgLoader::~SvgLoader() +{ + close(); +} + + +void SvgLoader::run(unsigned tid) +{ + //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering + if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { + TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); + root = Scene::gen(); + return; + } + + if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return; + + if (loaderData.doc) { + auto defs = loaderData.doc->node.doc.defs; + + if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle); + if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle); + + if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc); + + _updateComposite(loaderData.doc, loaderData.doc); + if (defs) _updateComposite(loaderData.doc, defs); + + _updateStyle(loaderData.doc, nullptr); + if (defs) _updateStyle(defs, nullptr); + + if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients); + if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients); + } + root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag); + + //In case no viewbox and width/height data is provided the completion of loading + //has to be forced, in order to establish this data based on the whole picture. + if (!(viewFlag & SvgViewFlag::Viewbox)) { + //Override viewbox & size again after svg loading. + vx = loaderData.doc->node.doc.vx; + vy = loaderData.doc->node.doc.vy; + vw = loaderData.doc->node.doc.vw; + vh = loaderData.doc->node.doc.vh; + w = loaderData.doc->node.doc.w; + h = loaderData.doc->node.doc.h; + } + + clear(false); +} + + +bool SvgLoader::header() +{ + //For valid check, only tag is parsed first. + //If the tag is found, the loaded file is valid and stores viewbox information. + //After that, the remaining content data is parsed in order with async. + loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser)); + if (!loaderData.svgParse) return false; + + loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault; + viewFlag = SvgViewFlag::None; + + simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData)); + + if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) { + viewFlag = loaderData.doc->node.doc.viewFlag; + align = loaderData.doc->node.doc.align; + meetOrSlice = loaderData.doc->node.doc.meetOrSlice; + + if (viewFlag & SvgViewFlag::Viewbox) { + vx = loaderData.doc->node.doc.vx; + vy = loaderData.doc->node.doc.vy; + vw = loaderData.doc->node.doc.vw; + vh = loaderData.doc->node.doc.vh; + + if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w; + else { + w = loaderData.doc->node.doc.vw; + if (viewFlag & SvgViewFlag::WidthInPercent) { + w *= loaderData.doc->node.doc.w; + viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent); + } + viewFlag = (viewFlag | SvgViewFlag::Width); + } + if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h; + else { + h = loaderData.doc->node.doc.vh; + if (viewFlag & SvgViewFlag::HeightInPercent) { + h *= loaderData.doc->node.doc.h; + viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent); + } + viewFlag = (viewFlag | SvgViewFlag::Height); + } + //In case no viewbox and width/height data is provided the completion of loading + //has to be forced, in order to establish this data based on the whole picture. + } else { + //Before loading, set default viewbox & size if they are empty + vx = vy = 0.0f; + if (viewFlag & SvgViewFlag::Width) { + vw = w = loaderData.doc->node.doc.w; + } else { + vw = 1.0f; + if (viewFlag & SvgViewFlag::WidthInPercent) { + w = loaderData.doc->node.doc.w; + } else w = 1.0f; + } + + if (viewFlag & SvgViewFlag::Height) { + vh = h = loaderData.doc->node.doc.h; + } else { + vh = 1.0f; + if (viewFlag & SvgViewFlag::HeightInPercent) { + h = loaderData.doc->node.doc.h; + } else h = 1.0f; + } + + run(0); + } + + return true; + } + + TVGLOG("SVG", "No SVG File. There is no "); + return false; +} + + +bool SvgLoader::open(const char* data, uint32_t size, bool copy) +{ + clear(); + + if (copy) { + content = (char*)malloc(size); + if (!content) return false; + memcpy((char*)content, data, size); + } else content = data; + + this->size = size; + this->copy = copy; + + return header(); +} + + +bool SvgLoader::open(const string& path) +{ + clear(); + + ifstream f; + f.open(path); + + if (!f.is_open()) return false; + + svgPath = path; + getline(f, filePath, '\0'); + f.close(); + + if (filePath.empty()) return false; + + content = filePath.c_str(); + size = filePath.size(); + + return header(); +} + + +bool SvgLoader::resize(Paint* paint, float w, float h) +{ + if (!paint) return false; + + auto sx = w / this->w; + auto sy = h / this->h; + Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1}; + paint->transform(m); + + return true; +} + + +bool SvgLoader::read() +{ + if (!content || size == 0) return false; + + //the loading has been already completed in header() + if (root) return true; + + TaskScheduler::request(this); + + return true; +} + + +bool SvgLoader::close() +{ + this->done(); + + clear(); + + return true; +} + + +unique_ptr SvgLoader::paint() +{ + this->done(); + return std::move(root); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.h b/project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.h new file mode 100644 index 000000000..38ead0ebc --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgLoader.h @@ -0,0 +1,75 @@ +/* + * 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_SVG_LOADER_H_ +#define _TVG_SVG_LOADER_H_ + +#include "tvgTaskScheduler.h" +#include "tvgSvgLoaderCommon.h" + +class SvgLoader : public LoadModule, public Task +{ +public: + string filePath; + string svgPath = ""; + const char* content = nullptr; + uint32_t size = 0; + + SvgLoaderData loaderData; + unique_ptr root; + + bool copy = false; + + SvgLoader(); + ~SvgLoader(); + + using LoadModule::open; + bool open(const string& path) override; + bool open(const char* data, uint32_t size, bool copy) override; + bool resize(Paint* paint, float w, float h) override; + bool read() override; + bool close() override; + + unique_ptr paint() override; + +private: + SvgViewFlag viewFlag = SvgViewFlag::None; + AspectRatioAlign align = AspectRatioAlign::XMidYMid; + AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; + float vx = 0; + float vy = 0; + float vw = 0; + float vh = 0; + + bool header(); + void clear(bool all = true); + void run(unsigned tid) override; +}; + + +#endif //_TVG_SVG_LOADER_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgLoaderCommon.h b/project/gui/lvgl/src/libs/thorvg/tvgSvgLoaderCommon.h new file mode 100644 index 000000000..6df7237d7 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgLoaderCommon.h @@ -0,0 +1,576 @@ +/* + * 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_SVG_LOADER_COMMON_H_ +#define _TVG_SVG_LOADER_COMMON_H_ + +#include "tvgCommon.h" +#include "tvgArray.h" + +struct SvgNode; +struct SvgStyleGradient; + +//NOTE: Please update simpleXmlNodeTypeToString() as well. +enum class SvgNodeType +{ + Doc, + G, + Defs, + Animation, + Arc, + Circle, + Ellipse, + Image, + Line, + Path, + Polygon, + Polyline, + Rect, + Text, + TextArea, + Tspan, + Use, + Video, + ClipPath, + Mask, + CssStyle, + Symbol, + Unknown +}; + +/* +// TODO - remove? +enum class SvgLengthType +{ + Percent, + Px, + Pc, + Pt, + Mm, + Cm, + In, +}; +*/ + +enum class SvgFillFlags +{ + Paint = 0x01, + Opacity = 0x02, + Gradient = 0x04, + FillRule = 0x08, + ClipPath = 0x16 +}; + +constexpr bool operator &(SvgFillFlags a, SvgFillFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b) +{ + return SvgFillFlags(int(a) | int(b)); +} + +enum class SvgStrokeFlags +{ + Paint = 0x1, + Opacity = 0x2, + Gradient = 0x4, + Scale = 0x8, + Width = 0x10, + Cap = 0x20, + Join = 0x40, + Dash = 0x80, + Miterlimit = 0x100, + DashOffset = 0x200 +}; + +constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return SvgStrokeFlags(int(a) | int(b)); +} + + +enum class SvgGradientType +{ + Linear, + Radial +}; + +enum class SvgStyleFlags +{ + Color = 0x01, + Fill = 0x02, + FillRule = 0x04, + FillOpacity = 0x08, + Opacity = 0x010, + Stroke = 0x20, + StrokeWidth = 0x40, + StrokeLineJoin = 0x80, + StrokeLineCap = 0x100, + StrokeOpacity = 0x200, + StrokeDashArray = 0x400, + Transform = 0x800, + ClipPath = 0x1000, + Mask = 0x2000, + MaskType = 0x4000, + Display = 0x8000, + PaintOrder = 0x10000, + StrokeMiterlimit = 0x20000, + StrokeDashOffset = 0x40000, +}; + +constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b) +{ + return SvgStyleFlags(int(a) | int(b)); +} + +enum class SvgStopStyleFlags +{ + StopDefault = 0x0, + StopOpacity = 0x01, + StopColor = 0x02 +}; + +constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return SvgStopStyleFlags(int(a) | int(b)); +} + +enum class SvgGradientFlags +{ + None = 0x0, + GradientUnits = 0x1, + SpreadMethod = 0x2, + X1 = 0x4, + X2 = 0x8, + Y1 = 0x10, + Y2 = 0x20, + Cx = 0x40, + Cy = 0x80, + R = 0x100, + Fx = 0x200, + Fy = 0x400, + Fr = 0x800 +}; + +constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b) +{ + return SvgGradientFlags(int(a) | int(b)); +} + +enum class SvgFillRule +{ + Winding = 0, + OddEven = 1 +}; + +enum class SvgMaskType +{ + Luminance = 0, + Alpha +}; + +//Length type to recalculate %, pt, pc, mm, cm etc +enum class SvgParserLengthType +{ + Vertical, + Horizontal, + //In case of, for example, radius of radial gradient + Other +}; + +enum class SvgViewFlag +{ + None = 0x0, + Width = 0x01, //viewPort width + Height = 0x02, //viewPort height + Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set + WidthInPercent = 0x08, + HeightInPercent = 0x10 +}; + +constexpr bool operator &(SvgViewFlag a, SvgViewFlag b) +{ + return static_cast(a) & static_cast(b); +} + +constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) | int(b)); +} + +constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) ^ int(b)); +} + +enum class AspectRatioAlign +{ + None, + XMinYMin, + XMidYMin, + XMaxYMin, + XMinYMid, + XMidYMid, + XMaxYMid, + XMinYMax, + XMidYMax, + XMaxYMax +}; + +enum class AspectRatioMeetOrSlice +{ + Meet, + Slice +}; + +struct SvgDocNode +{ + float w; //unit: point or in percentage see: SvgViewFlag + float h; //unit: point or in percentage see: SvgViewFlag + float vx; + float vy; + float vw; + float vh; + SvgViewFlag viewFlag; + SvgNode* defs; + SvgNode* style; + AspectRatioAlign align; + AspectRatioMeetOrSlice meetOrSlice; +}; + +struct SvgGNode +{ +}; + +struct SvgDefsNode +{ + Array gradients; +}; + +struct SvgSymbolNode +{ + float w, h; + float vx, vy, vw, vh; + AspectRatioAlign align; + AspectRatioMeetOrSlice meetOrSlice; + bool overflowVisible; + bool hasViewBox; + bool hasWidth; + bool hasHeight; +}; + +struct SvgUseNode +{ + float x, y, w, h; + bool isWidthSet; + bool isHeightSet; + SvgNode* symbol; +}; + +struct SvgEllipseNode +{ + float cx; + float cy; + float rx; + float ry; +}; + +struct SvgCircleNode +{ + float cx; + float cy; + float r; +}; + +struct SvgRectNode +{ + float x; + float y; + float w; + float h; + float rx; + float ry; + bool hasRx; + bool hasRy; +}; + +struct SvgLineNode +{ + float x1; + float y1; + float x2; + float y2; +}; + +struct SvgImageNode +{ + float x, y, w, h; + char* href; +}; + +struct SvgPathNode +{ + char* path; +}; + +struct SvgPolygonNode +{ + Array pts; +}; + +struct SvgClipNode +{ + bool userSpace; +}; + +struct SvgMaskNode +{ + SvgMaskType type; + bool userSpace; +}; + +struct SvgCssStyleNode +{ +}; + +struct SvgLinearGradient +{ + float x1; + float y1; + float x2; + float y2; + bool isX1Percentage; + bool isY1Percentage; + bool isX2Percentage; + bool isY2Percentage; +}; + +struct SvgRadialGradient +{ + float cx; + float cy; + float fx; + float fy; + float r; + float fr; + bool isCxPercentage; + bool isCyPercentage; + bool isFxPercentage; + bool isFyPercentage; + bool isRPercentage; + bool isFrPercentage; +}; + +struct SvgComposite +{ + char *url; + SvgNode* node; + bool applying; //flag for checking circular dependency. +}; + +struct SvgColor +{ + uint8_t r; + uint8_t g; + uint8_t b; +}; + +struct SvgPaint +{ + SvgStyleGradient* gradient; + char *url; + SvgColor color; + bool none; + bool curColor; +}; + +struct SvgDash +{ + Array array; + float offset; +}; + +struct SvgStyleGradient +{ + SvgGradientType type; + char* id; + char* ref; + FillSpread spread; + SvgRadialGradient* radial; + SvgLinearGradient* linear; + Matrix* transform; + Array stops; + SvgGradientFlags flags; + bool userSpace; + + void clear() + { + stops.reset(); + free(transform); + free(radial); + free(linear); + free(ref); + free(id); + } +}; + +struct SvgStyleFill +{ + SvgFillFlags flags; + SvgPaint paint; + int opacity; + FillRule fillRule; +}; + +struct SvgStyleStroke +{ + SvgStrokeFlags flags; + SvgPaint paint; + int opacity; + float scale; + float width; + float centered; + StrokeCap cap; + StrokeJoin join; + float miterlimit; + SvgDash dash; +}; + +struct SvgStyleProperty +{ + SvgStyleFill fill; + SvgStyleStroke stroke; + SvgComposite clipPath; + SvgComposite mask; + int opacity; + SvgColor color; + bool curColorSet; + char* cssClass; + bool paintOrder; //true if default (fill, stroke), false otherwise + SvgStyleFlags flags; + SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance) +}; + +struct SvgNode +{ + SvgNodeType type; + SvgNode* parent; + Array child; + char *id; + SvgStyleProperty *style; + Matrix* transform; + union { + SvgGNode g; + SvgDocNode doc; + SvgDefsNode defs; + SvgUseNode use; + SvgCircleNode circle; + SvgEllipseNode ellipse; + SvgPolygonNode polygon; + SvgPolygonNode polyline; + SvgRectNode rect; + SvgPathNode path; + SvgLineNode line; + SvgImageNode image; + SvgMaskNode mask; + SvgClipNode clip; + SvgCssStyleNode cssStyle; + SvgSymbolNode symbol; + } node; + bool display; + ~SvgNode(); +}; + +struct SvgParser +{ + SvgNode* node; + SvgStyleGradient* styleGrad; + Fill::ColorStop gradStop; + SvgStopStyleFlags flags; + struct + { + float x, y, w, h; + } global; + struct + { + bool parsedFx; + bool parsedFy; + } gradient; +}; + +struct SvgNodeIdPair +{ + SvgNode* node; + char *id; +}; + +struct SvgLoaderData +{ + Array stack; + SvgNode* doc = nullptr; + SvgNode* def = nullptr; + SvgNode* cssStyle = nullptr; + Array gradients; + SvgStyleGradient* latestGradient = nullptr; //For stops + SvgParser* svgParse = nullptr; + Array cloneNodes; + Array nodesToStyle; + Array images; //embedded images + int level = 0; + bool result = false; + bool style = false; +}; + +struct Box +{ + float x, y, w, h; +}; + +#endif + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgPath.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSvgPath.cpp new file mode 100644 index 000000000..46ec157f3 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgPath.cpp @@ -0,0 +1,561 @@ +/* + * 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 + +/* + * Copyright notice for the EFL: + + * Copyright (C) EFL developers (see AUTHORS) + + * All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++. + +#include +#include +#include +#include "tvgSvgLoaderCommon.h" +#include "tvgSvgPath.h" +#include "tvgStr.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static char* _skipComma(const char* content) +{ + while (*content && isspace(*content)) { + content++; + } + if (*content == ',') return (char*)content + 1; + return (char*)content; +} + + +static bool _parseNumber(char** content, float* number) +{ + char* end = NULL; + *number = strToFloat(*content, &end); + //If the start of string is not number + if ((*content) == end) return false; + //Skip comma if any + *content = _skipComma(end); + return true; +} + + +static bool _parseFlag(char** content, int* number) +{ + char* end = NULL; + if (*(*content) != '0' && *(*content) != '1') return false; + *number = *(*content) - '0'; + *content += 1; + end = *content; + *content = _skipComma(end); + + return true; +} + + +void _pathAppendArcTo(Array* cmds, Array* pts, Point* cur, Point* curCtl, float x, float y, float rx, float ry, float angle, bool largeArc, bool sweep) +{ + float cxp, cyp, cx, cy; + float sx, sy; + float cosPhi, sinPhi; + float dx2, dy2; + float x1p, y1p; + float x1p2, y1p2; + float rx2, ry2; + float lambda; + float c; + float at; + float theta1, deltaTheta; + float nat; + float delta, bcp; + float cosPhiRx, cosPhiRy; + float sinPhiRx, sinPhiRy; + float cosTheta1, sinTheta1; + int segments; + + //Some helpful stuff is available here: + //http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes + sx = cur->x; + sy = cur->y; + + //If start and end points are identical, then no arc is drawn + if ((fabsf(x - sx) < (1.0f / 256.0f)) && (fabsf(y - sy) < (1.0f / 256.0f))) return; + + //Correction of out-of-range radii, see F6.6.1 (step 2) + rx = fabsf(rx); + ry = fabsf(ry); + + angle = angle * (float)M_PI / 180.0f; + cosPhi = cosf(angle); + sinPhi = sinf(angle); + dx2 = (sx - x) / 2.0f; + dy2 = (sy - y) / 2.0f; + x1p = cosPhi * dx2 + sinPhi * dy2; + y1p = cosPhi * dy2 - sinPhi * dx2; + x1p2 = x1p * x1p; + y1p2 = y1p * y1p; + rx2 = rx * rx; + ry2 = ry * ry; + lambda = (x1p2 / rx2) + (y1p2 / ry2); + + //Correction of out-of-range radii, see F6.6.2 (step 4) + if (lambda > 1.0f) { + //See F6.6.3 + float lambdaRoot = sqrtf(lambda); + + rx *= lambdaRoot; + ry *= lambdaRoot; + //Update rx2 and ry2 + rx2 = rx * rx; + ry2 = ry * ry; + } + + c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2); + + //Check if there is no possible solution + //(i.e. we can't do a square root of a negative value) + if (c < 0.0f) { + //Scale uniformly until we have a single solution + //(see F6.2) i.e. when c == 0.0 + float scale = sqrtf(1.0f - c / (rx2 * ry2)); + rx *= scale; + ry *= scale; + //Update rx2 and ry2 + rx2 = rx * rx; + ry2 = ry * ry; + + //Step 2 (F6.5.2) - simplified since c == 0.0 + cxp = 0.0f; + cyp = 0.0f; + //Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0 + cx = 0.0f; + cy = 0.0f; + } else { + //Complete c calculation + c = sqrtf(c / ((rx2 * y1p2) + (ry2 * x1p2))); + //Inverse sign if Fa == Fs + if (largeArc == sweep) c = -c; + + //Step 2 (F6.5.2) + cxp = c * (rx * y1p / ry); + cyp = c * (-ry * x1p / rx); + + //Step 3 (F6.5.3 first part) + cx = cosPhi * cxp - sinPhi * cyp; + cy = sinPhi * cxp + cosPhi * cyp; + } + + //Step 3 (F6.5.3 second part) we now have the center point of the ellipse + cx += (sx + x) / 2.0f; + cy += (sy + y) / 2.0f; + + //Sstep 4 (F6.5.4) + //We dont' use arccos (as per w3c doc), see + //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm + //Note: atan2 (0.0, 1.0) == 0.0 + at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx)); + theta1 = (at < 0.0f) ? 2.0f * (float)M_PI + at : at; + + nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx)); + deltaTheta = (nat < at) ? 2.0f * (float)M_PI - at + nat : nat - at; + + if (sweep) { + //Ensure delta theta < 0 or else add 360 degrees + if (deltaTheta < 0.0f) deltaTheta += (float)(2.0f * (float)M_PI); + } else { + //Ensure delta theta > 0 or else substract 360 degrees + if (deltaTheta > 0.0f) deltaTheta -= (float)(2.0f * (float)M_PI); + } + + //Add several cubic bezier to approximate the arc + //(smaller than 90 degrees) + //We add one extra segment because we want something + //Smaller than 90deg (i.e. not 90 itself) + segments = static_cast(fabsf(deltaTheta / float(M_PI_2)) + 1.0f); + delta = deltaTheta / segments; + + //http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13) + bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f); + + cosPhiRx = cosPhi * rx; + cosPhiRy = cosPhi * ry; + sinPhiRx = sinPhi * rx; + sinPhiRy = sinPhi * ry; + + cosTheta1 = cosf(theta1); + sinTheta1 = sinf(theta1); + + for (int i = 0; i < segments; ++i) { + //End angle (for this segment) = current + delta + float c1x, c1y, ex, ey, c2x, c2y; + float theta2 = theta1 + delta; + float cosTheta2 = cosf(theta2); + float sinTheta2 = sinf(theta2); + Point p[3]; + + //First control point (based on start point sx,sy) + c1x = sx - bcp * (cosPhiRx * sinTheta1 + sinPhiRy * cosTheta1); + c1y = sy + bcp * (cosPhiRy * cosTheta1 - sinPhiRx * sinTheta1); + + //End point (for this segment) + ex = cx + (cosPhiRx * cosTheta2 - sinPhiRy * sinTheta2); + ey = cy + (sinPhiRx * cosTheta2 + cosPhiRy * sinTheta2); + + //Second control point (based on end point ex,ey) + c2x = ex + bcp * (cosPhiRx * sinTheta2 + sinPhiRy * cosTheta2); + c2y = ey + bcp * (sinPhiRx * sinTheta2 - cosPhiRy * cosTheta2); + cmds->push(PathCommand::CubicTo); + p[0] = {c1x, c1y}; + p[1] = {c2x, c2y}; + p[2] = {ex, ey}; + pts->push(p[0]); + pts->push(p[1]); + pts->push(p[2]); + *curCtl = p[1]; + *cur = p[2]; + + //Next start point is the current end point (same for angle) + sx = ex; + sy = ey; + theta1 = theta2; + //Avoid recomputations + cosTheta1 = cosTheta2; + sinTheta1 = sinTheta2; + } +} + +static int _numberCount(char cmd) +{ + int count = 0; + switch (cmd) { + case 'M': + case 'm': + case 'L': + case 'l': + case 'T': + case 't': { + count = 2; + break; + } + case 'C': + case 'c': + case 'E': + case 'e': { + count = 6; + break; + } + case 'H': + case 'h': + case 'V': + case 'v': { + count = 1; + break; + } + case 'S': + case 's': + case 'Q': + case 'q': { + count = 4; + break; + } + case 'A': + case 'a': { + count = 7; + break; + } + default: + break; + } + return count; +} + + +static bool _processCommand(Array* cmds, Array* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic) +{ + switch (cmd) { + case 'm': + case 'l': + case 'c': + case 's': + case 'q': + case 't': { + for (int i = 0; i < count - 1; i += 2) { + arr[i] = arr[i] + cur->x; + arr[i + 1] = arr[i + 1] + cur->y; + } + break; + } + case 'h': { + arr[0] = arr[0] + cur->x; + break; + } + case 'v': { + arr[0] = arr[0] + cur->y; + break; + } + case 'a': { + arr[5] = arr[5] + cur->x; + arr[6] = arr[6] + cur->y; + break; + } + default: { + break; + } + } + + switch (cmd) { + case 'm': + case 'M': { + Point p = {arr[0], arr[1]}; + cmds->push(PathCommand::MoveTo); + pts->push(p); + *cur = {arr[0], arr[1]}; + *startPoint = {arr[0], arr[1]}; + break; + } + case 'l': + case 'L': { + Point p = {arr[0], arr[1]}; + cmds->push(PathCommand::LineTo); + pts->push(p); + *cur = {arr[0], arr[1]}; + break; + } + case 'c': + case 'C': { + Point p[3]; + cmds->push(PathCommand::CubicTo); + p[0] = {arr[0], arr[1]}; + p[1] = {arr[2], arr[3]}; + p[2] = {arr[4], arr[5]}; + pts->push(p[0]); + pts->push(p[1]); + pts->push(p[2]); + *curCtl = p[1]; + *cur = p[2]; + *isQuadratic = false; + break; + } + case 's': + case 'S': { + Point p[3], ctrl; + if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) && + !(*isQuadratic)) { + ctrl.x = 2 * cur->x - curCtl->x; + ctrl.y = 2 * cur->y - curCtl->y; + } else { + ctrl = *cur; + } + cmds->push(PathCommand::CubicTo); + p[0] = ctrl; + p[1] = {arr[0], arr[1]}; + p[2] = {arr[2], arr[3]}; + pts->push(p[0]); + pts->push(p[1]); + pts->push(p[2]); + *curCtl = p[1]; + *cur = p[2]; + *isQuadratic = false; + break; + } + case 'q': + case 'Q': { + Point p[3]; + float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0f / 3.0f); + float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0f / 3.0f); + float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0f / 3.0f); + float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0f / 3.0f); + cmds->push(PathCommand::CubicTo); + p[0] = {ctrl_x0, ctrl_y0}; + p[1] = {ctrl_x1, ctrl_y1}; + p[2] = {arr[2], arr[3]}; + pts->push(p[0]); + pts->push(p[1]); + pts->push(p[2]); + *curCtl = {arr[0], arr[1]}; + *cur = p[2]; + *isQuadratic = true; + break; + } + case 't': + case 'T': { + Point p[3], ctrl; + if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) && + *isQuadratic) { + ctrl.x = 2 * cur->x - curCtl->x; + ctrl.y = 2 * cur->y - curCtl->y; + } else { + ctrl = *cur; + } + float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0f / 3.0f); + float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0f / 3.0f); + float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0f / 3.0f); + float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0f / 3.0f); + cmds->push(PathCommand::CubicTo); + p[0] = {ctrl_x0, ctrl_y0}; + p[1] = {ctrl_x1, ctrl_y1}; + p[2] = {arr[0], arr[1]}; + pts->push(p[0]); + pts->push(p[1]); + pts->push(p[2]); + *curCtl = {ctrl.x, ctrl.y}; + *cur = p[2]; + *isQuadratic = true; + break; + } + case 'h': + case 'H': { + Point p = {arr[0], cur->y}; + cmds->push(PathCommand::LineTo); + pts->push(p); + cur->x = arr[0]; + break; + } + case 'v': + case 'V': { + Point p = {cur->x, arr[0]}; + cmds->push(PathCommand::LineTo); + pts->push(p); + cur->y = arr[0]; + break; + } + case 'z': + case 'Z': { + cmds->push(PathCommand::Close); + *cur = *startPoint; + break; + } + case 'a': + case 'A': { + _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]); + *cur = *curCtl = {arr[5], arr[6]}; + *isQuadratic = false; + break; + } + default: { + return false; + } + } + return true; +} + + +static char* _nextCommand(char* path, char* cmd, float* arr, int* count) +{ + int large, sweep; + + path = _skipComma(path); + if (isalpha(*path)) { + *cmd = *path; + path++; + *count = _numberCount(*cmd); + } else { + if (*cmd == 'm') *cmd = 'l'; + else if (*cmd == 'M') *cmd = 'L'; + } + if (*count == 7) { + //Special case for arc command + if (_parseNumber(&path, &arr[0])) { + if (_parseNumber(&path, &arr[1])) { + if (_parseNumber(&path, &arr[2])) { + if (_parseFlag(&path, &large)) { + if (_parseFlag(&path, &sweep)) { + if (_parseNumber(&path, &arr[5])) { + if (_parseNumber(&path, &arr[6])) { + arr[3] = (float)large; + arr[4] = (float)sweep; + return path; + } + } + } + } + } + } + } + *count = 0; + return NULL; + } + for (int i = 0; i < *count; i++) { + if (!_parseNumber(&path, &arr[i])) { + *count = 0; + return NULL; + } + path = _skipComma(path); + } + return path; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + + +bool svgPathToTvgPath(const char* svgPath, Array& cmds, Array& pts) +{ + float numberArray[7]; + int numberCount = 0; + Point cur = { 0, 0 }; + Point curCtl = { 0, 0 }; + Point startPoint = { 0, 0 }; + char cmd = 0; + bool isQuadratic = false; + char* path = (char*)svgPath; + + while ((path[0] != '\0')) { + path = _nextCommand(path, &cmd, numberArray, &numberCount); + if (!path) break; + if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break; + } + + return true; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgPath.h b/project/gui/lvgl/src/libs/thorvg/tvgSvgPath.h new file mode 100644 index 000000000..5ac02cd6a --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgPath.h @@ -0,0 +1,36 @@ +/* + * 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_SVG_PATH_H_ +#define _TVG_SVG_PATH_H_ + +#include "tvgCommon.h" + +bool svgPathToTvgPath(const char* svgPath, Array& cmds, Array& pts); + +#endif //_TVG_SVG_PATH_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.cpp new file mode 100644 index 000000000..ace3d32e5 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.cpp @@ -0,0 +1,890 @@ +/* + * 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" /* to include math.h before cstring */ +#include +#include +#include "tvgShape.h" +#include "tvgCompressor.h" +#include "tvgPaint.h" +#include "tvgFill.h" +#include "tvgStr.h" +#include "tvgSvgLoaderCommon.h" +#include "tvgSvgSceneBuilder.h" +#include "tvgSvgPath.h" +#include "tvgSvgUtil.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static bool _appendShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath); +static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform); +static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr); + + +static inline bool _isGroupType(SvgNodeType type) +{ + if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol) return true; + return false; +} + + +//According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph) +//a stroke width should be ignored for bounding box calculations +static Box _boundingBox(const Shape* shape) +{ + float x, y, w, h; + shape->bounds(&x, &y, &w, &h, false); + + if (auto strokeW = shape->strokeWidth()) { + x += 0.5f * strokeW; + y += 0.5f * strokeW; + w -= strokeW; + h -= strokeW; + } + + return {x, y, w, h}; +} + + +static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) +{ + gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13; + gradTransf->e12 *= mBBox->e11; + gradTransf->e11 *= mBBox->e11; + + gradTransf->e23 = gradTransf->e23 * mBBox->e22 + mBBox->e23; + gradTransf->e22 *= mBBox->e22; + gradTransf->e21 *= mBBox->e22; +} + + +static unique_ptr _applyLinearGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity) +{ + Fill::ColorStop* stops; + int stopCount = 0; + auto fillGrad = LinearGradient::gen(); + + bool isTransform = (g->transform ? true : false); + Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (isTransform) finalTransform = *g->transform; + + if (g->userSpace) { + g->linear->x1 = g->linear->x1 * vBox.w; + g->linear->y1 = g->linear->y1 * vBox.h; + g->linear->x2 = g->linear->x2 * vBox.w; + g->linear->y2 = g->linear->y2 * vBox.h; + } else { + Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1}; + if (isTransform) _transformMultiply(&m, &finalTransform); + else { + finalTransform = m; + isTransform = true; + } + } + + if (isTransform) fillGrad->transform(finalTransform); + + fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2); + fillGrad->spread(g->spread); + + //Update the stops + stopCount = g->stops.count; + if (stopCount > 0) { + stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop)); + if (!stops) return fillGrad; + auto prevOffset = 0.0f; + for (uint32_t i = 0; i < g->stops.count; ++i) { + auto colorStop = &g->stops[i]; + //Use premultiplied color + stops[i].r = colorStop->r; + stops[i].g = colorStop->g; + stops[i].b = colorStop->b; + stops[i].a = static_cast((colorStop->a * opacity) / 255); + stops[i].offset = colorStop->offset; + //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes + if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; + else if (colorStop->offset > 1) stops[i].offset = 1; + prevOffset = stops[i].offset; + } + fillGrad->colorStops(stops, stopCount); + free(stops); + } + return fillGrad; +} + + +static unique_ptr _applyRadialGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity) +{ + Fill::ColorStop *stops; + int stopCount = 0; + auto fillGrad = RadialGradient::gen(); + + bool isTransform = (g->transform ? true : false); + Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (isTransform) finalTransform = *g->transform; + + if (g->userSpace) { + //The radius scalling is done according to the Units section: + //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html + g->radial->cx = g->radial->cx * vBox.w; + g->radial->cy = g->radial->cy * vBox.h; + g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f); + g->radial->fx = g->radial->fx * vBox.w; + g->radial->fy = g->radial->fy * vBox.h; + g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f); + } else { + Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1}; + if (isTransform) _transformMultiply(&m, &finalTransform); + else { + finalTransform = m; + isTransform = true; + } + } + + if (isTransform) fillGrad->transform(finalTransform); + + P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr); + fillGrad->spread(g->spread); + + //Update the stops + stopCount = g->stops.count; + if (stopCount > 0) { + stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop)); + if (!stops) return fillGrad; + auto prevOffset = 0.0f; + for (uint32_t i = 0; i < g->stops.count; ++i) { + auto colorStop = &g->stops[i]; + //Use premultiplied color + stops[i].r = colorStop->r; + stops[i].g = colorStop->g; + stops[i].b = colorStop->b; + stops[i].a = static_cast((colorStop->a * opacity) / 255); + stops[i].offset = colorStop->offset; + //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes + if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; + else if (colorStop->offset > 1) stops[i].offset = 1; + prevOffset = stops[i].offset; + } + fillGrad->colorStops(stops, stopCount); + free(stops); + } + return fillGrad; +} + + +//The SVG standard allows only for 'use' nodes that point directly to a basic shape. +static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath) +{ + if (node->child.count != 1) return false; + auto child = *(node->child.data); + + Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (node->transform) finalTransform = *node->transform; + if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { + Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; + finalTransform = mathMultiply(&finalTransform, &m); + } + if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform); + + return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform); +} + + +static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, bool clip) +{ + if (node->type == SvgNodeType::Use) { + return _appendClipUseNode(loaderData, node, shape, vBox, svgPath); + } + return _appendClipShape(loaderData, node, shape, vBox, svgPath, nullptr); +} + + +static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type) +{ + Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + //The initial mask transformation ignored according to the SVG standard. + if (node->transform && type != SvgNodeType::Mask) { + m = *node->transform; + } + if (compNode->transform) { + m = mathMultiply(&m, compNode->transform); + } + if (!compNode->node.clip.userSpace) { + float x, y, w, h; + P(paint)->bounds(&x, &y, &w, &h, false, false); + Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1}; + m = mathMultiply(&m, &mBBox); + } + return m; +} + + +static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath) +{ + /* ClipPath */ + /* Do not drop in Circular Dependency for ClipPath. + Composition can be applied recursively if its children nodes have composition target to this one. */ + if (node->style->clipPath.applying) { + TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?"); + } else { + auto compNode = node->style->clipPath.node; + if (compNode && compNode->child.count > 0) { + node->style->clipPath.applying = true; + + auto comp = Shape::gen(); + + auto child = compNode->child.data; + auto valid = false; //Composite only when valid shapes exist + + for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) { + if (_appendClipChild(loaderData, *child, comp.get(), vBox, svgPath, compNode->child.count > 1)) valid = true; + } + + if (valid) { + Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath); + comp->transform(finalTransform); + + paint->composite(std::move(comp), CompositeMethod::ClipPath); + } + + node->style->clipPath.applying = false; + } + } + + /* Mask */ + /* Do not drop in Circular Dependency for Mask. + Composition can be applied recursively if its children nodes have composition target to this one. */ + if (node->style->mask.applying) { + TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?"); + } else { + auto compNode = node->style->mask.node; + if (compNode && compNode->child.count > 0) { + node->style->mask.applying = true; + + bool isMaskWhite = true; + if (auto comp = _sceneBuildHelper(loaderData, compNode, vBox, svgPath, true, 0, &isMaskWhite)) { + if (!compNode->node.mask.userSpace) { + Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask); + comp->transform(finalTransform); + } else { + if (node->transform) comp->transform(*node->transform); + } + + if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) { + paint->composite(std::move(comp), CompositeMethod::LumaMask); + } else { + paint->composite(std::move(comp), CompositeMethod::AlphaMask); + } + } + + node->style->mask.applying = false; + } + } +} + + +static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip) +{ + SvgStyleProperty* style = node->style; + + //Clip transformation is applied directly to the path in the _appendClipShape function + if (node->transform && !clip) vg->transform(*node->transform); + if (node->type == SvgNodeType::Doc || !node->display) return; + + //If fill property is nullptr then do nothing + if (style->fill.paint.none) { + //Do nothing + } else if (style->fill.paint.gradient) { + Box bBox = vBox; + if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg); + + if (style->fill.paint.gradient->type == SvgGradientType::Linear) { + auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity); + vg->fill(std::move(linear)); + } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { + auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity); + vg->fill(std::move(radial)); + } + } else if (style->fill.paint.url) { + //TODO: Apply the color pointed by url + } else if (style->fill.paint.curColor) { + //Apply the current style color + vg->fill(style->color.r, style->color.g, style->color.b, style->fill.opacity); + } else { + //Apply the fill color + vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b, style->fill.opacity); + } + + //Apply the fill rule + vg->fill((tvg::FillRule)style->fill.fillRule); + //Rendering order + vg->order(!style->paintOrder); + + //Apply node opacity + if (style->opacity < 255) vg->opacity(style->opacity); + + if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return; + + //Apply the stroke style property + vg->stroke(style->stroke.width); + vg->stroke(style->stroke.cap); + vg->stroke(style->stroke.join); + vg->strokeMiterlimit(style->stroke.miterlimit); + if (style->stroke.dash.array.count > 0) { + P(vg)->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset); + } + + //If stroke property is nullptr then do nothing + if (style->stroke.paint.none) { + vg->stroke(0.0f); + } else if (style->stroke.paint.gradient) { + Box bBox = vBox; + if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg); + + if (style->stroke.paint.gradient->type == SvgGradientType::Linear) { + auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity); + vg->stroke(std::move(linear)); + } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) { + auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity); + vg->stroke(std::move(radial)); + } + } else if (style->stroke.paint.url) { + //TODO: Apply the color pointed by url + } else if (style->stroke.paint.curColor) { + //Apply the current style color + vg->stroke(style->color.r, style->color.g, style->color.b, style->stroke.opacity); + } else { + //Apply the stroke color + vg->stroke(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity); + } + + _applyComposition(loaderData, vg, node, vBox, svgPath); +} + + +static unique_ptr _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath) +{ + auto shape = Shape::gen(); + if (_appendShape(loaderData, node, shape.get(), vBox, svgPath)) return shape; + else return nullptr; +} + + +static bool _recognizeShape(SvgNode* node, Shape* shape) +{ + switch (node->type) { + case SvgNodeType::Path: { + if (node->node.path.path) { + Array cmds; + Array pts; + if (svgPathToTvgPath(node->node.path.path, cmds, pts)) { + shape->appendPath(cmds.data, cmds.count, pts.data, pts.count); + } + } + break; + } + case SvgNodeType::Ellipse: { + shape->appendCircle(node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry); + break; + } + case SvgNodeType::Polygon: { + if (node->node.polygon.pts.count < 2) break; + auto pts = node->node.polygon.pts.data; + shape->moveTo(pts[0], pts[1]); + for (pts += 2; pts < node->node.polygon.pts.end(); pts += 2) { + shape->lineTo(pts[0], pts[1]); + } + shape->close(); + break; + } + case SvgNodeType::Polyline: { + if (node->node.polyline.pts.count < 2) break; + auto pts = node->node.polyline.pts.data; + shape->moveTo(pts[0], pts[1]); + for (pts += 2; pts < node->node.polyline.pts.end(); pts += 2) { + shape->lineTo(pts[0], pts[1]); + } + break; + } + case SvgNodeType::Circle: { + shape->appendCircle(node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r); + break; + } + case SvgNodeType::Rect: { + shape->appendRect(node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry); + break; + } + case SvgNodeType::Line: { + shape->moveTo(node->node.line.x1, node->node.line.y1); + shape->lineTo(node->node.line.x2, node->node.line.y2); + break; + } + default: { + return false; + } + } + return true; +} + + +static bool _appendShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath) +{ + if (!_recognizeShape(node, shape)) return false; + + _applyProperty(loaderData, node, shape, vBox, svgPath, false); + return true; +} + + +static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform) +{ + //The 'transform' matrix has higher priority than the node->transform, since it already contains it + auto m = transform ? transform : (node->transform ? node->transform : nullptr); + + uint32_t currentPtsCnt = 0; + if (m) { + const Point *tmp = nullptr; + currentPtsCnt = shape->pathCoords(&tmp); + } + + if (!_recognizeShape(node, shape)) return false; + + if (m) { + const Point *pts = nullptr; + auto ptsCnt = shape->pathCoords(&pts); + + auto p = const_cast(pts) + currentPtsCnt; + while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m); + } + + _applyProperty(loaderData, node, shape, vBox, svgPath, true); + return true; +} + + +enum class imageMimeTypeEncoding +{ + base64 = 0x1, + utf8 = 0x2 +}; + +constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { + return (static_cast(a) & static_cast(b)); +} + + +static constexpr struct +{ + const char* name; + int sz; + imageMimeTypeEncoding encoding; +} imageMimeTypes[] = { + {"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64}, + {"png", sizeof("png"), imageMimeTypeEncoding::base64}, + {"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8}, +}; + + +static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mimetype, imageMimeTypeEncoding* encoding) { + if (strncmp(*href, "image/", sizeof("image/") - 1)) return false; //not allowed mime type + *href += sizeof("image/") - 1; + + //RFC2397 data:[][;base64], + //mediatype := [ type "/" subtype ] *( ";" parameter ) + //parameter := attribute "=" value + for (unsigned int i = 0; i < sizeof(imageMimeTypes) / sizeof(imageMimeTypes[0]); i++) { + if (!strncmp(*href, imageMimeTypes[i].name, imageMimeTypes[i].sz - 1)) { + *href += imageMimeTypes[i].sz - 1; + *mimetype = imageMimeTypes[i].name; + + while (**href && **href != ',') { + while (**href && **href != ';') ++(*href); + if (!**href) return false; + ++(*href); + + if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::base64) { + if (!strncmp(*href, "base64,", sizeof("base64,") - 1)) { + *href += sizeof("base64,") - 1; + *encoding = imageMimeTypeEncoding::base64; + return true; //valid base64 + } + } + if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8) { + if (!strncmp(*href, "utf8,", sizeof("utf8,") - 1)) { + *href += sizeof("utf8,") - 1; + *encoding = imageMimeTypeEncoding::utf8; + return true; //valid utf8 + } + } + } + //no encoding defined + if (**href == ',' && (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8)) { + ++(*href); + *encoding = imageMimeTypeEncoding::utf8; + return true; //allow no encoding defined if utf8 expected + } + return false; + } + } + return false; +} + +#include "tvgTaskScheduler.h" + +static unique_ptr _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath) +{ + if (!node->node.image.href) return nullptr; + auto picture = Picture::gen(); + + TaskScheduler::async(false); //force to load a picture on the same thread + + const char* href = node->node.image.href; + if (!strncmp(href, "data:", sizeof("data:") - 1)) { + href += sizeof("data:") - 1; + const char* mimetype; + imageMimeTypeEncoding encoding; + if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding + char *decoded = nullptr; + if (encoding == imageMimeTypeEncoding::base64) { + auto size = b64Decode(href, strlen(href), &decoded); + if (picture->load(decoded, size, mimetype, false) != Result::Success) { + free(decoded); + TaskScheduler::async(true); + return nullptr; + } + } else { + auto size = svgUtilURLDecode(href, &decoded); + if (picture->load(decoded, size, mimetype, false) != Result::Success) { + free(decoded); + TaskScheduler::async(true); + return nullptr; + } + } + loaderData.images.push(decoded); + } else { + if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1; + //TODO: protect against recursive svg image loading + //Temporarily disable embedded svg: + const char *dot = strrchr(href, '.'); + if (dot && !strcmp(dot, ".svg")) { + TVGLOG("SVG", "Embedded svg file is disabled."); + TaskScheduler::async(true); + return nullptr; + } + string imagePath = href; + if (strncmp(href, "/", 1)) { + auto last = svgPath.find_last_of("/"); + imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath; + } + if (picture->load(imagePath) != Result::Success) { + TaskScheduler::async(true); + return nullptr; + } + } + + TaskScheduler::async(true); + + float w, h; + Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { + auto sx = node->node.image.w / w; + auto sy = node->node.image.h / h; + m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; + } + if (node->transform) m = mathMultiply(node->transform, &m); + picture->transform(m); + + _applyComposition(loaderData, picture.get(), node, vBox, svgPath); + + return picture; +} + + +static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, float width, float height, const Box& box) +{ + auto sx = width / box.w; + auto sy = height / box.h; + auto tvx = box.x * sx; + auto tvy = box.y * sy; + + if (align == AspectRatioAlign::None) + return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; + + //Scale + if (meetOrSlice == AspectRatioMeetOrSlice::Meet) { + if (sx < sy) sy = sx; + else sx = sy; + } else { + if (sx < sy) sx = sy; + else sy = sx; + } + + //Align + tvx = box.x * sx; + tvy = box.y * sy; + auto tvw = box.w * sx; + auto tvh = box.h * sy; + + switch (align) { + case AspectRatioAlign::XMinYMin: { + break; + } + case AspectRatioAlign::XMidYMin: { + tvx -= (width - tvw) * 0.5f; + break; + } + case AspectRatioAlign::XMaxYMin: { + tvx -= width - tvw; + break; + } + case AspectRatioAlign::XMinYMid: { + tvy -= (height - tvh) * 0.5f; + break; + } + case AspectRatioAlign::XMidYMid: { + tvx -= (width - tvw) * 0.5f; + tvy -= (height - tvh) * 0.5f; + break; + } + case AspectRatioAlign::XMaxYMid: { + tvx -= width - tvw; + tvy -= (height - tvh) * 0.5f; + break; + } + case AspectRatioAlign::XMinYMax: { + tvy -= height - tvh; + break; + } + case AspectRatioAlign::XMidYMax: { + tvx -= (width - tvw) * 0.5f; + tvy -= height - tvh; + break; + } + case AspectRatioAlign::XMaxYMax: { + tvx -= width - tvw; + tvy -= height - tvh; + break; + } + default: { + break; + } + } + + return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; +} + + +static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite) +{ + unique_ptr finalScene; + auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite); + + // mUseTransform = mUseTransform * mTranslate + Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if (node->transform) mUseTransform = *node->transform; + if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { + Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; + mUseTransform = mathMultiply(&mUseTransform, &mTranslate); + } + + if (node->node.use.symbol) { + auto symbol = node->node.use.symbol->node.symbol; + + auto width = (symbol.hasWidth ? symbol.w : vBox.w); + if (node->node.use.isWidthSet) width = node->node.use.w; + auto height = (symbol.hasHeight ? symbol.h : vBox.h);; + if (node->node.use.isHeightSet) height = node->node.use.h; + auto vw = (symbol.hasViewBox ? symbol.vw : width); + auto vh = (symbol.hasViewBox ? symbol.vh : height); + + Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) { + Box box = {symbol.vx, symbol.vy, vw, vh}; + mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box); + } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) { + mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1}; + } + + // mSceneTransform = mUseTransform * mSymbolTransform * mViewBox + Matrix mSceneTransform = mViewBox; + if (node->node.use.symbol->transform) { + mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox); + } + mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform); + scene->transform(mSceneTransform); + + if (node->node.use.symbol->node.symbol.overflowVisible) { + finalScene = std::move(scene); + } else { + auto viewBoxClip = Shape::gen(); + viewBoxClip->appendRect(0, 0, width, height, 0, 0); + + // mClipTransform = mUseTransform * mSymbolTransform + Matrix mClipTransform = mUseTransform; + if (node->node.use.symbol->transform) { + mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform); + } + viewBoxClip->transform(mClipTransform); + + auto compositeLayer = Scene::gen(); + compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->push(std::move(scene)); + + auto root = Scene::gen(); + root->push(std::move(compositeLayer)); + + finalScene = std::move(root); + } + } else { + scene->transform(mUseTransform); + finalScene = std::move(scene); + } + + return finalScene; +} + + +static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite) +{ + /* Exception handling: Prevent invalid SVG data input. + The size is the arbitrary value, we need an experimental size. */ + if (depth > 2192) { + TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth); + return nullptr; + } + + if (_isGroupType(node->type) || mask) { + auto scene = Scene::gen(); + // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper() + if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform); + + if (node->display && node->style->opacity != 0) { + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + if (_isGroupType((*child)->type)) { + if ((*child)->type == SvgNodeType::Use) + scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1, isMaskWhite)); + else + scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1, isMaskWhite)); + } else if ((*child)->type == SvgNodeType::Image) { + auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath); + if (image) { + scene->push(std::move(image)); + if (isMaskWhite) *isMaskWhite = false; + } + } else if ((*child)->type != SvgNodeType::Mask) { + auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath); + if (shape) { + if (isMaskWhite) { + uint8_t r, g, b; + shape->fillColor(&r, &g, &b); + if (shape->fill() || r < 255 || g < 255 || b < 255 || shape->strokeFill() || + (shape->strokeColor(&r, &g, &b) == Result::Success && (r < 255 || g < 255 || b < 255))) { + *isMaskWhite = false; + } + } + scene->push(std::move(shape)); + } + } + } + _applyComposition(loaderData, scene.get(), node, vBox, svgPath); + scene->opacity(node->style->opacity); + } + return scene; + } + return nullptr; +} + + +static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag) +{ + bool validWidth = (viewFlag & SvgViewFlag::Width); + bool validHeight = (viewFlag & SvgViewFlag::Height); + + float x, y; + scene->bounds(&x, &y, &vBox.w, &vBox.h, false); + if (!validWidth && !validHeight) { + vBox.x = x; + vBox.y = y; + } else { + if (validWidth) vBox.w = w; + if (validHeight) vBox.h = h; + } + + //the size would have 1x1 or percentage values. + if (!validWidth) w *= vBox.w; + if (!validHeight) h *= vBox.h; +} + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +unique_ptr svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) +{ + //TODO: aspect ratio is valid only if viewBox was set + + if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr; + + auto docNode = _sceneBuildHelper(loaderData, loaderData.doc, vBox, svgPath, false, 0); + + if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag); + + if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) { + Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); + docNode->transform(m); + } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) { + docNode->translate(-vBox.x, -vBox.y); + } + + auto viewBoxClip = Shape::gen(); + viewBoxClip->appendRect(0, 0, w, h, 0, 0); + viewBoxClip->fill(0, 0, 0); + + auto compositeLayer = Scene::gen(); + compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->push(std::move(docNode)); + + auto root = Scene::gen(); + root->push(std::move(compositeLayer)); + + loaderData.doc->node.doc.vx = vBox.x; + loaderData.doc->node.doc.vy = vBox.y; + loaderData.doc->node.doc.vw = vBox.w; + loaderData.doc->node.doc.vh = vBox.h; + loaderData.doc->node.doc.w = w; + loaderData.doc->node.doc.h = h; + + return root; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.h b/project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.h new file mode 100644 index 000000000..5a401e713 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgSceneBuilder.h @@ -0,0 +1,36 @@ +/* + * 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_SVG_SCENE_BUILDER_H_ +#define _TVG_SVG_SCENE_BUILDER_H_ + +#include "tvgCommon.h" + +unique_ptr svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); + +#endif //_TVG_SVG_SCENE_BUILDER_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.cpp new file mode 100644 index 000000000..3c5da6cac --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.cpp @@ -0,0 +1,76 @@ +/* + * 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 +#include "tvgSvgUtil.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static uint8_t _hexCharToDec(const char c) +{ + if (c >= 'a') return c - 'a' + 10; + else if (c >= 'A') return c - 'A' + 10; + else return c - '0'; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +size_t svgUtilURLDecode(const char *src, char** dst) +{ + if (!src) return 0; + + auto length = strlen(src); + if (length == 0) return 0; + + char* decoded = (char*)malloc(sizeof(char) * length + 1); + decoded[length] = '\0'; + + char a, b; + int idx =0; + while (*src) { + if (*src == '%' && + ((a = src[1]) && (b = src[2])) && + (isxdigit(a) && isxdigit(b))) { + decoded[idx++] = (_hexCharToDec(a) << 4) + _hexCharToDec(b); + src+=3; + } else if (*src == '+') { + decoded[idx++] = ' '; + src++; + } else { + decoded[idx++] = *src++; + } + } + + *dst = decoded; + return length + 1; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.h b/project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.h new file mode 100644 index 000000000..c32298387 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSvgUtil.h @@ -0,0 +1,36 @@ +/* + * 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_SVG_UTIL_H_ +#define _TVG_SVG_UTIL_H_ + +#include "tvgCommon.h" + +size_t svgUtilURLDecode(const char *src, char** dst); + +#endif //_TVG_SVG_UTIL_H_ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwCanvas.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwCanvas.cpp new file mode 100644 index 000000000..b53fe26ed --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwCanvas.cpp @@ -0,0 +1,112 @@ +/* + * 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" + +#ifdef THORVG_SW_RASTER_SUPPORT + #include "tvgSwRenderer.h" +#else + class SwRenderer : public RenderMethod + { + //Non Supported. Dummy Class */ + }; +#endif + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct SwCanvas::Impl +{ +}; + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +#ifdef THORVG_SW_RASTER_SUPPORT +SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(new Impl) +#else +SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(new Impl) +#endif +{ +} + + +SwCanvas::~SwCanvas() +{ + delete(pImpl); +} + + +Result SwCanvas::mempool(MempoolPolicy policy) noexcept +{ +#ifdef THORVG_SW_RASTER_SUPPORT + //We know renderer type, avoid dynamic_cast for performance. + auto renderer = static_cast(Canvas::pImpl->renderer); + if (!renderer) return Result::MemoryCorruption; + + //It can't change the policy during the running. + if (!Canvas::pImpl->paints.empty()) return Result::InsufficientCondition; + + if (policy == MempoolPolicy::Individual) renderer->mempool(false); + else renderer->mempool(true); + + return Result::Success; +#endif + return Result::NonSupport; +} + + +Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept +{ +#ifdef THORVG_SW_RASTER_SUPPORT + //We know renderer type, avoid dynamic_cast for performance. + auto renderer = static_cast(Canvas::pImpl->renderer); + if (!renderer) return Result::MemoryCorruption; + + if (!renderer->target(buffer, stride, w, h, static_cast(cs))) return Result::InvalidArguments; + + //Paints must be updated again with this new target. + Canvas::pImpl->needRefresh(); + + return Result::Success; +#endif + return Result::NonSupport; +} + + +unique_ptr SwCanvas::gen() noexcept +{ +#ifdef THORVG_SW_RASTER_SUPPORT + if (SwRenderer::init() <= 0) return nullptr; + return unique_ptr(new SwCanvas); +#endif + return nullptr; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwCommon.h b/project/gui/lvgl/src/libs/thorvg/tvgSwCommon.h new file mode 100644 index 000000000..591b1c96f --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwCommon.h @@ -0,0 +1,580 @@ +/* + * 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_SW_COMMON_H_ +#define _TVG_SW_COMMON_H_ + +#include "tvgCommon.h" +#include "tvgRender.h" + +#include + +#if 0 +#include +static double timeStamp() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec + tv.tv_usec / 1000000.0); +} +#endif + +#define SW_CURVE_TYPE_POINT 0 +#define SW_CURVE_TYPE_CUBIC 1 +#define SW_ANGLE_PI (180L << 16) +#define SW_ANGLE_2PI (SW_ANGLE_PI << 1) +#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1) + +using SwCoord = signed long; +using SwFixed = signed long long; + + +static inline float TO_FLOAT(SwCoord val) +{ + return static_cast(val) / 64.0f; +} + +struct SwPoint +{ + SwCoord x, y; + + SwPoint& operator+=(const SwPoint& rhs) + { + x += rhs.x; + y += rhs.y; + return *this; + } + + SwPoint operator+(const SwPoint& rhs) const + { + return {x + rhs.x, y + rhs.y}; + } + + SwPoint operator-(const SwPoint& rhs) const + { + return {x - rhs.x, y - rhs.y}; + } + + bool operator==(const SwPoint& rhs) const + { + return (x == rhs.x && y == rhs.y); + } + + bool operator!=(const SwPoint& rhs) const + { + return (x != rhs.x || y != rhs.y); + } + + bool zero() const + { + if (x == 0 && y == 0) return true; + else return false; + } + + bool small() const + { + //2 is epsilon... + if (abs(x) < 2 && abs(y) < 2) return true; + else return false; + } + + Point toPoint() const + { + return {TO_FLOAT(x), TO_FLOAT(y)}; + } +}; + +struct SwSize +{ + SwCoord w, h; +}; + +struct SwOutline +{ + Array pts; //the outline's points + Array cntrs; //the contour end points + Array types; //curve type + Array closed; //opened or closed path? + FillRule fillRule; +}; + +struct SwSpan +{ + uint16_t x, y; + uint16_t len; + uint8_t coverage; +}; + +struct SwRleData +{ + SwSpan *spans; + uint32_t alloc; + uint32_t size; +}; + +struct SwBBox +{ + SwPoint min, max; + + void reset() + { + min.x = min.y = max.x = max.y = 0; + } +}; + +struct SwFill +{ + struct SwLinear { + float dx, dy; + float len; + float offset; + }; + + struct SwRadial { + float a11, a12, a13; + float a21, a22, a23; + float fx, fy, fr; + float dx, dy, dr; + float invA, a; + }; + + union { + SwLinear linear; + SwRadial radial; + }; + + uint32_t* ctable; + FillSpread spread; + + bool translucent; +}; + +struct SwStrokeBorder +{ + uint32_t ptsCnt; + uint32_t maxPts; + SwPoint* pts; + uint8_t* tags; + int32_t start; //index of current sub-path start point + bool movable; //true: for ends of lineto borders +}; + +struct SwStroke +{ + SwFixed angleIn; + SwFixed angleOut; + SwPoint center; + SwFixed lineLength; + SwFixed subPathAngle; + SwPoint ptStartSubPath; + SwFixed subPathLineLength; + SwFixed width; + SwFixed miterlimit; + + StrokeCap cap; + StrokeJoin join; + StrokeJoin joinSaved; + SwFill* fill = nullptr; + + SwStrokeBorder borders[2]; + + float sx, sy; + + bool firstPt; + bool closedSubPath; + bool handleWideStrokes; +}; + +struct SwDashStroke +{ + SwOutline* outline = nullptr; + float curLen = 0; + int32_t curIdx = 0; + Point ptStart = {0, 0}; + Point ptCur = {0, 0}; + float* pattern = nullptr; + uint32_t cnt = 0; + bool curOpGap = false; + bool move = true; +}; + +struct SwShape +{ + SwOutline* outline = nullptr; + SwStroke* stroke = nullptr; + SwFill* fill = nullptr; + SwRleData* rle = nullptr; + SwRleData* strokeRle = nullptr; + SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling. + + bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips? +}; + +struct SwImage +{ + SwOutline* outline = nullptr; + SwRleData* rle = nullptr; + union { + pixel_t* data; //system based data pointer + uint32_t* buf32; //for explicit 32bits channels + uint8_t* buf8; //for explicit 8bits grayscale + }; + uint32_t w, h, stride; + int32_t ox = 0; //offset x + int32_t oy = 0; //offset y + float scale; + uint8_t channelSize; + + bool direct = false; //draw image directly (with offset) + bool scaled = false; //draw scaled image +}; + +typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a); //src, dst, alpha +typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a); //src, dst, alpha +typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); //color channel join +typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha + +struct SwCompositor; + +struct SwSurface : Surface +{ + SwJoin join; + SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5 + SwBlender blender = nullptr; //blender (optional) + SwCompositor* compositor = nullptr; //compositor (optional) + BlendMethod blendMethod; //blending method (uint8_t) + + SwAlpha alpha(CompositeMethod method) + { + auto idx = (int)(method) - 2; //0: None, 1: ClipPath + return alphas[idx > 3 ? 0 : idx]; //CompositeMethod has only four Matting methods. + } +}; + +struct SwCompositor : Compositor +{ + SwSurface* recoverSfc; //Recover surface when composition is started + SwCompositor* recoverCmp; //Recover compositor when composition is done + SwImage image; + SwBBox bbox; + bool valid; +}; + +struct SwMpool +{ + SwOutline* outline; + SwOutline* strokeOutline; + SwOutline* dashOutline; + unsigned allocSize; +}; + +static inline SwCoord TO_SWCOORD(float val) +{ + return SwCoord(val * 64.0f); +} + +static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) +{ + return (c0 << 24 | c1 << 16 | c2 << 8 | c3); +} + +static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a) +{ + return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) + + ((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff)); +} + +static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a) +{ + return (((((((s >> 8) & 0xff00ff) - ((d >> 8) & 0xff00ff)) * a) + (d & 0xff00ff00)) & 0xff00ff00) + ((((((s & 0xff00ff) - (d & 0xff00ff)) * a) >> 8) + (d & 0xff00ff)) & 0xff00ff)); +} + +static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a) +{ + return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8); +} + +static inline SwCoord HALF_STROKE(float width) +{ + return TO_SWCOORD(width * 0.5f); +} + +static inline uint8_t A(uint32_t c) +{ + return ((c) >> 24); +} + +static inline uint8_t IA(uint32_t c) +{ + return (~(c) >> 24); +} + +static inline uint8_t C1(uint32_t c) +{ + return ((c) >> 16); +} + +static inline uint8_t C2(uint32_t c) +{ + return ((c) >> 8); +} + +static inline uint8_t C3(uint32_t c) +{ + return (c); +} + +static inline uint32_t opBlendInterp(uint32_t s, uint32_t d, uint8_t a) +{ + return INTERPOLATE(s, d, a); +} + +static inline uint32_t opBlendNormal(uint32_t s, uint32_t d, uint8_t a) +{ + auto t = ALPHA_BLEND(s, a); + return t + ALPHA_BLEND(d, IA(t)); +} + +static inline uint32_t opBlendPreNormal(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + return s + ALPHA_BLEND(d, IA(s)); +} + +static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNUSED uint8_t a) +{ + return s; +} + +//TODO: BlendMethod could remove the alpha parameter. +static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + //if (s > d) => s - d + //else => d - s + auto c1 = (C1(s) > C1(d)) ? (C1(s) - C1(d)) : (C1(d) - C1(s)); + auto c2 = (C2(s) > C2(d)) ? (C2(s) - C2(d)) : (C2(d) - C2(s)); + auto c3 = (C3(s) > C3(d)) ? (C3(s) - C3(d)) : (C3(d) - C3(s)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + //A + B - 2AB + auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1)); + auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1)); + auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // s + d + auto c1 = std::min(C1(s) + C1(d), 255); + auto c2 = std::min(C2(s) + C2(d), 255); + auto c3 = std::min(C3(s) + C3(d), 255); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // s + d - s * d + auto c1 = C1(s) + C1(d) - MULTIPLY(C1(s), C1(d)); + auto c2 = C2(s) + C2(d) - MULTIPLY(C2(s), C2(d)); + auto c3 = C3(s) + C3(d) - MULTIPLY(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + + +static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // s * d + auto c1 = MULTIPLY(C1(s), C1(d)); + auto c2 = MULTIPLY(C2(s), C2(d)); + auto c3 = MULTIPLY(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + + +static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // if (2 * d < da) => 2 * s * d, + // else => 1 - 2 * (1 - s) * (1 - d) + auto c1 = (C1(d) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d)))); + auto c2 = (C2(d) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d)))); + auto c3 = (C3(d) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d)))); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // min(s, d) + auto c1 = std::min(C1(s), C1(d)); + auto c2 = std::min(C2(s), C2(d)); + auto c3 = std::min(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // max(s, d) + auto c1 = std::max(C1(s), C1(d)); + auto c2 = std::max(C2(s), C2(d)); + auto c3 = std::max(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // d / (1 - s) + auto is = 0xffffffff - s; + auto c1 = (C1(is) > 0) ? (C1(d) / C1(is)) : C1(d); + auto c2 = (C2(is) > 0) ? (C2(d) / C2(is)) : C2(d); + auto c3 = (C3(is) > 0) ? (C3(d) / C3(is)) : C3(d); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // 1 - (1 - d) / s + auto id = 0xffffffff - d; + auto c1 = 255 - ((C1(s) > 0) ? (C1(id) / C1(s)) : C1(id)); + auto c2 = 255 - ((C2(s) > 0) ? (C2(id) / C2(s)) : C2(id)); + auto c3 = 255 - ((C3(s) > 0) ? (C3(id) / C3(s)) : C3(id)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d)))); + auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d)))); + auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d)))); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + //(255 - 2 * s) * (d * d) + (2 * s * b) + auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d))); + auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d))); + auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d))); + return JOIN(255, c1, c2, c3); +} + + +int64_t mathMultiply(int64_t a, int64_t b); +int64_t mathDivide(int64_t a, int64_t b); +int64_t mathMulDiv(int64_t a, int64_t b, int64_t c); +void mathRotate(SwPoint& pt, SwFixed angle); +SwFixed mathTan(SwFixed angle); +SwFixed mathAtan(const SwPoint& pt); +SwFixed mathCos(SwFixed angle); +SwFixed mathSin(SwFixed angle); +void mathSplitCubic(SwPoint* base); +SwFixed mathDiff(SwFixed angle1, SwFixed angle2); +SwFixed mathLength(const SwPoint& pt); +bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); +SwFixed mathMean(SwFixed angle1, SwFixed angle2); +SwPoint mathTransform(const Point* to, const Matrix* transform); +bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); +bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); + +void shapeReset(SwShape* shape); +bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); +bool shapePrepared(const SwShape* shape); +bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); +void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid); +void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform); +bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +void shapeFree(SwShape* shape); +void shapeDelStroke(SwShape* shape); +bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); +bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); +void shapeResetFill(SwShape* shape); +void shapeResetStrokeFill(SwShape* shape); +void shapeDelFill(SwShape* shape); +void shapeDelStrokeFill(SwShape* shape); + +void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform); +bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); +SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); +void strokeFree(SwStroke* stroke); + +bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); +void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); +void imageReset(SwImage* image); +void imageFree(SwImage* image); + +bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); +void fillReset(SwFill* fill); +void fillFree(SwFill* fill); + +//OPTIMIZE_ME: Skip the function pointer access +void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity); //composite masking ver. +void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity); //direct masking ver. +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver. +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. + +void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a); //composite masking ver. +void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ; //direct masking ver. +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver. +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver. +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver. + +SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); +SwRleData* rleRender(const SwBBox* bbox); +void rleFree(SwRleData* rle); +void rleReset(SwRleData* rle); +void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2); +void rleClipPath(SwRleData* rle, const SwRleData* clip); +void rleClipRect(SwRleData* rle, const SwBBox* clip); + +SwMpool* mpoolInit(uint32_t threads); +bool mpoolTerm(SwMpool* mpool); +bool mpoolClear(SwMpool* mpool); +SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx); +void mpoolRetOutline(SwMpool* mpool, unsigned idx); +SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx); +void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx); +SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx); +void mpoolRetDashOutline(SwMpool* mpool, unsigned idx); + +bool rasterCompositor(SwSurface* surface); +bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id); +bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity); +bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); +bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); +void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); +void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len); +void rasterUnpremultiply(Surface* surface); +void rasterPremultiply(Surface* surface); +bool rasterConvertCS(Surface* surface, ColorSpace to); + +#endif /* _TVG_SW_COMMON_H_ */ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwFill.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwFill.cpp new file mode 100644 index 000000000..c4a697d35 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwFill.cpp @@ -0,0 +1,785 @@ +/* + * 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 "tvgSwCommon.h" +#include "tvgFill.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +#define RADIAL_A_THRESHOLD 0.0005f +#define GRADIENT_STOP_SIZE 1024 +#define FIXPT_BITS 8 +#define FIXPT_SIZE (1<radial.a + * B = 2 * (dr * fr + rx * dx + ry * dy) + * C = fr^2 - rx^2 - ry^2 + * Derivatives are computed with respect to dx. + * This procedure aims to optimize and eliminate the need to calculate all values from the beginning + * for consecutive x values with a constant y. The Taylor series expansions are computed as long as + * its terms are non-zero. + */ +static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet) +{ + auto radial = &fill->radial; + + auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; + auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; + + b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA; + deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA; + + auto rr = rx * rx + ry * ry; + auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA; + auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA; + + det = b * b + (rr - radial->fr * radial->fr) * radial->invA; + deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr; + deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr; +} + + +static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity) +{ + if (!fill->ctable) { + fill->ctable = static_cast(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); + if (!fill->ctable) return false; + } + + const Fill::ColorStop* colors; + auto cnt = fdata->colorStops(&colors); + if (cnt == 0 || !colors) return false; + + auto pColors = colors; + + auto a = MULTIPLY(pColors->a, opacity); + if (a < 255) fill->translucent = true; + + auto r = pColors->r; + auto g = pColors->g; + auto b = pColors->b; + auto rgba = surface->join(r, g, b, a); + + auto inc = 1.0f / static_cast(GRADIENT_STOP_SIZE); + auto pos = 1.5f * inc; + uint32_t i = 0; + + fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a); + + while (pos <= pColors->offset) { + fill->ctable[i] = fill->ctable[i - 1]; + ++i; + pos += inc; + } + + for (uint32_t j = 0; j < cnt - 1; ++j) { + auto curr = colors + j; + auto next = curr + 1; + auto delta = 1.0f / (next->offset - curr->offset); + auto a2 = MULTIPLY(next->a, opacity); + if (!fill->translucent && a2 < 255) fill->translucent = true; + + auto rgba2 = surface->join(next->r, next->g, next->b, a2); + + while (pos < next->offset && i < GRADIENT_STOP_SIZE) { + auto t = (pos - curr->offset) * delta; + auto dist = static_cast(255 * t); + auto dist2 = 255 - dist; + + auto color = INTERPOLATE(rgba, rgba2, dist2); + fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); + + ++i; + pos += inc; + } + rgba = rgba2; + a = a2; + } + rgba = ALPHA_BLEND((rgba | 0xff000000), a); + + for (; i < GRADIENT_STOP_SIZE; ++i) + fill->ctable[i] = rgba; + + //Make sure the last color stop is represented at the end of the table + fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba; + + return true; +} + + +bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform) +{ + float x1, x2, y1, y2; + if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false; + + fill->linear.dx = x2 - x1; + fill->linear.dy = y2 - y1; + fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; + + if (fill->linear.len < FLT_EPSILON) return true; + + fill->linear.dx /= fill->linear.len; + fill->linear.dy /= fill->linear.len; + fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; + + auto gradTransform = linear->transform(); + bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); + + if (isTransformation) { + if (transform) gradTransform = mathMultiply(transform, &gradTransform); + } else if (transform) { + gradTransform = *transform; + isTransformation = true; + } + + if (isTransformation) { + Matrix invTransform; + if (!mathInverse(&gradTransform, &invTransform)) return false; + + fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23; + + auto dx = fill->linear.dx; + fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21; + fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22; + + fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; + if (fill->linear.len < FLT_EPSILON) return true; + } + + return true; +} + + +bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform) +{ + auto cx = P(radial)->cx; + auto cy = P(radial)->cy; + auto r = P(radial)->r; + auto fx = P(radial)->fx; + auto fy = P(radial)->fy; + auto fr = P(radial)->fr; + + if (r < FLT_EPSILON) return true; + + fill->radial.dr = r - fr; + fill->radial.dx = cx - fx; + fill->radial.dy = cy - fy; + fill->radial.fr = fr; + fill->radial.fx = fx; + fill->radial.fy = fy; + fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy; + + //This condition fulfills the SVG 1.1 std: + //the focal point, if outside the end circle, is moved to be on the end circle + //See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes + if (fill->radial.a < 0) { + auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy); + fill->radial.fx = cx + r * (fx - cx) / dist; + fill->radial.fy = cy + r * (fy - cy) / dist; + fill->radial.dx = cx - fill->radial.fx; + fill->radial.dy = cy - fill->radial.fy; + fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy; + } + + if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a; + + auto gradTransform = radial->transform(); + bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); + + if (transform) { + if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform); + else { + gradTransform = *transform; + isTransformation = true; + } + } + + if (isTransformation) { + Matrix invTransform; + if (!mathInverse(&gradTransform, &invTransform)) return false; + fill->radial.a11 = invTransform.e11; + fill->radial.a12 = invTransform.e12; + fill->radial.a13 = invTransform.e13; + fill->radial.a21 = invTransform.e21; + fill->radial.a22 = invTransform.e22; + fill->radial.a23 = invTransform.e23; + } else { + fill->radial.a11 = fill->radial.a22 = 1.0f; + fill->radial.a12 = fill->radial.a13 = 0.0f; + fill->radial.a21 = fill->radial.a23 = 0.0f; + } + return true; +} + + +static inline uint32_t _clamp(const SwFill* fill, int32_t pos) +{ + switch (fill->spread) { + case FillSpread::Pad: { + if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1; + else if (pos < 0) pos = 0; + break; + } + case FillSpread::Repeat: { + pos = pos % GRADIENT_STOP_SIZE; + if (pos < 0) pos = GRADIENT_STOP_SIZE + pos; + break; + } + case FillSpread::Reflect: { + auto limit = GRADIENT_STOP_SIZE * 2; + pos = pos % limit; + if (pos < 0) pos = limit + pos; + if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1); + break; + } + } + return pos; +} + + +static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos) +{ + int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; + return fill->ctable[_clamp(fill, i)]; +} + + +static inline uint32_t _pixel(const SwFill* fill, float pos) +{ + auto i = static_cast(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f); + return fill->ctable[_clamp(fill, i)]; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + + +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) +{ + //edge case + if (fill->radial.a < RADIAL_A_THRESHOLD) { + auto radial = &fill->radial; + auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; + auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; + + if (opacity == 255) { + for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + *dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp)); + rx += radial->a11; + ry += radial->a21; + } + } else { + for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + *dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp))); + rx += radial->a11; + ry += radial->a21; + } + } + } else { + float b, deltaB, det, deltaDet, deltaDeltaDet; + _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); + + if (opacity == 255) { + for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp)); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } else { + for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp))); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } + } +} + + +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) +{ + if (fill->radial.a < RADIAL_A_THRESHOLD) { + auto radial = &fill->radial; + auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; + auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + *dst = op(_pixel(fill, x0), *dst, a); + rx += radial->a11; + ry += radial->a21; + } + } else { + float b, deltaB, det, deltaDet, deltaDeltaDet; + _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); + + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = op(_pixel(fill, sqrtf(det) - b), *dst, a); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } +} + + +void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a) +{ + if (fill->radial.a < RADIAL_A_THRESHOLD) { + auto radial = &fill->radial; + auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; + auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + auto src = MULTIPLY(a, A(_pixel(fill, x0))); + *dst = maskOp(src, *dst, ~src); + rx += radial->a11; + ry += radial->a21; + } + } else { + float b, deltaB, det, deltaDet, deltaDeltaDet; + _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); + + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b))); + *dst = maskOp(src, *dst, ~src); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } +} + + +void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a) +{ + if (fill->radial.a < RADIAL_A_THRESHOLD) { + auto radial = &fill->radial; + auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; + auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; + for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + auto src = MULTIPLY(A(A(_pixel(fill, x0))), a); + auto tmp = maskOp(src, *cmp, 0); + *dst = tmp + MULTIPLY(*dst, ~tmp); + rx += radial->a11; + ry += radial->a21; + } + } else { + float b, deltaB, det, deltaDet, deltaDeltaDet; + _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); + + for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) { + auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a); + auto tmp = maskOp(src, *cmp, 0); + *dst = tmp + MULTIPLY(*dst, ~tmp); + deltaDet += deltaDeltaDet; + b += deltaB; + } + } +} + + +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) +{ + if (fill->radial.a < RADIAL_A_THRESHOLD) { + auto radial = &fill->radial; + auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx; + auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy; + + if (a == 255) { + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + auto tmp = op(_pixel(fill, x0), *dst, 255); + *dst = op2(tmp, *dst, 255); + rx += radial->a11; + ry += radial->a21; + } + } else { + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy); + auto tmp = op(_pixel(fill, x0), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + rx += radial->a11; + ry += radial->a21; + } + } + } else { + float b, deltaB, det, deltaDet, deltaDeltaDet; + _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); + if (a == 255) { + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255); + *dst = op2(tmp, *dst, 255); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } else { + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } + } +} + + +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + + if (opacity == 255) { + if (mathZero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(color, *dst, alpha(cmp)); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { + *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp)); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp)); + ++dst; + t += inc; + cmp += csize; + } + } + } else { + if (mathZero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity)); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { + *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity)); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp))); + ++dst; + t += inc; + cmp += csize; + } + } + } +} + + +void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + + if (mathZero(inc)) { + auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE)))); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = maskOp(src, *dst, ~src); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { + auto src = MULTIPLY(_fixedPixel(fill, t2), a); + *dst = maskOp(src, *dst, ~src); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a); + *dst = maskOp(src, *dst, ~src); + ++dst; + t += inc; + } + } +} + + +void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + + if (mathZero(inc)) { + auto src = A(_fixedPixel(fill, static_cast(t * FIXPT_SIZE))); + src = MULTIPLY(src, a); + for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) { + auto tmp = maskOp(src, *cmp, 0); + *dst = tmp + MULTIPLY(*dst, ~tmp); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) { + auto src = MULTIPLY(a, A(_fixedPixel(fill, t2))); + auto tmp = maskOp(src, *cmp, 0); + *dst = tmp + MULTIPLY(*dst, ~tmp); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a); + auto tmp = maskOp(src, *cmp, 0); + *dst = tmp + MULTIPLY(*dst, ~tmp); + ++dst; + ++cmp; + t += inc; + } + } +} + + +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + + if (mathZero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = op(color, *dst, a); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { + *dst = op(_fixedPixel(fill, t2), *dst, a); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + *dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a); + ++dst; + t += inc; + } + } +} + + +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1); + float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1); + + if (mathZero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + if (a == 255) { + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto tmp = op(color, *dst, a); + *dst = op2(tmp, *dst, 255); + } + } else { + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto tmp = op(color, *dst, a); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + } + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + if (a == 255) { + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { + auto tmp = op(_fixedPixel(fill, t2), *dst, 255); + *dst = op2(tmp, *dst, 255); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); + *dst = op2(tmp, *dst, 255); + ++dst; + t += inc; + } + } + } else { + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { + auto tmp = op(_fixedPixel(fill, t2), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + ++dst; + t += inc; + } + } + } +} + + +bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) +{ + if (!fill) return false; + + fill->spread = fdata->spread(); + + if (ctable) { + if (!_updateColorTable(fill, fdata, surface, opacity)) return false; + } + + if (fdata->identifier() == TVG_CLASS_ID_LINEAR) { + return _prepareLinear(fill, static_cast(fdata), transform); + } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { + return _prepareRadial(fill, static_cast(fdata), transform); + } + + //LOG: What type of gradient?! + + return false; +} + + +void fillReset(SwFill* fill) +{ + if (fill->ctable) { + free(fill->ctable); + fill->ctable = nullptr; + } + fill->translucent = false; +} + + +void fillFree(SwFill* fill) +{ + if (!fill) return; + + if (fill->ctable) free(fill->ctable); + + free(fill); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwImage.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwImage.cpp new file mode 100644 index 000000000..040c38930 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwImage.cpp @@ -0,0 +1,164 @@ +/* + * 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 "tvgSwCommon.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static inline bool _onlyShifted(const Matrix* m) +{ + if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true; + return false; +} + + +static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid) +{ + image->outline = mpoolReqOutline(mpool, tid); + auto outline = image->outline; + + outline->pts.reserve(5); + outline->types.reserve(5); + outline->cntrs.reserve(1); + outline->closed.reserve(1); + + Point to[4]; + if (mesh->triangleCnt > 0) { + // TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple + // places. We should be able to re-use one we have already done? Also see: + // tvgPicture.h --> bounds + // tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh + // + // TODO: Should we calculate the exact path(s) of the triangle mesh instead? + // i.e. copy tvgSwShape.capp -> _genOutline? + // + // TODO: Cntrs? + auto triangles = mesh->triangles; + auto min = triangles[0].vertex[0].pt; + auto max = triangles[0].vertex[0].pt; + + for (uint32_t i = 0; i < mesh->triangleCnt; ++i) { + if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; + else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; + if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; + else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y; + + if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x; + else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x; + if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y; + else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y; + + if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x; + else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x; + if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; + else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; + } + to[0] = {min.x, min.y}; + to[1] = {max.x, min.y}; + to[2] = {max.x, max.y}; + to[3] = {min.x, max.y}; + } else { + auto w = static_cast(image->w); + auto h = static_cast(image->h); + to[0] = {0, 0}; + to[1] = {w, 0}; + to[2] = {w, h}; + to[3] = {0, h}; + } + + for (int i = 0; i < 4; i++) { + outline->pts.push(mathTransform(&to[i], transform)); + outline->types.push(SW_CURVE_TYPE_POINT); + } + + outline->pts.push(outline->pts[0]); + outline->types.push(SW_CURVE_TYPE_POINT); + outline->cntrs.push(outline->pts.count - 1); + outline->closed.push(true); + + image->outline = outline; + + return true; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +{ + image->direct = _onlyShifted(transform); + + //Fast track: Non-transformed image but just shifted. + if (image->direct) { + image->ox = -static_cast(round(transform->e13)); + image->oy = -static_cast(round(transform->e23)); + //Figure out the scale factor by transform matrix + } else { + auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21)); + auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12)); + image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX; + + if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true; + else image->scaled = false; + } + + if (!_genOutline(image, mesh, transform, mpool, tid)) return false; + return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct); +} + + +bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias) +{ + if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true; + + return false; +} + + +void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid) +{ + mpoolRetOutline(mpool, tid); + image->outline = nullptr; +} + + +void imageReset(SwImage* image) +{ + rleReset(image->rle); +} + + +void imageFree(SwImage* image) +{ + rleFree(image->rle); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwMath.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwMath.cpp new file mode 100644 index 000000000..b636c04cf --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwMath.cpp @@ -0,0 +1,335 @@ +/* + * 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 "tvgSwCommon.h" + + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static float TO_RADIAN(SwFixed angle) +{ + return (float(angle) / 65536.0f) * (MATH_PI / 180.0f); +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +SwFixed mathMean(SwFixed angle1, SwFixed angle2) +{ + return angle1 + mathDiff(angle1, angle2) / 2; +} + + +bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) +{ + auto d1 = base[2] - base[3]; + auto d2 = base[1] - base[2]; + auto d3 = base[0] - base[1]; + + if (d1 == d2 || d2 == d3) { + if (d3.small()) angleIn = angleMid = angleOut = 0; + else angleIn = angleMid = angleOut = mathAtan(d3); + return true; + } + + if (d1.small()) { + if (d2.small()) { + if (d3.small()) { + angleIn = angleMid = angleOut = 0; + return true; + } else { + angleIn = angleMid = angleOut = mathAtan(d3); + } + } else { + if (d3.small()) { + angleIn = angleMid = angleOut = mathAtan(d2); + } else { + angleIn = angleMid = mathAtan(d2); + angleOut = mathAtan(d3); + } + } + } else { + if (d2.small()) { + if (d3.small()) { + angleIn = angleMid = angleOut = mathAtan(d1); + } else { + angleIn = mathAtan(d1); + angleOut = mathAtan(d3); + angleMid = mathMean(angleIn, angleOut); + } + } else { + if (d3.small()) { + angleIn = mathAtan(d1); + angleMid = angleOut = mathAtan(d2); + } else { + angleIn = mathAtan(d1); + angleMid = mathAtan(d2); + angleOut = mathAtan(d3); + } + } + } + + auto theta1 = abs(mathDiff(angleIn, angleMid)); + auto theta2 = abs(mathDiff(angleMid, angleOut)); + + if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true; + return false; +} + + +int64_t mathMultiply(int64_t a, int64_t b) +{ + int32_t s = 1; + + //move sign + if (a < 0) { + a = -a; + s = -s; + } + if (b < 0) { + b = -b; + s = -s; + } + int64_t c = (a * b + 0x8000L) >> 16; + return (s > 0) ? c : -c; +} + + +int64_t mathDivide(int64_t a, int64_t b) +{ + int32_t s = 1; + + //move sign + if (a < 0) { + a = -a; + s = -s; + } + if (b < 0) { + b = -b; + s = -s; + } + int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL; + return (s < 0 ? -q : q); +} + + +int64_t mathMulDiv(int64_t a, int64_t b, int64_t c) +{ + int32_t s = 1; + + //move sign + if (a < 0) { + a = -a; + s = -s; + } + if (b < 0) { + b = -b; + s = -s; + } + if (c < 0) { + c = -c; + s = -s; + } + int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL; + + return (s > 0 ? d : -d); +} + + +void mathRotate(SwPoint& pt, SwFixed angle) +{ + if (angle == 0 || pt.zero()) return; + + Point v = pt.toPoint(); + + auto radian = TO_RADIAN(angle); + auto cosv = cosf(radian); + auto sinv = sinf(radian); + + pt.x = SwCoord(roundf((v.x * cosv - v.y * sinv) * 64.0f)); + pt.y = SwCoord(roundf((v.x * sinv + v.y * cosv) * 64.0f)); +} + + +SwFixed mathTan(SwFixed angle) +{ + if (angle == 0) return 0; + return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f); +} + + +SwFixed mathAtan(const SwPoint& pt) +{ + if (pt.zero()) return 0; + return SwFixed(atan2f(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f); +} + + +SwFixed mathSin(SwFixed angle) +{ + if (angle == 0) return 0; + return mathCos(SW_ANGLE_PI2 - angle); +} + + +SwFixed mathCos(SwFixed angle) +{ + return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f); +} + + +SwFixed mathLength(const SwPoint& pt) +{ + if (pt.zero()) return 0; + + //trivial case + if (pt.x == 0) return abs(pt.y); + if (pt.y == 0) return abs(pt.x); + + auto v = pt.toPoint(); + //return static_cast(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f); + + /* 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. */ + if (v.x < 0) v.x = -v.x; + if (v.y < 0) v.y = -v.y; + return (SwFixed)((v.x > v.y) ? (v.x + v.y * 0.375f) : (v.y + v.x * 0.375f)); +} + + +void mathSplitCubic(SwPoint* base) +{ + SwCoord a, b, c, d; + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = (base[0].x + c) >> 1; + base[5].x = b = (base[3].x + d) >> 1; + c = (c + d) >> 1; + base[2].x = a = (a + c) >> 1; + base[4].x = b = (b + c) >> 1; + base[3].x = (a + b) >> 1; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = (base[0].y + c) >> 1; + base[5].y = b = (base[3].y + d) >> 1; + c = (c + d) >> 1; + base[2].y = a = (a + c) >> 1; + base[4].y = b = (b + c) >> 1; + base[3].y = (a + b) >> 1; +} + + +SwFixed mathDiff(SwFixed angle1, SwFixed angle2) +{ + auto delta = angle2 - angle1; + + delta %= SW_ANGLE_2PI; + if (delta < 0) delta += SW_ANGLE_2PI; + if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI; + + return delta; +} + + +SwPoint mathTransform(const Point* to, const Matrix* transform) +{ + if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)}; + + auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13; + auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23; + + return {TO_SWCOORD(tx), TO_SWCOORD(ty)}; +} + + +bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee) +{ + clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x; + clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y; + clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x; + clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y; + + //Check valid region + if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false; + + //Check boundary + if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y || + clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false; + + return true; +} + + +bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack) +{ + if (!outline) return false; + + auto pt = outline->pts.data; + + if (outline->pts.empty() || outline->cntrs.empty()) { + renderRegion.reset(); + return false; + } + + auto xMin = pt->x; + auto xMax = pt->x; + auto yMin = pt->y; + auto yMax = pt->y; + + for (++pt; pt < outline->pts.end(); ++pt) { + if (xMin > pt->x) xMin = pt->x; + if (xMax < pt->x) xMax = pt->x; + if (yMin > pt->y) yMin = pt->y; + if (yMax < pt->y) yMax = pt->y; + } + //Since no antialiasing is applied in the Fast Track case, + //the rasterization region has to be rearranged. + //https://github.com/Samsung/thorvg/issues/916 + if (fastTrack) { + renderRegion.min.x = static_cast(round(xMin / 64.0f)); + renderRegion.max.x = static_cast(round(xMax / 64.0f)); + renderRegion.min.y = static_cast(round(yMin / 64.0f)); + renderRegion.max.y = static_cast(round(yMax / 64.0f)); + } else { + renderRegion.min.x = xMin >> 6; + renderRegion.max.x = (xMax + 63) >> 6; + renderRegion.min.y = yMin >> 6; + renderRegion.max.y = (yMax + 63) >> 6; + } + return mathClipBBox(clipRegion, renderRegion); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwMemPool.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwMemPool.cpp new file mode 100644 index 000000000..efe780af6 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwMemPool.cpp @@ -0,0 +1,135 @@ +/* + * 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 "tvgSwCommon.h" + + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx) +{ + return &mpool->outline[idx]; +} + + +void mpoolRetOutline(SwMpool* mpool, unsigned idx) +{ + mpool->outline[idx].pts.clear(); + mpool->outline[idx].cntrs.clear(); + mpool->outline[idx].types.clear(); + mpool->outline[idx].closed.clear(); +} + + +SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx) +{ + return &mpool->strokeOutline[idx]; +} + + +void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx) +{ + mpool->strokeOutline[idx].pts.clear(); + mpool->strokeOutline[idx].cntrs.clear(); + mpool->strokeOutline[idx].types.clear(); + mpool->strokeOutline[idx].closed.clear(); +} + + +SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx) +{ + return &mpool->dashOutline[idx]; +} + + +void mpoolRetDashOutline(SwMpool* mpool, unsigned idx) +{ + mpool->dashOutline[idx].pts.clear(); + mpool->dashOutline[idx].cntrs.clear(); + mpool->dashOutline[idx].types.clear(); + mpool->dashOutline[idx].closed.clear(); +} + + +SwMpool* mpoolInit(unsigned threads) +{ + auto allocSize = threads + 1; + + auto mpool = static_cast(calloc(sizeof(SwMpool), 1)); + mpool->outline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); + mpool->strokeOutline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); + mpool->dashOutline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); + mpool->allocSize = allocSize; + + return mpool; +} + + +bool mpoolClear(SwMpool* mpool) +{ + for (unsigned i = 0; i < mpool->allocSize; ++i) { + mpool->outline[i].pts.reset(); + mpool->outline[i].cntrs.reset(); + mpool->outline[i].types.reset(); + mpool->outline[i].closed.reset(); + + mpool->strokeOutline[i].pts.reset(); + mpool->strokeOutline[i].cntrs.reset(); + mpool->strokeOutline[i].types.reset(); + mpool->strokeOutline[i].closed.reset(); + + mpool->dashOutline[i].pts.reset(); + mpool->dashOutline[i].cntrs.reset(); + mpool->dashOutline[i].types.reset(); + mpool->dashOutline[i].closed.reset(); + } + + return true; +} + + +bool mpoolTerm(SwMpool* mpool) +{ + if (!mpool) return false; + + mpoolClear(mpool); + + free(mpool->outline); + free(mpool->strokeOutline); + free(mpool->dashOutline); + free(mpool); + + return true; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRaster.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwRaster.cpp new file mode 100644 index 000000000..dc8c76eb1 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRaster.cpp @@ -0,0 +1,1957 @@ +/* + * 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 + +#ifdef _WIN32 + #include +#elif defined(__linux__) + #include +#else + #include +#endif + +#include "tvgMath.h" +#include "tvgRender.h" +#include "tvgSwCommon.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ +constexpr auto DOWN_SCALE_TOLERANCE = 0.5f; + +struct FillLinear +{ + void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a) + { + fillLinear(fill, dst, y, x, len, op, a); + } + + void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) + { + fillLinear(fill, dst, y, x, len, cmp, op, a); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) + { + fillLinear(fill, dst, y, x, len, op, a); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) + { + fillLinear(fill, dst, y, x, len, cmp, alpha, csize, opacity); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) + { + fillLinear(fill, dst, y, x, len, op, op2, a); + } + +}; + +struct FillRadial +{ + void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a) + { + fillRadial(fill, dst, y, x, len, op, a); + } + + void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) + { + fillRadial(fill, dst, y, x, len, cmp, op, a); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) + { + fillRadial(fill, dst, y, x, len, op, a); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity) + { + fillRadial(fill, dst, y, x, len, cmp, alpha, csize, opacity); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) + { + fillRadial(fill, dst, y, x, len, op, op2, a); + } +}; + + +static inline uint8_t _alpha(uint8_t* a) +{ + return *a; +} + + +static inline uint8_t _ialpha(uint8_t* a) +{ + return ~(*a); +} + + +static inline uint8_t _abgrLuma(uint8_t* c) +{ + auto v = *(uint32_t*)c; + return ((((v&0xff)*54) + (((v>>8)&0xff)*183) + (((v>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B +} + + +static inline uint8_t _argbLuma(uint8_t* c) +{ + auto v = *(uint32_t*)c; + return ((((v&0xff)*19) + (((v>>8)&0xff)*183) + (((v>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R +} + + +static inline uint8_t _abgrInvLuma(uint8_t* c) +{ + return ~_abgrLuma(c); +} + + +static inline uint8_t _argbInvLuma(uint8_t* c) +{ + return ~_argbLuma(c); +} + + +static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + return (a << 24 | b << 16 | g << 8 | r); +} + + +static inline uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + return (a << 24 | r << 16 | g << 8 | b); +} + +static inline bool _blending(const SwSurface* surface) +{ + return (surface->blender) ? true : false; +} + + +/* OPTIMIZE_ME: Probably, we can separate masking(8bits) / composition(32bits) + This would help to enhance the performance by avoiding the unnecessary matting from the composition */ +static inline bool _compositing(const SwSurface* surface) +{ + if (!surface->compositor || (int)surface->compositor->method <= (int)CompositeMethod::ClipPath) return false; + return true; +} + + +static inline bool _matting(const SwSurface* surface) +{ + if ((int)surface->compositor->method < (int)CompositeMethod::AddMask) return true; + else return false; +} + +static inline uint8_t _opMaskNone(uint8_t s, TVG_UNUSED uint8_t d, TVG_UNUSED uint8_t a) +{ + return s; +} + +static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a) +{ + return s + MULTIPLY(d, a); +} + + +static inline uint8_t _opMaskSubtract(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a) +{ + return MULTIPLY(s, 255 - d); +} + + +static inline uint8_t _opMaskIntersect(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a) +{ + return MULTIPLY(s, d); +} + + +static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a) +{ + return MULTIPLY(s, 255 - d) + MULTIPLY(d, a); +} + + +static inline bool _direct(CompositeMethod method) +{ + //subtract & Intersect allows the direct composition + if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true; + return false; +} + + +static inline SwMask _getMaskOp(CompositeMethod method) +{ + switch (method) { + case CompositeMethod::AddMask: return _opMaskAdd; + case CompositeMethod::SubtractMask: return _opMaskSubtract; + case CompositeMethod::DifferenceMask: return _opMaskDifference; + case CompositeMethod::IntersectMask: return _opMaskIntersect; + default: return nullptr; + } +} + + +static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const SwBBox& region) +{ + auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + + for (auto y = region.min.y; y < region.max.y; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { + *dst = *src + MULTIPLY(*dst, ~*src); + } + dbuffer += surface->stride; + sbuffer += image->stride; + } + return true; +} + + +#include "tvgSwRasterTexmap.h" +#include "tvgSwRasterC.h" +#include "tvgSwRasterAvx.h" +#include "tvgSwRasterNeon.h" + + +static inline uint32_t _sampleSize(float scale) +{ + auto sampleSize = static_cast(0.5f / scale); + if (sampleSize == 0) sampleSize = 1; + return sampleSize; +} + + +//Bilinear Interpolation +//OPTIMIZE_ME: Skip the function pointer access +static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED int32_t miny, TVG_UNUSED int32_t maxy, TVG_UNUSED int32_t n) +{ + auto rx = (size_t)(sx); + auto ry = (size_t)(sy); + auto rx2 = rx + 1; + if (rx2 >= w) rx2 = w - 1; + auto ry2 = ry + 1; + if (ry2 >= h) ry2 = h - 1; + + auto dx = static_cast((sx - rx) * 255.0f); + auto dy = static_cast((sy - ry) * 255.0f); + + auto c1 = img[rx + ry * w]; + auto c2 = img[rx2 + ry * w]; + auto c3 = img[rx2 + ry2 * w]; + auto c4 = img[rx + ry2 * w]; + + return INTERPOLATE(INTERPOLATE(c3, c4, dx), INTERPOLATE(c2, c1, dx), dy); +} + + +//2n x 2n Mean Kernel +//OPTIMIZE_ME: Skip the function pointer access +static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, TVG_UNUSED float sy, int32_t miny, int32_t maxy, int32_t n) +{ + size_t c[4] = {0, 0, 0, 0}; + + int32_t minx = (int32_t)sx - n; + if (minx < 0) minx = 0; + + int32_t maxx = (int32_t)sx + n; + if (maxx >= (int32_t)w) maxx = w; + + auto src = img + minx + miny * stride; + + for (auto y = miny; y < maxy; ++y) { + auto p = src; + for (auto x = minx; x < maxx; ++x, ++p) { + c[0] += *p >> 24; + c[1] += (*p >> 16) & 0xff; + c[2] += (*p >> 8) & 0xff; + c[3] += *p & 0xff; + } + src += stride; + } + + n = (maxy - miny) * (maxx - minx); + + c[0] /= n; + c[1] /= n; + c[2] /= n; + c[3] /= n; + + return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; +} + + +/************************************************************************/ +/* Rect */ +/************************************************************************/ + +static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer + auto ialpha = 255 - a; + + for (uint32_t y = 0; y < h; ++y) { + auto cmp = cbuffer; + for (uint32_t x = 0; x < w; ++x, ++cmp) { + *cmp = maskOp(a, *cmp, ialpha); + } + cbuffer += cstride; + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer + auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer + + for (uint32_t y = 0; y < h; ++y) { + auto cmp = cbuffer; + auto dst = dbuffer; + for (uint32_t x = 0; x < w; ++x, ++cmp, ++dst) { + auto tmp = maskOp(a, *cmp, 0); //not use alpha. + *dst = tmp + MULTIPLY(*dst, ~tmp); + } + cbuffer += surface->compositor->image.stride; + dbuffer += surface->stride; + } + return true; +} + + +static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + //8bit masking channels composition + if (surface->channelSize != sizeof(uint8_t)) return false; + + TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); + + auto maskOp = _getMaskOp(surface->compositor->method); + if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, r, g, b, a); + else return _rasterCompositeMaskedRect(surface, region, maskOp, r, g, b, a); + return false; +} + + +static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + ((region.min.y * surface->compositor->image.stride + region.min.x) * csize); //compositor buffer + auto alpha = surface->alpha(surface->compositor->method); + + TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); + + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; + for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { + *dst = INTERPOLATE(color, *dst, alpha(cmp)); + } + } + //8bits grayscale + } else if (surface->channelSize == sizeof(uint8_t)) { + auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; + for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { + *dst = INTERPOLATE8(a, *dst, alpha(cmp)); + } + } + } + return true; +} + + +static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) return false; + + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto color = surface->join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto ialpha = 255 - a; + + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = surface->blender(color, *dst, ialpha); + } + } + return true; +} + + +static bool _rasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ +#if defined(THORVG_AVX_VECTOR_SUPPORT) + return avxRasterTranslucentRect(surface, region, r, g, b, a); +#elif defined(THORVG_NEON_VECTOR_SUPPORT) + return neonRasterTranslucentRect(surface, region, r, g, b, a); +#else + return cRasterTranslucentRect(surface, region, r, g, b, a); +#endif +} + + +static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b) +{ + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->join(r, g, b, 255); + auto buffer = surface->buf32 + (region.min.y * surface->stride); + for (uint32_t y = 0; y < h; ++y) { + rasterPixel32(buffer + y * surface->stride, color, region.min.x, w); + } + return true; + } + //8bits grayscale + if (surface->channelSize == sizeof(uint8_t)) { + for (uint32_t y = 0; y < h; ++y) { + rasterGrayscale8(surface->buf8, 255, (y + region.min.y) * surface->stride + region.min.x, w); + } + return true; + } + return false; +} + + +static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (_compositing(surface)) { + if (_matting(surface)) return _rasterMattedRect(surface, region, r, g, b, a); + else return _rasterMaskedRect(surface, region, r, g, b, a); + } else if (_blending(surface)) { + return _rasterBlendingRect(surface, region, r, g, b, a); + } else { + if (a == 255) return _rasterSolidRect(surface, region, r, g, b); + else return _rasterTranslucentRect(surface, region, r, g, b, a); + } + return false; +} + + +/************************************************************************/ +/* Rle */ +/************************************************************************/ + +static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf8; + auto cstride = surface->compositor->image.stride; + uint8_t src; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto cmp = &cbuffer[span->y * cstride + span->x]; + if (span->coverage == 255) src = a; + else src = MULTIPLY(a, span->coverage); + auto ialpha = 255 - src; + for (auto x = 0; x < span->len; ++x, ++cmp) { + *cmp = maskOp(src, *cmp, ialpha); + } + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf8; + auto cstride = surface->compositor->image.stride; + uint8_t src; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto cmp = &cbuffer[span->y * cstride + span->x]; + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + if (span->coverage == 255) src = a; + else src = MULTIPLY(a, span->coverage); + for (auto x = 0; x < span->len; ++x, ++cmp, ++dst) { + auto tmp = maskOp(src, *cmp, 0); //not use alpha + *dst = tmp + MULTIPLY(*dst, ~tmp); + } + } + return true; +} + + +static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); + + //8bit masking channels composition + if (surface->channelSize != sizeof(uint8_t)) return false; + + auto maskOp = _getMaskOp(surface->compositor->method); + if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, r, g, b, a); + else return _rasterCompositeMaskedRle(surface, rle, maskOp, r, g, b, a); + return false; +} + + +static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); + + auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf8; + auto csize = surface->compositor->image.channelSize; + auto alpha = surface->alpha(surface->compositor->method); + + //32bit channels + if (surface->channelSize == sizeof(uint32_t)) { + uint32_t src; + auto color = surface->join(r, g, b, a); + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + if (span->coverage == 255) src = color; + else src = ALPHA_BLEND(color, span->coverage); + for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { + auto tmp = ALPHA_BLEND(src, alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } + return true; + } + //8bit grayscale + if (surface->channelSize == sizeof(uint8_t)) { + uint8_t src; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + if (span->coverage == 255) src = a; + else src = MULTIPLY(a, span->coverage); + for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { + *dst = INTERPOLATE8(src, *dst, alpha(cmp)); + } + } + return true; + } + return false; +} + + +static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) return false; + + auto span = rle->spans; + auto color = surface->join(r, g, b, a); + auto ialpha = 255 - a; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = surface->blender(color, *dst, ialpha); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + auto tmp = surface->blender(color, *dst, ialpha); + *dst = INTERPOLATE(tmp, *dst, span->coverage); + } + } + } + return true; +} + + +static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ +#if defined(THORVG_AVX_VECTOR_SUPPORT) + return avxRasterTranslucentRle(surface, rle, r, g, b, a); +#elif defined(THORVG_NEON_VECTOR_SUPPORT) + return neonRasterTranslucentRle(surface, rle, r, g, b, a); +#else + return cRasterTranslucentRle(surface, rle, r, g, b, a); +#endif +} + + +static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b) +{ + auto span = rle->spans; + + //32bit channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->join(r, g, b, 255); + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + if (span->coverage == 255) { + rasterPixel32(surface->buf32 + span->y * surface->stride, color, span->x, span->len); + } else { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto src = ALPHA_BLEND(color, span->coverage); + auto ialpha = 255 - span->coverage; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + } + } + } + //8bit grayscale + } else if (surface->channelSize == sizeof(uint8_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + if (span->coverage == 255) { + rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len); + } else { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + auto ialpha = 255 - span->coverage; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = span->coverage + MULTIPLY(*dst, ialpha); + } + } + } + } + return true; +} + + +static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (!rle) return false; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterMattedRle(surface, rle, r, g, b, a); + else return _rasterMaskedRle(surface, rle, r, g, b, a); + } else if (_blending(surface)) { + return _rasterBlendingRle(surface, rle, r, g, b, a); + } else { + if (a == 255) return _rasterSolidRle(surface, rle, r, g, b); + else return _rasterTranslucentRle(surface, rle, r, g, b, a); + } + return false; +} + + +/************************************************************************/ +/* RLE Scaled Image */ +/************************************************************************/ + +#define SCALED_IMAGE_RANGE_Y(y) \ + auto sy = (y) * itransform->e22 + itransform->e23; \ + auto my = (int32_t)round(sy); \ + if (my < 0 || (uint32_t)sy >= image->h) continue; \ + if (scaleMethod == _interpDownScaler) { \ + miny = my - (int32_t)sampleSize; \ + if (miny < 0) miny = 0; \ + maxy = my + (int32_t)sampleSize; \ + if (maxy >= (int32_t)image->h) maxy = (int32_t)image->h; \ + } + +#define SCALED_IMAGE_RANGE_X \ + auto sx = x * itransform->e11 + itransform->e13; \ + if ((int32_t)round(sx) < 0 || (uint32_t) sx >= image->w) continue; + + +#if 0 //Enable it when GRAYSCALE image is supported +static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +{ + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto span = image->rle->spans; + int32_t miny = 0, maxy = 0; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + SCALED_IMAGE_RANGE_Y(span->y) + auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x]; + auto a = MULTIPLY(span->coverage, opacity); + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++cmp) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (a < 255) src = MULTIPLY(src, a); + *cmp = maskOp(src, *cmp, ~src); + } + } + return true; +} + + +static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +{ + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto span = image->rle->spans; + int32_t miny = 0, maxy = 0; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + SCALED_IMAGE_RANGE_Y(span->y) + auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x]; + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + auto a = MULTIPLY(span->coverage, opacity); + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++cmp, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (a < 255) src = MULTIPLY(src, a); + src = maskOp(src, *cmp, 0); //not use alpha + *dst = src + MULTIPLY(*dst, ~src); + } + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} +#endif + +static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ +#if 0 //Enable it when GRAYSCALE image is supported + TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method); + + //8bit masking channels composition + if (surface->channelSize != sizeof(uint8_t)) return false; + + auto maskOp = _getMaskOp(surface->compositor->method); + if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity); + else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity); +#endif + return false; +} + + +static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method); + + auto span = image->rle->spans; + auto csize = surface->compositor->image.channelSize; + auto alpha = surface->alpha(surface->compositor->method); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + int32_t miny = 0, maxy = 0; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + SCALED_IMAGE_RANGE_Y(span->y) + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; + auto a = MULTIPLY(span->coverage, opacity); + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a)); + *dst = src + ALPHA_BLEND(*dst, IA(src)); + } + } + + return true; +} + + +static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + auto span = image->rle->spans; + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + int32_t miny = 0, maxy = 0; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + SCALED_IMAGE_RANGE_Y(span->y) + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); + } + } else { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (opacity < 255) src = ALPHA_BLEND(src, opacity); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); + } + } + } + return true; +} + + +static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + auto span = image->rle->spans; + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + int32_t miny = 0, maxy = 0; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + SCALED_IMAGE_RANGE_Y(span->y) + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto alpha = MULTIPLY(span->coverage, opacity); + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (alpha < 255) src = ALPHA_BLEND(src, alpha); + *dst = src + ALPHA_BLEND(*dst, IA(src)); + } + } + return true; +} + + +static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported scaled rle image!"); + return false; + } + + Matrix itransform; + + if (transform) { + if (!mathInverse(transform, &itransform)) return false; + } else mathIdentity(&itransform); + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity); + else return _rasterScaledMaskedRleImage(surface, image, &itransform, region, opacity); + } else if (_blending(surface)) { + return _rasterScaledBlendingRleImage(surface, image, &itransform, region, opacity); + } else { + return _rasterScaledRleImage(surface, image, &itransform, region, opacity); + } + return false; +} + + +/************************************************************************/ +/* RLE Direct Image */ +/************************************************************************/ + +#if 0 //Enable it when GRAYSCALE image is supported +static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity) +{ + auto span = image->rle->spans; + auto cbuffer = surface->compositor->image.buf8; + auto ctride = surface->compositor->image.stride; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto cmp = &cbuffer[span->y * ctride + span->x]; + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { + *cmp = maskOp(*src, *cmp, ~*src); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { + auto tmp = MULTIPLY(*src, alpha); + *cmp = maskOp(*src, *cmp, ~tmp); + } + } + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity) +{ + auto span = image->rle->spans; + auto cbuffer = surface->compositor->image.buf8; + auto ctride = surface->compositor->image.stride; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto cmp = &cbuffer[span->y * ctride + span->x]; + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) { + auto tmp = maskOp(*src, *cmp, 0); //not use alpha + *dst = INTERPOLATE8(tmp, *dst, (255 - tmp)); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) { + auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha + *dst = INTERPOLATE8(tmp, *dst, (255 - tmp)); + } + } + } + return true; +} +#endif + +static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ +#if 0 //Enable it when GRAYSCALE image is supported + TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method); + + //8bit masking channels composition + if (surface->channelSize != sizeof(uint8_t)) return false; + + auto maskOp = _getMaskOp(surface->compositor->method); + if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity); + else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity); +#endif + return false; +} + + +static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); + + auto span = image->rle->spans; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8; + auto alpha = surface->alpha(surface->compositor->method); + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto a = MULTIPLY(span->coverage, opacity); + if (a == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { + auto tmp = ALPHA_BLEND(*img, alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { + auto tmp = ALPHA_BLEND(*img, MULTIPLY(a, alpha(cmp))); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } + } + return true; +} + + +static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + auto span = image->rle->spans; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + *dst = surface->blender(*img, *dst, IA(*img)); + } + } else if (opacity == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto tmp = surface->blender(*img, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(*img))); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, opacity); + auto tmp = surface->blender(src, *dst, IA(src)); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); + } + } + } + return true; +} + + +static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + auto span = image->rle->spans; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + *dst = *img + ALPHA_BLEND(*dst, IA(*img)); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, alpha); + *dst = src + ALPHA_BLEND(*dst, IA(src)); + } + } + } + return true; +} + + +static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale rle image!"); + return false; + } + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity); + else return _rasterDirectMaskedRleImage(surface, image, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingRleImage(surface, image, opacity); + } else { + return _rasterDirectRleImage(surface, image, opacity); + } + return false; +} + + +/************************************************************************/ +/*Scaled Image */ +/************************************************************************/ + +#if 0 //Enable it when GRAYSCALE image is supported +static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +{ + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); + int32_t miny = 0, maxy = 0; + + for (auto y = region.min.y; y < region.max.y; ++y) { + SCALED_IMAGE_RANGE_Y(y) + auto cmp = cbuffer; + for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (opacity < 255) src = MULTIPLY(src, opacity); + *cmp = maskOp(src, *cmp, ~src); + } + cbuffer += cstride; + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity) +{ + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); + auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); + int32_t miny = 0, maxy = 0; + + for (auto y = region.min.y; y < region.max.y; ++y) { + SCALED_IMAGE_RANGE_Y(y) + auto cmp = cbuffer; + auto dst = dbuffer; + for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (opacity < 255) src = MULTIPLY(src, opacity); + src = maskOp(src, *cmp, 0); //not use alpha + *dst = src + MULTIPLY(*dst, ~src); + } + cbuffer += cstride; + dbuffer += surface->stride; + } + return true; +} +#endif + +static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ +#if 0 //Enable it when GRAYSCALE image is supported + TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); + + auto maskOp = _getMaskOp(surface->compositor->method); + if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity); + else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity); +#endif + return false; +} + + +static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; + auto alpha = surface->alpha(surface->compositor->method); + + TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); + + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + int32_t miny = 0, maxy = 0; + + for (auto y = region.min.y; y < region.max.y; ++y) { + SCALED_IMAGE_RANGE_Y(y) + auto dst = dbuffer; + auto cmp = cbuffer; + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp))); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + dbuffer += surface->stride; + cbuffer += surface->compositor->image.stride * csize; + } + return true; +} + + +static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + int32_t miny = 0, maxy = 0; + + for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { + SCALED_IMAGE_RANGE_Y(y) + auto dst = dbuffer; + for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (opacity < 255) ALPHA_BLEND(src, opacity); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); + } + } + return true; +} + + +static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + int32_t miny = 0, maxy = 0; + + for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { + SCALED_IMAGE_RANGE_Y(y) + auto dst = dbuffer; + for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { + SCALED_IMAGE_RANGE_X + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize); + if (opacity < 255) src = ALPHA_BLEND(src, opacity); + *dst = src + ALPHA_BLEND(*dst, IA(src)); + } + } + return true; +} + + +static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!"); + return false; + } + + Matrix itransform; + + if (transform) { + if (!mathInverse(transform, &itransform)) return false; + } else mathIdentity(&itransform); + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity); + else return _rasterScaledMaskedImage(surface, image, &itransform, region, opacity); + } else if (_blending(surface)) { + return _rasterScaledBlendingImage(surface, image, &itransform, region, opacity); + } else { + return _rasterScaledImage(surface, image, &itransform, region, opacity); + } + return false; +} + + +/************************************************************************/ +/* Direct Image */ +/************************************************************************/ + +#if 0 //Enable it when GRAYSCALE image is supported +static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cstride = surface->compositor->image.stride; + + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer + auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + + for (uint32_t y = 0; y < h; ++y) { + auto cmp = cbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { + *cmp = maskOp(*src, *cmp, ~*src); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { + auto tmp = MULTIPLY(*src, opacity); + *cmp = maskOp(tmp, *cmp, ~tmp); + } + } + cbuffer += cstride; + sbuffer += image->stride; + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cstride = surface->compositor->image.stride; + + auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer + auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer + auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + + for (uint32_t y = 0; y < h; ++y) { + auto cmp = cbuffer; + auto dst = dbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) { + auto tmp = maskOp(*src, *cmp, 0); //not use alpha + *dst = tmp + MULTIPLY(*dst, ~tmp); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) { + auto tmp = maskOp(MULTIPLY(*src, opacity), *cmp, 0); //not use alpha + *dst = tmp + MULTIPLY(*dst, ~tmp); + } + } + cbuffer += cstride; + dbuffer += surface->stride; + sbuffer += image->stride; + } + return true; +} +#endif + +static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ + TVGERR("SW_ENGINE", "Not Supported: Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); + +#if 0 //Enable it when GRAYSCALE image is supported + auto maskOp = _getMaskOp(surface->compositor->method); + if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, opacity); + else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, opacity); +#endif + return false; +} + + +static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto csize = surface->compositor->image.channelSize; + auto alpha = surface->alpha(surface->compositor->method); + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer + + TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); + + //32 bits + if (surface->channelSize == sizeof(uint32_t)) { + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + for (uint32_t y = 0; y < h; ++y) { + auto dst = buffer; + auto cmp = cbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp))); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } + buffer += surface->stride; + cbuffer += surface->compositor->image.stride * csize; + sbuffer += image->stride; + } + //8 bits + } else if (surface->channelSize == sizeof(uint8_t)) { + auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; + for (uint32_t y = 0; y < h; ++y) { + auto dst = buffer; + auto cmp = cbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + *dst = MULTIPLY(A(*src), alpha(cmp)); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + *dst = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp))); + } + } + buffer += surface->stride; + cbuffer += surface->compositor->image.stride * csize; + sbuffer += image->stride; + } + } + return true; +} + + +static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale image!"); + return false; + } + + auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + + for (auto y = region.min.y; y < region.max.y; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { + auto tmp = surface->blender(*src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(*src)); + } + } else { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { + auto tmp = ALPHA_BLEND(*src, opacity); + auto tmp2 = surface->blender(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, A(tmp)); + } + } + dbuffer += surface->stride; + sbuffer += image->stride; + } + return true; +} + + +static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale image!"); + return false; + } + + auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; + auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); + + for (auto y = region.min.y; y < region.max.y; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { + *dst = *src + ALPHA_BLEND(*dst, IA(*src)); + } + } else { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { + auto tmp = ALPHA_BLEND(*src, opacity); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } + dbuffer += surface->stride; + sbuffer += image->stride; + } + return true; +} + + +//Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent] +static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ + if (_compositing(surface)) { + if (_matting(surface)) return _rasterDirectMattedImage(surface, image, region, opacity); + else return _rasterDirectMaskedImage(surface, image, region, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingImage(surface, image, region, opacity); + } else { + return _rasterDirectImage(surface, image, region, opacity); + } + return false; +} + + +//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed] +static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) +{ + //RLE Image + if (image->rle) { + if (image->direct) return _directRleImage(surface, image, opacity); + else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity); + else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity); + //Whole Image + } else { + if (image->direct) return _directImage(surface, image, region, opacity); + else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity); + else return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity); + } +} + + +/************************************************************************/ +/* Rect Gradient */ +/************************************************************************/ + +template +static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwMask maskOp) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); + + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255); + cbuffer += surface->stride; + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +template +static bool _rasterDirectGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwMask maskOp) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); + auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); + + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, dbuffer, region.min.y + y, region.min.x, w, cbuffer, maskOp, 255); + cbuffer += cstride; + dbuffer += surface->stride; + } + return true; +} + + +template +static bool _rasterGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + auto method = surface->compositor->method; + + TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); + + auto maskOp = _getMaskOp(method); + + if (_direct(method)) return _rasterDirectGradientMaskedRect(surface, region, fill, maskOp); + else return _rasterCompositeGradientMaskedRect(surface, region, fill, maskOp); + + return false; +} + + +template +static bool _rasterGradientMattedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; + auto alpha = surface->alpha(surface->compositor->method); + + TVGLOG("SW_ENGINE", "Matted(%d) Gradient [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); + + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, cbuffer, alpha, csize, 255); + buffer += surface->stride; + cbuffer += surface->stride * csize; + } + return true; +} + + +template +static bool _rasterBlendingGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + + if (fill->translucent) { + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendPreNormal, surface->blender, 255); + } + } else { + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, surface->blender, 255); + } + } + return true; +} + +template +static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255); + buffer += surface->stride; + } + return true; +} + + +template +static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, 255); + } + return true; +} + + +static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + if (fill->linear.len < FLT_EPSILON) return false; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRect(surface, region, fill); + else return _rasterGradientMaskedRect(surface, region, fill); + } else if (_blending(surface)) { + return _rasterBlendingGradientRect(surface, region, fill); + } else { + if (fill->translucent) return _rasterTranslucentGradientRect(surface, region, fill); + else _rasterSolidGradientRect(surface, region, fill); + } + return false; +} + + +static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRect(surface, region, fill); + else return _rasterGradientMaskedRect(surface, region, fill); + } else if (_blending(surface)) { + return _rasterBlendingGradientRect(surface, region, fill); + } else { + if (fill->translucent) return _rasterTranslucentGradientRect(surface, region, fill); + else _rasterSolidGradientRect(surface, region, fill); + } + return false; +} + + +/************************************************************************/ +/* Rle Gradient */ +/************************************************************************/ + +template +static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp) +{ + auto span = rle->spans; + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto cmp = &cbuffer[span->y * cstride + span->x]; + fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage); + } + return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +template +static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp) +{ + auto span = rle->spans; + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf8; + auto dbuffer = surface->buf8; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto cmp = &cbuffer[span->y * cstride + span->x]; + auto dst = &dbuffer[span->y * surface->stride + span->x]; + fillMethod()(fill, dst, span->y, span->x, span->len, cmp, maskOp, span->coverage); + } + return true; +} + + +template +static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + auto method = surface->compositor->method; + + TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)method); + + auto maskOp = _getMaskOp(method); + + if (_direct(method)) return _rasterDirectGradientMaskedRle(surface, rle, fill, maskOp); + else return _rasterCompositeGradientMaskedRle(surface, rle, fill, maskOp); + return false; +} + + +template +static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method); + + auto span = rle->spans; + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8; + auto alpha = surface->alpha(surface->compositor->method); + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; + fillMethod()(fill, dst, span->y, span->x, span->len, cmp, alpha, csize, span->coverage); + } + return true; +} + + +template +static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + auto span = rle->spans; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, surface->blender, span->coverage); + } + return true; +} + + +template +static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + auto span = rle->spans; + + //32 bits + if (surface->channelSize == sizeof(uint32_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255); + else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage); + } + //8 bits + } else if (surface->channelSize == sizeof(uint8_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255); + } + } + return true; +} + + +template +static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + auto span = rle->spans; + + //32 bits + if (surface->channelSize == sizeof(uint32_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255); + else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage); + } + //8 bits + } else if (surface->channelSize == sizeof(uint8_t)) { + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskNone, 255); + else fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage); + } + } + + return true; +} + + +static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + if (!rle || fill->linear.len < FLT_EPSILON) return false; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); + else return _rasterGradientMaskedRle(surface, rle, fill); + } else if (_blending(surface)) { + return _rasterBlendingGradientRle(surface, rle, fill); + } else { + if (fill->translucent) return _rasterTranslucentGradientRle(surface, rle, fill); + else return _rasterSolidGradientRle(surface, rle, fill); + } + return false; +} + + +static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + if (!rle) return false; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); + else return _rasterGradientMaskedRle(surface, rle, fill); + } else if (_blending(surface)) { + _rasterBlendingGradientRle(surface, rle, fill); + } else { + if (fill->translucent) _rasterTranslucentGradientRle(surface, rle, fill); + else return _rasterSolidGradientRle(surface, rle, fill); + } + return false; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + + +void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) +{ + //OPTIMIZE_ME: Support SIMD + cRasterPixels(dst, val, offset, len); +} + + +void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) +{ +#if defined(THORVG_AVX_VECTOR_SUPPORT) + avxRasterPixel32(dst, val, offset, len); +#elif defined(THORVG_NEON_VECTOR_SUPPORT) + neonRasterPixel32(dst, val, offset, len); +#else + cRasterPixels(dst, val, offset, len); +#endif +} + + +bool rasterCompositor(SwSurface* surface) +{ + //See CompositeMethod, Alpha:3, InvAlpha:4, Luma:5, InvLuma:6 + surface->alphas[0] = _alpha; + surface->alphas[1] = _ialpha; + + if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { + surface->join = _abgrJoin; + surface->alphas[2] = _abgrLuma; + surface->alphas[3] = _abgrInvLuma; + } else if (surface->cs == ColorSpace::ARGB8888 || surface->cs == ColorSpace::ARGB8888S) { + surface->join = _argbJoin; + surface->alphas[2] = _argbLuma; + surface->alphas[3] = _argbInvLuma; + } else { + TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", surface->cs); + return false; + } + return true; +} + + +bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false; + + //32 bits + if (surface->channelSize == sizeof(uint32_t)) { + //full clear + if (w == surface->stride) { + rasterPixel32(surface->buf32, 0x00000000, surface->stride * y, w * h); + //partial clear + } else { + for (uint32_t i = 0; i < h; i++) { + rasterPixel32(surface->buf32, 0x00000000, (surface->stride * y + x) + (surface->stride * i), w); + } + } + //8 bits + } else if (surface->channelSize == sizeof(uint8_t)) { + //full clear + if (w == surface->stride) { + rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h); + //partial clear + } else { + for (uint32_t i = 0; i < h; i++) { + rasterGrayscale8(surface->buf8, 0x00, (surface->stride * y + x) + (surface->stride * i), w); + } + } + } + return true; +} + + +void rasterUnpremultiply(Surface* surface) +{ + if (surface->channelSize != sizeof(uint32_t)) return; + + TVGLOG("SW_ENGINE", "Unpremultiply [Size: %d x %d]", surface->w, surface->h); + + //OPTIMIZE_ME: +SIMD + for (uint32_t y = 0; y < surface->h; y++) { + auto buffer = surface->buf32 + surface->stride * y; + for (uint32_t x = 0; x < surface->w; ++x) { + uint8_t a = buffer[x] >> 24; + if (a == 255) { + continue; + } else if (a == 0) { + buffer[x] = 0x00ffffff; + } else { + uint16_t r = ((buffer[x] >> 8) & 0xff00) / a; + uint16_t g = ((buffer[x]) & 0xff00) / a; + uint16_t b = ((buffer[x] << 8) & 0xff00) / a; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b); + } + } + } + surface->premultiplied = false; +} + + +void rasterPremultiply(Surface* surface) +{ + if (surface->channelSize != sizeof(uint32_t)) return; + + TVGLOG("SW_ENGINE", "Premultiply [Size: %d x %d]", surface->w, surface->h); + + //OPTIMIZE_ME: +SIMD + auto buffer = surface->buf32; + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w; ++x, ++dst) { + auto c = *dst; + auto a = (c >> 24); + *dst = (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); + } + } + surface->premultiplied = true; +} + + +bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) +{ + if (!shape->fill) return false; + + if (shape->fastTrack) { + if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); + else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); + } else { + if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill); + else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill); + } + return false; +} + + +bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id) +{ + if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false; + + if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); + else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); + + return false; +} + + +bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (a < 255) { + r = MULTIPLY(r, a); + g = MULTIPLY(g, a); + b = MULTIPLY(b, a); + } + if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a); + else return _rasterRle(surface, shape->rle, r, g, b, a); +} + + +bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (a < 255) { + r = MULTIPLY(r, a); + g = MULTIPLY(g, a); + b = MULTIPLY(b, a); + } + + return _rasterRle(surface, shape->strokeRle, r, g, b, a); +} + + +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity) +{ + //Verify Boundary + if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(surface->h)) return false; + + if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity); + else return _rasterImage(surface, image, transform, bbox, opacity); +} + + +bool rasterConvertCS(Surface* surface, ColorSpace to) +{ + //TOOD: Support SIMD accelerations + auto from = surface->cs; + + if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) { + surface->cs = to; + return cRasterABGRtoARGB(surface); + } + if (((from == ColorSpace::ARGB8888) || (from == ColorSpace::ARGB8888S)) && ((to == ColorSpace::ABGR8888) || (to == ColorSpace::ABGR8888S))) { + surface->cs = to; + return cRasterARGBtoABGR(surface); + } + + return false; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRasterAvx.h b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterAvx.h new file mode 100644 index 000000000..633abf1db --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterAvx.h @@ -0,0 +1,197 @@ +/* + * 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 + +#ifdef THORVG_AVX_VECTOR_SUPPORT + +#include + +#define N_32BITS_IN_128REG 4 +#define N_32BITS_IN_256REG 8 + +static inline __m128i ALPHA_BLEND(__m128i c, __m128i a) +{ + //1. set the masks for the A/G and R/B channels + auto AG = _mm_set1_epi32(0xff00ff00); + auto RB = _mm_set1_epi32(0x00ff00ff); + + //2. mask the alpha vector - originally quartet [a, a, a, a] + auto aAG = _mm_and_si128(a, AG); + auto aRB = _mm_and_si128(a, RB); + + //3. calculate the alpha blending of the 2nd and 4th channel + //- mask the color vector + //- multiply it by the masked alpha vector + //- add the correction to compensate bit shifting used instead of dividing by 255 + //- shift bits - corresponding to division by 256 + auto even = _mm_and_si128(c, RB); + even = _mm_mullo_epi16(even, aRB); + even =_mm_add_epi16(even, RB); + even = _mm_srli_epi16(even, 8); + + //4. calculate the alpha blending of the 1st and 3rd channel: + //- mask the color vector + //- multiply it by the corresponding masked alpha vector and store the high bits of the result + //- add the correction to compensate division by 256 instead of by 255 (next step) + //- remove the low 8 bits to mimic the division by 256 + auto odd = _mm_and_si128(c, AG); + odd = _mm_mulhi_epu16(odd, aAG); + odd = _mm_add_epi16(odd, RB); + odd = _mm_and_si128(odd, AG); + + //5. the final result + return _mm_or_si128(odd, even); +} + + +static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) +{ + //1. calculate how many iterations we need to cover the length + uint32_t iterations = len / N_32BITS_IN_256REG; + uint32_t avxFilled = iterations * N_32BITS_IN_256REG; + + //2. set the beginning of the array + dst += offset; + + //3. fill the octets + for (uint32_t i = 0; i < iterations; ++i, dst += N_32BITS_IN_256REG) { + _mm256_storeu_si256((__m256i*)dst, _mm256_set1_epi32(val)); + } + + //4. fill leftovers (in the first step we have to set the pointer to the place where the avx job is done) + int32_t leftovers = len - avxFilled; + while (leftovers--) *dst++ = val; +} + + +static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + + uint32_t ialpha = 255 - a; + + auto avxColor = _mm_set1_epi32(color); + auto avxIalpha = _mm_set1_epi8(ialpha); + + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + + //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) + auto notAligned = ((uintptr_t)dst & 0xf) / 4; + if (notAligned) { + notAligned = (N_32BITS_IN_128REG - notAligned > w ? w : N_32BITS_IN_128REG - notAligned); + for (uint32_t x = 0; x < notAligned; ++x, ++dst) { + *dst = color + ALPHA_BLEND(*dst, ialpha); + } + } + + //2. fill the aligned memory - N_32BITS_IN_128REG pixels processed at once + uint32_t iterations = (w - notAligned) / N_32BITS_IN_128REG; + uint32_t avxFilled = iterations * N_32BITS_IN_128REG; + auto avxDst = (__m128i*)dst; + for (uint32_t x = 0; x < iterations; ++x, ++avxDst) { + *avxDst = _mm_add_epi32(avxColor, ALPHA_BLEND(*avxDst, avxIalpha)); + } + + //3. fill the remaining pixels + int32_t leftovers = w - notAligned - avxFilled; + dst += avxFilled; + while (leftovers--) { + *dst = color + ALPHA_BLEND(*dst, ialpha); + dst++; + } + } + return true; +} + + +static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->join(r, g, b, a); + auto span = rle->spans; + uint32_t src; + + for (uint32_t i = 0; i < rle->size; ++i) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + + if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); + else src = color; + + auto ialpha = IA(src); + + //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) + auto notAligned = ((uintptr_t)dst & 0xf) / 4; + if (notAligned) { + notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned); + for (uint32_t x = 0; x < notAligned; ++x, ++dst) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + } + } + + //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once + //In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all + uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG; + uint32_t avxFilled = 0; + if (iterations > 0) { + auto avxSrc = _mm_set1_epi32(src); + auto avxIalpha = _mm_set1_epi8(ialpha); + + avxFilled = iterations * N_32BITS_IN_128REG; + auto avxDst = (__m128i*)dst; + for (uint32_t x = 0; x < iterations; ++x, ++avxDst) { + *avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha)); + } + } + + //3. fill the remaining pixels + int32_t leftovers = span->len - notAligned - avxFilled; + dst += avxFilled; + while (leftovers--) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + dst++; + } + + ++span; + } + return true; +} + + +#endif + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRasterC.h b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterC.h new file mode 100644 index 000000000..c7f2840f8 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterC.h @@ -0,0 +1,169 @@ +/* + * 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 + +template +static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len) +{ + dst += offset; + + //fix the misaligned memory + auto alignOffset = (long long) dst % 8; + if (alignOffset > 0) { + if (sizeof(PIXEL_T) == 4) alignOffset /= 4; + else if (sizeof(PIXEL_T) == 1) alignOffset = 8 - alignOffset; + while (alignOffset > 0 && len > 0) { + *dst++ = val; + --len; + --alignOffset; + } + } + + //64bits faster clear + if ((sizeof(PIXEL_T) == 4)) { + auto val64 = (uint64_t(val) << 32) | uint64_t(val); + while (len > 1) { + *reinterpret_cast(dst) = val64; + len -= 2; + dst += 2; + } + } else if (sizeof(PIXEL_T) == 1) { + auto val32 = (uint32_t(val) << 24) | (uint32_t(val) << 16) | (uint32_t(val) << 8) | uint32_t(val); + auto val64 = (uint64_t(val32) << 32) | val32; + while (len > 7) { + *reinterpret_cast(dst) = val64; + len -= 8; + dst += 8; + } + } + + //leftovers + while (len--) *dst++ = val; +} + + +static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto span = rle->spans; + + //32bit channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->join(r, g, b, a); + uint32_t src; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); + else src = color; + auto ialpha = IA(src); + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = src + ALPHA_BLEND(*dst, ialpha); + } + } + //8bit grayscale + } else if (surface->channelSize == sizeof(uint8_t)) { + uint8_t src; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf8[span->y * surface->stride + span->x]; + if (span->coverage < 255) src = MULTIPLY(span->coverage, a); + else src = a; + auto ialpha = ~a; + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = src + MULTIPLY(*dst, ialpha); + } + } + } + return true; +} + + +static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + + //32bits channels + if (surface->channelSize == sizeof(uint32_t)) { + auto color = surface->join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto ialpha = 255 - a; + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = color + ALPHA_BLEND(*dst, ialpha); + } + } + //8bit grayscale + } else if (surface->channelSize == sizeof(uint8_t)) { + auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; + auto ialpha = ~a; + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = a + MULTIPLY(*dst, ialpha); + } + } + } + return true; +} + + +static bool inline cRasterABGRtoARGB(Surface* surface) +{ + TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h); + + //64bits faster converting + if (surface->w % 2 == 0) { + auto buffer = reinterpret_cast(surface->buf32); + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride / 2) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w / 2; ++x, ++dst) { + auto c = *dst; + //flip Blue, Red channels + *dst = (c & 0xff000000ff000000) + ((c & 0x00ff000000ff0000) >> 16) + (c & 0x0000ff000000ff00) + ((c & 0x000000ff000000ff) << 16); + } + } + //default converting + } else { + auto buffer = surface->buf32; + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w; ++x, ++dst) { + auto c = *dst; + //flip Blue, Red channels + *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16); + } + } + } + return true; +} + + +static bool inline cRasterARGBtoABGR(Surface* surface) +{ + //exactly same with ABGRtoARGB + return cRasterABGRtoARGB(surface); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRasterNeon.h b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterNeon.h new file mode 100644 index 000000000..97e4706a3 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterNeon.h @@ -0,0 +1,144 @@ +/* + * 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 + +#ifdef THORVG_NEON_VECTOR_SUPPORT + +#include + +static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a) +{ + uint16x8_t t = vmull_u8(c, a); + return vshrn_n_u16(t, 8); +} + + +static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) +{ + uint32_t iterations = len / 4; + uint32_t neonFilled = iterations * 4; + + dst += offset; + uint32x4_t vectorVal = {val, val, val, val}; + + for (uint32_t i = 0; i < iterations; ++i) { + vst1q_u32(dst, vectorVal); + dst += 4; + } + + int32_t leftovers = len - neonFilled; + while (leftovers--) *dst++ = val; +} + + +static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->blender.join(r, g, b, a); + auto span = rle->spans; + uint32_t src; + uint8x8_t *vDst = nullptr; + uint16_t align; + + for (uint32_t i = 0; i < rle->size; ++i) { + if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); + else src = color; + + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto ialpha = IALPHA(src); + + if ((((uint32_t) dst) & 0x7) != 0) { + //fill not aligned byte + *dst = src + ALPHA_BLEND(*dst, ialpha); + vDst = (uint8x8_t*)(dst + 1); + align = 1; + } else { + vDst = (uint8x8_t*) dst; + align = 0; + } + + uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src); + uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha); + + for (uint32_t x = 0; x < (span->len - align) / 2; ++x) + vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha)); + + auto leftovers = (span->len - align) % 2; + if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha); + + ++span; + } + return true; +} + + +static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) { + TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize); + return false; + } + + auto color = surface->blender.join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto ialpha = 255 - a; + + auto vColor = vdup_n_u32(color); + auto vIalpha = vdup_n_u8((uint8_t) ialpha); + + uint8x8_t* vDst = nullptr; + uint32_t align; + + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + + if ((((uint32_t) dst) & 0x7) != 0) { + //fill not aligned byte + *dst = color + ALPHA_BLEND(*dst, ialpha); + vDst = (uint8x8_t*) (dst + 1); + align = 1; + } else { + vDst = (uint8x8_t*) dst; + align = 0; + } + + for (uint32_t x = 0; x < (w - align) / 2; ++x) + vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha)); + + auto leftovers = (w - align) % 2; + if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha); + } + return true; +} + +#endif + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRasterTexmap.h b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterTexmap.h new file mode 100644 index 000000000..e6e87782d --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRasterTexmap.h @@ -0,0 +1,1213 @@ +/* + * 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 + +struct AALine +{ + int32_t x[2]; + int32_t coverage[2]; + int32_t length[2]; +}; + +struct AASpans +{ + AALine *lines; + int32_t yStart; + int32_t yEnd; +}; + +static inline void _swap(float& a, float& b, float& tmp) +{ + tmp = a; + a = b; + b = tmp; +} + + +//Careful! Shared resource, No support threading +static float dudx, dvdx; +static float dxdya, dxdyb, dudya, dvdya; +static float xa, xb, ua, va; + + +//Y Range exception handling +static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd) +{ + int32_t regionTop, regionBottom; + + if (region) { + regionTop = region->min.y; + regionBottom = region->max.y; + } else { + regionTop = image->rle->spans->y; + regionBottom = image->rle->spans[image->rle->size - 1].y; + } + + if (yStart >= regionBottom) return false; + + if (yStart < regionTop) yStart = regionTop; + if (yEnd > regionBottom) yEnd = regionBottom; + + return true; +} + + +static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) +{ + return false; + +#if 0 //Enable it when GRAYSCALE image is supported + auto maskOp = _getMaskOp(surface->compositor->method); + auto direct = _direct(surface->compositor->method); + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf8; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + SwSpan* span = nullptr; //used only when rle based. + + if (!_arrange(image, region, yStart, yEnd)) return false; + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + y = yStart; + + while (y < yEnd) { + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + + x = x1; + + auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1]; + auto dst = &surface->buf8[y * surface->stride + x1]; + + if (opacity == 255) { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + if (direct) { + auto tmp = maskOp(px, *cmp, 0); //not use alpha + *dst = tmp + MULTIPLY(*dst, ~tmp); + ++dst; + } else { + *cmp = maskOp(px, *cmp, ~px); + } + ++cmp; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } else { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + + if (direct) { + auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0); + *dst = tmp + MULTIPLY(*dst, ~tmp); + ++dst; + } else { + auto tmp = MULTIPLY(px, opacity); + *cmp = maskOp(tmp, *cmp, ~px); + } + ++cmp; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } + } + + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + + if (!region && spanIdx >= image->rle->size) break; + + ++y; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; + + return true; +#endif +} + + +static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) +{ + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf32; + auto dbuf = surface->buf32; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t dw = surface->stride; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + uint32_t* buf; + SwSpan* span = nullptr; //used only when rle based. + + if (!_arrange(image, region, yStart, yEnd)) return; + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + y = yStart; + + while (y < yEnd) { + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + + buf = dbuf + ((y * dw) + x1); + + x = x1; + + if (opacity == 255) { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + *buf = surface->blender(px, *buf, IA(px)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } else { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + auto src = ALPHA_BLEND(px, opacity); + *buf = surface->blender(src, *buf, IA(src)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } + } + + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + + if (!region && spanIdx >= image->rle->size) break; + + ++y; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; +} + + +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) +{ + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf32; + auto dbuf = surface->buf32; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t dw = surface->stride; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + uint32_t* buf; + SwSpan* span = nullptr; //used only when rle based. + + //for matting(composition) + auto csize = matting ? surface->compositor->image.channelSize: 0; + auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; + uint8_t* cmp = nullptr; + + if (!_arrange(image, region, yStart, yEnd)) return; + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + y = yStart; + + while (y < yEnd) { + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + + buf = dbuf + ((y * dw) + x1); + + x = x1; + + if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize]; + + if (opacity == 255) { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + uint32_t src; + if (matting) { + src = ALPHA_BLEND(px, alpha(cmp)); + cmp += csize; + } else { + src = px; + } + *buf = src + ALPHA_BLEND(*buf, IA(src)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } else { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + vv = (int) v; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + if (vv >= sh) continue; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + uint32_t src; + if (matting) { + src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp))); + cmp += csize; + } else { + src = ALPHA_BLEND(px, opacity); + } + *buf = src + ALPHA_BLEND(*buf, IA(src)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } + } + + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + + if (!region && spanIdx >= image->rle->size) break; + + ++y; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; +} + + +/* This mapping algorithm is based on Mikael Kalms's. */ +static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) +{ + float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; + float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; + float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x}; + float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y}; + + float off_y; + float dxdy[3] = {0.0f, 0.0f, 0.0f}; + float tmp; + + auto upper = false; + + //Sort the vertices in ascending Y order + if (y[0] > y[1]) { + _swap(x[0], x[1], tmp); + _swap(y[0], y[1], tmp); + _swap(u[0], u[1], tmp); + _swap(v[0], v[1], tmp); + } + if (y[0] > y[2]) { + _swap(x[0], x[2], tmp); + _swap(y[0], y[2], tmp); + _swap(u[0], u[2], tmp); + _swap(v[0], v[2], tmp); + } + if (y[1] > y[2]) { + _swap(x[1], x[2], tmp); + _swap(y[1], y[2], tmp); + _swap(u[1], u[2], tmp); + _swap(v[1], v[2], tmp); + } + + //Y indexes + int yi[3] = {(int)y[0], (int)y[1], (int)y[2]}; + + //Skip drawing if it's too thin to cover any pixels at all. + if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return; + + //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0) + auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0])); + + //Skip poly if it's an infinitely thin line + if (mathZero(denom)) return; + + denom = 1 / denom; //Reciprocal for speeding up + dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom; + dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom; + auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom; + auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom; + + //Calculate X-slopes along the edges + if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]); + if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]); + if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]); + + //Determine which side of the polygon the longer edge is on + auto side = (dxdy[1] > dxdy[0]) ? true : false; + + if (mathEqual(y[0], y[1])) side = x[0] > x[1]; + if (mathEqual(y[1], y[2])) side = x[2] > x[1]; + + auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image? + auto compositing = _compositing(surface); //Composition required + auto blending = _blending(surface); //Blending required + + //Longer edge is on the left side + if (!side) { + //Calculate slopes along left edge + dxdya = dxdy[1]; + dudya = dxdya * dudx + dudy; + dvdya = dxdya * dvdx + dvdy; + + //Perform subpixel pre-stepping along left edge + auto dy = 1.0f - (y[0] - yi[0]); + xa = x[0] + dy * dxdya; + ua = u[0] + dy * dudya; + va = v[0] + dy * dvdya; + + //Draw upper segment if possibly visible + if (yi[0] < yi[1]) { + off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; + xa += (off_y * dxdya); + ua += (off_y * dudya); + va += (off_y * dvdya); + + // Set right edge X-slope and perform subpixel pre-stepping + dxdyb = dxdy[0]; + xb = x[0] + dy * dxdyb + (off_y * dxdyb); + + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); + } + upper = true; + } + //Draw lower segment if possibly visible + if (yi[1] < yi[2]) { + off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; + if (!upper) { + xa += (off_y * dxdya); + ua += (off_y * dudya); + va += (off_y * dvdya); + } + // Set right edge X-slope and perform subpixel pre-stepping + dxdyb = dxdy[2]; + xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + } + } + //Longer edge is on the right side + } else { + //Set right edge X-slope and perform subpixel pre-stepping + dxdyb = dxdy[1]; + auto dy = 1.0f - (y[0] - yi[0]); + xb = x[0] + dy * dxdyb; + + //Draw upper segment if possibly visible + if (yi[0] < yi[1]) { + off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; + xb += (off_y *dxdyb); + + // Set slopes along left edge and perform subpixel pre-stepping + dxdya = dxdy[0]; + dudya = dxdya * dudx + dudy; + dvdya = dxdya * dvdx + dvdy; + + xa = x[0] + dy * dxdya + (off_y * dxdya); + ua = u[0] + dy * dudya + (off_y * dudya); + va = v[0] + dy * dvdya + (off_y * dvdya); + + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); + } + upper = true; + } + //Draw lower segment if possibly visible + if (yi[1] < yi[2]) { + off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; + if (!upper) xb += (off_y *dxdyb); + + // Set slopes along left edge and perform subpixel pre-stepping + dxdya = dxdy[2]; + dudya = dxdya * dudx + dudy; + dvdya = dxdya * dvdx + dvdy; + dy = 1 - (y[1] - yi[1]); + xa = x[1] + dy * dxdya + (off_y * dxdya); + ua = u[1] + dy * dudya + (off_y * dudya); + va = v[1] + dy * dvdya + (off_y * dvdya); + + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + } + } + } +} + + +static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region) +{ + auto yStart = static_cast(ymin); + auto yEnd = static_cast(ymax); + + if (!_arrange(image, region, yStart, yEnd)) return nullptr; + + auto aaSpans = static_cast(malloc(sizeof(AASpans))); + aaSpans->yStart = yStart; + aaSpans->yEnd = yEnd; + + //Initialize X range + auto height = yEnd - yStart; + + aaSpans->lines = static_cast(calloc(height, sizeof(AALine))); + + for (int32_t i = 0; i < height; i++) { + aaSpans->lines[i].x[0] = INT32_MAX; + aaSpans->lines[i].x[1] = INT32_MIN; + } + return aaSpans; +} + + +static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse) +{ + if (eidx == 1) reverse = !reverse; + int32_t coverage = (255 / (diagonal + 2)); + int32_t tmp; + for (int32_t ry = 0; ry < (diagonal + 2); ry++) { + tmp = y - ry - edgeDist; + if (tmp < 0) return; + lines[tmp].length[eidx] = 1; + if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry); + else lines[tmp].coverage[eidx] = (coverage * ry); + } +} + + +static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse) +{ + if (eidx == 1) reverse = !reverse; + int32_t coverage = (255 / (rewind + 1)); + int32_t tmp; + for (int ry = 1; ry < (rewind + 1); ry++) { + tmp = y - ry; + if (tmp < 0) return; + lines[tmp].length[eidx] = 1; + if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry)); + else lines[tmp].coverage[eidx] = (coverage * ry); + } +} + + +static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2) +{ + if (lines[y].length[eidx] < abs(x - x2)) { + lines[y].length[eidx] = abs(x - x2); + lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1)); + } +} + + +/* + * This Anti-Aliasing mechanism is originated from Hermet Park's idea. + * To understand this AA logic, you can refer this page: + * www.hermet.pe.kr/122 (hermetpark@gmail.com) +*/ +static void _calcAAEdge(AASpans *aaSpans, int32_t eidx) +{ +//Previous edge direction: +#define DirOutHor 0x0011 +#define DirOutVer 0x0001 +#define DirInHor 0x0010 +#define DirInVer 0x0000 +#define DirNone 0x1000 + +#define PUSH_VERTEX() \ + do { \ + pEdge.x = lines[y].x[eidx]; \ + pEdge.y = y; \ + ptx[0] = tx[0]; \ + ptx[1] = tx[1]; \ + } while (0) + + int32_t y = 0; + SwPoint pEdge = {-1, -1}; //previous edge point + SwPoint edgeDiff = {0, 0}; //temporary used for point distance + + /* store bigger to tx[0] between prev and current edge's x positions. */ + int32_t tx[2] = {0, 0}; + /* back up prev tx values */ + int32_t ptx[2] = {0, 0}; + int32_t diagonal = 0; //straight diagonal pixels count + + auto yStart = aaSpans->yStart; + auto yEnd = aaSpans->yEnd; + auto lines = aaSpans->lines; + + int32_t prevDir = DirNone; + int32_t curDir = DirNone; + + yEnd -= yStart; + + //Start Edge + if (y < yEnd) { + pEdge.x = lines[y].x[eidx]; + pEdge.y = y; + } + + //Calculates AA Edges + for (y++; y < yEnd; y++) { + //Ready tx + if (eidx == 0) { + tx[0] = pEdge.x; + tx[1] = lines[y].x[0]; + } else { + tx[0] = lines[y].x[1]; + tx[1] = pEdge.x; + } + edgeDiff.x = (tx[0] - tx[1]); + edgeDiff.y = (y - pEdge.y); + + //Confirm current edge direction + if (edgeDiff.x > 0) { + if (edgeDiff.y == 1) curDir = DirOutHor; + else curDir = DirOutVer; + } else if (edgeDiff.x < 0) { + if (edgeDiff.y == 1) curDir = DirInHor; + else curDir = DirInVer; + } else curDir = DirNone; + + //straight diagonal increase + if ((curDir == prevDir) && (y < yEnd)) { + if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) { + ++diagonal; + PUSH_VERTEX(); + continue; + } + } + + switch (curDir) { + case DirOutHor: { + _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]); + if (diagonal > 0) { + _calcIrregularCoverage(lines, eidx, y, diagonal, 0, true); + diagonal = 0; + } + /* Increment direction is changed: Outside Vertical -> Outside Horizontal */ + if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]); + + //Trick, but fine-tunning! + if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]); + PUSH_VERTEX(); + } + break; + case DirOutVer: { + _calcVertCoverage(lines, eidx, y, edgeDiff.y, true); + if (diagonal > 0) { + _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false); + diagonal = 0; + } + /* Increment direction is changed: Outside Horizontal -> Outside Vertical */ + if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]); + PUSH_VERTEX(); + } + break; + case DirInHor: { + _calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]); + if (diagonal > 0) { + _calcIrregularCoverage(lines, eidx, y, diagonal, 0, false); + diagonal = 0; + } + /* Increment direction is changed: Outside Horizontal -> Inside Horizontal */ + if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]); + PUSH_VERTEX(); + } + break; + case DirInVer: { + _calcVertCoverage(lines, eidx, y, edgeDiff.y, false); + if (prevDir == DirOutHor) edgeDiff.y -= 1; //Weird, fine tuning????????????????????? + if (diagonal > 0) { + _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true); + diagonal = 0; + } + /* Increment direction is changed: Outside Horizontal -> Inside Vertical */ + if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]); + PUSH_VERTEX(); + } + break; + } + if (curDir != DirNone) prevDir = curDir; + } + + //leftovers...? + if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) { + if (y >= yEnd) y = (yEnd - 1); + _calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]); + _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]); + } else { + ++y; + if (y > yEnd) y = yEnd; + _calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001)); + } +} + + +static bool _apply(SwSurface* surface, AASpans* aaSpans) +{ + auto y = aaSpans->yStart; + uint32_t pixel; + uint32_t* dst; + int32_t pos; + + //left side + _calcAAEdge(aaSpans, 0); + //right side + _calcAAEdge(aaSpans, 1); + + while (y < aaSpans->yEnd) { + auto line = &aaSpans->lines[y - aaSpans->yStart]; + auto width = line->x[1] - line->x[0]; + if (width > 0) { + auto offset = y * surface->stride; + + //Left edge + dst = surface->buf32 + (offset + line->x[0]); + if (line->x[0] > 1) pixel = *(dst - 1); + else pixel = *dst; + + pos = 1; + while (pos <= line->length[0]) { + *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos); + ++dst; + ++pos; + } + + //Right edge + dst = surface->buf32 + (offset + line->x[1] - 1); + if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1); + else pixel = *dst; + + pos = width; + while ((int32_t)(width - line->length[1]) < pos) { + *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos)))); + --dst; + --pos; + } + } + y++; + } + + free(aaSpans->lines); + free(aaSpans); + + return true; +} + + +/* + 2 triangles constructs 1 mesh. + below figure illustrates vert[4] index info. + If you need better quality, please divide a mesh by more number of triangles. + + 0 -- 1 + | / | + | / | + 3 -- 2 +*/ +static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); + return false; + } + + //Exceptions: No dedicated drawing area? + if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; + + /* Prepare vertices. + shift XY coordinates to match the sub-pixeling technique. */ + Vertex vertices[4]; + vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; + vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}}; + vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}}; + vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}}; + + float ys = FLT_MAX, ye = -1.0f; + for (int i = 0; i < 4; i++) { + mathMultiply(&vertices[i].pt, transform); + + if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; + if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; + } + + auto aaSpans = _AASpans(ys, ye, image, region); + if (!aaSpans) return true; + + Polygon polygon; + + //Draw the first polygon + polygon.vertex[0] = vertices[0]; + polygon.vertex[1] = vertices[1]; + polygon.vertex[2] = vertices[3]; + + _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); + + //Draw the second polygon + polygon.vertex[0] = vertices[1]; + polygon.vertex[1] = vertices[2]; + polygon.vertex[2] = vertices[3]; + + _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); + +#if 0 + if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) { + _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); + } +#endif + return _apply(surface, aaSpans); +} + + +/* + Provide any number of triangles to draw a mesh using the supplied image. + Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one. + Example: + + 0 -- 1 0 -- 1 0 + | / | --> | / / | + | / | | / / | + 2 -- 3 2 1 -- 2 + + Should provide two Polygons, one for each triangle. + // TODO: region? +*/ +static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity) +{ + if (surface->channelSize == sizeof(uint8_t)) { + TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!"); + return false; + } + + //Exceptions: No dedicated drawing area? + if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; + + // Step polygons once to transform + auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt); + float ys = FLT_MAX, ye = -1.0f; + for (uint32_t i = 0; i < mesh->triangleCnt; i++) { + transformedTris[i] = mesh->triangles[i]; + mathMultiply(&transformedTris[i].vertex[0].pt, transform); + mathMultiply(&transformedTris[i].vertex[1].pt, transform); + mathMultiply(&transformedTris[i].vertex[2].pt, transform); + + if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y; + else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y; + if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y; + else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y; + if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y; + else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y; + + // Convert normalized UV coordinates to image coordinates + transformedTris[i].vertex[0].uv.x *= (float)image->w; + transformedTris[i].vertex[0].uv.y *= (float)image->h; + transformedTris[i].vertex[1].uv.x *= (float)image->w; + transformedTris[i].vertex[1].uv.y *= (float)image->h; + transformedTris[i].vertex[2].uv.x *= (float)image->w; + transformedTris[i].vertex[2].uv.y *= (float)image->h; + } + + // Get AA spans and step polygons again to draw + if (auto aaSpans = _AASpans(ys, ye, image, region)) { + for (uint32_t i = 0; i < mesh->triangleCnt; i++) { + _rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity); + } +#if 0 + if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) { + _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); + } +#endif + _apply(surface, aaSpans); + } + free(transformedTris); + return true; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.cpp new file mode 100644 index 000000000..d8e00dc86 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.cpp @@ -0,0 +1,862 @@ +/* + * 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 "tvgSwCommon.h" +#include "tvgTaskScheduler.h" +#include "tvgSwRenderer.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ +static int32_t initEngineCnt = false; +static int32_t rendererCnt = 0; +static SwMpool* globalMpool = nullptr; +static uint32_t threadsCnt = 0; + +struct SwTask : Task +{ + SwSurface* surface = nullptr; + SwMpool* mpool = nullptr; + SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region + Matrix* transform = nullptr; + Array clips; + RenderUpdateFlag flags = RenderUpdateFlag::None; + uint8_t opacity; + bool pushed = false; //Pushed into task list? + bool disposed = false; //Disposed task? + + RenderRegion bounds() const + { + RenderRegion region; + + //Range over? + region.x = bbox.min.x > 0 ? bbox.min.x : 0; + region.y = bbox.min.y > 0 ? bbox.min.y : 0; + region.w = bbox.max.x - region.x; + region.h = bbox.max.y - region.y; + if (region.w < 0) region.w = 0; + if (region.h < 0) region.h = 0; + + return region; + } + + virtual bool dispose() = 0; + virtual bool clip(SwRleData* target) = 0; + virtual SwRleData* rle() = 0; + + virtual ~SwTask() + { + free(transform); + } +}; + + +struct SwShapeTask : SwTask +{ + SwShape shape; + const RenderShape* rshape = nullptr; + bool cmpStroking = false; + bool clipper = false; + + /* We assume that if the stroke width is greater than 2, + the shape's outline beneath the stroke could be adequately covered by the stroke drawing. + Therefore, antialiasing is disabled under this condition. + Additionally, the stroke style should not be dashed. */ + bool antialiasing(float strokeWidth) + { + return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst; + } + + float validStrokeWidth() + { + if (!rshape->stroke) return 0.0f; + + auto width = rshape->stroke->width; + if (mathZero(width)) return 0.0f; + + if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f; + if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; + + if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12)); + else return width; + } + + + bool clip(SwRleData* target) override + { + if (shape.fastTrack) rleClipRect(target, &bbox); + else if (shape.rle) rleClipPath(target, shape.rle); + else return false; + + return true; + } + + SwRleData* rle() override + { + if (!shape.rle && shape.fastTrack) { + shape.rle = rleRender(&shape.bbox); + } + return shape.rle; + } + + void run(unsigned tid) override + { + if (opacity == 0 && !clipper) return; //Invisible + + auto strokeWidth = validStrokeWidth(); + bool visibleFill = false; + auto clipRegion = bbox; + + //This checks also for the case, if the invisible shape turned to visible by alpha. + auto prepareShape = false; + if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true; + + //Shape + if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { + uint8_t alpha = 0; + rshape->fillColor(nullptr, nullptr, nullptr, &alpha); + alpha = MULTIPLY(alpha, opacity); + visibleFill = (alpha > 0 || rshape->fill); + if (visibleFill || clipper) { + shapeReset(&shape); + if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err; + } + } + //Fill + if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) { + if (visibleFill || clipper) { + if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err; + } + if (auto fill = rshape->fill) { + auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; + if (ctable) shapeResetFill(&shape); + if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err; + } else { + shapeDelFill(&shape); + } + } + //Stroke + if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { + if (strokeWidth > 0.0f) { + shapeResetStroke(&shape, rshape, transform); + if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err; + + if (auto fill = rshape->strokeFill()) { + auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; + if (ctable) shapeResetStrokeFill(&shape); + if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err; + } else { + shapeDelStrokeFill(&shape); + } + } else { + shapeDelStroke(&shape); + } + } + + //Clear current task memorypool here if the clippers would use the same memory pool + shapeDelOutline(&shape, mpool, tid); + + //Clip Path + for (auto clip = clips.data; clip < clips.end(); ++clip) { + auto clipper = static_cast(*clip); + //Clip shape rle + if (shape.rle && !clipper->clip(shape.rle)) goto err; + //Clip stroke rle + if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; + } + return; + + err: + shapeReset(&shape); + shapeDelOutline(&shape, mpool, tid); + } + + bool dispose() override + { + shapeFree(&shape); + return true; + } +}; + + +struct SwSceneTask : SwTask +{ + Array scene; //list of paints render data (SwTask) + SwRleData* sceneRle = nullptr; + + bool clip(SwRleData* target) override + { + //Only one shape + if (scene.count == 1) { + return static_cast(*scene.data)->clip(target); + } + + //More than one shapes + if (sceneRle) rleClipPath(target, sceneRle); + else TVGLOG("SW_ENGINE", "No clippers in a scene?"); + + return true; + } + + SwRleData* rle() override + { + return sceneRle; + } + + void run(unsigned tid) override + { + //TODO: Skip the run if the scene hans't changed. + if (!sceneRle) sceneRle = static_cast(calloc(1, sizeof(SwRleData))); + else rleReset(sceneRle); + + //Merge shapes if it has more than one shapes + if (scene.count > 1) { + //Merge first two clippers + auto clipper1 = static_cast(*scene.data); + auto clipper2 = static_cast(*(scene.data + 1)); + + rleMerge(sceneRle, clipper1->rle(), clipper2->rle()); + + //Unify the remained clippers + for (auto rd = scene.data + 2; rd < scene.end(); ++rd) { + auto clipper = static_cast(*rd); + rleMerge(sceneRle, sceneRle, clipper->rle()); + } + } + } + + bool dispose() override + { + rleFree(sceneRle); + return true; + } +}; + + +struct SwImageTask : SwTask +{ + SwImage image; + Surface* source; //Image source + const RenderMesh* mesh = nullptr; //Should be valid ptr in action + + bool clip(SwRleData* target) override + { + TVGERR("SW_ENGINE", "Image is used as ClipPath?"); + return true; + } + + SwRleData* rle() override + { + TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?"); + return nullptr; + } + + void run(unsigned tid) override + { + auto clipRegion = bbox; + + //Convert colorspace if it's not aligned. + if (source->owner) { + if (source->cs != surface->cs) rasterConvertCS(source, surface->cs); + if (!source->premultiplied) rasterPremultiply(source); + } + + image.data = source->data; + image.w = source->w; + image.h = source->h; + image.stride = source->stride; + image.channelSize = source->channelSize; + + //Invisible shape turned to visible by alpha. + if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) { + imageReset(&image); + if (!image.data || image.w == 0 || image.h == 0) goto end; + + if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end; + + // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now + if (mesh->triangleCnt == 0 && clips.count > 0) { + if (!imageGenRle(&image, bbox, false)) goto end; + if (image.rle) { + //Clear current task memorypool here if the clippers would use the same memory pool + imageDelOutline(&image, mpool, tid); + for (auto clip = clips.data; clip < clips.end(); ++clip) { + auto clipper = static_cast(*clip); + if (!clipper->clip(image.rle)) goto err; + } + return; + } + } + } + goto end; + err: + rleReset(image.rle); + end: + imageDelOutline(&image, mpool, tid); + } + + bool dispose() override + { + imageFree(&image); + return true; + } +}; + + +static void _termEngine() +{ + if (rendererCnt > 0) return; + + mpoolTerm(globalMpool); + globalMpool = nullptr; +} + + +static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity) +{ + uint8_t r, g, b, a; + if (auto fill = task->rshape->fill) { + rasterGradientShape(surface, &task->shape, fill->identifier()); + } else { + task->rshape->fillColor(&r, &g, &b, &a); + a = MULTIPLY(opacity, a); + if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); + } +} + +static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity) +{ + uint8_t r, g, b, a; + if (auto strokeFill = task->rshape->strokeFill()) { + rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); + } else { + if (task->rshape->strokeColor(&r, &g, &b, &a)) { + a = MULTIPLY(opacity, a); + if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); + } + } +} + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +SwRenderer::~SwRenderer() +{ + clearCompositors(); + + delete(surface); + + if (!sharedMpool) mpoolTerm(mpool); + + --rendererCnt; + + if (rendererCnt == 0 && initEngineCnt == 0) _termEngine(); +} + + +bool SwRenderer::clear() +{ + for (auto task = tasks.data; task < tasks.end(); ++task) { + if ((*task)->disposed) { + delete(*task); + } else { + (*task)->done(); + (*task)->pushed = false; + } + } + tasks.clear(); + + if (!sharedMpool) mpoolClear(mpool); + + if (surface) { + vport.x = vport.y = 0; + vport.w = surface->w; + vport.h = surface->h; + } + + return true; +} + + +bool SwRenderer::sync() +{ + return true; +} + + +RenderRegion SwRenderer::viewport() +{ + return vport; +} + + +bool SwRenderer::viewport(const RenderRegion& vp) +{ + vport = vp; + return true; +} + + +bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs) +{ + if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false; + + if (!surface) surface = new SwSurface; + + surface->data = data; + surface->stride = stride; + surface->w = w; + surface->h = h; + surface->cs = cs; + surface->channelSize = CHANNEL_SIZE(cs); + surface->premultiplied = true; + surface->owner = true; + + vport.x = vport.y = 0; + vport.w = surface->w; + vport.h = surface->h; + + return rasterCompositor(surface); +} + + +bool SwRenderer::preRender() +{ + return rasterClear(surface, 0, 0, surface->w, surface->h); +} + + +void SwRenderer::clearCompositors() +{ + //Free Composite Caches + for (auto comp = compositors.data; comp < compositors.end(); ++comp) { + free((*comp)->compositor->image.data); + delete((*comp)->compositor); + delete(*comp); + } + compositors.reset(); +} + + +bool SwRenderer::postRender() +{ + //Unmultiply alpha if needed + if (surface->cs == ColorSpace::ABGR8888S || surface->cs == ColorSpace::ARGB8888S) { + rasterUnpremultiply(surface); + } + + for (auto task = tasks.data; task < tasks.end(); ++task) { + if ((*task)->disposed) delete(*task); + else (*task)->pushed = false; + } + tasks.clear(); + + clearCompositors(); + return true; +} + + +bool SwRenderer::renderImage(RenderData data) +{ + auto task = static_cast(data); + task->done(); + + if (task->opacity == 0) return true; + + return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity); +} + + +bool SwRenderer::renderShape(RenderData data) +{ + auto task = static_cast(data); + if (!task) return false; + + task->done(); + + if (task->opacity == 0) return true; + + //Main raster stage + if (task->rshape->stroke && task->rshape->stroke->strokeFirst) { + _renderStroke(task, surface, task->opacity); + _renderFill(task, surface, task->opacity); + } else { + _renderFill(task, surface, task->opacity); + _renderStroke(task, surface, task->opacity); + } + + return true; +} + + +bool SwRenderer::blend(BlendMethod method) +{ + if (surface->blendMethod == method) return true; + surface->blendMethod = method; + + switch (method) { + case BlendMethod::Add: + surface->blender = opBlendAdd; + break; + case BlendMethod::Screen: + surface->blender = opBlendScreen; + break; + case BlendMethod::Multiply: + surface->blender = opBlendMultiply; + break; + case BlendMethod::Overlay: + surface->blender = opBlendOverlay; + break; + case BlendMethod::Difference: + surface->blender = opBlendDifference; + break; + case BlendMethod::Exclusion: + surface->blender = opBlendExclusion; + break; + case BlendMethod::SrcOver: + surface->blender = opBlendSrcOver; + break; + case BlendMethod::Darken: + surface->blender = opBlendDarken; + break; + case BlendMethod::Lighten: + surface->blender = opBlendLighten; + break; + case BlendMethod::ColorDodge: + surface->blender = opBlendColorDodge; + break; + case BlendMethod::ColorBurn: + surface->blender = opBlendColorBurn; + break; + case BlendMethod::HardLight: + surface->blender = opBlendHardLight; + break; + case BlendMethod::SoftLight: + surface->blender = opBlendSoftLight; + break; + default: + surface->blender = nullptr; + break; + } + return false; +} + + +RenderRegion SwRenderer::region(RenderData data) +{ + return static_cast(data)->bounds(); +} + + +bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) +{ + if (!cmp) return false; + auto p = static_cast(cmp); + + p->method = method; + p->opacity = opacity; + + //Current Context? + if (p->method != CompositeMethod::None) { + surface = p->recoverSfc; + surface->compositor = p; + } + + return true; +} + + +bool SwRenderer::mempool(bool shared) +{ + if (shared == sharedMpool) return true; + + if (shared) { + if (!sharedMpool) { + if (!mpoolTerm(mpool)) return false; + mpool = globalMpool; + } + } else { + if (sharedMpool) mpool = mpoolInit(threadsCnt); + } + + sharedMpool = shared; + + if (mpool) return true; + return false; +} + + +Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) +{ + auto x = region.x; + auto y = region.y; + auto w = region.w; + auto h = region.h; + auto sw = static_cast(surface->w); + auto sh = static_cast(surface->h); + + //Out of boundary + if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr; + + SwSurface* cmp = nullptr; + + auto reqChannelSize = CHANNEL_SIZE(cs); + + //Use cached data + for (auto p = compositors.data; p < compositors.end(); ++p) { + if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) { + cmp = *p; + break; + } + } + + //New Composition + if (!cmp) { + cmp = new SwSurface; + + //Inherits attributes from main surface + *cmp = *surface; + + cmp->compositor = new SwCompositor; + + //TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h) + cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h); + cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize; + + compositors.push(cmp); + } + + //Boundary Check + if (x + w > sw) w = (sw - x); + if (y + h > sh) h = (sh - y); + + cmp->compositor->recoverSfc = surface; + cmp->compositor->recoverCmp = surface->compositor; + cmp->compositor->valid = false; + cmp->compositor->bbox.min.x = x; + cmp->compositor->bbox.min.y = y; + cmp->compositor->bbox.max.x = x + w; + cmp->compositor->bbox.max.y = y + h; + cmp->compositor->image.stride = surface->stride; + cmp->compositor->image.w = surface->w; + cmp->compositor->image.h = surface->h; + cmp->compositor->image.direct = true; + + cmp->data = cmp->compositor->image.data; + cmp->w = cmp->compositor->image.w; + cmp->h = cmp->compositor->image.h; + + rasterClear(cmp, x, y, w, h); + + //Switch render target + surface = cmp; + + return cmp->compositor; +} + + +bool SwRenderer::endComposite(Compositor* cmp) +{ + if (!cmp) return false; + + auto p = static_cast(cmp); + p->valid = true; + + //Recover Context + surface = p->recoverSfc; + surface->compositor = p->recoverCmp; + + //Default is alpha blending + if (p->method == CompositeMethod::None) { + return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity); + } + + return true; +} + + +ColorSpace SwRenderer::colorSpace() +{ + if (surface) return surface->cs; + else return ColorSpace::Unsupported; +} + + +bool SwRenderer::dispose(RenderData data) +{ + auto task = static_cast(data); + if (!task) return true; + task->done(); + task->dispose(); + + if (task->pushed) task->disposed = true; + else delete(task); + + return true; +} + + +void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags) +{ + if (!surface) return task; + if (flags == RenderUpdateFlag::None) return task; + + //Finish previous task if it has duplicated request. + task->done(); + + //TODO: Failed threading them. It would be better if it's possible. + //See: https://github.com/thorvg/thorvg/issues/1409 + //Guarantee composition targets get ready. + for (auto clip = clips.data; clip < clips.end(); ++clip) { + static_cast(*clip)->done(); + } + + task->clips = clips; + + if (transform) { + if (!task->transform) task->transform = static_cast(malloc(sizeof(Matrix))); + *task->transform = transform->m; + } else { + if (task->transform) free(task->transform); + task->transform = nullptr; + } + + //zero size? + if (task->transform) { + if (task->transform->e11 == 0.0f && task->transform->e12 == 0.0f) return task; //zero width + if (task->transform->e21 == 0.0f && task->transform->e22 == 0.0f) return task; //zero height + } + + task->opacity = opacity; + task->surface = surface; + task->mpool = mpool; + task->flags = flags; + task->bbox.min.x = mathMax(static_cast(0), static_cast(vport.x)); + task->bbox.min.y = mathMax(static_cast(0), static_cast(vport.y)); + task->bbox.max.x = mathMin(static_cast(surface->w), static_cast(vport.x + vport.w)); + task->bbox.max.y = mathMin(static_cast(surface->h), static_cast(vport.y + vport.h)); + + if (!task->pushed) { + task->pushed = true; + tasks.push(task); + } + + TaskScheduler::request(task); + + return task; +} + + +RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) +{ + //prepare task + auto task = static_cast(data); + if (!task) task = new SwImageTask; + task->source = surface; + task->mesh = mesh; + return prepareCommon(task, transform, clips, opacity, flags); +} + + +RenderData SwRenderer::prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) +{ + //prepare task + auto task = static_cast(data); + if (!task) task = new SwSceneTask; + task->scene = scene; + + //TODO: Failed threading them. It would be better if it's possible. + //See: https://github.com/thorvg/thorvg/issues/1409 + //Guarantee composition targets get ready. + for (auto task = scene.data; task < scene.end(); ++task) { + static_cast(*task)->done(); + } + return prepareCommon(task, transform, clips, opacity, flags); +} + + +RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) +{ + //prepare task + auto task = static_cast(data); + if (!task) { + task = new SwShapeTask; + task->rshape = &rshape; + } + task->clipper = clipper; + + return prepareCommon(task, transform, clips, opacity, flags); +} + + +SwRenderer::SwRenderer():mpool(globalMpool) +{ +} + + +bool SwRenderer::init(uint32_t threads) +{ + if ((initEngineCnt++) > 0) return true; + + threadsCnt = threads; + + //Share the memory pool among the renderer + globalMpool = mpoolInit(threads); + if (!globalMpool) { + --initEngineCnt; + return false; + } + + return true; +} + + +int32_t SwRenderer::init() +{ + return initEngineCnt; +} + + +bool SwRenderer::term() +{ + if ((--initEngineCnt) > 0) return true; + + initEngineCnt = 0; + + _termEngine(); + + return true; +} + +SwRenderer* SwRenderer::gen() +{ + ++rendererCnt; + return new SwRenderer(); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.h b/project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.h new file mode 100644 index 000000000..8df5a2143 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRenderer.h @@ -0,0 +1,90 @@ +/* + * 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_SW_RENDERER_H_ +#define _TVG_SW_RENDERER_H_ + +#include "tvgRender.h" + +struct SwSurface; +struct SwTask; +struct SwCompositor; +struct SwMpool; + +namespace tvg +{ + +class SwRenderer : public RenderMethod +{ +public: + RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; + RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; + RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; + bool preRender() override; + bool renderShape(RenderData data) override; + bool renderImage(RenderData data) override; + bool postRender() override; + bool dispose(RenderData data) override; + RenderRegion region(RenderData data) override; + RenderRegion viewport() override; + bool viewport(const RenderRegion& vp) override; + bool blend(BlendMethod method) override; + ColorSpace colorSpace() override; + + bool clear() override; + bool sync() override; + bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); + bool mempool(bool shared); + + Compositor* target(const RenderRegion& region, ColorSpace cs) override; + bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override; + bool endComposite(Compositor* cmp) override; + void clearCompositors(); + + static SwRenderer* gen(); + static bool init(uint32_t threads); + static int32_t init(); + static bool term(); + +private: + SwSurface* surface = nullptr; //active surface + Array tasks; //async task list + Array compositors; //render targets cache list + SwMpool* mpool; //private memory pool + RenderRegion vport; //viewport + bool sharedMpool = true; //memory-pool behavior policy + + SwRenderer(); + ~SwRenderer(); + + RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); +}; + +} + +#endif /* _TVG_SW_RENDERER_H_ */ + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwRle.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwRle.cpp new file mode 100644 index 000000000..e541e943b --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwRle.cpp @@ -0,0 +1,1134 @@ +/* + * 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 + +/* + * The FreeType Project LICENSE + * ---------------------------- + + * 2006-Jan-27 + + * Copyright 1996-2002, 2006 by + * David Turner, Robert Wilhelm, and Werner Lemberg + + + + * Introduction + * ============ + + * The FreeType Project is distributed in several archive packages; + * some of them may contain, in addition to the FreeType font engine, + * various tools and contributions which rely on, or relate to, the + * FreeType Project. + + * This license applies to all files found in such packages, and + * which do not fall under their own explicit license. The license + * affects thus the FreeType font engine, the test programs, + * documentation and makefiles, at the very least. + + * This license was inspired by the BSD, Artistic, and IJG + * (Independent JPEG Group) licenses, which all encourage inclusion + * and use of free software in commercial and freeware products + * alike. As a consequence, its main points are that: + + * o We don't promise that this software works. However, we will be + * interested in any kind of bug reports. (`as is' distribution) + + * o You can use this software for whatever you want, in parts or + * full form, without having to pay us. (`royalty-free' usage) + + * o You may not pretend that you wrote this software. If you use + * it, or only parts of it, in a program, you must acknowledge + * somewhere in your documentation that you have used the + * FreeType code. (`credits') + + * We specifically permit and encourage the inclusion of this + * software, with or without modifications, in commercial products. + * We disclaim all warranties covering The FreeType Project and + * assume no liability related to The FreeType Project. + + + * Finally, many people asked us for a preferred form for a + * credit/disclaimer to use in compliance with this license. We thus + * encourage you to use the following text: + + * """ + * Portions of this software are copyright � The FreeType + * Project (www.freetype.org). All rights reserved. + * """ + + * Please replace with the value from the FreeType version you + * actually use. + +* Legal Terms +* =========== + +* 0. Definitions +* -------------- + +* Throughout this license, the terms `package', `FreeType Project', +* and `FreeType archive' refer to the set of files originally +* distributed by the authors (David Turner, Robert Wilhelm, and +* Werner Lemberg) as the `FreeType Project', be they named as alpha, +* beta or final release. + +* `You' refers to the licensee, or person using the project, where +* `using' is a generic term including compiling the project's source +* code as well as linking it to form a `program' or `executable'. +* This program is referred to as `a program using the FreeType +* engine'. + +* This license applies to all files distributed in the original +* FreeType Project, including all source code, binaries and +* documentation, unless otherwise stated in the file in its +* original, unmodified form as distributed in the original archive. +* If you are unsure whether or not a particular file is covered by +* this license, you must contact us to verify this. + +* The FreeType Project is copyright (C) 1996-2000 by David Turner, +* Robert Wilhelm, and Werner Lemberg. All rights reserved except as +* specified below. + +* 1. No Warranty +* -------------- + +* THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY +* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO +* USE, OF THE FREETYPE PROJECT. + +* 2. Redistribution +* ----------------- + +* This license grants a worldwide, royalty-free, perpetual and +* irrevocable right and license to use, execute, perform, compile, +* display, copy, create derivative works of, distribute and +* sublicense the FreeType Project (in both source and object code +* forms) and derivative works thereof for any purpose; and to +* authorize others to exercise some or all of the rights granted +* herein, subject to the following conditions: + +* o Redistribution of source code must retain this license file +* (`FTL.TXT') unaltered; any additions, deletions or changes to +* the original files must be clearly indicated in accompanying +* documentation. The copyright notices of the unaltered, +* original files must be preserved in all copies of source +* files. + +* o Redistribution in binary form must provide a disclaimer that +* states that the software is based in part of the work of the +* FreeType Team, in the distribution documentation. We also +* encourage you to put an URL to the FreeType web page in your +* documentation, though this isn't mandatory. + +* These conditions apply to any software derived from or based on +* the FreeType Project, not just the unmodified files. If you use +* our work, you must acknowledge us. However, no fee need be paid +* to us. + +* 3. Advertising +* -------------- + +* Neither the FreeType authors and contributors nor you shall use +* the name of the other for commercial, advertising, or promotional +* purposes without specific prior written permission. + +* We suggest, but do not require, that you use one or more of the +* following phrases to refer to this software in your documentation +* or advertising materials: `FreeType Project', `FreeType Engine', +* `FreeType library', or `FreeType Distribution'. + +* As you have not signed this license, you are not required to +* accept it. However, as the FreeType Project is copyrighted +* material, only this license, or another one contracted with the +* authors, grants you the right to use, distribute, and modify it. +* Therefore, by using, distributing, or modifying the FreeType +* Project, you indicate that you understand and accept all the terms +* of this license. + +* 4. Contacts +* ----------- + +* There are two mailing lists related to FreeType: + +* o freetype@nongnu.org + +* Discusses general use and applications of FreeType, as well as +* future and wanted additions to the library and distribution. +* If you are looking for support, start in this list if you +* haven't found anything to help you in the documentation. + +* o freetype-devel@nongnu.org + +* Discusses bugs, as well as engine internals, design issues, +* specific licenses, porting, etc. + +* Our home page can be found at + +* http://www.freetype.org +*/ + +#include +#include +#include +#include "tvgSwCommon.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +constexpr auto MAX_SPANS = 256; +constexpr auto PIXEL_BITS = 8; //must be at least 6 bits! +constexpr auto ONE_PIXEL = (1L << PIXEL_BITS); + +using Area = long; + +struct Band +{ + SwCoord min, max; +}; + +struct Cell +{ + SwCoord x; + SwCoord cover; + Area area; + Cell *next; +}; + +struct RleWorker +{ + SwRleData* rle; + + SwPoint cellPos; + SwPoint cellMin; + SwPoint cellMax; + SwCoord cellXCnt; + SwCoord cellYCnt; + + Area area; + SwCoord cover; + + Cell* cells; + ptrdiff_t maxCells; + ptrdiff_t cellsCnt; + + SwPoint pos; + + SwPoint bezStack[32 * 3 + 1]; + int levStack[32]; + + SwOutline* outline; + + SwSpan spans[MAX_SPANS]; + int spansCnt; + int ySpan; + + int bandSize; + int bandShoot; + + jmp_buf jmpBuf; + + void* buffer; + long bufferSize; + + Cell** yCells; + SwCoord yCnt; + + bool invalid; + bool antiAlias; +}; + + +static inline SwPoint UPSCALE(const SwPoint& pt) +{ + return {SwCoord(((unsigned long) pt.x) << (PIXEL_BITS - 6)), SwCoord(((unsigned long) pt.y) << (PIXEL_BITS - 6))}; +} + + +static inline SwPoint TRUNC(const SwPoint& pt) +{ + return {pt.x >> PIXEL_BITS, pt.y >> PIXEL_BITS}; +} + + +static inline SwCoord TRUNC(const SwCoord x) +{ + return x >> PIXEL_BITS; +} + + +static inline SwPoint SUBPIXELS(const SwPoint& pt) +{ + return {SwCoord(((unsigned long) pt.x) << PIXEL_BITS), SwCoord(((unsigned long) pt.y) << PIXEL_BITS)}; +} + + +static inline SwCoord SUBPIXELS(const SwCoord x) +{ + return SwCoord(((unsigned long) x) << PIXEL_BITS); +} + +/* + * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' + * algorithm. We use alpha = 1, beta = 3/8, giving us results with a + * largest error less than 7% compared to the exact value. + */ +static inline SwCoord HYPOT(SwPoint pt) +{ + if (pt.x < 0) pt.x = -pt.x; + if (pt.y < 0) pt.y = -pt.y; + return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3))); +} + +static void _genSpan(SwRleData* rle, const SwSpan* spans, uint32_t count) +{ + auto newSize = rle->size + count; + + /* allocate enough memory for new spans */ + /* alloc is required to prevent free and reallocation */ + /* when the rle needs to be regenerated because of attribute change. */ + if (rle->alloc < newSize) { + rle->alloc = (newSize * 2); + //OPTIMIZE: use mempool! + rle->spans = static_cast(realloc(rle->spans, rle->alloc * sizeof(SwSpan))); + } + + //copy the new spans to the allocated memory + SwSpan* lastSpan = rle->spans + rle->size; + memcpy(lastSpan, spans, count * sizeof(SwSpan)); + + rle->size = newSize; +} + + +static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount) +{ + x += rw.cellMin.x; + y += rw.cellMin.y; + + //Clip Y range + if (y < rw.cellMin.y || y >= rw.cellMax.y) return; + + /* compute the coverage line's coverage, depending on the outline fill rule */ + /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ + auto coverage = static_cast(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 255 + + if (coverage < 0) coverage = -coverage; + + if (rw.outline->fillRule == FillRule::EvenOdd) { + coverage &= 511; + if (coverage > 255) coverage = 511 - coverage; + } else { + //normal non-zero winding rule + if (coverage > 255) coverage = 255; + } + + //span has ushort coordinates. check limit overflow + if (x >= SHRT_MAX) { + TVGERR("SW_ENGINE", "X-coordiante overflow!"); + x = SHRT_MAX; + } + if (y >= SHRT_MAX) { + TVGERR("SW_ENGINE", "Y Coordiante overflow!"); + y = SHRT_MAX; + } + + if (coverage > 0) { + if (!rw.antiAlias) coverage = 255; + auto count = rw.spansCnt; + auto span = rw.spans + count - 1; + + //see whether we can add this span to the current list + if ((count > 0) && (rw.ySpan == y) && + (span->x + span->len == x) && (span->coverage == coverage)) { + + //Clip x range + SwCoord xOver = 0; + if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); + if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x); + + //span->len += (acount + xOver) - 1; + span->len += (acount + xOver); + return; + } + + if (count >= MAX_SPANS) { + _genSpan(rw.rle, rw.spans, count); + rw.spansCnt = 0; + rw.ySpan = 0; + span = rw.spans; + } else { + ++span; + } + + //Clip x range + SwCoord xOver = 0; + if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x); + if (x < rw.cellMin.x) { + xOver -= (rw.cellMin.x - x); + x = rw.cellMin.x; + } + + //Nothing to draw + if (acount + xOver <= 0) return; + + //add a span to the current list + span->x = x; + span->y = y; + span->len = (acount + xOver); + span->coverage = coverage; + ++rw.spansCnt; + rw.ySpan = y; + } +} + + +static void _sweep(RleWorker& rw) +{ + if (rw.cellsCnt == 0) return; + + rw.spansCnt = 0; + rw.ySpan = 0; + + for (int y = 0; y < rw.yCnt; ++y) { + auto cover = 0; + auto x = 0; + auto cell = rw.yCells[y]; + + while (cell) { + if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x); + cover += cell->cover; + auto area = cover * (ONE_PIXEL * 2) - cell->area; + if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1); + x = cell->x + 1; + cell = cell->next; + } + + if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x); + } + + if (rw.spansCnt > 0) _genSpan(rw.rle, rw.spans, rw.spansCnt); +} + + +static Cell* _findCell(RleWorker& rw) +{ + auto x = rw.cellPos.x; + if (x > rw.cellXCnt) x = rw.cellXCnt; + + auto pcell = &rw.yCells[rw.cellPos.y]; + + while(true) { + Cell* cell = *pcell; + if (!cell || cell->x > x) break; + if (cell->x == x) return cell; + pcell = &cell->next; + } + + if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1); + + auto cell = rw.cells + rw.cellsCnt++; + cell->x = x; + cell->area = 0; + cell->cover = 0; + cell->next = *pcell; + *pcell = cell; + + return cell; +} + + +static void _recordCell(RleWorker& rw) +{ + if (rw.area | rw.cover) { + auto cell = _findCell(rw); + cell->area += rw.area; + cell->cover += rw.cover; + } +} + + +static void _setCell(RleWorker& rw, SwPoint pos) +{ + /* Move the cell pointer to a new position. We set the `invalid' */ + /* flag to indicate that the cell isn't part of those we're interested */ + /* in during the render phase. This means that: */ + /* */ + /* . the new vertical position must be within min_ey..max_ey-1. */ + /* . the new horizontal position must be strictly less than max_ex */ + /* */ + /* Note that if a cell is to the left of the clipping region, it is */ + /* actually set to the (min_ex-1) horizontal position. */ + + /* All cells that are on the left of the clipping region go to the + min_ex - 1 horizontal position. */ + pos.x -= rw.cellMin.x; + pos.y -= rw.cellMin.y; + + if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; + + //Are we moving to a different cell? + if (pos != rw.cellPos) { + //Record the current one if it is valid + if (!rw.invalid) _recordCell(rw); + } + + rw.area = 0; + rw.cover = 0; + rw.cellPos = pos; + rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt); +} + + +static void _startCell(RleWorker& rw, SwPoint pos) +{ + if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; + if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x; + + rw.area = 0; + rw.cover = 0; + rw.cellPos = pos - rw.cellMin; + rw.invalid = false; + + _setCell(rw, pos); +} + + +static void _moveTo(RleWorker& rw, const SwPoint& to) +{ + //record current cell, if any */ + if (!rw.invalid) _recordCell(rw); + + //start to a new position + _startCell(rw, TRUNC(to)); + + rw.pos = to; +} + + +static void _lineTo(RleWorker& rw, const SwPoint& to) +{ +#define SW_UDIV(a, b) \ + static_cast(((unsigned long)(a) * (unsigned long)(b)) >> \ + (sizeof(long) * CHAR_BIT - PIXEL_BITS)) + + auto e1 = TRUNC(rw.pos); + auto e2 = TRUNC(to); + + //vertical clipping + if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) { + rw.pos = to; + return; + } + + auto diff = to - rw.pos; + auto f1 = rw.pos - SUBPIXELS(e1); + SwPoint f2; + + //inside one cell + if (e1 == e2) { + ; + //any horizontal line + } else if (diff.y == 0) { + e1.x = e2.x; + _setCell(rw, e1); + } else if (diff.x == 0) { + //vertical line up + if (diff.y > 0) { + do { + f2.y = ONE_PIXEL; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * f1.x * 2; + f1.y = 0; + ++e1.y; + _setCell(rw, e1); + } while(e1.y != e2.y); + //vertical line down + } else { + do { + f2.y = 0; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * f1.x * 2; + f1.y = ONE_PIXEL; + --e1.y; + _setCell(rw, e1); + } while(e1.y != e2.y); + } + //any other line + } else { + Area prod = diff.x * f1.y - diff.y * f1.x; + + /* These macros speed up repetitive divisions by replacing them + with multiplications and right shifts. */ + auto dx_r = static_cast(ULONG_MAX >> PIXEL_BITS) / (diff.x); + auto dy_r = static_cast(ULONG_MAX >> PIXEL_BITS) / (diff.y); + + /* The fundamental value `prod' determines which side and the */ + /* exact coordinate where the line exits current cell. It is */ + /* also easily updated when moving from one cell to the next. */ + do { + auto px = diff.x * ONE_PIXEL; + auto py = diff.y * ONE_PIXEL; + + //left + if (prod <= 0 && prod - px > 0) { + f2 = {0, SW_UDIV(-prod, -dx_r)}; + prod -= py; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * (f1.x + f2.x); + f1 = {ONE_PIXEL, f2.y}; + --e1.x; + //up + } else if (prod - px <= 0 && prod - px + py > 0) { + prod -= px; + f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL}; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * (f1.x + f2.x); + f1 = {f2.x, 0}; + ++e1.y; + //right + } else if (prod - px + py <= 0 && prod + py >= 0) { + prod += py; + f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)}; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * (f1.x + f2.x); + f1 = {0, f2.y}; + ++e1.x; + //down + } else { + f2 = {SW_UDIV(prod, -dy_r), 0}; + prod += px; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * (f1.x + f2.x); + f1 = {f2.x, ONE_PIXEL}; + --e1.y; + } + + _setCell(rw, e1); + + } while(e1 != e2); + } + + f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)}; + rw.cover += (f2.y - f1.y); + rw.area += (f2.y - f1.y) * (f1.x + f2.x); + rw.pos = to; +} + + +static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) +{ + auto arc = rw.bezStack; + arc[0] = to; + arc[1] = ctrl2; + arc[2] = ctrl1; + arc[3] = rw.pos; + + //Short-cut the arc that crosses the current band + auto min = arc[0].y; + auto max = arc[0].y; + + SwCoord y; + for (auto i = 1; i < 4; ++i) { + y = arc[i].y; + if (y < min) min = y; + if (y > max) max = y; + } + + if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw; + + /* Decide whether to split or draw. See `Rapid Termination */ + /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ + /* F. Hain, at */ + /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */ + while (true) { + { + //diff is the P0 - P3 chord vector + auto diff = arc[3] - arc[0]; + auto L = HYPOT(diff); + + //avoid possible arithmetic overflow below by splitting + if (L > SHRT_MAX) goto split; + + //max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1) + auto sLimit = L * (ONE_PIXEL / 6); + + auto diff1 = arc[1] - arc[0]; + auto s = diff.y * diff1.x - diff.x * diff1.y; + if (s < 0) s = -s; + if (s > sLimit) goto split; + + //s is L * the perpendicular distance from P2 to the line P0 - P3 + auto diff2 = arc[2] - arc[0]; + s = diff.y * diff2.x - diff.x * diff2.y; + if (s < 0) s = -s; + if (s > sLimit) goto split; + + /* Split super curvy segments where the off points are so far + from the chord that the angles P0-P1-P3 or P0-P2-P3 become + acute as detected by appropriate dot products */ + if (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 || + diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0) + goto split; + + //no reason to split + goto draw; + } + split: + mathSplitCubic(arc); + arc += 3; + continue; + + draw: + _lineTo(rw, arc[0]); + if (arc == rw.bezStack) return; + arc -= 3; + } +} + + +static void _decomposeOutline(RleWorker& rw) +{ + auto outline = rw.outline; + auto first = 0; //index of first point in contour + + for (auto cntr = outline->cntrs.data; cntr < outline->cntrs.end(); ++cntr) { + auto last = *cntr; + auto limit = outline->pts.data + last; + auto start = UPSCALE(outline->pts[first]); + auto pt = outline->pts.data + first; + auto types = outline->types.data + first; + + _moveTo(rw, UPSCALE(outline->pts[first])); + + while (pt < limit) { + ++pt; + ++types; + + //emit a single line_to + if (types[0] == SW_CURVE_TYPE_POINT) { + _lineTo(rw, UPSCALE(*pt)); + //types cubic + } else { + pt += 2; + types += 2; + + if (pt <= limit) { + _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); + continue; + } + _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); + goto close; + } + } + _lineTo(rw, start); + close: + first = last + 1; + } +} + + +static int _genRle(RleWorker& rw) +{ + if (setjmp(rw.jmpBuf) == 0) { + _decomposeOutline(rw); + if (!rw.invalid) _recordCell(rw); + return 0; + } + return -1; //lack of cell memory +} + + +static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt) +{ + auto out = outSpans; + auto spans = target->spans; + auto end = target->spans + target->size; + auto clipSpans = clip->spans; + auto clipEnd = clip->spans + clip->size; + + while (spans < end && clipSpans < clipEnd) { + //align y cooridnates. + if (clipSpans->y > spans->y) { + ++spans; + continue; + } + if (spans->y > clipSpans->y) { + ++clipSpans; + continue; + } + + //Try clipping with all clip spans which have a same y coordinate. + auto temp = clipSpans; + while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) { + auto sx1 = spans->x; + auto sx2 = sx1 + spans->len; + auto cx1 = temp->x; + auto cx2 = cx1 + temp->len; + + //The span must be left(x1) to right(x2) direction. Not intersected. + if (cx2 < sx1 || sx2 < cx1) { + ++temp; + continue; + } + + //clip span region. + auto x = sx1 > cx1 ? sx1 : cx1; + auto len = (sx2 < cx2 ? sx2 : cx2) - x; + if (len > 0) { + out->x = x; + out->y = temp->y; + out->len = len; + out->coverage = (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8); + ++out; + --outSpansCnt; + } + ++temp; + } + ++spans; + } + return out; +} + + +static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) +{ + auto out = outSpans; + auto spans = targetRle->spans; + auto end = targetRle->spans + targetRle->size; + auto minx = static_cast(bbox->min.x); + auto miny = static_cast(bbox->min.y); + auto maxx = minx + static_cast(bbox->max.x - bbox->min.x) - 1; + auto maxy = miny + static_cast(bbox->max.y - bbox->min.y) - 1; + + while (outSpansCnt > 0 && spans < end) { + if (spans->y > maxy) { + spans = end; + break; + } + if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) { + ++spans; + continue; + } + if (spans->x < minx) { + out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1); + out->x = minx; + } + else { + out->x = spans->x; + out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1); + } + if (out->len > 0) { + out->y = spans->y; + out->coverage = spans->coverage; + ++out; + --outSpansCnt; + } + ++spans; + } + return out; +} + + +static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans) +{ + auto out = outSpans; + auto spans1 = clip1->spans; + auto end1 = clip1->spans + clip1->size; + auto spans2 = clip2->spans; + auto end2 = clip2->spans + clip2->size; + + //list two spans up in y order + //TODO: Remove duplicated regions? + while (spans1 < end1 && spans2 < end2) { + while (spans1 < end1 && spans1->y <= spans2->y) { + *out = *spans1; + ++spans1; + ++out; + } + if (spans1 >= end1) break; + while (spans2 < end2 && spans2->y <= spans1->y) { + *out = *spans2; + ++spans2; + ++out; + } + } + + //Leftovers + while (spans1 < end1) { + *out = *spans1; + ++spans1; + ++out; + } + while (spans2 < end2) { + *out = *spans2; + ++spans2; + ++out; + } + + return out; +} + + +void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) +{ + free(rle->spans); + rle->spans = clippedSpans; + rle->size = rle->alloc = size; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias) +{ + constexpr auto RENDER_POOL_SIZE = 16384L; + constexpr auto BAND_SIZE = 40; + + //TODO: We can preserve several static workers in advance + RleWorker rw; + Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)]; + + //Init Cells + rw.buffer = buffer; + rw.bufferSize = sizeof(buffer); + rw.yCells = reinterpret_cast(buffer); + rw.cells = nullptr; + rw.maxCells = 0; + rw.cellsCnt = 0; + rw.area = 0; + rw.cover = 0; + rw.invalid = true; + rw.cellMin = renderRegion.min; + rw.cellMax = renderRegion.max; + rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; + rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; + rw.ySpan = 0; + rw.outline = const_cast(outline); + rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64 + rw.bandShoot = 0; + rw.antiAlias = antiAlias; + + if (!rle) rw.rle = reinterpret_cast(calloc(1, sizeof(SwRleData))); + else rw.rle = rle; + + //Generate RLE + Band bands[BAND_SIZE]; + Band* band; + + /* set up vertical bands */ + auto bandCnt = static_cast((rw.cellMax.y - rw.cellMin.y) / rw.bandSize); + if (bandCnt == 0) bandCnt = 1; + else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1); + + auto min = rw.cellMin.y; + auto yMax = rw.cellMax.y; + SwCoord max; + int ret; + + for (int n = 0; n < bandCnt; ++n, min = max) { + max = min + rw.bandSize; + if (n == bandCnt -1 || max > yMax) max = yMax; + + bands[0].min = min; + bands[0].max = max; + band = bands; + + while (band >= bands) { + rw.yCells = static_cast(rw.buffer); + rw.yCnt = band->max - band->min; + + int cellStart = sizeof(Cell*) * (int)rw.yCnt; + int cellMod = cellStart % sizeof(Cell); + + if (cellMod > 0) cellStart += sizeof(Cell) - cellMod; + + auto cellEnd = rw.bufferSize; + cellEnd -= cellEnd % sizeof(Cell); + + auto cellsMax = reinterpret_cast((char*)rw.buffer + cellEnd); + rw.cells = reinterpret_cast((char*)rw.buffer + cellStart); + + if (rw.cells >= cellsMax) goto reduce_bands; + + rw.maxCells = cellsMax - rw.cells; + if (rw.maxCells < 2) goto reduce_bands; + + for (int y = 0; y < rw.yCnt; ++y) + rw.yCells[y] = nullptr; + + rw.cellsCnt = 0; + rw.invalid = true; + rw.cellMin.y = band->min; + rw.cellMax.y = band->max; + rw.cellYCnt = band->max - band->min; + + ret = _genRle(rw); + if (ret == 0) { + _sweep(rw); + --band; + continue; + } else if (ret == 1) { + goto error; + } + + reduce_bands: + /* render pool overflow: we will reduce the render band by half */ + auto bottom = band->min; + auto top = band->max; + auto middle = bottom + ((top - bottom) >> 1); + + /* This is too complex for a single scanline; there must + be some problems */ + if (middle == bottom) goto error; + + if (bottom - top >= rw.bandSize) ++rw.bandShoot; + + band[1].min = bottom; + band[1].max = middle; + band[0].min = middle; + band[0].max = top; + ++band; + } + } + + if (rw.bandShoot > 8 && rw.bandSize > 16) + rw.bandSize = (rw.bandSize >> 1); + + return rw.rle; + +error: + free(rw.rle); + rw.rle = nullptr; + return nullptr; +} + + +SwRleData* rleRender(const SwBBox* bbox) +{ + auto width = static_cast(bbox->max.x - bbox->min.x); + auto height = static_cast(bbox->max.y - bbox->min.y); + + auto rle = static_cast(malloc(sizeof(SwRleData))); + rle->spans = static_cast(malloc(sizeof(SwSpan) * height)); + rle->size = height; + rle->alloc = height; + + auto span = rle->spans; + for (uint16_t i = 0; i < height; ++i, ++span) { + span->x = bbox->min.x; + span->y = bbox->min.y + i; + span->len = width; + span->coverage = 255; + } + + return rle; +} + + +void rleReset(SwRleData* rle) +{ + if (!rle) return; + rle->size = 0; +} + + +void rleFree(SwRleData* rle) +{ + if (!rle) return; + if (rle->spans) free(rle->spans); + free(rle); +} + + +void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2) +{ + if (!rle || (!clip1 && !clip2)) return; + if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return; + + TVGLOG("SW_ENGINE", "Unifying Rle!"); + + //clip1 is empty, just copy clip2 + if (!clip1 || clip1->size == 0) { + if (clip2) { + auto spans = static_cast(malloc(sizeof(SwSpan) * (clip2->size))); + memcpy(spans, clip2->spans, clip2->size); + _replaceClipSpan(rle, spans, clip2->size); + } else { + _replaceClipSpan(rle, nullptr, 0); + } + return; + } + + //clip2 is empty, just copy clip1 + if (!clip2 || clip2->size == 0) { + if (clip1) { + auto spans = static_cast(malloc(sizeof(SwSpan) * (clip1->size))); + memcpy(spans, clip1->spans, clip1->size); + _replaceClipSpan(rle, spans, clip1->size); + } else { + _replaceClipSpan(rle, nullptr, 0); + } + return; + } + + auto spanCnt = clip1->size + clip2->size; + auto spans = static_cast(malloc(sizeof(SwSpan) * spanCnt)); + auto spansEnd = _mergeSpansRegion(clip1, clip2, spans); + + _replaceClipSpan(rle, spans, spansEnd - spans); +} + + +void rleClipPath(SwRleData *rle, const SwRleData *clip) +{ + if (rle->size == 0 || clip->size == 0) return; + auto spanCnt = rle->size > clip->size ? rle->size : clip->size; + auto spans = static_cast(malloc(sizeof(SwSpan) * (spanCnt))); + auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt); + + _replaceClipSpan(rle, spans, spansEnd - spans); + + TVGLOG("SW_ENGINE", "Using ClipPath!"); +} + + +void rleClipRect(SwRleData *rle, const SwBBox* clip) +{ + if (rle->size == 0) return; + auto spans = static_cast(malloc(sizeof(SwSpan) * (rle->size))); + auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size); + + _replaceClipSpan(rle, spans, spansEnd - spans); + + TVGLOG("SW_ENGINE", "Using ClipRect!"); +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwShape.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwShape.cpp new file mode 100644 index 000000000..6a9ea5330 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwShape.cpp @@ -0,0 +1,686 @@ +/* + * 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 "tvgSwCommon.h" +#include "tvgMath.h" +#include "tvgBezier.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct Line +{ + Point pt1; + Point pt2; +}; + + +static float _lineLength(const Point& pt1, const Point& pt2) +{ + /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm. + With alpha = 1, beta = 3/8, giving results with the largest error less + than 7% compared to the exact value. */ + Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; + if (diff.x < 0) diff.x = -diff.x; + if (diff.y < 0) diff.y = -diff.y; + return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f); +} + + +static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right) +{ + auto len = _lineLength(cur.pt1, cur.pt2); + auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at; + auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at; + left.pt1 = cur.pt1; + left.pt2.x = left.pt1.x + dx; + left.pt2.y = left.pt1.y + dy; + right.pt1 = left.pt2; + right.pt2 = cur.pt2; +} + + +static void _outlineEnd(SwOutline& outline) +{ + if (outline.pts.empty()) return; + outline.cntrs.push(outline.pts.count - 1); +} + + +static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform) +{ + if (outline.pts.count > 0) outline.cntrs.push(outline.pts.count - 1); + + outline.pts.push(mathTransform(to, transform)); + outline.types.push(SW_CURVE_TYPE_POINT); +} + + +static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform) +{ + outline.pts.push(mathTransform(to, transform)); + outline.types.push(SW_CURVE_TYPE_POINT); +} + + +static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform) +{ + outline.pts.push(mathTransform(ctrl1, transform)); + outline.types.push(SW_CURVE_TYPE_CUBIC); + + outline.pts.push(mathTransform(ctrl2, transform)); + outline.types.push(SW_CURVE_TYPE_CUBIC); + + outline.pts.push(mathTransform(to, transform)); + outline.types.push(SW_CURVE_TYPE_POINT); +} + + +static void _outlineClose(SwOutline& outline) +{ + uint32_t i = 0; + + if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1; + else i = 0; //First Path + + //Make sure there is at least one point in the current path + if (outline.pts.count == i) return; + + //Close the path + outline.pts.push(outline.pts[i]); + outline.types.push(SW_CURVE_TYPE_POINT); + outline.closed.push(true); +} + + +static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform) +{ + Line cur = {dash.ptCur, *to}; + auto len = _lineLength(cur.pt1, cur.pt2); + + if (len < dash.curLen) { + dash.curLen -= len; + if (!dash.curOpGap) { + if (dash.move) { + _outlineMoveTo(*dash.outline, &dash.ptCur, transform); + dash.move = false; + } + _outlineLineTo(*dash.outline, to, transform); + } + } else { + while (len > dash.curLen) { + Line left, right; + if (dash.curLen > 0) { + len -= dash.curLen; + _lineSplitAt(cur, dash.curLen, left, right); + if (!dash.curOpGap) { + if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) { + _outlineMoveTo(*dash.outline, &left.pt1, transform); + dash.move = false; + } + _outlineLineTo(*dash.outline, &left.pt2, transform); + } + } else { + right = cur; + } + dash.curIdx = (dash.curIdx + 1) % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx]; + dash.curOpGap = !dash.curOpGap; + cur = right; + dash.ptCur = cur.pt1; + dash.move = true; + } + //leftovers + dash.curLen -= len; + if (!dash.curOpGap) { + if (dash.move) { + _outlineMoveTo(*dash.outline, &cur.pt1, transform); + dash.move = false; + } + _outlineLineTo(*dash.outline, &cur.pt2, transform); + } + if (dash.curLen < 1 && TO_SWCOORD(len) > 1) { + //move to next dash + dash.curIdx = (dash.curIdx + 1) % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx]; + dash.curOpGap = !dash.curOpGap; + } + } + dash.ptCur = *to; +} + + +static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform) +{ + Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to}; + auto len = bezLength(cur); + + if (len < dash.curLen) { + dash.curLen -= len; + if (!dash.curOpGap) { + if (dash.move) { + _outlineMoveTo(*dash.outline, &dash.ptCur, transform); + dash.move = false; + } + _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform); + } + } else { + while (len > dash.curLen) { + Bezier left, right; + if (dash.curLen > 0) { + len -= dash.curLen; + bezSplitAt(cur, dash.curLen, left, right); + if (!dash.curOpGap) { + if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) { + _outlineMoveTo(*dash.outline, &left.start, transform); + dash.move = false; + } + _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform); + } + } else { + right = cur; + } + dash.curIdx = (dash.curIdx + 1) % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx]; + dash.curOpGap = !dash.curOpGap; + cur = right; + dash.ptCur = right.start; + dash.move = true; + } + //leftovers + dash.curLen -= len; + if (!dash.curOpGap) { + if (dash.move) { + _outlineMoveTo(*dash.outline, &cur.start, transform); + dash.move = false; + } + _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform); + } + if (dash.curLen < 1 && TO_SWCOORD(len) > 1) { + //move to next dash + dash.curIdx = (dash.curIdx + 1) % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx]; + dash.curOpGap = !dash.curOpGap; + } + } + dash.ptCur = *to; +} + + +static void _dashClose(SwDashStroke& dash, const Matrix* transform) +{ + _dashLineTo(dash, &dash.ptStart, transform); +} + + +static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts, const Matrix* transform) +{ + dash.curIdx = offIdx % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx] - offset; + dash.curOpGap = offIdx % 2; + dash.ptStart = dash.ptCur = *pts; + dash.move = true; +} + + +static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length, SwMpool* mpool, unsigned tid) +{ + const PathCommand* cmds = rshape->path.cmds.data; + auto cmdCnt = rshape->path.cmds.count; + const Point* pts = rshape->path.pts.data; + auto ptsCnt = rshape->path.pts.count; + + //No actual shape data + if (cmdCnt == 0 || ptsCnt == 0) return nullptr; + + SwDashStroke dash; + auto offset = 0.0f; + auto trimmed = false; + + dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset); + + //dash by trimming. + if (length > 0.0f && dash.cnt == 0) { + auto begin = length * rshape->stroke->trim.begin; + auto end = length * rshape->stroke->trim.end; + + //TODO: mix trimming + dash style + + //default + if (end > begin) { + if (begin > 0) dash.cnt += 4; + else dash.cnt += 2; + //looping + } else dash.cnt += 3; + + dash.pattern = (float*)malloc(sizeof(float) * dash.cnt); + + if (dash.cnt == 2) { + dash.pattern[0] = end - begin; + dash.pattern[1] = length - (end - begin); + } else if (dash.cnt == 3) { + dash.pattern[0] = end; + dash.pattern[1] = (begin - end); + dash.pattern[2] = length - begin; + } else { + dash.pattern[0] = 0; //zero dash to start with a space. + dash.pattern[1] = begin; + dash.pattern[2] = end - begin; + dash.pattern[3] = length - (end - begin); + } + + trimmed = true; + //just a dasy style. + } else { + if (dash.cnt == 0) return nullptr; + } + + //offset? + auto patternLength = 0.0f; + uint32_t offIdx = 0; + if (!mathZero(offset)) { + for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i]; + bool isOdd = dash.cnt % 2; + if (isOdd) patternLength *= 2; + + offset = fmod(offset, patternLength); + if (offset < 0) offset += patternLength; + + for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) { + auto curPattern = dash.pattern[i % dash.cnt]; + if (offset < curPattern) break; + offset -= curPattern; + } + } + + dash.outline = mpoolReqDashOutline(mpool, tid); + + //smart reservation + auto closeCnt = 0; + auto moveCnt = 0; + + for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) { + if (*cmd == PathCommand::Close) ++closeCnt; + else if (*cmd == PathCommand::MoveTo) ++moveCnt; + } + + //No idea exact count.... Reserve Approximitely 20x... + //OPTIMIZE: we can directly copy the path points when the close is occupied with a point. + dash.outline->pts.grow(20 * (closeCnt + ptsCnt + 1)); + dash.outline->types.grow(20 * (closeCnt + ptsCnt + 1)); + dash.outline->cntrs.grow(20 * (moveCnt + 1)); + + while (cmdCnt-- > 0) { + switch (*cmds) { + case PathCommand::Close: { + _dashClose(dash, transform); + break; + } + case PathCommand::MoveTo: { + _dashMoveTo(dash, offIdx, offset, pts, transform); + ++pts; + break; + } + case PathCommand::LineTo: { + _dashLineTo(dash, pts, transform); + ++pts; + break; + } + case PathCommand::CubicTo: { + _dashCubicTo(dash, pts, pts + 1, pts + 2, transform); + pts += 3; + break; + } + } + ++cmds; + } + + _outlineEnd(*dash.outline); + + if (trimmed) free(dash.pattern); + + return dash.outline; +} + + +static float _outlineLength(const RenderShape* rshape) +{ + const PathCommand* cmds = rshape->path.cmds.data; + auto cmdCnt = rshape->path.cmds.count; + const Point* pts = rshape->path.pts.data; + auto ptsCnt = rshape->path.pts.count; + + //No actual shape data + if (cmdCnt == 0 || ptsCnt == 0) return 0.0f; + + const Point* close = nullptr; + auto length = 0.0f; + + //Compute the whole length + while (cmdCnt-- > 0) { + switch (*cmds) { + case PathCommand::Close: { + length += mathLength(pts - 1, close); + ++pts; + break; + } + case PathCommand::MoveTo: { + close = pts; + ++pts; + break; + } + case PathCommand::LineTo: { + length += mathLength(pts - 1, pts); + ++pts; + break; + } + case PathCommand::CubicTo: { + length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)}); + pts += 3; + break; + } + } + ++cmds; + } + return length; +} + + +static bool _axisAlignedRect(const SwOutline* outline) +{ + //Fast Track: axis-aligned rectangle? + if (outline->pts.count != 5) return false; + + auto pt1 = outline->pts.data + 0; + auto pt2 = outline->pts.data + 1; + auto pt3 = outline->pts.data + 2; + auto pt4 = outline->pts.data + 3; + + auto a = SwPoint{pt1->x, pt3->y}; + auto b = SwPoint{pt3->x, pt1->y}; + + if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true; + + return false; +} + + +static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) +{ + const PathCommand* cmds = rshape->path.cmds.data; + auto cmdCnt = rshape->path.cmds.count; + const Point* pts = rshape->path.pts.data; + auto ptsCnt = rshape->path.pts.count; + + //No actual shape data + if (cmdCnt == 0 || ptsCnt == 0) return false; + + //smart reservation + auto moveCnt = 0; + auto closeCnt = 0; + + for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) { + if (*cmd == PathCommand::Close) ++closeCnt; + else if (*cmd == PathCommand::MoveTo) ++moveCnt; + } + + shape->outline = mpoolReqOutline(mpool, tid); + auto outline = shape->outline; + + //OPTIMIZE: we can directly copy the path points when the close is occupied with a point. + outline->pts.grow(ptsCnt + closeCnt + 1); + outline->types.grow(ptsCnt + closeCnt + 1); + outline->cntrs.grow(moveCnt + 1); + + //Dash outlines are always opened. + //Only normal outlines use this information, it sholud be same to their contour counts. + outline->closed.reserve(outline->cntrs.reserved); + + memset(outline->closed.data, 0x0, sizeof(bool) * outline->closed.reserved); + + //Generate Outlines + while (cmdCnt-- > 0) { + switch (*cmds) { + case PathCommand::Close: { + _outlineClose(*outline); + break; + } + case PathCommand::MoveTo: { + _outlineMoveTo(*outline, pts, transform); + ++pts; + break; + } + case PathCommand::LineTo: { + _outlineLineTo(*outline, pts, transform); + ++pts; + break; + } + case PathCommand::CubicTo: { + _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform); + pts += 3; + break; + } + } + ++cmds; + } + + _outlineEnd(*outline); + + outline->fillRule = rshape->rule; + shape->outline = outline; + + shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline)); + return true; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite) +{ + if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false; + if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false; + + //Keep it for Rasterization Region + shape->bbox = renderRegion; + + //Check valid region + if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false; + + //Check boundary + if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y || + renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false; + + return true; +} + + +bool shapePrepared(const SwShape* shape) +{ + return shape->rle ? true : false; +} + + +bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias) +{ + //FIXME: Should we draw it? + //Case: Stroke Line + //if (shape.outline->opened) return true; + + //Case A: Fast Track Rectangle Drawing + if (shape->fastTrack) return true; + + //Case B: Normal Shape RLE Drawing + if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true; + + return false; +} + + +void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid) +{ + mpoolRetOutline(mpool, tid); + shape->outline = nullptr; +} + + +void shapeReset(SwShape* shape) +{ + rleReset(shape->rle); + rleReset(shape->strokeRle); + shape->fastTrack = false; + shape->bbox.reset(); +} + + +void shapeFree(SwShape* shape) +{ + rleFree(shape->rle); + shapeDelFill(shape); + + if (shape->stroke) { + rleFree(shape->strokeRle); + strokeFree(shape->stroke); + } +} + + +void shapeDelStroke(SwShape* shape) +{ + if (!shape->stroke) return; + rleFree(shape->strokeRle); + shape->strokeRle = nullptr; + strokeFree(shape->stroke); + shape->stroke = nullptr; +} + + +void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform) +{ + if (!shape->stroke) shape->stroke = static_cast(calloc(1, sizeof(SwStroke))); + auto stroke = shape->stroke; + if (!stroke) return; + + strokeReset(stroke, rshape, transform); + rleReset(shape->strokeRle); +} + + +bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +{ + SwOutline* shapeOutline = nullptr; + SwOutline* strokeOutline = nullptr; + auto dashStroking = false; + auto ret = true; + + auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f; + + //Dash style (+trimming) + if (rshape->stroke->dashCnt > 0 || length > 0) { + shapeOutline = _genDashOutline(rshape, transform, length, mpool, tid); + if (!shapeOutline) return false; + dashStroking = true; + //Normal style + } else { + if (!shape->outline) { + if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false; + } + shapeOutline = shape->outline; + } + + if (!strokeParseOutline(shape->stroke, *shapeOutline)) { + ret = false; + goto clear; + } + + strokeOutline = strokeExportOutline(shape->stroke, mpool, tid); + + if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) { + ret = false; + goto clear; + } + + shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true); + +clear: + if (dashStroking) mpoolRetDashOutline(mpool, tid); + mpoolRetStrokeOutline(mpool, tid); + + return ret; +} + + +bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) +{ + return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable); +} + + +bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) +{ + return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable); +} + + +void shapeResetFill(SwShape* shape) +{ + if (!shape->fill) { + shape->fill = static_cast(calloc(1, sizeof(SwFill))); + if (!shape->fill) return; + } + fillReset(shape->fill); +} + + +void shapeResetStrokeFill(SwShape* shape) +{ + if (!shape->stroke->fill) { + shape->stroke->fill = static_cast(calloc(1, sizeof(SwFill))); + if (!shape->stroke->fill) return; + } + fillReset(shape->stroke->fill); +} + + +void shapeDelFill(SwShape* shape) +{ + if (!shape->fill) return; + fillFree(shape->fill); + shape->fill = nullptr; +} + + +void shapeDelStrokeFill(SwShape* shape) +{ + if (!shape->stroke->fill) return; + fillFree(shape->stroke->fill); + shape->stroke->fill = nullptr; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgSwStroke.cpp b/project/gui/lvgl/src/libs/thorvg/tvgSwStroke.cpp new file mode 100644 index 000000000..c097c6063 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgSwStroke.cpp @@ -0,0 +1,915 @@ +/* + * 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 +#include +#include "tvgSwCommon.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static constexpr auto SW_STROKE_TAG_POINT = 1; +static constexpr auto SW_STROKE_TAG_CUBIC = 2; +static constexpr auto SW_STROKE_TAG_BEGIN = 4; +static constexpr auto SW_STROKE_TAG_END = 8; + +static inline SwFixed SIDE_TO_ROTATE(const int32_t s) +{ + return (SW_ANGLE_PI2 - static_cast(s) * SW_ANGLE_PI); +} + + +static inline void SCALE(const SwStroke& stroke, SwPoint& pt) +{ + pt.x = static_cast(pt.x * stroke.sx); + pt.y = static_cast(pt.y * stroke.sy); +} + + +static void _growBorder(SwStrokeBorder* border, uint32_t newPts) +{ + auto maxOld = border->maxPts; + auto maxNew = border->ptsCnt + newPts; + + if (maxNew <= maxOld) return; + + auto maxCur = maxOld; + + while (maxCur < maxNew) + maxCur += (maxCur >> 1) + 16; + //OPTIMIZE: use mempool! + border->pts = static_cast(realloc(border->pts, maxCur * sizeof(SwPoint))); + border->tags = static_cast(realloc(border->tags, maxCur * sizeof(uint8_t))); + border->maxPts = maxCur; +} + + +static void _borderClose(SwStrokeBorder* border, bool reverse) +{ + auto start = border->start; + auto count = border->ptsCnt; + + //Don't record empty paths! + if (count <= start + 1U) { + border->ptsCnt = start; + } else { + /* Copy the last point to the start of this sub-path, + since it contains the adjusted starting coordinates */ + border->ptsCnt = --count; + border->pts[start] = border->pts[count]; + + if (reverse) { + //reverse the points + auto pt1 = border->pts + start + 1; + auto pt2 = border->pts + count - 1; + + while (pt1 < pt2) { + auto tmp = *pt1; + *pt1 = *pt2; + *pt2 = tmp; + ++pt1; + --pt2; + } + + //reverse the tags + auto tag1 = border->tags + start + 1; + auto tag2 = border->tags + count - 1; + + while (tag1 < tag2) { + auto tmp = *tag1; + *tag1 = *tag2; + *tag2 = tmp; + ++tag1; + --tag2; + } + } + + border->tags[start] |= SW_STROKE_TAG_BEGIN; + border->tags[count - 1] |= SW_STROKE_TAG_END; + } + + border->start = -1; + border->movable = false; +} + + +static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) +{ + _growBorder(border, 3); + + auto pt = border->pts + border->ptsCnt; + auto tag = border->tags + border->ptsCnt; + + pt[0] = ctrl1; + pt[1] = ctrl2; + pt[2] = to; + + tag[0] = SW_STROKE_TAG_CUBIC; + tag[1] = SW_STROKE_TAG_CUBIC; + tag[2] = SW_STROKE_TAG_POINT; + + border->ptsCnt += 3; + border->movable = false; +} + + +static void _borderArcTo(SwStrokeBorder* border, const SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke) +{ + constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2; + SwPoint a = {static_cast(radius), 0}; + mathRotate(a, angleStart); + SCALE(stroke, a); + a += center; + + auto total = angleDiff; + auto angle = angleStart; + auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2; + + while (total != 0) { + auto step = total; + if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE; + else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE; + + auto next = angle + step; + auto theta = step; + if (theta < 0) theta = -theta; + + theta >>= 1; + + //compute end point + SwPoint b = {static_cast(radius), 0}; + mathRotate(b, next); + SCALE(stroke, b); + b += center; + + //compute first and second control points + auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3); + + SwPoint a2 = {static_cast(length), 0}; + mathRotate(a2, angle + rotate); + SCALE(stroke, a2); + a2 += a; + + SwPoint b2 = {static_cast(length), 0}; + mathRotate(b2, next - rotate); + SCALE(stroke, b2); + b2 += b; + + //add cubic arc + _borderCubicTo(border, a2, b2, b); + + //process the rest of the arc? + a = b; + total -= step; + angle = next; + } +} + + +static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movable) +{ + if (border->movable) { + //move last point + border->pts[border->ptsCnt - 1] = to; + } else { + //don't add zero-length line_to + if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return; + + _growBorder(border, 1); + border->pts[border->ptsCnt] = to; + border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT; + border->ptsCnt += 1; + } + + border->movable = movable; +} + + +static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to) +{ + //close current open path if any? + if (border->start >= 0) _borderClose(border, false); + + border->start = border->ptsCnt; + border->movable = false; + + _borderLineTo(border, to, false); +} + + +static void _arcTo(SwStroke& stroke, int32_t side) +{ + auto border = stroke.borders + side; + auto rotate = SIDE_TO_ROTATE(side); + auto total = mathDiff(stroke.angleIn, stroke.angleOut); + if (total == SW_ANGLE_PI) total = -rotate * 2; + + _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke); + border->movable = false; +} + + +static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength) +{ + auto border = stroke.borders + side; + + if (stroke.join == StrokeJoin::Round) { + _arcTo(stroke, side); + } else { + //this is a mitered (pointed) or beveled (truncated) corner + auto rotate = SIDE_TO_ROTATE(side); + auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false; + SwFixed phi = 0; + SwFixed thcos = 0; + + if (!bevel) { + auto theta = mathDiff(stroke.angleIn, stroke.angleOut); + if (theta == SW_ANGLE_PI) { + theta = rotate; + phi = stroke.angleIn; + } else { + theta /= 2; + phi = stroke.angleIn + theta + rotate; + } + + thcos = mathCos(theta); + auto sigma = mathMultiply(stroke.miterlimit, thcos); + + //is miter limit exceeded? + if (sigma < 0x10000L) bevel = true; + } + + //this is a bevel (broken angle) + if (bevel) { + SwPoint delta = {static_cast(stroke.width), 0}; + mathRotate(delta, stroke.angleOut + rotate); + SCALE(stroke, delta); + delta += stroke.center; + border->movable = false; + _borderLineTo(border, delta, false); + //this is a miter (intersection) + } else { + auto length = mathDivide(stroke.width, thcos); + SwPoint delta = {static_cast(length), 0}; + mathRotate(delta, phi); + SCALE(stroke, delta); + delta += stroke.center; + _borderLineTo(border, delta, false); + + /* Now add and end point + Only needed if not lineto (lineLength is zero for curves) */ + if (lineLength == 0) { + delta = {static_cast(stroke.width), 0}; + mathRotate(delta, stroke.angleOut + rotate); + SCALE(stroke, delta); + delta += stroke.center; + _borderLineTo(border, delta, false); + } + } + } +} + + +static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength) +{ + auto border = stroke.borders + side; + auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2; + SwPoint delta; + bool intersect = false; + + /* Only intersect borders if between two line_to's and both + lines are long enough (line length is zero for curves). */ + if (border->movable && lineLength > 0) { + //compute minimum required length of lines + SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta))); + if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true; + } + + auto rotate = SIDE_TO_ROTATE(side); + + if (!intersect) { + delta = {static_cast(stroke.width), 0}; + mathRotate(delta, stroke.angleOut + rotate); + SCALE(stroke, delta); + delta += stroke.center; + border->movable = false; + } else { + //compute median angle + auto phi = stroke.angleIn + theta; + auto thcos = mathCos(theta); + delta = {static_cast(mathDivide(stroke.width, thcos)), 0}; + mathRotate(delta, phi + rotate); + SCALE(stroke, delta); + delta += stroke.center; + } + + _borderLineTo(border, delta, false); +} + + +void _processCorner(SwStroke& stroke, SwFixed lineLength) +{ + auto turn = mathDiff(stroke.angleIn, stroke.angleOut); + + //no specific corner processing is required if the turn is 0 + if (turn == 0) return; + + //when we turn to the right, the inside side is 0 + int32_t inside = 0; + + //otherwise, the inside is 1 + if (turn < 0) inside = 1; + + //process the inside + _inside(stroke, inside, lineLength); + + //process the outside + _outside(stroke, 1 - inside, lineLength); +} + + +void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength) +{ + SwPoint delta = {static_cast(stroke.width), 0}; + mathRotate(delta, startAngle + SW_ANGLE_PI2); + SCALE(stroke, delta); + + auto pt = stroke.center + delta; + auto border = stroke.borders; + _borderMoveTo(border, pt); + + pt = stroke.center - delta; + ++border; + _borderMoveTo(border, pt); + + /* Save angle, position and line length for last join + lineLength is zero for curves */ + stroke.subPathAngle = startAngle; + stroke.firstPt = false; + stroke.subPathLineLength = lineLength; +} + + +static void _lineTo(SwStroke& stroke, const SwPoint& to) +{ + auto delta = to - stroke.center; + + //a zero-length lineto is a no-op; avoid creating a spurious corner + if (delta.zero()) return; + + //compute length of line + auto angle = mathAtan(delta); + + /* The lineLength is used to determine the intersection of strokes outlines. + The scale needs to be reverted since the stroke width has not been scaled. + An alternative option is to scale the width of the stroke properly by + calculating the mixture of the sx/sy rating on the stroke direction. */ + delta.x = static_cast(delta.x / stroke.sx); + delta.y = static_cast(delta.y / stroke.sy); + auto lineLength = mathLength(delta); + + delta = {static_cast(stroke.width), 0}; + mathRotate(delta, angle + SW_ANGLE_PI2); + SCALE(stroke, delta); + + //process corner if necessary + if (stroke.firstPt) { + /* This is the first segment of a subpath. We need to add a point to each border + at their respective starting point locations. */ + _firstSubPath(stroke, angle, lineLength); + } else { + //process the current corner + stroke.angleOut = angle; + _processCorner(stroke, lineLength); + } + + //now add a line segment to both the inside and outside paths + auto border = stroke.borders; + auto side = 1; + + while (side >= 0) { + auto pt = to + delta; + + //the ends of lineto borders are movable + _borderLineTo(border, pt, true); + + delta.x = -delta.x; + delta.y = -delta.y; + + --side; + ++border; + } + + stroke.angleIn = angle; + stroke.center = to; + stroke.lineLength = lineLength; +} + + +static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) +{ + SwPoint bezStack[37]; //TODO: static? + auto limit = bezStack + 32; + auto arc = bezStack; + auto firstArc = true; + arc[0] = to; + arc[1] = ctrl2; + arc[2] = ctrl1; + arc[3] = stroke.center; + + while (arc >= bezStack) { + SwFixed angleIn, angleOut, angleMid; + + //initialize with current direction + angleIn = angleOut = angleMid = stroke.angleIn; + + if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) { + if (stroke.firstPt) stroke.angleIn = angleIn; + mathSplitCubic(arc); + arc += 3; + continue; + } + + if (firstArc) { + firstArc = false; + //process corner if necessary + if (stroke.firstPt) { + _firstSubPath(stroke, angleIn, 0); + } else { + stroke.angleOut = angleIn; + _processCorner(stroke, 0); + } + } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) { + //if the deviation from one arc to the next is too great add a round corner + stroke.center = arc[3]; + stroke.angleOut = angleIn; + stroke.join = StrokeJoin::Round; + + _processCorner(stroke, 0); + + //reinstate line join style + stroke.join = stroke.joinSaved; + } + + //the arc's angle is small enough; we can add it directly to each border + auto theta1 = mathDiff(angleIn, angleMid) / 2; + auto theta2 = mathDiff(angleMid, angleOut) / 2; + auto phi1 = mathMean(angleIn, angleMid); + auto phi2 = mathMean(angleMid, angleOut); + auto length1 = mathDivide(stroke.width, mathCos(theta1)); + auto length2 = mathDivide(stroke.width, mathCos(theta2)); + SwFixed alpha0 = 0; + + //compute direction of original arc + if (stroke.handleWideStrokes) { + alpha0 = mathAtan(arc[0] - arc[3]); + } + + auto border = stroke.borders; + int32_t side = 0; + + while (side < 2) { + auto rotate = SIDE_TO_ROTATE(side); + + //compute control points + SwPoint _ctrl1 = {static_cast(length1), 0}; + mathRotate(_ctrl1, phi1 + rotate); + SCALE(stroke, _ctrl1); + _ctrl1 += arc[2]; + + SwPoint _ctrl2 = {static_cast(length2), 0}; + mathRotate(_ctrl2, phi2 + rotate); + SCALE(stroke, _ctrl2); + _ctrl2 += arc[1]; + + //compute end point + SwPoint _end = {static_cast(stroke.width), 0}; + mathRotate(_end, angleOut + rotate); + SCALE(stroke, _end); + _end += arc[0]; + + if (stroke.handleWideStrokes) { + /* determine whether the border radius is greater than the radius of + curvature of the original arc */ + auto _start = border->pts[border->ptsCnt - 1]; + auto alpha1 = mathAtan(_end - _start); + + //is the direction of the border arc opposite to that of the original arc? + if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) { + + //use the sine rule to find the intersection point + auto beta = mathAtan(arc[3] - _start); + auto gamma = mathAtan(arc[0] - _end); + auto bvec = _end - _start; + auto blen = mathLength(bvec); + auto sinA = abs(mathSin(alpha1 - gamma)); + auto sinB = abs(mathSin(beta - gamma)); + auto alen = mathMulDiv(blen, sinA, sinB); + + SwPoint delta = {static_cast(alen), 0}; + mathRotate(delta, beta); + delta += _start; + + //circumnavigate the negative sector backwards + border->movable = false; + _borderLineTo(border, delta, false); + _borderLineTo(border, _end, false); + _borderCubicTo(border, _ctrl2, _ctrl1, _start); + + //and then move to the endpoint + _borderLineTo(border, _end, false); + + ++side; + ++border; + continue; + } + } + _borderCubicTo(border, _ctrl1, _ctrl2, _end); + ++side; + ++border; + } + arc -= 3; + stroke.angleIn = angleOut; + } + stroke.center = to; +} + + +static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side) +{ + if (stroke.cap == StrokeCap::Square) { + auto rotate = SIDE_TO_ROTATE(side); + auto border = stroke.borders + side; + + SwPoint delta = {static_cast(stroke.width), 0}; + mathRotate(delta, angle); + SCALE(stroke, delta); + + SwPoint delta2 = {static_cast(stroke.width), 0}; + mathRotate(delta2, angle + rotate); + SCALE(stroke, delta2); + delta += stroke.center + delta2; + + _borderLineTo(border, delta, false); + + delta = {static_cast(stroke.width), 0}; + mathRotate(delta, angle); + SCALE(stroke, delta); + + delta2 = {static_cast(stroke.width), 0}; + mathRotate(delta2, angle - rotate); + SCALE(stroke, delta2); + delta += delta2 + stroke.center; + + _borderLineTo(border, delta, false); + + } else if (stroke.cap == StrokeCap::Round) { + + stroke.angleIn = angle; + stroke.angleOut = angle + SW_ANGLE_PI; + _arcTo(stroke, side); + return; + + } else { //Butt + auto rotate = SIDE_TO_ROTATE(side); + auto border = stroke.borders + side; + + SwPoint delta = {static_cast(stroke.width), 0}; + mathRotate(delta, angle + rotate); + SCALE(stroke, delta); + delta += stroke.center; + + _borderLineTo(border, delta, false); + + delta = {static_cast(stroke.width), 0}; + mathRotate(delta, angle - rotate); + SCALE(stroke, delta); + delta += stroke.center; + + _borderLineTo(border, delta, false); + } +} + + +static void _addReverseLeft(SwStroke& stroke, bool opened) +{ + auto right = stroke.borders + 0; + auto left = stroke.borders + 1; + auto newPts = left->ptsCnt - left->start; + + if (newPts <= 0) return; + + _growBorder(right, newPts); + + auto dstPt = right->pts + right->ptsCnt; + auto dstTag = right->tags + right->ptsCnt; + auto srcPt = left->pts + left->ptsCnt - 1; + auto srcTag = left->tags + left->ptsCnt - 1; + + while (srcPt >= left->pts + left->start) { + *dstPt = *srcPt; + *dstTag = *srcTag; + + if (opened) { + dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); + } else { + //switch begin/end tags if necessary + auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); + if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END) + dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); + } + --srcPt; + --srcTag; + ++dstPt; + ++dstTag; + } + + left->ptsCnt = left->start; + right->ptsCnt += newPts; + right->movable = false; + left->movable = false; +} + + +static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed) +{ + /* We cannot process the first point because there is not enough + information regarding its corner/cap. Later, it will be processed + in the _endSubPath() */ + + stroke.firstPt = true; + stroke.center = to; + stroke.closedSubPath = closed; + + /* Determine if we need to check whether the border radius is greater + than the radius of curvature of a curve, to handle this case specially. + This is only required if bevel joins or butt caps may be created because + round & miter joins and round & square caps cover the nagative sector + created with wide strokes. */ + if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt)) + stroke.handleWideStrokes = true; + else + stroke.handleWideStrokes = false; + + stroke.ptStartSubPath = to; + stroke.angleIn = 0; +} + + +static void _endSubPath(SwStroke& stroke) +{ + if (stroke.closedSubPath) { + //close the path if needed + if (stroke.center != stroke.ptStartSubPath) + _lineTo(stroke, stroke.ptStartSubPath); + + //process the corner + stroke.angleOut = stroke.subPathAngle; + auto turn = mathDiff(stroke.angleIn, stroke.angleOut); + + //No specific corner processing is required if the turn is 0 + if (turn != 0) { + //when we turn to the right, the inside is 0 + int32_t inside = 0; + + //otherwise, the inside is 1 + if (turn < 0) inside = 1; + + _inside(stroke, inside, stroke.subPathLineLength); //inside + _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside + } + + _borderClose(stroke.borders + 0, false); + _borderClose(stroke.borders + 1, true); + } else { + auto right = stroke.borders; + + /* all right, this is an opened path, we need to add a cap between + right & left, add the reverse of left, then add a final cap + between left & right */ + _addCap(stroke, stroke.angleIn, 0); + + //add reversed points from 'left' to 'right' + _addReverseLeft(stroke, true); + + //now add the final cap + stroke.center = stroke.ptStartSubPath; + _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0); + + /* now end the right subpath accordingly. The left one is rewind + and deosn't need further processing */ + _borderClose(right, false); + } +} + + +static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt) +{ + auto count = border->ptsCnt; + auto tags = border->tags; + uint32_t _ptsCnt = 0; + uint32_t _cntrsCnt = 0; + bool inCntr = false; + + while (count > 0) { + if (tags[0] & SW_STROKE_TAG_BEGIN) { + if (inCntr) goto fail; + inCntr = true; + } else if (!inCntr) goto fail; + + if (tags[0] & SW_STROKE_TAG_END) { + inCntr = false; + ++_cntrsCnt; + } + --count; + ++_ptsCnt; + ++tags; + } + + if (inCntr) goto fail; + + ptsCnt = _ptsCnt; + cntrsCnt = _cntrsCnt; + + return; + +fail: + ptsCnt = 0; + cntrsCnt = 0; +} + + +static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side) +{ + auto border = stroke.borders + side; + if (border->ptsCnt == 0) return; + + memcpy(outline->pts.data + outline->pts.count, border->pts, border->ptsCnt * sizeof(SwPoint)); + + auto cnt = border->ptsCnt; + auto src = border->tags; + auto tags = outline->types.data + outline->types.count; + auto idx = outline->pts.count; + + while (cnt > 0) { + if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT; + else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC; + else TVGERR("SW_ENGINE", "Invalid stroke tag was given! = %d", *src); + if (*src & SW_STROKE_TAG_END) outline->cntrs.push(idx); + ++src; + ++tags; + ++idx; + --cnt; + } + outline->pts.count += border->ptsCnt; + outline->types.count += border->ptsCnt; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +void strokeFree(SwStroke* stroke) +{ + if (!stroke) return; + + //free borders + if (stroke->borders[0].pts) free(stroke->borders[0].pts); + if (stroke->borders[0].tags) free(stroke->borders[0].tags); + if (stroke->borders[1].pts) free(stroke->borders[1].pts); + if (stroke->borders[1].tags) free(stroke->borders[1].tags); + + fillFree(stroke->fill); + stroke->fill = nullptr; + + free(stroke); +} + + +void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform) +{ + if (transform) { + stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f)); + stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f)); + } else { + stroke->sx = stroke->sy = 1.0f; + } + + stroke->width = HALF_STROKE(rshape->strokeWidth()); + stroke->cap = rshape->strokeCap(); + stroke->miterlimit = static_cast(rshape->strokeMiterlimit()) << 16; + + //Save line join: it can be temporarily changed when stroking curves... + stroke->joinSaved = stroke->join = rshape->strokeJoin(); + + stroke->borders[0].ptsCnt = 0; + stroke->borders[0].start = -1; + stroke->borders[1].ptsCnt = 0; + stroke->borders[1].start = -1; +} + + +bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline) +{ + uint32_t first = 0; + uint32_t i = 0; + + for (auto cntr = outline.cntrs.data; cntr < outline.cntrs.end(); ++cntr, ++i) { + auto last = *cntr; //index of last point in contour + auto limit = outline.pts.data + last; + + //Skip empty points + if (last <= first) { + first = last + 1; + continue; + } + + auto start = outline.pts[first]; + auto pt = outline.pts.data + first; + auto types = outline.types.data + first; + auto type = types[0]; + + //A contour cannot start with a cubic control point + if (type == SW_CURVE_TYPE_CUBIC) return false; + + auto closed = outline.closed.data ? outline.closed.data[i]: false; + + _beginSubPath(*stroke, start, closed); + + while (pt < limit) { + ++pt; + ++types; + + //emit a signel line_to + if (types[0] == SW_CURVE_TYPE_POINT) { + _lineTo(*stroke, *pt); + //types cubic + } else { + if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false; + + pt += 2; + types += 2; + + if (pt <= limit) { + _cubicTo(*stroke, pt[-2], pt[-1], pt[0]); + continue; + } + _cubicTo(*stroke, pt[-2], pt[-1], start); + goto close; + } + } + close: + if (!stroke->firstPt) _endSubPath(*stroke); + first = last + 1; + } + return true; +} + + +SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid) +{ + uint32_t count1, count2, count3, count4; + + _getCounts(stroke->borders + 0, count1, count2); + _getCounts(stroke->borders + 1, count3, count4); + + auto ptsCnt = count1 + count3; + auto cntrsCnt = count2 + count4; + + auto outline = mpoolReqStrokeOutline(mpool, tid); + outline->pts.reserve(ptsCnt); + outline->types.reserve(ptsCnt); + outline->cntrs.reserve(cntrsCnt); + + _exportBorderOutline(*stroke, outline, 0); //left + _exportBorderOutline(*stroke, outline, 1); //right + + return outline; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.cpp b/project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.cpp new file mode 100644 index 000000000..970369029 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.cpp @@ -0,0 +1,209 @@ +/* + * 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 +#include +#include +#include +#include +#include "tvgTaskScheduler.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +namespace tvg { + +struct TaskQueue { + deque taskDeque; + mutex mtx; + condition_variable ready; + bool done = false; + + bool tryPop(Task** task) + { + unique_lock lock{mtx, try_to_lock}; + if (!lock || taskDeque.empty()) return false; + *task = taskDeque.front(); + taskDeque.pop_front(); + + return true; + } + + bool tryPush(Task* task) + { + { + unique_lock lock{mtx, try_to_lock}; + if (!lock) return false; + taskDeque.push_back(task); + } + + ready.notify_one(); + + return true; + } + + void complete() + { + { + unique_lock lock{mtx}; + done = true; + } + ready.notify_all(); + } + + bool pop(Task** task) + { + unique_lock lock{mtx}; + + while (taskDeque.empty() && !done) { + ready.wait(lock); + } + + if (taskDeque.empty()) return false; + + *task = taskDeque.front(); + taskDeque.pop_front(); + + return true; + } + + void push(Task* task) + { + { + unique_lock lock{mtx}; + taskDeque.push_back(task); + } + + ready.notify_one(); + } + +}; + + +static thread_local bool _async = true; //toggle async tasking for each thread on/off + + +struct TaskSchedulerImpl +{ + uint32_t threadCnt; + vector threads; + vector taskQueues; + atomic idx{0}; + + TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt) + { + threads.reserve(threadCnt); + + for (unsigned i = 0; i < threadCnt; ++i) { + threads.emplace_back([&, i] { run(i); }); + } + } + + ~TaskSchedulerImpl() + { + for (auto& queue : taskQueues) queue.complete(); + for (auto& thread : threads) thread.join(); + } + + void run(unsigned i) + { + Task* task; + + //Thread Loop + while (true) { + auto success = false; + for (unsigned x = 0; x < threadCnt * 2; ++x) { + if (taskQueues[(i + x) % threadCnt].tryPop(&task)) { + success = true; + break; + } + } + + if (!success && !taskQueues[i].pop(&task)) break; + (*task)(i + 1); + } + } + + void request(Task* task) + { + //Async + if (threadCnt > 0 && _async) { + task->prepare(); + auto i = idx++; + for (unsigned n = 0; n < threadCnt; ++n) { + if (taskQueues[(i + n) % threadCnt].tryPush(task)) return; + } + taskQueues[i % threadCnt].push(task); + //Sync + } else { + task->run(0); + } + } +}; + +} + +static TaskSchedulerImpl* inst = nullptr; + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +void TaskScheduler::init(unsigned threads) +{ + if (inst) return; + inst = new TaskSchedulerImpl(threads); +} + + +void TaskScheduler::term() +{ + if (!inst) return; + delete(inst); + inst = nullptr; +} + + +void TaskScheduler::request(Task* task) +{ + if (inst) inst->request(task); +} + + +unsigned TaskScheduler::threads() +{ + if (inst) return inst->threadCnt; + return 0; +} + + +void TaskScheduler::async(bool on) +{ + _async = on; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.h b/project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.h new file mode 100644 index 000000000..f45ab2915 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgTaskScheduler.h @@ -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_TASK_SCHEDULER_H_ +#define _TVG_TASK_SCHEDULER_H_ + +#include +#include +#include "tvgCommon.h" + +namespace tvg +{ + +struct Task; + +struct TaskScheduler +{ + static unsigned threads(); + static void init(unsigned threads); + static void term(); + static void request(Task* task); + static void async(bool on); +}; + +struct Task +{ +private: + mutex mtx; + condition_variable cv; + bool ready = true; + bool pending = false; + +public: + virtual ~Task() = default; + + void done() + { + if (!pending) return; + + unique_lock lock(mtx); + while (!ready) cv.wait(lock); + pending = false; + } + +protected: + virtual void run(unsigned tid) = 0; + +private: + void operator()(unsigned tid) + { + run(tid); + + lock_guard lock(mtx); + ready = true; + cv.notify_one(); + } + + void prepare() + { + ready = false; + pending = true; + } + + friend struct TaskSchedulerImpl; +}; + +} + +#endif //_TVG_TASK_SCHEDULER_H_ + + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgXmlParser.cpp b/project/gui/lvgl/src/libs/thorvg/tvgXmlParser.cpp new file mode 100644 index 000000000..6bd35ba81 --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgXmlParser.cpp @@ -0,0 +1,591 @@ +/* + * 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 +#include +#include + +#ifdef _WIN32 + #include +#elif defined(__linux__) + #include +#else + #include +#endif + +#include "tvgXmlParser.h" +#include "tvgStr.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue) +{ +#ifdef THORVG_LOG_ENABLED + const auto attributesNum = 6; + const struct + { + const char* tag; + bool tagWildcard; //If true, it is assumed that a wildcard is used after the tag. (ex: tagName*) + const char* value; + } attributes[] = { + {"id", false, nullptr}, + {"data-name", false, nullptr}, + {"overflow", false, "visible"}, + {"version", false, nullptr}, + {"xmlns", true, nullptr}, + {"xml:space", false, nullptr}, + }; + + for (unsigned int i = 0; i < attributesNum; ++i) { + if (!strncmp(tagAttribute, attributes[i].tag, attributes[i].tagWildcard ? strlen(attributes[i].tag) : strlen(tagAttribute))) { + if (attributes[i].value && tagValue) { + if (!strncmp(tagValue, attributes[i].value, strlen(tagValue))) { + return true; + } else continue; + } + return true; + } + } + return false; +#endif + return true; +} + + +static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd) +{ + for (; itr < itrEnd; itr++) { + if (isspace((unsigned char)*itr)) break; + } + return itr; +} + + +static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd) +{ + for (; itr < itrEnd; itr++) { + if (!isspace((unsigned char)*itr)) break; + } + return itr; +} + + +static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart) +{ + for (itr--; itr > itrStart; itr--) { + if (!isspace((unsigned char)*itr)) break; + } + return itr + 1; +} + + +static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd) +{ + auto p = itr; + while (itr < itrEnd && *itr == '&') { + for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) { + if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) { + itr += xmlEntityLength[i]; + break; + } + } + if (itr == p) break; + p = itr; + } + return itr; +} + + +static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart) +{ + auto p = itr; + while (itr > itrStart && *(itr - 1) == ';') { + for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) { + if (itr - xmlEntityLength[i] > itrStart && + strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) { + itr -= xmlEntityLength[i]; + break; + } + } + if (itr == p) break; + p = itr; + } + return itr; +} + + +static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd) +{ + itr = _simpleXmlSkipWhiteSpace(itr, itrEnd); + auto p = itr; + while (true) { + if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr; + else break; + if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr; + else break; + } + return itr; +} + + +static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart) +{ + itr = _simpleXmlUnskipWhiteSpace(itr, itrStart); + auto p = itr; + while (true) { + if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr; + else break; + if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr; + else break; + } + return itr; +} + + +static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd) +{ + return (const char*)memchr(itr, '<', itrEnd - itr); +} + + +static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd) +{ + bool insideQuote = false; + for (; itr < itrEnd; itr++) { + if (*itr == '"') insideQuote = !insideQuote; + if (!insideQuote) { + if ((*itr == '>') || (*itr == '<')) + return itr; + } + } + return nullptr; +} + + +static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd) +{ + for (; itr < itrEnd; itr++) { + if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2; + } + return nullptr; +} + + +static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd) +{ + for (; itr < itrEnd; itr++) { + if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2; + } + return nullptr; +} + + +static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd) +{ + for (; itr < itrEnd; itr++) { + if (*itr == '>') return itr; + } + return nullptr; +} + + +static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff) +{ + toff = 0; + if (itr[1] == '/') { + toff = 1; + return SimpleXMLType::Close; + } else if (itr[1] == '?') { + toff = 1; + return SimpleXMLType::Processing; + } else if (itr[1] == '!') { + if ((itr + sizeof("") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) { + toff = sizeof("!DOCTYPE") - 1; + return SimpleXMLType::Doctype; + } else if ((itr + sizeof("") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) { + toff = sizeof("![CDATA[") - 1; + return SimpleXMLType::CData; + } else if ((itr + sizeof("") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) { + toff = sizeof("!--") - 1; + return SimpleXMLType::Comment; + } else if (itr + sizeof("") - 1 < itrEnd) { + toff = sizeof("!") - 1; + return SimpleXMLType::DoctypeChild; + } + return SimpleXMLType::Open; + } + return SimpleXMLType::Open; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type) +{ +#ifdef THORVG_LOG_ENABLED + static const char* TYPE_NAMES[] = { + "Svg", + "G", + "Defs", + "Animation", + "Arc", + "Circle", + "Ellipse", + "Image", + "Line", + "Path", + "Polygon", + "Polyline", + "Rect", + "Text", + "TextArea", + "Tspan", + "Use", + "Video", + "ClipPath", + "Mask", + "Symbol", + "Unknown", + }; + return TYPE_NAMES[(int) type]; +#endif + return nullptr; +} + + +bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName) +{ +#ifdef THORVG_LOG_ENABLED + const auto elementsNum = 1; + const char* const elements[] = { "title" }; + + for (unsigned int i = 0; i < elementsNum; ++i) { + if (!strncmp(tagName, elements[i], strlen(tagName))) { + return true; + } + } + return false; +#else + return true; +#endif +} + + +bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data) +{ + const char *itr = buf, *itrEnd = buf + bufLength; + char* tmpBuf = (char*)malloc(bufLength + 1); + + if (!buf || !func || !tmpBuf) goto error; + + while (itr < itrEnd) { + const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd); + const char *key, *keyEnd, *value, *valueEnd; + char* tval; + + if (p == itrEnd) goto success; + + key = p; + for (keyEnd = key; keyEnd < itrEnd; keyEnd++) { + if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break; + } + if (keyEnd == itrEnd) goto error; + if (keyEnd == key) continue; + + if (*keyEnd == '=') value = keyEnd + 1; + else { + value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd); + if (!value) goto error; + value++; + } + keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key); + + value = _skipWhiteSpacesAndXmlEntities(value, itrEnd); + if (value == itrEnd) goto error; + + if ((*value == '"') || (*value == '\'')) { + valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value); + if (!valueEnd) goto error; + value++; + } else { + valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd); + } + + itr = valueEnd + 1; + + value = _skipWhiteSpacesAndXmlEntities(value, itrEnd); + valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value); + + memcpy(tmpBuf, key, keyEnd - key); + tmpBuf[keyEnd - key] = '\0'; + + tval = tmpBuf + (keyEnd - key) + 1; + int i = 0; + while (value < valueEnd) { + value = _simpleXmlSkipXmlEntities(value, valueEnd); + tval[i++] = *value; + value++; + } + tval[i] = '\0'; + + if (!func((void*)data, tmpBuf, tval)) { + if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) { + TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE"); + } + } + } + +success: + free(tmpBuf); + return true; + +error: + free(tmpBuf); + return false; +} + + +bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data) +{ + const char *itr = buf, *itrEnd = buf + bufLength; + + if (!buf || !func) return false; + + while (itr < itrEnd) { + if (itr[0] == '<') { + //Invalid case + if (itr + 1 >= itrEnd) return false; + + size_t toff = 0; + SimpleXMLType type = _getXMLType(itr, itrEnd, toff); + + const char* p; + if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd); + else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd); + else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd); + else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd); + + if (p) { + //Invalid case: '<' nested + if (*p == '<' && type != SimpleXMLType::Doctype) return false; + const char *start, *end; + + start = itr + 1 + toff; + end = p; + + switch (type) { + case SimpleXMLType::Open: { + if (p[-1] == '/') { + type = SimpleXMLType::OpenEmpty; + end--; + } + break; + } + case SimpleXMLType::CData: { + if (!memcmp(p - 2, "]]", 2)) end -= 2; + break; + } + case SimpleXMLType::Processing: { + if (p[-1] == '?') end--; + break; + } + case SimpleXMLType::Comment: { + if (!memcmp(p - 2, "--", 2)) end -= 2; + break; + } + default: { + break; + } + } + + if (strip && (type != SimpleXMLType::CData)) { + start = _skipWhiteSpacesAndXmlEntities(start, end); + end = _unskipWhiteSpacesAndXmlEntities(end, start); + } + + if (!func((void*)data, type, start, (unsigned int)(end - start))) return false; + + itr = p + 1; + } else { + return false; + } + } else { + const char *p, *end; + + if (strip) { + p = itr; + p = _skipWhiteSpacesAndXmlEntities(p, itrEnd); + if (p) { + if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false; + itr = p; + } + } + + p = _simpleXmlFindStartTag(itr, itrEnd); + if (!p) p = itrEnd; + + end = p; + if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr); + + if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false; + + if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false; + + itr = p; + } + } + return true; +} + + +bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data) +{ + const char* end; + char* key; + char* val; + char* next; + + if (!buf) return false; + + end = buf + bufLength; + key = (char*)alloca(end - buf + 1); + val = (char*)alloca(end - buf + 1); + + if (buf == end) return true; + + do { + char* sep = (char*)strchr(buf, ':'); + next = (char*)strchr(buf, ';'); + if (sep >= end) { + next = nullptr; + sep = nullptr; + } + if (next >= end) next = nullptr; + + key[0] = '\0'; + val[0] = '\0'; + + if (next == nullptr && sep != nullptr) { + memcpy(key, buf, sep - buf); + key[sep - buf] = '\0'; + + memcpy(val, sep + 1, end - sep - 1); + val[end - sep - 1] = '\0'; + } else if (sep < next && sep != nullptr) { + memcpy(key, buf, sep - buf); + key[sep - buf] = '\0'; + + memcpy(val, sep + 1, next - sep - 1); + val[next - sep - 1] = '\0'; + } else if (next) { + memcpy(key, buf, next - buf); + key[next - buf] = '\0'; + } + + if (key[0]) { + key = const_cast(_simpleXmlSkipWhiteSpace(key, key + strlen(key))); + key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0'; + val = const_cast(_simpleXmlSkipWhiteSpace(val, val + strlen(val))); + val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0'; + + if (!func((void*)data, key, val)) { + if (!_isIgnoreUnsupportedLogAttributes(key, val)) { + TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE"); + } + } + } + + buf = next + 1; + } while (next != nullptr); + + return true; +} + + +/* + * Supported formats: + * tag {}, .name {}, tag.name{} + */ +const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength) +{ + if (!buf) return nullptr; + + *tag = *name = nullptr; + *attrsLength = 0; + + auto itr = _simpleXmlSkipWhiteSpace(buf, buf + bufLength); + auto itrEnd = (const char*)memchr(buf, '{', bufLength); + + if (!itrEnd || itr == itrEnd) return nullptr; + + auto nextElement = (const char*)memchr(itrEnd, '}', bufLength - (itrEnd - buf)); + if (!nextElement) return nullptr; + + *attrs = itrEnd + 1; + *attrsLength = nextElement - *attrs; + + const char *p; + + itrEnd = _simpleXmlUnskipWhiteSpace(itrEnd, itr); + if (*(itrEnd - 1) == '.') return nullptr; + + for (p = itr; p < itrEnd; p++) { + if (*p == '.') break; + } + + if (p == itr) *tag = strdup("all"); + else *tag = strDuplicate(itr, p - itr); + + if (p == itrEnd) *name = nullptr; + else *name = strDuplicate(p + 1, itrEnd - p - 1); + + return (nextElement ? nextElement + 1 : nullptr); +} + + +const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength) +{ + const char *itr = buf, *itrEnd = buf + bufLength; + + for (; itr < itrEnd; itr++) { + if (!isspace((unsigned char)*itr)) { + //User skip tagname and already gave it the attributes. + if (*itr == '=') return buf; + } else { + itr = _simpleXmlUnskipXmlEntities(itr, buf); + if (itr == itrEnd) return nullptr; + return itr; + } + } + + return nullptr; +} + +#endif /* LV_USE_THORVG_INTERNAL */ + diff --git a/project/gui/lvgl/src/libs/thorvg/tvgXmlParser.h b/project/gui/lvgl/src/libs/thorvg/tvgXmlParser.h new file mode 100644 index 000000000..e1cf2de2e --- /dev/null +++ b/project/gui/lvgl/src/libs/thorvg/tvgXmlParser.h @@ -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_SIMPLE_XML_PARSER_H_ +#define _TVG_SIMPLE_XML_PARSER_H_ + +#include "tvgSvgLoaderCommon.h" + +#define NUMBER_OF_XML_ENTITIES 8 +const char* const xmlEntity[] = {""", " ", "'", "&", "<", ">", "#", "'"}; +const int xmlEntityLength[] = {6, 6, 6, 5, 4, 4, 6, 6}; + +enum class SimpleXMLType +{ + Open = 0, //!< \ + OpenEmpty, //!< \ + Close, //!< \ + Data, //!< tag text data + CData, //!< \ + Error, //!< error contents + Processing, //!< \ \ + Doctype, //!< \ + Ignored, //!< whatever is ignored by parser, like whitespace + DoctypeChild //!< \file, path, LV_FS_MODE_RD)) { - TTF_FREE(dsc); + lv_free(dsc); LV_LOG_ERROR("tiny_ttf: unable to open %s\n", path); return NULL; } dsc->stream.file = &dsc->file; } else { - dsc->stream.file = NULL; dsc->stream.data = (const uint8_t *)data; dsc->stream.size = data_size; - dsc->stream.position = 0; } if(0 == stbtt_InitFont(&dsc->info, &dsc->stream, stbtt_GetFontOffsetForIndex(&dsc->stream, 0))) { - TTF_FREE(dsc); - + lv_free(dsc); LV_LOG_ERROR("tiny_ttf: init failed\n"); return NULL; } @@ -213,19 +208,18 @@ static lv_font_t * lv_tiny_ttf_create(const char * path, const void * data, size #else dsc->stream = (const uint8_t *)data; if(0 == stbtt_InitFont(&dsc->info, dsc->stream, stbtt_GetFontOffsetForIndex(dsc->stream, 0))) { - TTF_FREE(dsc); + lv_free(dsc); LV_LOG_ERROR("tiny_ttf: init failed\n"); return NULL; } #endif - lv_font_t * out_font = (lv_font_t *)TTF_MALLOC(sizeof(lv_font_t)); + lv_font_t * out_font = lv_malloc_zeroed(sizeof(lv_font_t)); if(out_font == NULL) { - TTF_FREE(dsc); + lv_free(dsc); LV_LOG_ERROR("tiny_ttf: out of memory\n"); return NULL; } - lv_memzero(out_font, sizeof(lv_font_t)); out_font->get_glyph_dsc = ttf_get_glyph_dsc_cb; out_font->get_glyph_bitmap = ttf_get_glyph_bitmap_cb; out_font->dsc = dsc; @@ -273,9 +267,9 @@ void lv_tiny_ttf_destroy(lv_font_t * font) lv_fs_close(&ttf->file); } #endif - TTF_FREE(ttf); + lv_free(ttf); } - TTF_FREE(font); + lv_free(font); } } #endif diff --git a/project/gui/lvgl/src/libs/tiny_ttf/stb_rect_pack.h b/project/gui/lvgl/src/libs/tiny_ttf/stb_rect_pack.h index 413e5047d..b265df907 100644 --- a/project/gui/lvgl/src/libs/tiny_ttf/stb_rect_pack.h +++ b/project/gui/lvgl/src/libs/tiny_ttf/stb_rect_pack.h @@ -97,7 +97,6 @@ typedef int stbrp_coord; #pragma GCC diagnostic ignored "-Wunused-function" #endif - STBRP_DEF int stbrp_pack_rects(stbrp_context * context, stbrp_rect * rects, int num_rects); // Assign packed locations to rectangles. The rectangles are of type // 'stbrp_rect' defined below, stored in the array 'rects', and there @@ -136,7 +135,6 @@ struct stbrp_rect { }; // 16 bytes, nominally - STBRP_DEF void stbrp_init_target(stbrp_context * context, int width, int height, stbrp_node * nodes, int num_nodes); // Initialize a rectangle packer to: // pack a rectangle that is 'width' by 'height' in dimensions @@ -163,7 +161,6 @@ STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context * context, int allow_o // change the handling of the out-of-temp-memory scenario, described above. // If you call init again, this will be reset to the default (false). - STBRP_DEF void stbrp_setup_heuristic(stbrp_context * context, int heuristic); // Optionally select which packing heuristic the library should use. Different // heuristics will produce better/worse results for different data sets. @@ -175,7 +172,6 @@ enum { STBRP_HEURISTIC_Skyline_BF_sortHeight }; - ////////////////////////////////////////////////////////////////////////////// // // the details of the following structures don't matter to you, but they must diff --git a/project/gui/lvgl/src/libs/tiny_ttf/stb_truetype_htcw.h b/project/gui/lvgl/src/libs/tiny_ttf/stb_truetype_htcw.h index a153df0f9..e91997b4b 100644 --- a/project/gui/lvgl/src/libs/tiny_ttf/stb_truetype_htcw.h +++ b/project/gui/lvgl/src/libs/tiny_ttf/stb_truetype_htcw.h @@ -418,7 +418,6 @@ int main(int arg, char ** argv) } #endif - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //// @@ -590,7 +589,6 @@ STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar * chardata, int pw, int // // It's inefficient; you might want to c&p it and optimize it. - ////////////////////////////////////////////////////////////////////////////// // // NEW TEXTURE BAKING API @@ -668,7 +666,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context * spc, unsigned int STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context * spc, int skip); // If skip != 0, this tells stb_truetype to skip any codepoints for which // there is no corresponding glyph. If skip=0, which is the default, then -// codepoints without a glyph recived the font's "missing character" glyph, +// codepoints without a glyph received the font's "missing character" glyph, // typically an empty box by convention. STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar * chardata, int pw, int ph, // same data as above @@ -769,7 +767,6 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo * info, const unsigned char * data, // need to do anything special to free it, because the contents are pure // value data with no additional data structures. Returns 0 on failure. - ////////////////////////////////////////////////////////////////////////////// // // CHARACTER TO GLYPH-INDEX CONVERSIOn @@ -781,7 +778,6 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo * info, int unicode_code // codepoint-based functions. // Returns 0 if the character codepoint is not defined in the font. - ////////////////////////////////////////////////////////////////////////////// // // CHARACTER PROPERTIES @@ -968,7 +964,6 @@ STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo * font, int glyph, f STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo * font, int glyph, float scale_x, float scale_y, float shift_x, float shift_y, int * ix0, int * iy0, int * ix1, int * iy1); - // @TODO: don't expose this structure typedef struct { int w, h, stride; @@ -1044,8 +1039,6 @@ STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo * info, flo // The algorithm has not been optimized at all, so expect it to be slow // if computing lots of characters or very large sizes. - - ////////////////////////////////////////////////////////////////////////////// // // Finding the right font... @@ -1374,7 +1367,6 @@ static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) // on platforms that don't allow misaligned reads, if we want to allow // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE - #ifdef STBTT_STREAM_TYPE static stbtt_uint8 ttBYTE(STBTT_STREAM_TYPE s, stbtt_uint32 offset) { @@ -3075,7 +3067,6 @@ typedef struct stbtt__edge { int invert; } stbtt__edge; - typedef struct stbtt__active_edge { struct stbtt__active_edge * next; #if STBTT_RASTERIZER_VERSION==1 @@ -4637,7 +4628,6 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context * spc, const unsigned char stbtt_pack_range * ranges, int num_ranges); #endif - STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context * spc, stbrp_rect * rects, int num_rects) { stbrp_pack_rects((stbrp_context *)spc->pack_info, rects, num_rects); @@ -4711,7 +4701,6 @@ STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context * spc, const unsigned char return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); } - #ifdef STBTT_STREAM_TYPE STBTT_DEF void stbtt_GetScaledFontVMetrics(STBTT_STREAM_TYPE fontdata, int index, float size, float * ascent, float * descent, float * lineGap); @@ -4720,7 +4709,6 @@ STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char * fontdata, int i float * descent, float * lineGap); #endif - #ifdef STBTT_STREAM_TYPE STBTT_DEF void stbtt_GetScaledFontVMetrics(STBTT_STREAM_TYPE fontdata, int index, float size, float * ascent, float * descent, float * lineGap) @@ -5372,7 +5360,6 @@ static int stbtt__matchpair(stbtt_uint8 * fc, stbtt_uint32 nm, stbtt_uint8 * nam #pragma GCC diagnostic ignored "-Wcast-qual" #endif - #ifdef STBTT_STREAM_TYPE STBTT_DEF int stbtt_BakeFontBitmap(STBTT_STREAM_TYPE data, int offset, float pixel_height, unsigned char * pixels, int pw, int ph, @@ -5465,7 +5452,6 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char * s1, int len1, cons #endif // STB_TRUETYPE_IMPLEMENTATION - // FULL VERSION HISTORY // // 1.25 (2021-07-11) many fixes diff --git a/project/gui/lvgl/src/lv_api_map.h b/project/gui/lvgl/src/lv_api_map.h index 0f763dbf6..b594655a1 100644 --- a/project/gui/lvgl/src/lv_api_map.h +++ b/project/gui/lvgl/src/lv_api_map.h @@ -44,7 +44,7 @@ extern "C" { /********************** * TYPEDEFS **********************/ -typedef int32_t int32_t; +typedef int32_t lv_coord_t; typedef lv_result_t lv_res_t; typedef lv_image_dsc_t lv_img_dsc_t; typedef lv_display_t lv_disp_t; @@ -88,7 +88,6 @@ static inline void lv_obj_move_background(lv_obj_t * obj) lv_obj_move_to_index(obj, 0); } - /********************** * MACROS **********************/ @@ -132,7 +131,7 @@ static inline void lv_obj_move_background(lv_obj_t * obj) #define lv_disp_flush_ready lv_display_flush_ready #define lv_disp_flush_is_last lv_display_flush_is_last #define lv_disp_is_double_buffered lv_display_is_double_buffered -#define lv_disp_get_scr_act lv_display_get_screen_act +#define lv_disp_get_scr_act lv_display_get_screen_active #define lv_disp_get_scr_prev lv_display_get_screen_prev #define lv_disp_load_scr lv_display_load_scr #define lv_disp_get_layer_top lv_display_get_layer_top diff --git a/project/gui/lvgl/src/lv_conf_internal.h b/project/gui/lvgl/src/lv_conf_internal.h index 295ea7b26..dc583b3b6 100644 --- a/project/gui/lvgl/src/lv_conf_internal.h +++ b/project/gui/lvgl/src/lv_conf_internal.h @@ -876,6 +876,15 @@ #endif #endif +/*Prefix all global extern data with this*/ +#ifndef LV_ATTRIBUTE_EXTERN_DATA + #ifdef CONFIG_LV_ATTRIBUTE_EXTERN_DATA + #define LV_ATTRIBUTE_EXTERN_DATA CONFIG_LV_ATTRIBUTE_EXTERN_DATA + #else + #define LV_ATTRIBUTE_EXTERN_DATA + #endif +#endif + /* Use `float` as `lv_value_precise_t` */ #ifndef LV_USE_FLOAT #ifdef CONFIG_LV_USE_FLOAT @@ -2145,6 +2154,33 @@ #endif #endif +/*Enable Vector Graphic APIs*/ +#ifndef LV_USE_VECTOR_GRAPHIC + #ifdef CONFIG_LV_USE_VECTOR_GRAPHIC + #define LV_USE_VECTOR_GRAPHIC CONFIG_LV_USE_VECTOR_GRAPHIC + #else + #define LV_USE_VECTOR_GRAPHIC 0 + #endif +#endif + +/* Enable ThorVG (vector graphics library) from the src/libs folder */ +#ifndef LV_USE_THORVG_INTERNAL + #ifdef CONFIG_LV_USE_THORVG_INTERNAL + #define LV_USE_THORVG_INTERNAL CONFIG_LV_USE_THORVG_INTERNAL + #else + #define LV_USE_THORVG_INTERNAL 0 + #endif +#endif + +/* Enable ThorVG by assuming that its installed and linked to the project */ +#ifndef LV_USE_THORVG_EXTERNAL + #ifdef CONFIG_LV_USE_THORVG_EXTERNAL + #define LV_USE_THORVG_EXTERNAL CONFIG_LV_USE_THORVG_EXTERNAL + #else + #define LV_USE_THORVG_EXTERNAL 0 + #endif +#endif + /*FFmpeg library for image decoding and playing videos *Supports all major image formats so do not enable other image decoder with it*/ #ifndef LV_USE_FFMPEG diff --git a/project/gui/lvgl/src/lv_init.c b/project/gui/lvgl/src/lv_init.c index f4488c87d..71e2f7f8d 100644 --- a/project/gui/lvgl/src/lv_init.c +++ b/project/gui/lvgl/src/lv_init.c @@ -68,7 +68,7 @@ static inline void lv_global_init(lv_global_t * global) return; } - lv_memset(global, 0, sizeof(lv_global_t)); + lv_memzero(global, sizeof(lv_global_t)); _lv_ll_init(&(global->disp_ll), sizeof(lv_display_t)); _lv_ll_init(&(global->indev_ll), sizeof(lv_indev_t)); @@ -286,6 +286,10 @@ void lv_deinit(void) lv_span_stack_deinit(); #endif +#if LV_USE_DRAW_SW + lv_draw_sw_deinit(); +#endif + #if LV_USE_FREETYPE lv_freetype_uninit(); #endif @@ -302,15 +306,54 @@ void lv_deinit(void) lv_theme_mono_deinit(); #endif - lv_mem_deinit(); + _lv_cache_builtin_deinit(); -#if LV_USE_LOG - lv_log_register_print_cb(NULL); + _lv_cache_deinit(); + + _lv_image_decoder_deinit(); + + _lv_refr_deinit(); + + _lv_obj_style_deinit(); + +#if LV_USE_DRAW_PXP + lv_draw_pxp_deinit(); +#endif + +#if LV_USE_DRAW_VGLITE + lv_draw_vglite_deinit(); +#endif + +#if LV_USE_DRAW_SW + lv_draw_sw_deinit(); +#endif + + lv_draw_deinit(); + + _lv_group_deinit(); + + _lv_anim_core_deinit(); + + _lv_layout_deinit(); + + _lv_fs_deinit(); + + _lv_timer_core_deinit(); + +#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN + lv_profiler_builtin_uninit(); #endif #if LV_USE_OBJ_ID_BUILTIN lv_objid_builtin_destroy(); #endif + + lv_mem_deinit(); + +#if LV_USE_LOG + lv_log_register_print_cb(NULL); +#endif + #endif lv_initialized = false; diff --git a/project/gui/lvgl/src/misc/Makefile b/project/gui/lvgl/src/misc/Makefile index 8ea3f58ed..7a8d8f7ca 100644 --- a/project/gui/lvgl/src/misc/Makefile +++ b/project/gui/lvgl/src/misc/Makefile @@ -22,3 +22,4 @@ obj-y += lv_text.o obj-y += lv_utils.o obj-y += lv_profiler_builtin.o obj-y += lv_palette.o +obj-y += lv_array.o diff --git a/project/gui/lvgl/src/misc/lv_anim.c b/project/gui/lvgl/src/misc/lv_anim.c index 7c75fb734..3935714d5 100644 --- a/project/gui/lvgl/src/misc/lv_anim.c +++ b/project/gui/lvgl/src/misc/lv_anim.c @@ -50,7 +50,6 @@ static int32_t lv_anim_path_cubic_bezier(const lv_anim_t * a, int32_t x1, #define LV_TRACE_ANIM(...) #endif - /********************** * GLOBAL FUNCTIONS **********************/ @@ -64,6 +63,11 @@ void _lv_anim_core_init(void) state.anim_run_round = false; } +void _lv_anim_core_deinit(void) +{ + lv_anim_delete_all(); +} + void lv_anim_init(lv_anim_t * a) { lv_memzero(a, sizeof(lv_anim_t)); @@ -322,7 +326,6 @@ static void anim_timer(lv_timer_t * param) { LV_UNUSED(param); - /*Flip the run round*/ state.anim_run_round = state.anim_run_round ? false : true; diff --git a/project/gui/lvgl/src/misc/lv_anim.h b/project/gui/lvgl/src/misc/lv_anim.h index 6a7745133..b0ab12df9 100644 --- a/project/gui/lvgl/src/misc/lv_anim.h +++ b/project/gui/lvgl/src/misc/lv_anim.h @@ -173,6 +173,11 @@ typedef struct _lv_anim_t { */ void _lv_anim_core_init(void); +/** + * Deinit. the animation module + */ +void _lv_anim_core_deinit(void); + /** * Initialize an animation variable. * E.g.: @@ -383,7 +388,6 @@ static inline void lv_anim_set_bezier3_param(lv_anim_t * a, int16_t x1, int16_t para->y2 = y2; } - /** * Create an animation * @param a an initialized 'anim_t' variable. Not required after call. diff --git a/project/gui/lvgl/src/misc/lv_anim_timeline.c b/project/gui/lvgl/src/misc/lv_anim_timeline.c index 4edaca23d..382bfc1fa 100644 --- a/project/gui/lvgl/src/misc/lv_anim_timeline.c +++ b/project/gui/lvgl/src/misc/lv_anim_timeline.c @@ -19,8 +19,6 @@ * TYPEDEFS **********************/ - - /********************** * STATIC PROTOTYPES **********************/ @@ -40,12 +38,8 @@ static void lv_anim_timeline_virtual_exec_cb(void * var, int32_t v); lv_anim_timeline_t * lv_anim_timeline_create(void) { - lv_anim_timeline_t * at = (lv_anim_timeline_t *)lv_malloc(sizeof(lv_anim_timeline_t)); - + lv_anim_timeline_t * at = lv_malloc_zeroed(sizeof(lv_anim_timeline_t)); LV_ASSERT_MALLOC(at); - - if(at) lv_memzero(at, sizeof(lv_anim_timeline_t)); - return at; } diff --git a/project/gui/lvgl/src/misc/lv_array.c b/project/gui/lvgl/src/misc/lv_array.c new file mode 100644 index 000000000..33bc615d4 --- /dev/null +++ b/project/gui/lvgl/src/misc/lv_array.c @@ -0,0 +1,130 @@ +/** + * @file lv_array.c + * Array. + * The nodes are dynamically allocated by the 'lv_mem' module, + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_array.h" +#include "../stdlib/lv_mem.h" +#include "../stdlib/lv_string.h" + +#include "lv_assert.h" +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_array_init(lv_array_t * array, uint32_t capacity, uint32_t element_size) +{ + array->size = 0; + array->capacity = capacity; + array->element_size = element_size; + + array->data = lv_malloc(capacity * element_size); + LV_ASSERT_MALLOC(array->data); +} + +void lv_array_destroy(lv_array_t * array) +{ + if(array->data) { + lv_free(array->data); + array->data = NULL; + } +} + +void lv_array_copy(lv_array_t * target, const lv_array_t * array) +{ + if(lv_array_is_empty(array)) { + return; + } + lv_array_destroy(target); + lv_array_init(target, array->capacity, array->element_size); + lv_memcpy(target->data, array->data, array->size * array->element_size); + target->size = array->size; +} + +void lv_array_clear(lv_array_t * array) +{ + array->size = 0; +} + +void lv_array_resize(lv_array_t * array, uint32_t new_capacity) +{ + if(new_capacity > array->size) { + if(new_capacity > array->capacity) { + uint8_t * data = lv_malloc(new_capacity * array->element_size); + lv_memcpy(data, array->data, array->size * array->element_size); + lv_free(array->data); + array->data = data; + array->capacity = new_capacity; + } + } + else { + array->size = new_capacity; + } +} + +bool lv_array_append(lv_array_t * array, const uint8_t * element) +{ + if(array->size >= array->capacity) { + // array is full + return false; + } + + LV_ASSERT_NULL(array->data); + uint8_t * data = (uint8_t *)(array->data + array->size * array->element_size); + lv_memcpy(data, element, array->element_size); + array->size++; + return true; +} + +uint8_t * lv_array_get(const lv_array_t * array, uint32_t index) +{ + if(index > (array->size - 1)) { + return NULL; + } + + LV_ASSERT_NULL(array->data); + return array->data + index * array->element_size; +} + +uint32_t lv_array_length(const lv_array_t * array) +{ + return array->size; +} + +uint32_t lv_array_capacity(const lv_array_t * array) +{ + return array->capacity; +} + +bool lv_array_is_full(const lv_array_t * array) +{ + return array->size == array->capacity; +} + +bool lv_array_is_empty(const lv_array_t * array) +{ + return array->size == 0; +} diff --git a/project/gui/lvgl/src/misc/lv_array.h b/project/gui/lvgl/src/misc/lv_array.h new file mode 100644 index 000000000..1e8c7ddb2 --- /dev/null +++ b/project/gui/lvgl/src/misc/lv_array.h @@ -0,0 +1,86 @@ +/** + * @file lv_array.h + * Array. The elements are dynamically allocated by the 'lv_mem' module. + */ + +#ifndef LV_ARRAY_H +#define LV_ARRAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include +#include + +/********************* + * DEFINES + *********************/ +#define DEFAULT_CAPS 8 + +/********************** + * TYPEDEFS + **********************/ + +/** Description of a array*/ +typedef struct { + uint8_t * data; + uint32_t size; + uint32_t capacity; + uint32_t element_size; +} lv_array_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_array_init(lv_array_t * array, uint32_t capacity, uint32_t element_size); + +void lv_array_copy(lv_array_t * target, const lv_array_t * array); + +void lv_array_clear(lv_array_t * array); + +void lv_array_resize(lv_array_t * array, uint32_t new_capacity); + +void lv_array_destroy(lv_array_t * array); + +bool lv_array_append(lv_array_t * array, const uint8_t * element); + +uint8_t * lv_array_get(const lv_array_t * array, uint32_t index); + +uint32_t lv_array_length(const lv_array_t * array); + +uint32_t lv_array_capacity(const lv_array_t * array); + +bool lv_array_is_empty(const lv_array_t * array); + +bool lv_array_is_full(const lv_array_t * array); +/********************** + * MACROS + **********************/ + +#define LV_ARRAY_INIT(array, type) lv_array_init((array), DEFAULT_CAPS, sizeof(type)) + +#define LV_ARRAY_INIT_CAPACITY(array, caps, type) lv_array_init((array), (caps), sizeof(type)) + +#define LV_ARRAY_APPEND_VALUE(array, element) lv_array_append((array), (uint8_t*)&(element)) + +#define LV_ARRAY_APPEND(array, element) lv_array_append((array), (uint8_t*)(element)) + +#define LV_ARRAY_GET(array, index, type) (type*)lv_array_get((array), (index)) + +#define LV_ARRAY_SET(array, index, data, type) \ + do { \ + type* elem = (type*)lv_array_get((array), (index)); \ + *elem = *((type*)(data)); \ + } while(0) + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/project/gui/lvgl/src/misc/lv_cache.c b/project/gui/lvgl/src/misc/lv_cache.c index 5991fccae..203944ca8 100644 --- a/project/gui/lvgl/src/misc/lv_cache.c +++ b/project/gui/lvgl/src/misc/lv_cache.c @@ -42,6 +42,11 @@ void _lv_cache_init(void) lv_mutex_init(&_cache_manager.mutex); } +void _lv_cache_deinit(void) +{ + lv_mutex_delete(&_cache_manager.mutex); +} + void lv_cache_set_manager(lv_cache_manager_t * manager) { LV_ASSERT(_cache_manager.locked); diff --git a/project/gui/lvgl/src/misc/lv_cache.h b/project/gui/lvgl/src/misc/lv_cache.h index c3e259ee9..5a60ea900 100644 --- a/project/gui/lvgl/src/misc/lv_cache.h +++ b/project/gui/lvgl/src/misc/lv_cache.h @@ -75,7 +75,6 @@ typedef struct _lv_cache_entry_t { void * user_data; } lv_cache_entry_t; - /** * Add a new entry to the cache with the given size. * It won't allocate any buffers just free enough space to be a new entry @@ -152,6 +151,11 @@ typedef struct { */ void _lv_cache_init(void); +/** + * Deinitialize the cache module + */ +void _lv_cache_deinit(void); + /** * Set new cache manager * @param manager the new cache manager with callback functions set diff --git a/project/gui/lvgl/src/misc/lv_cache_builtin.c b/project/gui/lvgl/src/misc/lv_cache_builtin.c index 4b1421d0e..223da5b35 100644 --- a/project/gui/lvgl/src/misc/lv_cache_builtin.c +++ b/project/gui/lvgl/src/misc/lv_cache_builtin.c @@ -65,6 +65,11 @@ void _lv_cache_builtin_init(void) _cache_manager.empty_cb = empty_cb; } +void _lv_cache_builtin_deinit(void) +{ + _lv_ll_clear(&dsc.entry_ll); +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/misc/lv_cache_builtin.h b/project/gui/lvgl/src/misc/lv_cache_builtin.h index 7b88cfc6d..846a9029d 100644 --- a/project/gui/lvgl/src/misc/lv_cache_builtin.h +++ b/project/gui/lvgl/src/misc/lv_cache_builtin.h @@ -34,6 +34,8 @@ typedef struct { void _lv_cache_builtin_init(void); +void _lv_cache_builtin_deinit(void); + /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/misc/lv_color.c b/project/gui/lvgl/src/misc/lv_color.c index 7725b3a7a..85fc5f55e 100644 --- a/project/gui/lvgl/src/misc/lv_color.c +++ b/project/gui/lvgl/src/misc/lv_color.c @@ -44,8 +44,6 @@ const lv_color_filter_dsc_t lv_color_filter_shade = {.filter_cb = lv_color_filte uint8_t lv_color_format_get_bpp(lv_color_format_t cf) { switch(cf) { - case LV_COLOR_FORMAT_NATIVE_REVERSED: - return LV_COLOR_DEPTH / 8; case LV_COLOR_FORMAT_I1: case LV_COLOR_FORMAT_A1: return 1; diff --git a/project/gui/lvgl/src/misc/lv_color.h b/project/gui/lvgl/src/misc/lv_color.h index 24b952f4b..00cddc007 100644 --- a/project/gui/lvgl/src/misc/lv_color.h +++ b/project/gui/lvgl/src/misc/lv_color.h @@ -120,9 +120,6 @@ enum _lv_color_format_t { LV_COLOR_FORMAT_ARGB8888 = 0x10, LV_COLOR_FORMAT_XRGB8888 = 0x11, - /*Miscellaneous formats*/ - LV_COLOR_FORMAT_NATIVE_REVERSED = 0x1A, - /*Formats not supported by software renderer but kept here so GPU can use it*/ LV_COLOR_FORMAT_A1 = 0x0B, LV_COLOR_FORMAT_A2 = 0x0C, diff --git a/project/gui/lvgl/src/misc/lv_fs.c b/project/gui/lvgl/src/misc/lv_fs.c index 3090ce04e..f6bdd3700 100644 --- a/project/gui/lvgl/src/misc/lv_fs.c +++ b/project/gui/lvgl/src/misc/lv_fs.c @@ -1,4 +1,4 @@ -/** +/** * @file lv_fs.c * */ @@ -45,6 +45,11 @@ void _lv_fs_init(void) _lv_ll_init(fsdrv_ll_p, sizeof(lv_fs_drv_t *)); } +void _lv_fs_deinit(void) +{ + _lv_ll_clear(fsdrv_ll_p); +} + bool lv_fs_is_ready(char letter) { lv_fs_drv_t * drv = lv_fs_get_drv(letter); @@ -99,9 +104,8 @@ lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mo } if(drv->cache_size) { - file_p->cache = lv_malloc(sizeof(lv_fs_file_cache_t)); + file_p->cache = lv_malloc_zeroed(sizeof(lv_fs_file_cache_t)); LV_ASSERT_MALLOC(file_p->cache); - lv_memzero(file_p->cache, sizeof(lv_fs_file_cache_t)); /* If this is a memory-mapped file, then set "cache" to the memory buffer */ if(drv->cache_size == LV_FS_CACHE_FROM_BUFFER) { @@ -225,8 +229,6 @@ static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t buffer = file_p->cache->buffer; } - - uint32_t bytes_read_to_buffer = 0; res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer); file_p->cache->start = file_position; diff --git a/project/gui/lvgl/src/misc/lv_fs.h b/project/gui/lvgl/src/misc/lv_fs.h index b2eef0e39..09e7270a2 100644 --- a/project/gui/lvgl/src/misc/lv_fs.h +++ b/project/gui/lvgl/src/misc/lv_fs.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_fs.h * */ @@ -55,7 +55,6 @@ typedef _lv_fs_res_t lv_fs_res_t; typedef uint8_t lv_fs_res_t; #endif /*DOXYGEN*/ - /** * File open mode. */ @@ -70,7 +69,6 @@ typedef _lv_fs_mode_t lv_fs_mode_t; typedef uint8_t lv_fs_mode_t; #endif /*DOXYGEN*/ - /** * Seek modes. */ @@ -133,6 +131,11 @@ typedef struct { */ void _lv_fs_init(void); +/** + * Deinitialize the File system interface + */ +void _lv_fs_deinit(void); + /** * Initialize a file system driver with default values. * It is used to ensure all fields have known values and not memory junk. diff --git a/project/gui/lvgl/src/misc/lv_lru.c b/project/gui/lvgl/src/misc/lv_lru.c index 4c3600434..5f3618cf4 100644 --- a/project/gui/lvgl/src/misc/lv_lru.c +++ b/project/gui/lvgl/src/misc/lv_lru.c @@ -75,8 +75,7 @@ lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_c lv_lru_free_cb_t key_free) { // create the cache - lv_lru_t * cache = (lv_lru_t *) lv_malloc(sizeof(lv_lru_t)); - lv_memzero(cache, sizeof(lv_lru_t)); + lv_lru_t * cache = lv_malloc_zeroed(sizeof(lv_lru_t)); if(!cache) { LV_LOG_WARN("LRU Cache unable to create cache object"); return NULL; @@ -90,8 +89,7 @@ lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_c cache->key_free = key_free ? key_free : lv_free; // size the hash table to a guestimate of the number of slots required (assuming a perfect hash) - cache->items = (lv_lru_item_t **) lv_malloc(sizeof(lv_lru_item_t *) * cache->hash_table_size); - lv_memzero(cache->items, sizeof(lv_lru_item_t *) * cache->hash_table_size); + cache->items = lv_malloc_zeroed(sizeof(lv_lru_item_t *) * cache->hash_table_size); if(!cache->items) { LV_LOG_WARN("LRU Cache unable to create cache hash table"); lv_free(cache); @@ -100,7 +98,6 @@ lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_c return cache; } - void lv_lru_delete(lv_lru_t * cache) { LV_ASSERT_NULL(cache); @@ -136,7 +133,6 @@ void lv_lru_delete(lv_lru_t * cache) lv_free(cache); } - lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length) { test_for_missing_cache(); @@ -189,7 +185,6 @@ lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, v return LV_LRU_OK; } - lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value) { test_for_missing_cache(); @@ -342,8 +337,7 @@ static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache) lv_memzero(item, sizeof(lv_lru_item_t)); } else { - item = (lv_lru_item_t *) lv_malloc(sizeof(lv_lru_item_t)); - lv_memzero(item, sizeof(lv_lru_item_t)); + item = lv_malloc_zeroed(sizeof(lv_lru_item_t)); } return item; diff --git a/project/gui/lvgl/src/misc/lv_text.h b/project/gui/lvgl/src/misc/lv_text.h index 65d83cd5e..3aaecabac 100644 --- a/project/gui/lvgl/src/misc/lv_text.h +++ b/project/gui/lvgl/src/misc/lv_text.h @@ -39,9 +39,8 @@ extern "C" { enum _lv_text_flag_t { LV_TEXT_FLAG_NONE = 0x00, - LV_TEXT_FLAG_RECOLOR = 0x01, /**< Enable parsing of recolor command*/ - LV_TEXT_FLAG_EXPAND = 0x02, /**< Ignore max-width to avoid automatic word wrapping*/ - LV_TEXT_FLAG_FIT = 0x04, /**< Max-width is already equal to the longest line. (Used to skip some calculation)*/ + LV_TEXT_FLAG_EXPAND = 0x01, /**< Ignore max-width to avoid automatic word wrapping*/ + LV_TEXT_FLAG_FIT = 0x02, /**< Max-width is already equal to the longest line. (Used to skip some calculation)*/ }; #ifdef DOXYGEN @@ -50,7 +49,6 @@ typedef _lv_text_flag_t lv_text_flag_t; typedef uint8_t lv_text_flag_t; #endif /*DOXYGEN*/ - /** Label align policy*/ enum _lv_text_align_t { LV_TEXT_ALIGN_AUTO, /**< Align text auto*/ @@ -65,7 +63,6 @@ typedef _lv_text_align_t lv_text_align_t; typedef uint8_t lv_text_align_t; #endif /*DOXYGEN*/ - /********************** * GLOBAL PROTOTYPES **********************/ @@ -170,7 +167,6 @@ static inline bool _lv_text_is_break_char(uint32_t letter) return ret; } - /** * Test if char is break char or not (a text can broken here or not) * @param letter a letter diff --git a/project/gui/lvgl/src/misc/lv_timer.c b/project/gui/lvgl/src/misc/lv_timer.c index 29ccbbdf9..1af2785e5 100644 --- a/project/gui/lvgl/src/misc/lv_timer.c +++ b/project/gui/lvgl/src/misc/lv_timer.c @@ -242,6 +242,13 @@ void lv_timer_enable(bool en) if(en) lv_timer_handler_resume(); } +void _lv_timer_core_deinit(void) +{ + lv_timer_enable(false); + + _lv_ll_clear(timer_ll_p); +} + uint8_t lv_timer_get_idle(void) { return state.idle_last; diff --git a/project/gui/lvgl/src/misc/lv_timer.h b/project/gui/lvgl/src/misc/lv_timer.h index 9fd0a8cc7..4323d7d94 100644 --- a/project/gui/lvgl/src/misc/lv_timer.h +++ b/project/gui/lvgl/src/misc/lv_timer.h @@ -85,6 +85,11 @@ typedef struct { */ void _lv_timer_core_init(void); +/** + * Deinit the lv_timer module + */ +void _lv_timer_core_deinit(void); + //! @cond Doxygen_Suppress /** diff --git a/project/gui/lvgl/src/misc/lv_types.h b/project/gui/lvgl/src/misc/lv_types.h index ecfc12d97..53bdeec07 100644 --- a/project/gui/lvgl/src/misc/lv_types.h +++ b/project/gui/lvgl/src/misc/lv_types.h @@ -37,6 +37,7 @@ extern "C" { #define LV_OS_FREERTOS 2 #define LV_OS_CMSIS_RTOS2 3 #define LV_OS_RTTHREAD 4 +#define LV_OS_WINDOWS 5 #define LV_OS_CUSTOM 255 #define LV_STDLIB_BUILTIN 0 @@ -47,6 +48,7 @@ extern "C" { #define LV_DRAW_SW_ASM_NONE 0 #define LV_DRAW_SW_ASM_NEON 1 #define LV_DRAW_SW_ASM_MVE 2 +#define LV_STDLIB_RTTHREAD 3 #define LV_DRAW_SW_ASM_CUSTOM 255 /********************** diff --git a/project/gui/lvgl/src/osal/Makefile b/project/gui/lvgl/src/osal/Makefile index 12001a007..2b5c4e35f 100644 --- a/project/gui/lvgl/src/osal/Makefile +++ b/project/gui/lvgl/src/osal/Makefile @@ -3,3 +3,4 @@ obj-y += lv_freertos.o obj-y += lv_pthread.o obj-y += lv_os_none.o obj-y += lv_rtthread.o +obj-y += lv_windows.o diff --git a/project/gui/lvgl/src/osal/lv_os.h b/project/gui/lvgl/src/osal/lv_os.h index 2b007af11..9b3254fc2 100644 --- a/project/gui/lvgl/src/osal/lv_os.h +++ b/project/gui/lvgl/src/osal/lv_os.h @@ -32,6 +32,8 @@ extern "C" { #include "lv_cmsis_rtos2.h" #elif LV_USE_OS == LV_OS_RTTHREAD #include "lv_rtthread.h" +#elif LV_USE_OS == LV_OS_WINDOWS +#include "lv_windows.h" #elif LV_USE_OS == LV_OS_CUSTOM #include LV_OS_CUSTOM_INCLUDE #endif diff --git a/project/gui/lvgl/src/osal/lv_pthread.c b/project/gui/lvgl/src/osal/lv_pthread.c index df89259df..68676c946 100644 --- a/project/gui/lvgl/src/osal/lv_pthread.c +++ b/project/gui/lvgl/src/osal/lv_pthread.c @@ -51,8 +51,12 @@ lv_result_t lv_thread_init(lv_thread_t * thread, lv_thread_prio_t prio, void (*c lv_result_t lv_thread_delete(lv_thread_t * thread) { - LV_UNUSED(thread); - /*How?*/ + int ret = pthread_join(thread->thread, NULL); + if(ret != 0) { + LV_LOG_WARN("Error: %d", ret); + return LV_RESULT_INVALID; + } + return LV_RESULT_OK; } diff --git a/project/gui/lvgl/src/osal/lv_rtthread.c b/project/gui/lvgl/src/osal/lv_rtthread.c index 956cbd2a3..39f78d784 100644 --- a/project/gui/lvgl/src/osal/lv_rtthread.c +++ b/project/gui/lvgl/src/osal/lv_rtthread.c @@ -83,7 +83,7 @@ lv_result_t lv_mutex_init(lv_mutex_t * mutex) lv_result_t lv_mutex_lock(lv_mutex_t * mutex) { - rt_err_t ret = rt_mutex_take(mutex->mutex, RT_WAITING_NO); + rt_err_t ret = rt_mutex_take(mutex->mutex, RT_WAITING_FOREVER); if(ret) { LV_LOG_WARN("Error: %d", ret); return LV_RESULT_INVALID; @@ -131,7 +131,7 @@ lv_result_t lv_mutex_delete(lv_mutex_t * mutex) lv_result_t lv_thread_sync_init(lv_thread_sync_t * sync) { - sync->sem = rt_sem_create("sem", RT_IPC_FLAG_PRIO); + sync->sem = rt_sem_create("sem", 0, RT_IPC_FLAG_PRIO); if(sync->sem == RT_NULL) { LV_LOG_WARN("create semaphore failed"); return LV_RESULT_INVALID; diff --git a/project/gui/lvgl/src/osal/lv_windows.c b/project/gui/lvgl/src/osal/lv_windows.c new file mode 100644 index 000000000..0d6a33d66 --- /dev/null +++ b/project/gui/lvgl/src/osal/lv_windows.c @@ -0,0 +1,215 @@ +/** + * @file lv_windows.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_os.h" + +#if LV_USE_OS == LV_OS_WINDOWS + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + void (*callback)(void *); + void * user_data; +} lv_thread_init_data_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static unsigned __stdcall thread_start_routine(void * parameter); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_result_t lv_thread_init( + lv_thread_t * thread, + lv_thread_prio_t prio, + void (*callback)(void *), + size_t stack_size, + void * user_data) +{ + if(!thread) { + return LV_RESULT_INVALID; + } + + static const int prio_map[] = { + [LV_THREAD_PRIO_LOWEST] = THREAD_PRIORITY_LOWEST, + [LV_THREAD_PRIO_LOW] = THREAD_PRIORITY_BELOW_NORMAL, + [LV_THREAD_PRIO_MID] = THREAD_PRIORITY_NORMAL, + [LV_THREAD_PRIO_HIGH] = THREAD_PRIORITY_ABOVE_NORMAL, + [LV_THREAD_PRIO_HIGHEST] = THREAD_PRIORITY_HIGHEST, + }; + + lv_thread_init_data_t * init_data = + (lv_thread_init_data_t *)(malloc( + sizeof(lv_thread_init_data_t))); + if(!init_data) { + return LV_RESULT_INVALID; + } + init_data->callback = callback; + init_data->user_data = user_data; + + /* + Reference: https://learn.microsoft.com/en-us/windows/win32/api + /processthreadsapi/nf-processthreadsapi-createthread + + A thread in an executable that calls the C run-time library (CRT) should + use the _beginthreadex and _endthreadex functions for thread management + rather than CreateThread and ExitThread; this requires the use of the + multithreaded version of the CRT. If a thread created using CreateThread + calls the CRT, the CRT may terminate the process in low-memory conditions. + */ + *thread = (HANDLE)(_beginthreadex( + NULL, + (unsigned)(stack_size), + thread_start_routine, + init_data, + 0, + NULL)); + if(!*thread) { + return LV_RESULT_INVALID; + } + + /* + Try to set the thread priority. (Not mandatory for creating a new thread.) + */ + SetThreadPriority(*thread, prio_map[prio]); + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_delete(lv_thread_t * thread) +{ + lv_result_t result = LV_RESULT_OK; + + if(!TerminateThread(thread, 0)) { + result = LV_RESULT_INVALID; + } + + CloseHandle(thread); + + return result; +} + +lv_result_t lv_mutex_init(lv_mutex_t * mutex) +{ + InitializeCriticalSection(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_lock(lv_mutex_t * mutex) +{ + EnterCriticalSection(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_lock_isr(lv_mutex_t * mutex) +{ + EnterCriticalSection(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_unlock(lv_mutex_t * mutex) +{ + LeaveCriticalSection(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_delete(lv_mutex_t * mutex) +{ + DeleteCriticalSection(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_init(lv_thread_sync_t * sync) +{ + if(!sync) { + return LV_RESULT_INVALID; + } + + InitializeCriticalSection(&sync->cs); + InitializeConditionVariable(&sync->cv); + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_wait(lv_thread_sync_t * sync) +{ + if(!sync) { + return LV_RESULT_INVALID; + } + + EnterCriticalSection(&sync->cs); + while(!sync->v) { + SleepConditionVariableCS(&sync->cv, &sync->cs, INFINITE); + } + sync->v = false; + LeaveCriticalSection(&sync->cs); + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_signal(lv_thread_sync_t * sync) +{ + if(!sync) { + return LV_RESULT_INVALID; + } + + EnterCriticalSection(&sync->cs); + sync->v = true; + WakeConditionVariable(&sync->cv); + LeaveCriticalSection(&sync->cs); + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_delete(lv_thread_sync_t * sync) +{ + if(!sync) { + return LV_RESULT_INVALID; + } + + DeleteCriticalSection(&sync->cs); + + return LV_RESULT_OK; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static unsigned __stdcall thread_start_routine(void * parameter) +{ + lv_thread_init_data_t * init_data = (lv_thread_init_data_t *)(parameter); + if(init_data) { + init_data->callback(init_data->user_data); + free(init_data); + } + + return 0; +} + +#endif /*LV_USE_OS == LV_OS_WINDOWS*/ diff --git a/project/gui/lvgl/src/osal/lv_windows.h b/project/gui/lvgl/src/osal/lv_windows.h new file mode 100644 index 000000000..fd930e872 --- /dev/null +++ b/project/gui/lvgl/src/osal/lv_windows.h @@ -0,0 +1,54 @@ +/** + * @file lv_windows.h + * + */ + +#ifndef LV_WINDOWS_H +#define LV_WINDOWS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#if LV_USE_OS == LV_OS_WINDOWS + +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef HANDLE lv_thread_t; + +typedef CRITICAL_SECTION lv_mutex_t; + +typedef struct { + CRITICAL_SECTION cs; + CONDITION_VARIABLE cv; + bool v; +} lv_thread_sync_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_OS == LV_OS_WINDOWS*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WINDOWS_H*/ diff --git a/project/gui/lvgl/src/others/fragment/README.md b/project/gui/lvgl/src/others/fragment/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/project/gui/lvgl/src/others/fragment/lv_fragment.c b/project/gui/lvgl/src/others/fragment/lv_fragment.c index c36273778..a0bce5d06 100644 --- a/project/gui/lvgl/src/others/fragment/lv_fragment.c +++ b/project/gui/lvgl/src/others/fragment/lv_fragment.c @@ -27,8 +27,7 @@ lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args) LV_ASSERT_NULL(cls); LV_ASSERT_NULL(cls->create_obj_cb); LV_ASSERT(cls->instance_size >= sizeof(lv_fragment_t)); - lv_fragment_t * instance = lv_malloc(cls->instance_size); - lv_memzero(instance, cls->instance_size); + lv_fragment_t * instance = lv_malloc_zeroed(cls->instance_size); instance->cls = cls; instance->child_manager = lv_fragment_manager_create(instance); if(cls->constructor_cb) { diff --git a/project/gui/lvgl/src/others/fragment/lv_fragment.h b/project/gui/lvgl/src/others/fragment/lv_fragment.h index a460c2938..9d71754f1 100644 --- a/project/gui/lvgl/src/others/fragment/lv_fragment.h +++ b/project/gui/lvgl/src/others/fragment/lv_fragment.h @@ -17,7 +17,6 @@ extern "C" { #if LV_USE_FRAGMENT - /********************* * DEFINES *********************/ @@ -265,7 +264,6 @@ lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * ma */ lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager); - /** * Create a fragment instance. * @@ -324,7 +322,6 @@ void lv_fragment_delete_obj(lv_fragment_t * fragment); */ void lv_fragment_recreate_obj(lv_fragment_t * fragment); - /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/others/fragment/lv_fragment_manager.c b/project/gui/lvgl/src/others/fragment/lv_fragment_manager.c index b2d605ed7..73b0c9df2 100644 --- a/project/gui/lvgl/src/others/fragment/lv_fragment_manager.c +++ b/project/gui/lvgl/src/others/fragment/lv_fragment_manager.c @@ -37,7 +37,6 @@ struct _lv_fragment_manager_t { lv_ll_t stack; }; - /********************** * STATIC PROTOTYPES **********************/ @@ -65,8 +64,7 @@ static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * ma lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent) { - lv_fragment_manager_t * instance = lv_malloc(sizeof(lv_fragment_manager_t)); - lv_memzero(instance, sizeof(lv_fragment_manager_t)); + lv_fragment_manager_t * instance = lv_malloc_zeroed(sizeof(lv_fragment_manager_t)); instance->parent = parent; _lv_ll_init(&instance->attached, sizeof(lv_fragment_managed_states_t)); _lv_ll_init(&instance->stack, sizeof(lv_fragment_stack_item_t)); @@ -259,7 +257,6 @@ static void item_delete_fragment(lv_fragment_managed_states_t * item) item->instance = NULL; } - static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container) { diff --git a/project/gui/lvgl/src/others/imgfont/lv_imgfont.c b/project/gui/lvgl/src/others/imgfont/lv_imgfont.c index 1a38a4c0b..401120cc2 100644 --- a/project/gui/lvgl/src/others/imgfont/lv_imgfont.c +++ b/project/gui/lvgl/src/others/imgfont/lv_imgfont.c @@ -51,9 +51,8 @@ lv_font_t * lv_imgfont_create(uint16_t height, lv_imgfont_get_path_cb_t path_cb, "LV_IMGFONT_PATH_MAX_LEN must be greater than sizeof(lv_image_dsc_t)"); size_t size = sizeof(imgfont_dsc_t) + sizeof(lv_font_t); - imgfont_dsc_t * dsc = (imgfont_dsc_t *)lv_malloc(size); + imgfont_dsc_t * dsc = lv_malloc_zeroed(size); if(dsc == NULL) return NULL; - lv_memzero(dsc, size); dsc->font = (lv_font_t *)(((char *)dsc) + sizeof(imgfont_dsc_t)); dsc->path_cb = path_cb; diff --git a/project/gui/lvgl/src/others/monkey/lv_monkey.c b/project/gui/lvgl/src/others/monkey/lv_monkey.c index 23ee48cbc..1259a9d3c 100644 --- a/project/gui/lvgl/src/others/monkey/lv_monkey.c +++ b/project/gui/lvgl/src/others/monkey/lv_monkey.c @@ -64,11 +64,9 @@ void lv_monkey_config_init(lv_monkey_config_t * config) lv_monkey_t * lv_monkey_create(const lv_monkey_config_t * config) { - lv_monkey_t * monkey = lv_malloc(sizeof(lv_monkey_t)); + lv_monkey_t * monkey = lv_malloc_zeroed(sizeof(lv_monkey_t)); LV_ASSERT_MALLOC(monkey); - lv_memzero(monkey, sizeof(lv_monkey_t)); - monkey->config = *config; monkey->timer = lv_timer_create(lv_monkey_timer_cb, monkey->config.period_range.min, monkey); lv_timer_pause(monkey->timer); diff --git a/project/gui/lvgl/src/others/snapshot/lv_snapshot.c b/project/gui/lvgl/src/others/snapshot/lv_snapshot.c index 858d6b4e8..6c687c8ca 100644 --- a/project/gui/lvgl/src/others/snapshot/lv_snapshot.c +++ b/project/gui/lvgl/src/others/snapshot/lv_snapshot.c @@ -100,6 +100,8 @@ lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_ima if(lv_snapshot_buf_size_needed(obj, cf) > buff_size || buff_size == 0) return LV_RESULT_INVALID; + LV_ASSERT_MSG(buf == lv_draw_buf_align(buf, cf), "Buffer is not aligned"); + /*Width and height determine snapshot image size.*/ int32_t w = lv_obj_get_width(obj); int32_t h = lv_obj_get_height(obj); @@ -122,7 +124,7 @@ lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_ima lv_layer_t layer; lv_memzero(&layer, sizeof(layer)); - layer.buf = lv_draw_buf_align(buf, cf); + layer.buf = buf; layer.buf_area.x1 = snapshot_area.x1; layer.buf_area.y1 = snapshot_area.y1; layer.buf_area.x2 = snapshot_area.x1 + w - 1; @@ -152,7 +154,7 @@ lv_image_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_color_format_t cf) uint32_t buff_size = lv_snapshot_buf_size_needed(obj, cf); if(buff_size == 0) return NULL; - void * buf = lv_malloc(buff_size); + void * buf = lv_draw_buf_malloc(buff_size, cf); LV_ASSERT_MALLOC(buf); if(buf == NULL) { return NULL; @@ -161,12 +163,12 @@ lv_image_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_color_format_t cf) lv_image_dsc_t * dsc = lv_malloc(sizeof(lv_image_dsc_t)); LV_ASSERT_MALLOC(buf); if(dsc == NULL) { - lv_free(buf); + lv_draw_buf_free(buf); return NULL; } - if(lv_snapshot_take_to_buf(obj, cf, dsc, buf, buff_size) == LV_RESULT_INVALID) { - lv_free(buf); + if(lv_snapshot_take_to_buf(obj, cf, dsc, buf, buff_size) != LV_RESULT_OK) { + lv_draw_buf_free(buf); lv_free(dsc); return NULL; } @@ -185,7 +187,7 @@ void lv_snapshot_free(lv_image_dsc_t * dsc) return; if(dsc->data) - lv_free((void *)dsc->data); + lv_draw_buf_free((void *)dsc->data); lv_free(dsc); } diff --git a/project/gui/lvgl/src/others/sysmon/lv_sysmon.c b/project/gui/lvgl/src/others/sysmon/lv_sysmon.c index de9404865..17f522526 100644 --- a/project/gui/lvgl/src/others/sysmon/lv_sysmon.c +++ b/project/gui/lvgl/src/others/sysmon/lv_sysmon.c @@ -84,12 +84,10 @@ void _lv_sysmon_builtin_deinit(void) { lv_async_call_cancel(sysmon_backend_init_async_cb, NULL); #if LV_USE_PERF_MONITOR - // lv_subject_deinit(&sysmon_perf->subject); lv_timer_delete(sysmon_perf.timer); #endif } - lv_obj_t * lv_sysmon_create(lv_obj_t * parent) { LV_LOG_INFO("begin"); @@ -102,13 +100,10 @@ lv_obj_t * lv_sysmon_create(lv_obj_t * parent) return label; } - - /********************** * STATIC FUNCTIONS **********************/ - #if LV_USE_PERF_MONITOR static void perf_monitor_disp_event_cb(lv_event_t * e) @@ -147,6 +142,7 @@ static void perf_monitor_disp_event_cb(lv_event_t * e) static void perf_update_timer_cb(lv_timer_t * t) { lv_sysmon_perf_info_t * info = lv_timer_get_user_data(t); + info->calculated.run_cnt++; info->calculated.fps = info->measured.refr_interval_sum ? (1000 * info->measured.refr_cnt / info->measured.refr_interval_sum) : 0; @@ -159,12 +155,19 @@ static void perf_update_timer_cb(lv_timer_t * t) : 0; info->calculated.render_real_avg_time = info->calculated.render_avg_time - info->calculated.flush_avg_time; + info->calculated.cpu_avg_total = ((info->calculated.cpu_avg_total * (info->calculated.run_cnt - 1)) + + info->calculated.cpu) / info->calculated.run_cnt; + info->calculated.fps_avg_total = ((info->calculated.fps_avg_total * (info->calculated.run_cnt - 1)) + + info->calculated.fps) / info->calculated.run_cnt; + lv_subject_set_pointer(&sysmon_perf.subject, info); - uint32_t refr_start = info->measured.refr_start; + lv_sysmon_perf_info_t prev_info = *info; lv_memzero(info, sizeof(lv_sysmon_perf_info_t)); - info->measured.refr_start = refr_start; - + info->measured.refr_start = prev_info.measured.refr_start; + info->calculated.cpu_avg_total = prev_info.calculated.cpu_avg_total; + info->calculated.fps_avg_total = prev_info.calculated.fps_avg_total; + info->calculated.run_cnt = prev_info.calculated.run_cnt; } static void perf_observer_cb(lv_subject_t * subject, lv_observer_t * observer) @@ -242,5 +245,4 @@ static void sysmon_backend_init_async_cb(void * user_data) #endif } - #endif /*LV_USE_SYSMON*/ diff --git a/project/gui/lvgl/src/others/sysmon/lv_sysmon.h b/project/gui/lvgl/src/others/sysmon/lv_sysmon.h index 0420f8e64..9016ddc16 100644 --- a/project/gui/lvgl/src/others/sysmon/lv_sysmon.h +++ b/project/gui/lvgl/src/others/sysmon/lv_sysmon.h @@ -23,6 +23,10 @@ extern "C" { #error "lv_sysmon: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1) " #endif +#if LV_USE_OBSERVER == 0 +#error "lv_observer: lv_observer is required. Enable it in lv_conf.h (LV_USE_OBSERVER 1) " +#endif + /********************* * DEFINES *********************/ @@ -58,6 +62,9 @@ typedef struct { uint32_t render_avg_time; uint32_t flush_avg_time; uint32_t render_real_avg_time; + uint32_t cpu_avg_total; + uint32_t fps_avg_total; + uint32_t run_cnt; } calculated; } lv_sysmon_perf_info_t; diff --git a/project/gui/lvgl/src/stdlib/Makefile b/project/gui/lvgl/src/stdlib/Makefile index c628e6396..db3c2b6af 100644 --- a/project/gui/lvgl/src/stdlib/Makefile +++ b/project/gui/lvgl/src/stdlib/Makefile @@ -1,5 +1,6 @@ obj-y += builtin/ obj-y += clib/ obj-y += micropython/ +obj-y += rtthread/ obj-y += lv_mem.o diff --git a/project/gui/lvgl/src/stdlib/builtin/lv_mem_core_builtin.c b/project/gui/lvgl/src/stdlib/builtin/lv_mem_core_builtin.c index 17e8576f5..40dfa254d 100644 --- a/project/gui/lvgl/src/stdlib/builtin/lv_mem_core_builtin.c +++ b/project/gui/lvgl/src/stdlib/builtin/lv_mem_core_builtin.c @@ -38,7 +38,6 @@ #endif #define state LV_GLOBAL_DEFAULT()->tlsf_state - /********************** * TYPEDEFS **********************/ @@ -82,6 +81,11 @@ void lv_mem_init(void) #else state.tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_ADR, LV_MEM_SIZE); #endif + +#if LV_USE_OS + lv_mutex_init(&state.mutex); +#endif + _lv_ll_init(&state.pool_ll, sizeof(lv_pool_t)); /*Record the first pool*/ @@ -89,10 +93,6 @@ void lv_mem_init(void) LV_ASSERT_MALLOC(pool_p); *pool_p = lv_tlsf_get_pool(state.tlsf); -#if LV_USE_OS - lv_mutex_init(&state.mutex); -#endif - #if LV_MEM_ADD_JUNK LV_LOG_WARN("LV_MEM_ADD_JUNK is enabled which makes LVGL much slower"); #endif @@ -102,7 +102,9 @@ void lv_mem_deinit(void) { _lv_ll_clear(&state.pool_ll); lv_tlsf_destroy(state.tlsf); - lv_mem_init(); +#if LV_USE_OS + lv_mutex_delete(&state.mutex); +#endif } lv_mem_pool_t lv_mem_add_pool(void * mem, size_t bytes) @@ -134,7 +136,6 @@ void lv_mem_remove_pool(lv_mem_pool_t pool) LV_LOG_WARN("invalid pool: %p", pool); } - void * lv_malloc_core(size_t size) { #if LV_USE_OS @@ -186,7 +187,7 @@ void lv_free_core(void * p) void lv_mem_monitor_core(lv_mem_monitor_t * mon_p) { /*Init the data*/ - lv_memset(mon_p, 0, sizeof(lv_mem_monitor_t)); + lv_memzero(mon_p, sizeof(lv_mem_monitor_t)); LV_TRACE_MEM("begin"); lv_pool_t * pool_p; @@ -208,7 +209,6 @@ void lv_mem_monitor_core(lv_mem_monitor_t * mon_p) LV_TRACE_MEM("finished"); } - lv_result_t lv_mem_test_core(void) { #if LV_USE_OS diff --git a/project/gui/lvgl/src/stdlib/lv_mem.c b/project/gui/lvgl/src/stdlib/lv_mem.c index 4bba10713..cdf5b7fef 100644 --- a/project/gui/lvgl/src/stdlib/lv_mem.c +++ b/project/gui/lvgl/src/stdlib/lv_mem.c @@ -42,7 +42,6 @@ void lv_free_core(void * p); void lv_mem_monitor_core(lv_mem_monitor_t * mon_p); lv_result_t lv_mem_test_core(void); - /********************** * STATIC VARIABLES **********************/ @@ -83,8 +82,8 @@ void * lv_malloc(size_t size) LV_LOG_INFO("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d", (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct, (int)mon.free_biggest_size); - return NULL; #endif + return NULL; } #if LV_MEM_ADD_JUNK @@ -92,7 +91,33 @@ void * lv_malloc(size_t size) #endif LV_TRACE_MEM("allocated at %p", alloc); + return alloc; +} + +void * lv_malloc_zeroed(size_t size) +{ + LV_TRACE_MEM("allocating %lu bytes", (unsigned long)size); + if(size == 0) { + LV_TRACE_MEM("using zero_mem"); + return &zero_mem; + } + + void * alloc = lv_malloc_core(size); + if(alloc == NULL) { + LV_LOG_INFO("couldn't allocate memory (%lu bytes)", (unsigned long)size); +#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO + lv_mem_monitor_t mon; + lv_mem_monitor(&mon); + LV_LOG_INFO("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d", + (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct, + (int)mon.free_biggest_size); +#endif + return NULL; + } + lv_memzero(alloc, size); + + LV_TRACE_MEM("allocated at %p", alloc); return alloc; } @@ -107,7 +132,6 @@ void lv_free(void * data) if(data == NULL) return; lv_free_core(data); - } /** @@ -151,7 +175,7 @@ lv_result_t lv_mem_test(void) void lv_mem_monitor(lv_mem_monitor_t * mon_p) { - lv_memset(mon_p, 0, sizeof(lv_mem_monitor_t)); + lv_memzero(mon_p, sizeof(lv_mem_monitor_t)); lv_mem_monitor_core(mon_p); } diff --git a/project/gui/lvgl/src/stdlib/lv_mem.h b/project/gui/lvgl/src/stdlib/lv_mem.h index e15ab7c56..48a176379 100644 --- a/project/gui/lvgl/src/stdlib/lv_mem.h +++ b/project/gui/lvgl/src/stdlib/lv_mem.h @@ -64,12 +64,19 @@ lv_mem_pool_t lv_mem_add_pool(void * mem, size_t bytes); void lv_mem_remove_pool(lv_mem_pool_t pool); /** - * Allocate a memory dynamically - * @param size size of the memory to allocate in bytes - * @return pointer to the allocated memory + * Allocate memory dynamically + * @param size requested size in bytes + * @return pointer to allocated uninitialized memory, or NULL on failure */ void * lv_malloc(size_t size); +/** + * Allocate zeroed memory dynamically + * @param size requested size in bytes + * @return pointer to allocated zeroed memory, or NULL on failure + */ +void * lv_malloc_zeroed(size_t size); + /** * Free an allocated data * @param data pointer to an allocated memory @@ -85,15 +92,12 @@ void lv_free(void * data); */ void * lv_realloc(void * data_p, size_t new_size); - /** * Used internally to execute a plain `malloc` operation * @param size size in bytes to `malloc` */ void * lv_malloc_core(size_t size); - - /** * Used internally to execute a plain `free` operation * @param p memory address to free @@ -113,7 +117,6 @@ void * lv_realloc_core(void * p, size_t new_size); */ void lv_mem_monitor_core(lv_mem_monitor_t * mon_p); - lv_result_t lv_mem_test_core(void); /** diff --git a/project/gui/lvgl/src/stdlib/rtthread/Makefile b/project/gui/lvgl/src/stdlib/rtthread/Makefile new file mode 100644 index 000000000..60700aa22 --- /dev/null +++ b/project/gui/lvgl/src/stdlib/rtthread/Makefile @@ -0,0 +1,3 @@ +obj-y += lv_mem_core_rtthread.o +obj-y += lv_sprintf_rtthread.o +obj-y += lv_string_rtthread.o diff --git a/project/gui/lvgl/src/stdlib/rtthread/lv_mem_core_rtthread.c b/project/gui/lvgl/src/stdlib/rtthread/lv_mem_core_rtthread.c new file mode 100644 index 000000000..8e1f6fd26 --- /dev/null +++ b/project/gui/lvgl/src/stdlib/rtthread/lv_mem_core_rtthread.c @@ -0,0 +1,98 @@ +/** + * @file lv_malloc_core_rtthread.c + */ + +/********************* + * INCLUDES + *********************/ +#include "../lv_mem.h" +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_RTTHREAD +#include "../../stdlib/lv_mem.h" +#include + +#ifndef RT_USING_HEAP + #error "lv_mem_core_rtthread: RT_USING_HEAP is required. Define it in rtconfig.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_mem_init(void) +{ + return; /*Nothing to init*/ +} + +void lv_mem_deinit(void) +{ + return; /*Nothing to deinit*/ +} + +lv_mem_pool_t lv_mem_add_pool(void * mem, size_t bytes) +{ + /*Not supported*/ + LV_UNUSED(mem); + LV_UNUSED(bytes); + return NULL; +} + +void lv_mem_remove_pool(lv_mem_pool_t pool) +{ + /*Not supported*/ + LV_UNUSED(pool); + return; +} + +void * lv_malloc_core(size_t size) +{ + return rt_malloc(size); +} + +void * lv_realloc_core(void * p, size_t new_size) +{ + return rt_realloc(p, new_size); +} + +void lv_free_core(void * p) +{ + rt_free(p); +} + +void lv_mem_monitor_core(lv_mem_monitor_t * mon_p) +{ + /*Not supported*/ + LV_UNUSED(mon_p); + return; +} + +lv_result_t lv_mem_test_core(void) +{ + /*Not supported*/ + return LV_RESULT_OK; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_STDLIB_MALLOC*/ diff --git a/project/gui/lvgl/src/stdlib/rtthread/lv_sprintf_rtthread.c b/project/gui/lvgl/src/stdlib/rtthread/lv_sprintf_rtthread.c new file mode 100644 index 000000000..835269dd3 --- /dev/null +++ b/project/gui/lvgl/src/stdlib/rtthread/lv_sprintf_rtthread.c @@ -0,0 +1,61 @@ +/** + * @file lv_sprintf_rtthread.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" +#if LV_USE_STDLIB_SPRINTF == LV_STDLIB_RTTHREAD +#include +#include +#include "../lv_sprintf.h" + +#if LV_USE_FLOAT == 1 + #warning "lv_sprintf_rtthread: rtthread not support float in sprintf" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +int lv_snprintf(char * buffer, size_t count, const char * format, ...) +{ + va_list va; + va_start(va, format); + const int ret = rt_vsnprintf(buffer, count, format, va); + va_end(va); + return ret; +} + +int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va) +{ + return rt_vsnprintf(buffer, count, format, va); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_STDLIB_SPRINTF*/ diff --git a/project/gui/lvgl/src/stdlib/rtthread/lv_string_rtthread.c b/project/gui/lvgl/src/stdlib/rtthread/lv_string_rtthread.c new file mode 100644 index 000000000..8b23922ab --- /dev/null +++ b/project/gui/lvgl/src/stdlib/rtthread/lv_string_rtthread.c @@ -0,0 +1,85 @@ +/** + * @file lv_string_rtthread.c + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" +#if LV_USE_STDLIB_STRING == LV_STDLIB_RTTHREAD +#include "../lv_string.h" +#include + +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN + #include "../lv_mem.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +LV_ATTRIBUTE_FAST_MEM void * lv_memcpy(void * dst, const void * src, size_t len) +{ + return rt_memcpy(dst, src, len); +} + +LV_ATTRIBUTE_FAST_MEM void lv_memset(void * dst, uint8_t v, size_t len) +{ + rt_memset(dst, v, len); +} + +size_t lv_strlen(const char * str) +{ + return rt_strlen(str); +} + +char * lv_strncpy(char * dst, const char * src, size_t dest_size) +{ + return rt_strncpy(dst, src, dest_size); +} + +char * lv_strcpy(char * dst, const char * src) +{ + return rt_strcpy(dst, src); +} + +char * lv_strdup(const char * src) +{ + /*strdup uses malloc, so use the built in malloc if it's enabled */ +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN + size_t len = lv_strlen(src) + 1; + char * dst = lv_malloc(len); + if(dst == NULL) return NULL; + + lv_memcpy(dst, src, len); /*do memcpy is faster than strncpy when length is known*/ + return dst; +#else + return rt_strdup(src); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_STDLIB_STRING*/ diff --git a/project/gui/lvgl/src/themes/basic/lv_theme_basic.c b/project/gui/lvgl/src/themes/basic/lv_theme_basic.c index a73c22a09..b6a4eb60c 100644 --- a/project/gui/lvgl/src/themes/basic/lv_theme_basic.c +++ b/project/gui/lvgl/src/themes/basic/lv_theme_basic.c @@ -45,7 +45,6 @@ typedef struct { #endif } my_theme_styles_t; - typedef struct _my_theme_t { lv_theme_t base; my_theme_styles_t styles; @@ -82,7 +81,6 @@ static void style_init(struct _my_theme_t * theme) lv_style_set_bg_color(&theme->styles.scr, COLOR_SCR); lv_style_set_text_color(&theme->styles.scr, COLOR_DIM); - style_init_reset(&theme->styles.transp); lv_style_set_bg_opa(&theme->styles.transp, LV_OPA_TRANSP); @@ -135,7 +133,6 @@ static void style_init(struct _my_theme_t * theme) #endif } - /********************** * GLOBAL FUNCTIONS **********************/ @@ -149,7 +146,15 @@ bool lv_theme_basic_is_inited(void) void lv_theme_basic_deinit(void) { - if(theme_def) { + struct _my_theme_t * theme = theme_def; + if(theme) { + if(theme->inited) { + lv_style_t * theme_styles = (lv_style_t *)(&(theme->styles)); + uint32_t i; + for(i = 0; i < sizeof(my_theme_styles_t) / sizeof(lv_style_t); i++) { + lv_style_reset(theme_styles + i); + } + } lv_free(theme_def); theme_def = NULL; } @@ -161,8 +166,7 @@ lv_theme_t * lv_theme_basic_init(lv_display_t * disp) *styles' data if LVGL is used in a binding (e.g. Micropython) *In a general case styles could be in simple `static lv_style_t my_style...` variables*/ if(!lv_theme_basic_is_inited()) { - theme_def = (my_theme_t *)lv_malloc(sizeof(my_theme_t)); - lv_memzero(theme_def, sizeof(my_theme_t)); + theme_def = lv_malloc_zeroed(sizeof(my_theme_t)); } struct _my_theme_t * theme = theme_def; diff --git a/project/gui/lvgl/src/themes/default/lv_theme_default.c b/project/gui/lvgl/src/themes/default/lv_theme_default.c index 723f37dd4..51f44aaa0 100644 --- a/project/gui/lvgl/src/themes/default/lv_theme_default.c +++ b/project/gui/lvgl/src/themes/default/lv_theme_default.c @@ -81,7 +81,6 @@ typedef struct { /*Parts*/ lv_style_t knob; - lv_style_t indic; #if LV_USE_ARC lv_style_t arc_indic; @@ -138,7 +137,7 @@ typedef struct { #endif #if LV_USE_LIST - lv_style_t list_bg, list_btn, list_item_grow, list_label; + lv_style_t list_bg, list_btn, list_item_grow; #endif #if LV_USE_TABVIEW @@ -179,7 +178,6 @@ typedef struct _my_theme_t { #endif } my_theme_t; - /********************** * STATIC PROTOTYPES **********************/ @@ -198,7 +196,6 @@ static void style_init_reset(lv_style_t * style); * STATIC FUNCTIONS **********************/ - static lv_color_t dark_color_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t c, lv_opa_t opa) { LV_UNUSED(f); @@ -634,7 +631,6 @@ static void style_init(struct _my_theme_t * theme) lv_style_set_transform_width(&theme->styles.list_item_grow, PAD_DEF); #endif - #if LV_USE_LED style_init_reset(&theme->styles.led); lv_style_set_bg_opa(&theme->styles.led, LV_OPA_COVER); @@ -667,8 +663,7 @@ lv_theme_t * lv_theme_default_init(lv_display_t * disp, lv_color_t color_primary *In a general case styles could be in a simple `static lv_style_t my_style...` variables*/ if(!lv_theme_default_is_inited()) { - theme_def = (my_theme_t *)lv_malloc(sizeof(my_theme_t)); - lv_memzero(theme_def, sizeof(my_theme_t)); + theme_def = lv_malloc_zeroed(sizeof(my_theme_t)); } struct _my_theme_t * theme = theme_def; @@ -715,7 +710,16 @@ lv_theme_t * lv_theme_default_init(lv_display_t * disp, lv_color_t color_primary void lv_theme_default_deinit(void) { - if(theme_def) { + struct _my_theme_t * theme = theme_def; + if(theme) { + if(theme->inited) { + lv_style_t * theme_styles = (lv_style_t *)(&(theme->styles)); + uint32_t i; + for(i = 0; i < sizeof(my_theme_styles_t) / sizeof(lv_style_t); i++) { + lv_style_reset(theme_styles + i); + } + + } lv_free(theme_def); theme_def = NULL; } @@ -737,7 +741,6 @@ bool lv_theme_default_is_inited(void) return theme->inited; } - static void theme_apply(lv_theme_t * th, lv_obj_t * obj) { LV_UNUSED(th); @@ -784,7 +787,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) } #endif - #if LV_USE_CALENDAR if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_calendar_class)) { /*No style*/ @@ -1023,7 +1025,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) } #endif - #if LV_USE_SPINNER else if(lv_obj_check_type(obj, &lv_spinner_class)) { lv_obj_add_style(obj, &theme->styles.arc_indic, 0); diff --git a/project/gui/lvgl/src/themes/mono/lv_theme_mono.c b/project/gui/lvgl/src/themes/mono/lv_theme_mono.c index 0e0f803e3..ca9a21155 100644 --- a/project/gui/lvgl/src/themes/mono/lv_theme_mono.c +++ b/project/gui/lvgl/src/themes/mono/lv_theme_mono.c @@ -35,7 +35,6 @@ typedef struct { lv_style_t scr; lv_style_t card; lv_style_t scrollbar; - lv_style_t btn; lv_style_t pr; lv_style_t inv; lv_style_t disabled; @@ -186,7 +185,15 @@ bool lv_theme_mono_is_inited(void) void lv_theme_mono_deinit(void) { - if(theme_def) { + struct _my_theme_t * theme = theme_def; + if(theme) { + if(theme->inited) { + lv_style_t * theme_styles = (lv_style_t *)(&(theme->styles)); + uint32_t i; + for(i = 0; i < sizeof(my_theme_styles_t) / sizeof(lv_style_t); i++) { + lv_style_reset(theme_styles + i); + } + } lv_free(theme_def); theme_def = NULL; } @@ -198,8 +205,7 @@ lv_theme_t * lv_theme_mono_init(lv_display_t * disp, bool dark_bg, const lv_font *styles' data if LVGL is used in a binding (e.g. Micropython) *In a general case styles could be in simple `static lv_style_t my_style...` variables*/ if(!lv_theme_mono_is_inited()) { - theme_def = (my_theme_t *)lv_malloc(sizeof(my_theme_t)); - lv_memzero(theme_def, sizeof(my_theme_t)); + theme_def = lv_malloc_zeroed(sizeof(my_theme_t)); } struct _my_theme_t * theme = theme_def; diff --git a/project/gui/lvgl/src/widgets/animimage/lv_animimage.h b/project/gui/lvgl/src/widgets/animimage/lv_animimage.h index 99bf2632c..c186ed897 100644 --- a/project/gui/lvgl/src/widgets/animimage/lv_animimage.h +++ b/project/gui/lvgl/src/widgets/animimage/lv_animimage.h @@ -30,7 +30,7 @@ extern "C" { * TYPEDEFS **********************/ -extern const lv_obj_class_t lv_animimg_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_animimg_class; /*Data of image*/ typedef struct { @@ -41,20 +41,17 @@ typedef struct { int8_t pic_count; } lv_animimg_t; - /*Image parts*/ enum _lv_animimg_part_t { LV_ANIM_IMAGE_PART_MAIN, }; - #ifdef DOXYGEN typedef _lv_animimg_part_t lv_animimg_part_t; #else typedef uint8_t lv_animimg_part_t; #endif - /********************** * GLOBAL PROTOTYPES **********************/ diff --git a/project/gui/lvgl/src/widgets/arc/lv_arc.h b/project/gui/lvgl/src/widgets/arc/lv_arc.h index f13e920ff..9948f36ab 100644 --- a/project/gui/lvgl/src/widgets/arc/lv_arc.h +++ b/project/gui/lvgl/src/widgets/arc/lv_arc.h @@ -38,7 +38,6 @@ typedef _lv_arc_mode_t lv_arc_mode_t; typedef uint8_t lv_arc_mode_t; #endif /*DOXYGEN*/ - typedef struct { lv_obj_t obj; uint32_t rotation; @@ -59,7 +58,7 @@ typedef struct { int16_t knob_offset; /*knob offset from the main arc*/ } lv_arc_t; -extern const lv_obj_class_t lv_arc_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_arc_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/bar/lv_bar.h b/project/gui/lvgl/src/widgets/bar/lv_bar.h index d40437303..513cefd5d 100644 --- a/project/gui/lvgl/src/widgets/bar/lv_bar.h +++ b/project/gui/lvgl/src/widgets/bar/lv_bar.h @@ -40,7 +40,6 @@ typedef _lv_bar_mode_t lv_bar_mode_t; typedef uint8_t lv_bar_mode_t; #endif /*DOXYGEN*/ - typedef struct { lv_obj_t * bar; int32_t anim_start; @@ -61,8 +60,7 @@ typedef struct { lv_bar_mode_t mode : 2; /**< Type of bar*/ } lv_bar_t; -extern const lv_obj_class_t lv_bar_class; - +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_bar_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/button/lv_button.h b/project/gui/lvgl/src/widgets/button/lv_button.h index 669945d06..0a982b1ec 100644 --- a/project/gui/lvgl/src/widgets/button/lv_button.h +++ b/project/gui/lvgl/src/widgets/button/lv_button.h @@ -30,7 +30,7 @@ typedef struct { lv_obj_t obj; } lv_button_t; -extern const lv_obj_class_t lv_button_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_button_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c b/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c index 0f6cdca59..345ca42a7 100644 --- a/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c +++ b/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c @@ -46,7 +46,6 @@ static bool button_is_inactive(lv_buttonmatrix_ctrl_t ctrl_bits); static bool button_is_click_trig(lv_buttonmatrix_ctrl_t ctrl_bits); static bool button_is_popover(lv_buttonmatrix_ctrl_t ctrl_bits); static bool button_is_checkable(lv_buttonmatrix_ctrl_t ctrl_bits); -static bool button_is_recolor(lv_buttonmatrix_ctrl_t ctrl_bits); static bool button_get_checked(lv_buttonmatrix_ctrl_t ctrl_bits); static uint32_t get_button_from_point(lv_obj_t * obj, lv_point_t * p); static void allocate_button_areas_and_controls(const lv_obj_t * obj, const char ** map); @@ -478,7 +477,6 @@ static void lv_buttonmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e if(btnm->one_check) make_one_button_checked(obj, btnm->btn_id_sel); } - if((button_is_click_trig(btnm->ctrl_bits[btnm->btn_id_sel]) == true || button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == true) && button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel]) == false && @@ -750,10 +748,6 @@ static void draw_main(lv_event_t * e) draw_rect_dsc_act.base.id1 = btn_i; - bool recolor = button_is_recolor(btnm->ctrl_bits[btn_i]); - if(recolor) draw_label_dsc_act.flag |= LV_TEXT_FLAG_RECOLOR; - else draw_label_dsc_act.flag &= ~LV_TEXT_FLAG_RECOLOR; - /*Remove borders on the edges if `LV_BORDER_SIDE_INTERNAL`*/ if(draw_rect_dsc_act.border_side & LV_BORDER_SIDE_INTERNAL) { draw_rect_dsc_act.border_side = LV_BORDER_SIDE_FULL; @@ -907,10 +901,6 @@ static bool button_get_checked(lv_buttonmatrix_ctrl_t ctrl_bits) return ctrl_bits & LV_BUTTONMATRIX_CTRL_CHECKED; } -static bool button_is_recolor(lv_buttonmatrix_ctrl_t ctrl_bits) -{ - return ctrl_bits & LV_BUTTONMATRIX_CTRL_RECOLOR; -} /** * Gives the button id of a button under a given point * @param obj pointer to a button matrix object diff --git a/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h b/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h index 552fd80eb..616e40d5f 100644 --- a/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h +++ b/project/gui/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h @@ -40,9 +40,9 @@ enum _lv_buttonmatrix_ctrl_t { LV_BUTTONMATRIX_CTRL_CHECKED = 0x0100, /**< Button is currently toggled (e.g. checked).*/ LV_BUTTONMATRIX_CTRL_CLICK_TRIG = 0x0200, /**< 1: Send LV_EVENT_VALUE_CHANGE on CLICK, 0: Send LV_EVENT_VALUE_CHANGE on PRESS*/ LV_BUTTONMATRIX_CTRL_POPOVER = 0x0400, /**< Show a popover when pressing this key*/ - LV_BUTTONMATRIX_CTRL_RECOLOR = 0x0800, /**< Enable text recoloring with `#color`*/ - _LV_BUTTONMATRIX_CTRL_RESERVED_1 = 0x1000, /**< Reserved for later use*/ - _LV_BUTTONMATRIX_CTRL_RESERVED_2 = 0x2000, /**< Reserved for later use*/ + _LV_BUTTONMATRIX_CTRL_RESERVED_1 = 0x0800, /**< Reserved for later use*/ + _LV_BUTTONMATRIX_CTRL_RESERVED_2 = 0x1000, /**< Reserved for later use*/ + _LV_BUTTONMATRIX_CTRL_RESERVED_3 = 0x2000, /**< Reserved for later use*/ LV_BUTTONMATRIX_CTRL_CUSTOM_1 = 0x4000, /**< Custom free to use flag*/ LV_BUTTONMATRIX_CTRL_CUSTOM_2 = 0x8000, /**< Custom free to use flag*/ }; @@ -53,7 +53,6 @@ typedef _lv_buttonmatrix_ctrl_t lv_buttonmatrix_ctrl_t; typedef uint32_t lv_buttonmatrix_ctrl_t; #endif /*DOXYGEN*/ - typedef bool (*lv_buttonmatrix_button_draw_cb_t)(lv_obj_t * btnm, uint32_t btn_id, const lv_area_t * draw_area, const lv_area_t * clip_area); @@ -69,8 +68,7 @@ typedef struct { uint32_t one_check : 1; /*Single button toggled at once*/ } lv_buttonmatrix_t; -extern const lv_obj_class_t lv_buttonmatrix_class; - +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_buttonmatrix_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/calendar/lv_calendar.c b/project/gui/lvgl/src/widgets/calendar/lv_calendar.c index 7d7bc1863..9a601b542 100644 --- a/project/gui/lvgl/src/widgets/calendar/lv_calendar.c +++ b/project/gui/lvgl/src/widgets/calendar/lv_calendar.c @@ -234,7 +234,6 @@ lv_result_t lv_calendar_get_pressed_date(const lv_obj_t * obj, lv_calendar_date_ return LV_RESULT_OK; } - /********************** * STATIC FUNCTIONS **********************/ diff --git a/project/gui/lvgl/src/widgets/calendar/lv_calendar.h b/project/gui/lvgl/src/widgets/calendar/lv_calendar.h index 67827cc09..03fc02317 100644 --- a/project/gui/lvgl/src/widgets/calendar/lv_calendar.h +++ b/project/gui/lvgl/src/widgets/calendar/lv_calendar.h @@ -47,7 +47,7 @@ typedef struct { char nums [7 * 6][4]; } lv_calendar_t; -extern const lv_obj_class_t lv_calendar_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_calendar_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_arrow.h b/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_arrow.h index 787e2b7db..a04b355cc 100644 --- a/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_arrow.h +++ b/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_arrow.h @@ -23,7 +23,7 @@ extern "C" { /********************** * TYPEDEFS **********************/ -extern const lv_obj_class_t lv_calendar_header_arrow_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_calendar_header_arrow_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_dropdown.h b/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_dropdown.h index 4118f37a1..92f1f26bf 100644 --- a/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_dropdown.h +++ b/project/gui/lvgl/src/widgets/calendar/lv_calendar_header_dropdown.h @@ -23,7 +23,7 @@ extern "C" { /********************** * TYPEDEFS **********************/ -extern const lv_obj_class_t lv_calendar_header_dropdown_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_calendar_header_dropdown_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/canvas/lv_canvas.c b/project/gui/lvgl/src/widgets/canvas/lv_canvas.c index 5a0ce4354..1ab521ad7 100644 --- a/project/gui/lvgl/src/widgets/canvas/lv_canvas.c +++ b/project/gui/lvgl/src/widgets/canvas/lv_canvas.c @@ -288,8 +288,6 @@ void lv_canvas_fill_bg(lv_obj_t * obj, lv_color_t color, lv_opa_t opa) lv_obj_invalidate(obj); } - - void lv_canvas_init_layer(lv_obj_t * canvas, lv_layer_t * layer) { LV_ASSERT_NULL(canvas); @@ -303,9 +301,9 @@ void lv_canvas_init_layer(lv_obj_t * canvas, lv_layer_t * layer) layer->color_format = dsc->header.cf; layer->buf_area = canvas_area; layer->clip_area = canvas_area; + layer->buf_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), layer->color_format); } - void lv_canvas_finish_layer(lv_obj_t * canvas, lv_layer_t * layer) { while(layer->draw_task_head) { @@ -349,5 +347,4 @@ static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) lv_cache_unlock(); } - #endif diff --git a/project/gui/lvgl/src/widgets/canvas/lv_canvas.h b/project/gui/lvgl/src/widgets/canvas/lv_canvas.h index c63bf98b4..fa7b16fbc 100644 --- a/project/gui/lvgl/src/widgets/canvas/lv_canvas.h +++ b/project/gui/lvgl/src/widgets/canvas/lv_canvas.h @@ -27,7 +27,7 @@ extern "C" { /********************** * TYPEDEFS **********************/ -extern const lv_obj_class_t lv_canvas_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_canvas_class; /*Data of canvas*/ typedef struct { @@ -82,7 +82,6 @@ void lv_canvas_set_palette(lv_obj_t * canvas, uint8_t id, lv_color32_t c); * Getter functions *====================*/ - lv_color32_t lv_canvas_get_px(lv_obj_t * obj, int32_t x, int32_t y); /** diff --git a/project/gui/lvgl/src/widgets/chart/lv_chart.h b/project/gui/lvgl/src/widgets/chart/lv_chart.h index 032f21de1..c3628f9e3 100644 --- a/project/gui/lvgl/src/widgets/chart/lv_chart.h +++ b/project/gui/lvgl/src/widgets/chart/lv_chart.h @@ -22,11 +22,7 @@ extern "C" { *********************/ /**Default value of points. Can be used to not draw a point*/ -#if LV_USE_LARGE_COORD -#define LV_CHART_POINT_NONE (INT32_MAX) -#else -#define LV_CHART_POINT_NONE (INT16_MAX) -#endif +#define LV_CHART_POINT_NONE (INT32_MAX) LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE); /********************** @@ -49,7 +45,6 @@ typedef _lv_chart_type_t lv_chart_type_t; typedef uint8_t lv_chart_type_t; #endif /*DOXYGEN*/ - /** * Chart update mode for `lv_chart_set_next` */ @@ -64,7 +59,6 @@ typedef _lv_chart_update_mode_t lv_chart_update_mode_t; typedef uint8_t lv_chart_update_mode_t; #endif /*DOXYGEN*/ - /** * Enumeration of the axis' */ @@ -82,7 +76,6 @@ typedef _lv_chart_axis_t lv_chart_axis_t; typedef uint8_t lv_chart_axis_t; #endif /*DOXYGEN*/ - /** * Descriptor a chart series */ @@ -107,7 +100,6 @@ typedef struct { uint32_t pos_set: 1; /*1: pos is set; 0: point_id is set*/ } lv_chart_cursor_t; - typedef struct { lv_obj_t obj; lv_ll_t series_ll; /**< Linked list for the series (stores lv_chart_series_t)*/ @@ -124,7 +116,7 @@ typedef struct { lv_chart_update_mode_t update_mode : 1; } lv_chart_t; -extern const lv_obj_class_t lv_chart_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_chart_class; /********************** * GLOBAL PROTOTYPES @@ -264,8 +256,6 @@ void lv_chart_set_x_start_point(lv_obj_t * obj, lv_chart_series_t * ser, uint32_ */ lv_chart_series_t * lv_chart_get_series_next(const lv_obj_t * chart, const lv_chart_series_t * ser); - - /*===================== * Cursor *====================*/ diff --git a/project/gui/lvgl/src/widgets/checkbox/lv_checkbox.h b/project/gui/lvgl/src/widgets/checkbox/lv_checkbox.h index 363f7580f..62b156938 100644 --- a/project/gui/lvgl/src/widgets/checkbox/lv_checkbox.h +++ b/project/gui/lvgl/src/widgets/checkbox/lv_checkbox.h @@ -32,7 +32,7 @@ typedef struct { uint32_t static_txt : 1; } lv_checkbox_t; -extern const lv_obj_class_t lv_checkbox_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_checkbox_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/image/lv_image.c b/project/gui/lvgl/src/widgets/image/lv_image.c index 8880f4024..b0f951452 100644 --- a/project/gui/lvgl/src/widgets/image/lv_image.c +++ b/project/gui/lvgl/src/widgets/image/lv_image.c @@ -28,6 +28,7 @@ static void lv_image_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); static void lv_image_event(const lv_obj_class_t * class_p, lv_event_t * e); static void draw_image(lv_event_t * e); static void scale_update(lv_obj_t * obj, int32_t scale_x, int32_t scale_y); +static void update_align(lv_obj_t * obj); #if LV_USE_OBJ_PROPERTY static const lv_property_ops_t properties[] = { @@ -67,9 +68,9 @@ static const lv_property_ops_t properties[] = { .getter = lv_image_get_antialias, }, { - .id = LV_PROPERTY_IMAGE_SIZE_MODE, - .setter = lv_image_set_size_mode, - .getter = lv_image_get_size_mode, + .id = LV_PROPERTY_IMAGE_ALIGN, + .setter = lv_image_set_align, + .getter = lv_image_get_align, }, }; #endif @@ -204,8 +205,12 @@ void lv_image_set_src(lv_obj_t * obj, const void * src) lv_obj_refresh_self_size(obj); + update_align(obj); + /*Provide enough room for the rotated corners*/ - if(img->rotation || img->scale_x != LV_SCALE_NONE || img->scale_y != LV_SCALE_NONE) lv_obj_refresh_ext_draw_size(obj); + if(img->rotation || img->scale_x != LV_SCALE_NONE || img->scale_y != LV_SCALE_NONE) { + lv_obj_refresh_ext_draw_size(obj); + } lv_obj_invalidate(obj); } @@ -232,18 +237,18 @@ void lv_image_set_offset_y(lv_obj_t * obj, int32_t y) void lv_image_set_rotation(lv_obj_t * obj, int32_t angle) { - while(angle >= 3600) angle -= 3600; - while(angle < 0) angle += 3600; lv_image_t * img = (lv_image_t *)obj; - if((uint32_t)angle == img->rotation) return; - - if(img->obj_size_mode == LV_IMAGE_SIZE_MODE_REAL) { - img->rotation = angle; - lv_obj_invalidate_area(obj, &obj->coords); - return; + if(img->align > _LV_IMAGE_ALIGN_AUTO_TRANSFORM) { + angle = 0; + } + else { + while(angle >= 3600) angle -= 3600; + while(angle < 0) angle += 3600; } + if((uint32_t)angle == img->rotation) return; + lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/ int32_t w = lv_obj_get_width(obj); int32_t h = lv_obj_get_height(obj); @@ -277,15 +282,13 @@ void lv_image_set_rotation(lv_obj_t * obj, int32_t angle) void lv_image_set_pivot(lv_obj_t * obj, int32_t x, int32_t y) { lv_image_t * img = (lv_image_t *)obj; - if(img->pivot.x == x && img->pivot.y == y) return; - - if(img->obj_size_mode == LV_IMAGE_SIZE_MODE_REAL) { - img->pivot.x = x; - img->pivot.y = y; - lv_obj_invalidate_area(obj, &obj->coords); - return; + if(img->align > _LV_IMAGE_ALIGN_AUTO_TRANSFORM) { + x = 0; + y = 0; } + if(img->pivot.x == x && img->pivot.y == y) return; + lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/ int32_t w = lv_obj_get_width(obj); int32_t h = lv_obj_get_height(obj); @@ -321,6 +324,10 @@ void lv_image_set_pivot(lv_obj_t * obj, int32_t x, int32_t y) void lv_image_set_scale(lv_obj_t * obj, uint32_t zoom) { lv_image_t * img = (lv_image_t *)obj; + + /*If scale is set internally, do no overwrite it*/ + if(img->align > _LV_IMAGE_ALIGN_AUTO_TRANSFORM) return; + if(zoom == img->scale_x && zoom == img->scale_y) return; if(zoom == 0) zoom = 1; @@ -331,6 +338,10 @@ void lv_image_set_scale(lv_obj_t * obj, uint32_t zoom) void lv_image_set_scale_x(lv_obj_t * obj, uint32_t zoom) { lv_image_t * img = (lv_image_t *)obj; + + /*If scale is set internally, do no overwrite it*/ + if(img->align > _LV_IMAGE_ALIGN_AUTO_TRANSFORM) return; + if(zoom == img->scale_x) return; if(zoom == 0) zoom = 1; @@ -341,6 +352,10 @@ void lv_image_set_scale_x(lv_obj_t * obj, uint32_t zoom) void lv_image_set_scale_y(lv_obj_t * obj, uint32_t zoom) { lv_image_t * img = (lv_image_t *)obj; + + /*If scale is set internally, do no overwrite it*/ + if(img->align > _LV_IMAGE_ALIGN_AUTO_TRANSFORM) return; + if(zoom == img->scale_y) return; if(zoom == 0) zoom = 1; @@ -357,13 +372,16 @@ void lv_image_set_antialias(lv_obj_t * obj, bool antialias) lv_obj_invalidate(obj); } -void lv_image_set_size_mode(lv_obj_t * obj, lv_image_size_mode_t mode) +void lv_image_set_align(lv_obj_t * obj, lv_image_align_t align) { LV_ASSERT_OBJ(obj, MY_CLASS); lv_image_t * img = (lv_image_t *)obj; - if(mode == img->obj_size_mode) return; + if(align == img->align) return; + + img->align = align; + + update_align(obj); - img->obj_size_mode = mode; lv_obj_invalidate(obj); } @@ -453,11 +471,11 @@ bool lv_image_get_antialias(lv_obj_t * obj) return img->antialias ? true : false; } -lv_image_size_mode_t lv_image_get_size_mode(lv_obj_t * obj) +lv_image_align_t lv_image_get_align(lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); lv_image_t * img = (lv_image_t *)obj; - return img->obj_size_mode; + return img->align; } /********************** @@ -484,7 +502,7 @@ static void lv_image_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) img->offset.y = 0; img->pivot.x = LV_PCT(50); /*Default pivot to image center*/ img->pivot.y = LV_PCT(50); - img->obj_size_mode = LV_IMAGE_SIZE_MODE_VIRTUAL; + img->align = LV_IMAGE_ALIGN_CENTER; lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE); lv_obj_add_flag(obj, LV_OBJ_FLAG_ADV_HITTEST); @@ -503,35 +521,15 @@ static void lv_image_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) } } -static lv_point_t lv_image_get_transformed_size(lv_obj_t * obj) -{ - lv_image_t * img = (lv_image_t *)obj; - - - lv_area_t area_transform; - - lv_point_t pivot_px; - lv_image_get_pivot(obj, &pivot_px); - _lv_image_buf_get_transformed_area(&area_transform, img->w, img->h, - img->rotation, img->scale_x, img->scale_y, &pivot_px); - - return (lv_point_t) { - lv_area_get_width(&area_transform), lv_area_get_height(&area_transform) - }; -} - static void lv_image_event(const lv_obj_class_t * class_p, lv_event_t * e) { LV_UNUSED(class_p); lv_event_code_t code = lv_event_get_code(e); - /*Ancestor events will be called during drawing*/ - if(code != LV_EVENT_DRAW_MAIN && code != LV_EVENT_DRAW_POST) { - /*Call the ancestor's event handler*/ - lv_result_t res = lv_obj_event_base(MY_CLASS, e); - if(res != LV_RESULT_OK) return; - } + /*Call the ancestor's event handler*/ + lv_result_t res = lv_obj_event_base(MY_CLASS, e); + if(res != LV_RESULT_OK) return; lv_obj_t * obj = lv_event_get_target(e); lv_image_t * img = (lv_image_t *)obj; @@ -592,13 +590,8 @@ static void lv_image_event(const lv_obj_class_t * class_p, lv_event_t * e) } else if(code == LV_EVENT_GET_SELF_SIZE) { lv_point_t * p = lv_event_get_param(e); - if(img->obj_size_mode == LV_IMAGE_SIZE_MODE_REAL) { - *p = lv_image_get_transformed_size(obj); - } - else { - p->x = img->w; - p->y = img->h; - } + p->x = img->w; + p->y = img->h; } else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) { draw_image(e); @@ -659,132 +652,61 @@ static void draw_image(lv_event_t * e) } } } - else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST) { - - int32_t obj_w = lv_obj_get_width(obj); - int32_t obj_h = lv_obj_get_height(obj); - - int32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); - int32_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width; - int32_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width; - int32_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width; - int32_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width; - - lv_point_t bg_pivot; - lv_point_t pivot_px; - lv_image_get_pivot(obj, &pivot_px); - - bg_pivot.x = pivot_px.x + pleft; - bg_pivot.y = pivot_px.y + ptop; - lv_area_t bg_coords; - - if(img->obj_size_mode == LV_IMAGE_SIZE_MODE_REAL) { - /*Object size equals to transformed image size*/ - lv_obj_get_coords(obj, &bg_coords); - } - else { - _lv_image_buf_get_transformed_area(&bg_coords, obj_w, obj_h, - img->rotation, img->scale_x, img->scale_y, &bg_pivot); - - /*Modify the coordinates to draw the background for the rotated and scaled coordinates*/ - bg_coords.x1 += obj->coords.x1; - bg_coords.y1 += obj->coords.y1; - bg_coords.x2 += obj->coords.x1; - bg_coords.y2 += obj->coords.y1; - } - - lv_area_t ori_coords; - lv_area_copy(&ori_coords, &obj->coords); - lv_area_copy(&obj->coords, &bg_coords); + else if(code == LV_EVENT_DRAW_MAIN) { - lv_result_t res = lv_obj_event_base(MY_CLASS, e); - if(res != LV_RESULT_OK) return; + if(img->h == 0 || img->w == 0) return; + if(img->scale_x == 0 || img->scale_y == 0) return; - lv_area_copy(&obj->coords, &ori_coords); + lv_layer_t * layer = lv_event_get_layer(e); - if(code == LV_EVENT_DRAW_MAIN) { - if(img->h == 0 || img->w == 0) return; - if(img->scale_x == 0 || img->scale_y == 0) return; + if(img->src_type == LV_IMAGE_SRC_FILE || img->src_type == LV_IMAGE_SRC_VARIABLE) { + lv_draw_image_dsc_t draw_dsc; + lv_draw_image_dsc_init(&draw_dsc); + lv_obj_init_draw_image_dsc(obj, LV_PART_MAIN, &draw_dsc); - lv_layer_t * layer = lv_event_get_layer(e); + lv_area_t clip_area_ori = layer->clip_area; - lv_area_t img_max_area; - lv_area_copy(&img_max_area, &obj->coords); + lv_image_get_pivot(obj, &draw_dsc.pivot); + draw_dsc.scale_x = img->scale_x; + draw_dsc.scale_y = img->scale_y; + draw_dsc.rotation = img->rotation; + draw_dsc.antialias = img->antialias; + draw_dsc.src = img->src; - lv_point_t img_size_final = lv_image_get_transformed_size(obj); - - if(img->obj_size_mode == LV_IMAGE_SIZE_MODE_REAL) { - img_max_area.x1 -= ((img->w - img_size_final.x) + 1) / 2; - img_max_area.x2 -= ((img->w - img_size_final.x) + 1) / 2; - img_max_area.y1 -= ((img->h - img_size_final.y) + 1) / 2; - img_max_area.y2 -= ((img->h - img_size_final.y) + 1) / 2; + lv_area_t img_area = {obj->coords.x1, obj->coords.y1, + obj->coords.x1 + img->w - 1, obj->coords.y1 + img->h - 1 + }; + if(img->align < _LV_IMAGE_ALIGN_AUTO_TRANSFORM) { + lv_area_align(&obj->coords, &img_area, img->align, img->offset.x, img->offset.y); } - else { - img_max_area.x2 = img_max_area.x1 + lv_area_get_width(&bg_coords) - 1; - img_max_area.y2 = img_max_area.y1 + lv_area_get_height(&bg_coords) - 1; + else if(img->align == LV_IMAGE_ALIGN_TILE) { + _lv_area_intersect(&layer->clip_area, &layer->clip_area, &obj->coords); + lv_area_move(&img_area, img->offset.x, img->offset.y); + + lv_area_move(&img_area, + ((layer->clip_area.x1 - img_area.x1 - (img->w - 1)) / img->w) * img->w, + ((layer->clip_area.y1 - img_area.y1 - (img->h - 1)) / img->h) * img->h); + draw_dsc.tile = 1; } - img_max_area.x1 += pleft; - img_max_area.y1 += ptop; - img_max_area.x2 -= pright; - img_max_area.y2 -= pbottom; - - if(img->src_type == LV_IMAGE_SRC_FILE || img->src_type == LV_IMAGE_SRC_VARIABLE) { - lv_draw_image_dsc_t img_dsc; - lv_draw_image_dsc_init(&img_dsc); - lv_obj_init_draw_image_dsc(obj, LV_PART_MAIN, &img_dsc); - - img_dsc.scale_x = img->scale_x; - img_dsc.scale_y = img->scale_y; - img_dsc.rotation = img->rotation; - img_dsc.pivot.x = pivot_px.x; - img_dsc.pivot.y = pivot_px.y; - img_dsc.antialias = img->antialias; - img_dsc.src = img->src; - - lv_area_t img_clip_area; - img_clip_area.x1 = bg_coords.x1 + pleft; - img_clip_area.y1 = bg_coords.y1 + ptop; - img_clip_area.x2 = bg_coords.x2 - pright; - img_clip_area.y2 = bg_coords.y2 - pbottom; - const lv_area_t clip_area_ori = layer->clip_area; - - if(!_lv_area_intersect(&img_clip_area, &layer->clip_area, &img_clip_area)) return; - layer->clip_area = img_clip_area; - - lv_area_t coords_tmp; - int32_t offset_x = img->offset.x % img->w; - int32_t offset_y = img->offset.y % img->h; - coords_tmp.y1 = img_max_area.y1 + offset_y; - if(coords_tmp.y1 > img_max_area.y1) coords_tmp.y1 -= img->h; - coords_tmp.y2 = coords_tmp.y1 + img->h - 1; - - for(; coords_tmp.y1 < img_max_area.y2; coords_tmp.y1 += img_size_final.y, coords_tmp.y2 += img_size_final.y) { - coords_tmp.x1 = img_max_area.x1 + offset_x; - if(coords_tmp.x1 > img_max_area.x1) coords_tmp.x1 -= img->w; - coords_tmp.x2 = coords_tmp.x1 + img->w - 1; - - for(; coords_tmp.x1 < img_max_area.x2; coords_tmp.x1 += img_size_final.x, coords_tmp.x2 += img_size_final.x) { - lv_draw_image(layer, &img_dsc, &coords_tmp); - } - } - layer->clip_area = clip_area_ori; - } - else if(img->src_type == LV_IMAGE_SRC_SYMBOL) { - lv_draw_label_dsc_t label_dsc; - lv_draw_label_dsc_init(&label_dsc); - lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_dsc); - label_dsc.text = img->src; - lv_draw_label(layer, &label_dsc, &obj->coords); - } - else if(img->src == NULL) { - /*Do not need to draw image when src is NULL*/ - LV_LOG_WARN("image source is NULL"); - } - else { - /*Trigger the error handler of image draw*/ - LV_LOG_WARN("image source type is unknown"); - } + lv_draw_image(layer, &draw_dsc, &img_area); + layer->clip_area = clip_area_ori; + + } + else if(img->src_type == LV_IMAGE_SRC_SYMBOL) { + lv_draw_label_dsc_t label_dsc; + lv_draw_label_dsc_init(&label_dsc); + lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_dsc); + label_dsc.text = img->src; + lv_draw_label(layer, &label_dsc, &obj->coords); + } + else if(img->src == NULL) { + /*Do not need to draw image when src is NULL*/ + LV_LOG_WARN("image source is NULL"); + } + else { + /*Trigger the error handler of image draw*/ + LV_LOG_WARN("image source type is unknown"); } } } @@ -793,13 +715,6 @@ static void scale_update(lv_obj_t * obj, int32_t scale_x, int32_t scale_y) { lv_image_t * img = (lv_image_t *)obj; - if(img->obj_size_mode == LV_IMAGE_SIZE_MODE_REAL) { - img->scale_x = scale_x; - img->scale_y = scale_y; - lv_obj_invalidate_area(obj, &obj->coords); - return; - } - lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/ int32_t w = lv_obj_get_width(obj); int32_t h = lv_obj_get_height(obj); @@ -829,7 +744,23 @@ static void scale_update(lv_obj_t * obj, int32_t scale_x, int32_t scale_y) a.x2 += obj->coords.x1 + 1; a.y2 += obj->coords.y1 + 1; lv_obj_invalidate_area(obj, &a); - } +static void update_align(lv_obj_t * obj) +{ + lv_image_t * img = (lv_image_t *)obj; + if(img->align == LV_IMAGE_ALIGN_STRETCH) { + lv_image_set_rotation(obj, 0); + lv_image_set_pivot(obj, 0, 0); + int32_t scale_x = lv_obj_get_width(obj) * LV_SCALE_NONE / img->w; + int32_t scale_y = lv_obj_get_height(obj) * LV_SCALE_NONE / img->h; + scale_update(obj, scale_x, scale_y); + } + else if(img->align == LV_IMAGE_ALIGN_TILE) { + lv_image_set_rotation(obj, 0); + lv_image_set_pivot(obj, 0, 0); + scale_update(obj, LV_SCALE_NONE, LV_SCALE_NONE); + + } +} #endif diff --git a/project/gui/lvgl/src/widgets/image/lv_image.h b/project/gui/lvgl/src/widgets/image/lv_image.h index b6f1ef063..8cbc2d420 100644 --- a/project/gui/lvgl/src/widgets/image/lv_image.h +++ b/project/gui/lvgl/src/widgets/image/lv_image.h @@ -39,41 +39,45 @@ extern "C" { */ typedef struct { lv_obj_t obj; - const void * src; /*Image source: Pointer to an array or a file or a symbol*/ + const void * src; /**< Image source: Pointer to an array or a file or a symbol*/ lv_point_t offset; - int32_t w; /*Width of the image (Handled by the library)*/ - int32_t h; /*Height of the image (Handled by the library)*/ - uint32_t rotation; /*rotation angle of the image*/ - uint32_t scale_x; /*256 means no zoom, 512 double size, 128 half size*/ - uint32_t scale_y; /*256 means no zoom, 512 double size, 128 half size*/ - lv_point_t pivot; /*rotation center of the image*/ - uint8_t src_type : 2; /*See: lv_image_src_t*/ - uint8_t cf : 5; /*Color format from `lv_color_format_t`*/ - uint8_t antialias : 1; /*Apply anti-aliasing in transformations (rotate, zoom)*/ - uint8_t obj_size_mode: 2; /*Image size mode when image size and object size is different.*/ + int32_t w; /**< Width of the image (Handled by the library)*/ + int32_t h; /**< Height of the image (Handled by the library)*/ + uint32_t rotation; /**< Rotation angle of the image*/ + uint32_t scale_x; /**< 256 means no zoom, 512 double size, 128 half size*/ + uint32_t scale_y; /**< 256 means no zoom, 512 double size, 128 half size*/ + lv_point_t pivot; /**< Rotation center of the image*/ + uint8_t src_type : 2; /**< See: lv_image_src_t*/ + uint8_t cf : 5; /**< Color format from `lv_color_format_t`*/ + uint8_t antialias : 1; /**< Apply anti-aliasing in transformations (rotate, zoom)*/ + uint8_t align: 4; /**< Image size mode when image size and object size is different. See `lv_image_align_t`*/ } lv_image_t; -extern const lv_obj_class_t lv_image_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_image_class; /** * Image size mode, when image size and object size is different */ -enum _lv_image_size_mode_t { - /** Zoom doesn't affect the coordinates of the object, - * however if zoomed in the image is drawn out of the its coordinates. - * The layout's won't change on zoom */ - LV_IMAGE_SIZE_MODE_VIRTUAL = 0, - - /** If the object size is set to SIZE_CONTENT, then object size equals zoomed image size. - * It causes layout recalculation. - * If the object size is set explicitly, the image will be cropped when zoomed in.*/ - LV_IMAGE_SIZE_MODE_REAL, +enum _lv_image_align_t { + LV_IMAGE_ALIGN_DEFAULT = 0, + LV_IMAGE_ALIGN_TOP_LEFT, + LV_IMAGE_ALIGN_TOP_MID, + LV_IMAGE_ALIGN_TOP_RIGHT, + LV_IMAGE_ALIGN_BOTTOM_LEFT, + LV_IMAGE_ALIGN_BOTTOM_MID, + LV_IMAGE_ALIGN_BOTTOM_RIGHT, + LV_IMAGE_ALIGN_LEFT_MID, + LV_IMAGE_ALIGN_RIGHT_MID, + LV_IMAGE_ALIGN_CENTER, + _LV_IMAGE_ALIGN_AUTO_TRANSFORM, + LV_IMAGE_ALIGN_STRETCH, + LV_IMAGE_ALIGN_TILE, }; #ifdef DOXYGEN -typedef _lv_image_size_mode_t lv_image_size_mode_t; +typedef _lv_image_align_t lv_image_align_t; #else -typedef uint8_t lv_image_size_mode_t; +typedef uint8_t lv_image_align_t; #endif /*DOXYGEN*/ #if LV_USE_OBJ_PROPERTY @@ -85,7 +89,7 @@ enum { LV_PROPERTY_ID(IMAGE, PIVOT, LV_PROPERTY_TYPE_POINTER, 4), LV_PROPERTY_ID(IMAGE, SCALE, LV_PROPERTY_TYPE_INT, 5), LV_PROPERTY_ID(IMAGE, ANTIALIAS, LV_PROPERTY_TYPE_INT, 6), - LV_PROPERTY_ID(IMAGE, SIZE_MODE, LV_PROPERTY_TYPE_INT, 7), + LV_PROPERTY_ID(IMAGE, ALIGN, LV_PROPERTY_TYPE_INT, 7), LV_PROPERTY_IMAGE_END, }; #endif @@ -129,13 +133,15 @@ void lv_image_set_offset_x(lv_obj_t * obj, int32_t x); */ void lv_image_set_offset_y(lv_obj_t * obj, int32_t y); - /** * Set the rotation angle of the image. * The image will be rotated around the set pivot set by `lv_image_set_pivot()` * Note that indexed and alpha only images can't be transformed. * @param obj pointer to an image object - * @param angle rotation in degree with 0.1 degree resolution (0..3600: clock wise) + * @param angle rotation in degree with 0.1 degree resolution (0..3600: clock wise) + * @note if image_align is `LV_IMAGE_ALIGN_STRETCH` or `LV_IMAGE_ALIGN_FIT` + * rotation will be set to 0 automatically. + * */ void lv_image_set_rotation(lv_obj_t * obj, int32_t angle); @@ -157,7 +163,6 @@ static inline void _lv_image_set_pivot(lv_obj_t * obj, lv_point_t * pivot) lv_image_set_pivot(obj, pivot->x, pivot->y); } - /** * Set the zoom factor of the image. * Note that indexed and alpha only images can't be transformed. @@ -197,7 +202,6 @@ void lv_image_set_scale_x(lv_obj_t * obj, uint32_t zoom); */ void lv_image_set_scale_y(lv_obj_t * obj, uint32_t zoom); - /** * Enable/disable anti-aliasing for the transformations (rotate, zoom) or not. * The quality is better with anti-aliasing looks better but slower. @@ -208,11 +212,13 @@ void lv_image_set_antialias(lv_obj_t * obj, bool antialias); /** * Set the image object size mode. - * * @param obj pointer to an image object - * @param mode the new size mode. + * @param align the new align mode. + * @note if image_align is `LV_IMAGE_ALIGN_STRETCH` or `LV_IMAGE_ALIGN_FIT` + * rotation, scale and pivot will be overwritten and controlled internally. */ -void lv_image_set_size_mode(lv_obj_t * obj, lv_image_size_mode_t mode); +void lv_image_set_align(lv_obj_t * obj, lv_image_align_t align); + /*===================== * Getter functions *====================*/ @@ -241,7 +247,9 @@ int32_t lv_image_get_offset_y(lv_obj_t * obj); /** * Get the rotation of the image. * @param obj pointer to an image object - * @return rotation in 0.1 degrees (0..3600) + * @return rotation in 0.1 degrees (0..3600) + * @note if image_align is `LV_IMAGE_ALIGN_STRETCH` or `LV_IMAGE_ALIGN_FIT` + * rotation will be set to 0 automatically. */ int32_t lv_image_get_rotation(lv_obj_t * obj); @@ -284,9 +292,9 @@ bool lv_image_get_antialias(lv_obj_t * obj); /** * Get the size mode of the image * @param obj pointer to an image object - * @return element of @ref lv_image_size_mode_t + * @return element of @ref lv_image_align_t */ -lv_image_size_mode_t lv_image_get_size_mode(lv_obj_t * obj); +lv_image_align_t lv_image_get_align(lv_obj_t * obj); /********************** * MACROS diff --git a/project/gui/lvgl/src/widgets/imgbtn/lv_imgbtn.h b/project/gui/lvgl/src/widgets/imgbtn/lv_imgbtn.h index de2847e64..d656946f4 100644 --- a/project/gui/lvgl/src/widgets/imgbtn/lv_imgbtn.h +++ b/project/gui/lvgl/src/widgets/imgbtn/lv_imgbtn.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_imgbtn.h * */ @@ -46,7 +46,7 @@ typedef struct { lv_imgbtn_src_info_t src_right[_LV_IMGBTN_STATE_NUM]; /*Store right side images to each state*/ } lv_imgbtn_t; -extern const lv_obj_class_t lv_imgbtn_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_imgbtn_class; /********************** * GLOBAL PROTOTYPES @@ -81,7 +81,6 @@ lv_obj_t * lv_imgbtn_create(lv_obj_t * parent); void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_imgbtn_state_t state, const void * src_left, const void * src_mid, const void * src_right); - /** * Use this function instead of `lv_obj_add/remove_state` to set a state manually * @param imgbtn pointer to an image button object @@ -117,7 +116,6 @@ const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_imgbtn_state_t state */ const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_imgbtn_state_t state); - /*===================== * Other functions *====================*/ diff --git a/project/gui/lvgl/src/widgets/keyboard/lv_keyboard.h b/project/gui/lvgl/src/widgets/keyboard/lv_keyboard.h index 35bbc81a8..543be9299 100644 --- a/project/gui/lvgl/src/widgets/keyboard/lv_keyboard.h +++ b/project/gui/lvgl/src/widgets/keyboard/lv_keyboard.h @@ -56,7 +56,6 @@ typedef _lv_keyboard_mode_t lv_keyboard_mode_t; typedef uint8_t lv_keyboard_mode_t; #endif /*DOXYGEN*/ - /*Data of keyboard*/ typedef struct { lv_buttonmatrix_t btnm; @@ -65,7 +64,7 @@ typedef struct { uint8_t popovers : 1; /*Show button titles in popovers on press*/ } lv_keyboard_t; -extern const lv_obj_class_t lv_keyboard_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_keyboard_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/label/lv_label.c b/project/gui/lvgl/src/widgets/label/lv_label.c index 77c75739f..96dd89fe7 100644 --- a/project/gui/lvgl/src/widgets/label/lv_label.c +++ b/project/gui/lvgl/src/widgets/label/lv_label.c @@ -205,19 +205,6 @@ void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode) lv_label_refr_text(obj); } -void lv_label_set_recolor(lv_obj_t * obj, bool en) -{ - LV_ASSERT_OBJ(obj, MY_CLASS); - - lv_label_t * label = (lv_label_t *)obj; - if(label->recolor == en) return; - - label->recolor = en ? 1 : 0; - - /*Refresh the text because the potential color codes in text needs to be hidden or revealed*/ - lv_label_refr_text(obj); -} - void lv_label_set_text_selection_start(lv_obj_t * obj, uint32_t index) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -264,14 +251,6 @@ lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * obj) return label->long_mode; } -bool lv_label_get_recolor(const lv_obj_t * obj) -{ - LV_ASSERT_OBJ(obj, MY_CLASS); - - lv_label_t * label = (lv_label_t *)obj; - return label->recolor; -} - void lv_label_get_letter_pos(const lv_obj_t * obj, uint32_t char_id, lv_point_t * pos) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -641,7 +620,6 @@ static void lv_label_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) label->text = NULL; label->static_txt = 0; - label->recolor = 0; label->dot_end = LV_LABEL_DOT_END_INV; label->long_mode = LV_LABEL_LONG_WRAP; label->offset.x = 0; @@ -709,7 +687,6 @@ static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e) int32_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN); int32_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); lv_text_flag_t flag = LV_TEXT_FLAG_NONE; - if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR; if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND; int32_t w = lv_obj_get_content_width(obj); @@ -729,7 +706,6 @@ static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e) } } - static void draw_main(lv_event_t * e) { lv_obj_t * obj = lv_event_get_target(e); @@ -740,7 +716,6 @@ static void draw_main(lv_event_t * e) lv_obj_get_content_coords(obj, &txt_coords); lv_text_flag_t flag = LV_TEXT_FLAG_NONE; - if(label->recolor) flag |= LV_TEXT_FLAG_RECOLOR; if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND; if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT; @@ -876,7 +851,6 @@ static void lv_label_refr_text(lv_obj_t * obj) /*Calc. the height and longest line*/ lv_point_t size; lv_text_flag_t flag = LV_TEXT_FLAG_NONE; - if(label->recolor) flag |= LV_TEXT_FLAG_RECOLOR; if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND; if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT; @@ -1142,7 +1116,6 @@ static void lv_label_refr_text(lv_obj_t * obj) lv_obj_invalidate(obj); } - static void lv_label_revert_dots(lv_obj_t * obj) { lv_label_t * label = (lv_label_t *)obj; @@ -1230,7 +1203,6 @@ static void lv_label_dot_tmp_free(lv_obj_t * obj) label->dot.tmp_ptr = NULL; } - static void set_ofs_x_anim(void * obj, int32_t v) { lv_label_t * label = (lv_label_t *)obj; @@ -1270,7 +1242,6 @@ static lv_text_flag_t get_label_flags(lv_label_t * label) { lv_text_flag_t flag = LV_TEXT_FLAG_NONE; - if(label->recolor) flag |= LV_TEXT_FLAG_RECOLOR; if(label->expand) flag |= LV_TEXT_FLAG_EXPAND; return flag; diff --git a/project/gui/lvgl/src/widgets/label/lv_label.h b/project/gui/lvgl/src/widgets/label/lv_label.h index 05bd23a09..0a0677129 100644 --- a/project/gui/lvgl/src/widgets/label/lv_label.h +++ b/project/gui/lvgl/src/widgets/label/lv_label.h @@ -60,7 +60,6 @@ typedef _lv_label_long_mode_t lv_label_long_mode_t; typedef uint8_t lv_label_long_mode_t; #endif /*DOXYGEN*/ - typedef struct { lv_obj_t obj; char * text; @@ -83,13 +82,12 @@ typedef struct { lv_point_t offset; /*Text draw position offset*/ lv_label_long_mode_t long_mode : 3; /*Determine what to do with the long texts*/ uint8_t static_txt : 1; /*Flag to indicate the text is static*/ - uint8_t recolor : 1; /*Enable in-line letter re-coloring*/ uint8_t expand : 1; /*Ignore real width (used by the library with LV_LABEL_LONG_SCROLL)*/ uint8_t dot_tmp_alloc : 1; /*1: dot is allocated, 0: dot directly holds up to 4 chars*/ uint8_t invalid_size_cache : 1; /*1: Recalculate size and update cache*/ } lv_label_t; -extern const lv_obj_class_t lv_label_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_label_class; /********************** * GLOBAL PROTOTYPES @@ -137,14 +135,6 @@ void lv_label_set_text_static(lv_obj_t * obj, const char * text); */ void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode); -/** - * Enable the recoloring by in-line commands - * @param obj pointer to a label object - * @param en true: enable recoloring, false: disable - * @example "This is a #ff0000 red# word" - */ -void lv_label_set_recolor(lv_obj_t * obj, bool en); - /** * Set where text selection should start * @param obj pointer to a label object @@ -177,13 +167,6 @@ char * lv_label_get_text(const lv_obj_t * obj); */ lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * obj); -/** - * Get the recoloring attribute - * @param obj pointer to a label object - * @return true: recoloring is enabled, false: disable - */ -bool lv_label_get_recolor(const lv_obj_t * obj); - /** * Get the relative x and y coordinates of a letter * @param obj pointer to a label object diff --git a/project/gui/lvgl/src/widgets/led/lv_led.h b/project/gui/lvgl/src/widgets/led/lv_led.h index fd7da38fd..208c09b15 100644 --- a/project/gui/lvgl/src/widgets/led/lv_led.h +++ b/project/gui/lvgl/src/widgets/led/lv_led.h @@ -41,7 +41,7 @@ typedef struct { uint8_t bright; /**< Current brightness of the LED (0..255)*/ } lv_led_t; -extern const lv_obj_class_t lv_led_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_led_class; /********************** * GLOBAL PROTOTYPES @@ -103,5 +103,4 @@ uint8_t lv_led_get_brightness(const lv_obj_t * obj); } /*extern "C"*/ #endif - #endif /*LV_LED_H*/ diff --git a/project/gui/lvgl/src/widgets/line/lv_line.h b/project/gui/lvgl/src/widgets/line/lv_line.h index 7989eabcd..6a2423a22 100644 --- a/project/gui/lvgl/src/widgets/line/lv_line.h +++ b/project/gui/lvgl/src/widgets/line/lv_line.h @@ -32,7 +32,7 @@ typedef struct { uint32_t y_inv : 1; /**< 1: y == 0 will be on the bottom*/ } lv_line_t; -extern const lv_obj_class_t lv_line_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_line_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/list/lv_list.h b/project/gui/lvgl/src/widgets/list/lv_list.h index fdf7e9a50..7b751aa03 100644 --- a/project/gui/lvgl/src/widgets/list/lv_list.h +++ b/project/gui/lvgl/src/widgets/list/lv_list.h @@ -25,9 +25,9 @@ extern "C" { * TYPEDEFS **********************/ -extern const lv_obj_class_t lv_list_class; -extern const lv_obj_class_t lv_list_text_class; -extern const lv_obj_class_t lv_list_button_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_list_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_list_text_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_list_button_class; /********************** * GLOBAL PROTOTYPES **********************/ diff --git a/project/gui/lvgl/src/widgets/menu/lv_menu.h b/project/gui/lvgl/src/widgets/menu/lv_menu.h index ba6939a50..d7a40c858 100644 --- a/project/gui/lvgl/src/widgets/menu/lv_menu.h +++ b/project/gui/lvgl/src/widgets/menu/lv_menu.h @@ -36,7 +36,6 @@ typedef _lv_menu_mode_header_t lv_menu_mode_header_t; typedef uint8_t lv_menu_mode_header_t; #endif /*DOXYGEN*/ - enum _lv_menu_mode_root_back_button_t { LV_MENU_ROOT_BACK_BUTTON_DISABLED, LV_MENU_ROOT_BACK_BUTTON_ENABLED @@ -93,15 +92,15 @@ typedef struct { bool static_title; } lv_menu_page_t; -extern const lv_obj_class_t lv_menu_class; -extern const lv_obj_class_t lv_menu_page_class; -extern const lv_obj_class_t lv_menu_cont_class; -extern const lv_obj_class_t lv_menu_section_class; -extern const lv_obj_class_t lv_menu_separator_class; -extern const lv_obj_class_t lv_menu_sidebar_cont_class; -extern const lv_obj_class_t lv_menu_main_cont_class; -extern const lv_obj_class_t lv_menu_sidebar_header_cont_class; -extern const lv_obj_class_t lv_menu_main_header_cont_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_page_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_cont_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_section_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_separator_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_sidebar_cont_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_main_cont_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_sidebar_header_cont_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_menu_main_header_cont_class; /********************** * GLOBAL PROTOTYPES **********************/ diff --git a/project/gui/lvgl/src/widgets/msgbox/lv_msgbox.h b/project/gui/lvgl/src/widgets/msgbox/lv_msgbox.h index b2cbb8536..77efdf5af 100644 --- a/project/gui/lvgl/src/widgets/msgbox/lv_msgbox.h +++ b/project/gui/lvgl/src/widgets/msgbox/lv_msgbox.h @@ -43,9 +43,9 @@ typedef struct { lv_obj_t * buttons; } lv_msgbox_t; -extern const lv_obj_class_t lv_msgbox_class; -extern const lv_obj_class_t lv_msgbox_content_class; -extern const lv_obj_class_t lv_msgbox_backdrop_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_msgbox_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_msgbox_content_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_msgbox_backdrop_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/objx_templ/lv_objx_templ.h b/project/gui/lvgl/src/widgets/objx_templ/lv_objx_templ.h index 9de5285b5..ccf1e1c3e 100644 --- a/project/gui/lvgl/src/widgets/objx_templ/lv_objx_templ.h +++ b/project/gui/lvgl/src/widgets/objx_templ/lv_objx_templ.h @@ -39,7 +39,7 @@ typedef struct { /*New data for this type*/ } lv_templ_t; -extern const lv_obj_class_t lv_templ_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_templ_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/roller/lv_roller.c b/project/gui/lvgl/src/widgets/roller/lv_roller.c index a6bf1b3ce..eac5ae734 100644 --- a/project/gui/lvgl/src/widgets/roller/lv_roller.c +++ b/project/gui/lvgl/src/widgets/roller/lv_roller.c @@ -266,7 +266,6 @@ void lv_roller_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_s buf[c] = '\0'; } - /** * Get the options of a roller * @param roller pointer to roller object @@ -279,7 +278,6 @@ const char * lv_roller_get_options(const lv_obj_t * obj) return lv_label_get_text(get_label(obj)); } - /** * Get the total number of options * @param roller pointer to a roller object @@ -302,7 +300,6 @@ uint32_t lv_roller_get_option_cnt(const lv_obj_t * obj) * STATIC FUNCTIONS **********************/ - static void lv_roller_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) { LV_UNUSED(class_p); @@ -461,7 +458,6 @@ static void lv_roller_label_event(const lv_obj_class_t * class_p, lv_event_t * e } } - static void draw_main(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); @@ -492,7 +488,6 @@ static void draw_main(lv_event_t * e) area_ok = _lv_area_intersect(&mask_sel, &layer->clip_area, &sel_area); if(area_ok) { lv_obj_t * label = get_label(obj); - if(lv_label_get_recolor(label)) label_dsc.flag |= LV_TEXT_FLAG_RECOLOR; /*Get the size of the "selected text"*/ lv_point_t res_p; @@ -546,7 +541,6 @@ static void draw_label(lv_event_t * e) lv_draw_label_dsc_t label_draw_dsc; lv_draw_label_dsc_init(&label_draw_dsc); lv_obj_init_draw_label_dsc(roller, LV_PART_MAIN, &label_draw_dsc); - if(lv_label_get_recolor(label_obj)) label_draw_dsc.flag |= LV_TEXT_FLAG_RECOLOR; lv_layer_t * layer = lv_event_get_layer(e); @@ -794,7 +788,6 @@ static lv_obj_t * get_label(const lv_obj_t * obj) return lv_obj_get_child(obj, 0); } - static int32_t get_selected_label_width(const lv_obj_t * obj) { lv_obj_t * label = get_label(obj); @@ -819,7 +812,6 @@ static void set_y_anim(void * obj, int32_t v) lv_obj_set_y(obj, v); } - static void transform_vect_recursive(lv_obj_t * roller, lv_point_t * vect) { int16_t angle = 0; diff --git a/project/gui/lvgl/src/widgets/roller/lv_roller.h b/project/gui/lvgl/src/widgets/roller/lv_roller.h index 0ed5cb5d3..a7f7bb678 100644 --- a/project/gui/lvgl/src/widgets/roller/lv_roller.h +++ b/project/gui/lvgl/src/widgets/roller/lv_roller.h @@ -44,7 +44,6 @@ typedef _lv_roller_mode_t lv_roller_mode_t; typedef uint8_t lv_roller_mode_t; #endif /*DOXYGEN*/ - typedef struct { lv_obj_t obj; uint32_t option_cnt; /**< Number of options*/ @@ -55,8 +54,7 @@ typedef struct { uint32_t moved : 1; } lv_roller_t; -extern const lv_obj_class_t lv_roller_class; - +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_roller_class; /********************** * GLOBAL PROTOTYPES @@ -115,7 +113,6 @@ uint32_t lv_roller_get_selected(const lv_obj_t * obj); */ void lv_roller_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size); - /** * Get the options of a roller * @param obj pointer to roller object diff --git a/project/gui/lvgl/src/widgets/scale/lv_scale.h b/project/gui/lvgl/src/widgets/scale/lv_scale.h index 1cd889d4e..a6b9fae4f 100644 --- a/project/gui/lvgl/src/widgets/scale/lv_scale.h +++ b/project/gui/lvgl/src/widgets/scale/lv_scale.h @@ -90,7 +90,7 @@ typedef struct { int32_t rotation; } lv_scale_t; -extern const lv_obj_class_t lv_scale_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_scale_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/slider/lv_slider.h b/project/gui/lvgl/src/widgets/slider/lv_slider.h index 1a41c3608..6c83c14f0 100644 --- a/project/gui/lvgl/src/widgets/slider/lv_slider.h +++ b/project/gui/lvgl/src/widgets/slider/lv_slider.h @@ -22,7 +22,6 @@ extern "C" { #error "lv_slider: lv_bar is required. Enable it in lv_conf.h (LV_USE_BAR 1)" #endif - /********************* * DEFINES *********************/ @@ -42,7 +41,6 @@ typedef _lv_slider_mode_t lv_slider_mode_t; typedef uint8_t lv_slider_mode_t; #endif /*DOXYGEN*/ - typedef struct { lv_bar_t bar; /*Add the ancestor's type first*/ lv_area_t left_knob_area; @@ -53,7 +51,7 @@ typedef struct { uint8_t left_knob_focus : 1; /*1: with encoder now the right knob can be adjusted*/ } lv_slider_t; -extern const lv_obj_class_t lv_slider_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_slider_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/span/lv_span.h b/project/gui/lvgl/src/widgets/span/lv_span.h index 99fd1cc3f..e337d814e 100644 --- a/project/gui/lvgl/src/widgets/span/lv_span.h +++ b/project/gui/lvgl/src/widgets/span/lv_span.h @@ -52,7 +52,6 @@ typedef _lv_span_mode_t lv_span_mode_t; typedef uint32_t lv_span_mode_t; #endif /*DOXYGEN*/ - typedef struct { char * txt; /* a pointer to display text */ lv_obj_t * spangroup; /* a pointer to spangroup */ @@ -73,7 +72,7 @@ typedef struct { uint32_t refresh : 1; /* the spangroup need refresh cache_w and cache_h */ } lv_spangroup_t; -extern const lv_obj_class_t lv_spangroup_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_spangroup_class; /********************** * GLOBAL PROTOTYPES @@ -239,7 +238,6 @@ uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width); */ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width); - /*===================== * Other functions *====================*/ diff --git a/project/gui/lvgl/src/widgets/spinbox/lv_spinbox.h b/project/gui/lvgl/src/widgets/spinbox/lv_spinbox.h index 079ec29bf..c835c6158 100644 --- a/project/gui/lvgl/src/widgets/spinbox/lv_spinbox.h +++ b/project/gui/lvgl/src/widgets/spinbox/lv_spinbox.h @@ -45,7 +45,7 @@ typedef struct { uint32_t digit_step_dir : 2; /* the direction the digit will step on encoder button press when editing*/ } lv_spinbox_t; -extern const lv_obj_class_t lv_spinbox_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_spinbox_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/spinner/lv_spinner.h b/project/gui/lvgl/src/widgets/spinner/lv_spinner.h index b9c018103..3f0355f6c 100644 --- a/project/gui/lvgl/src/widgets/spinner/lv_spinner.h +++ b/project/gui/lvgl/src/widgets/spinner/lv_spinner.h @@ -29,7 +29,7 @@ extern "C" { /********************** * TYPEDEFS **********************/ -extern const lv_obj_class_t lv_spinner_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_spinner_class; /********************** * GLOBAL PROTOTYPES @@ -37,7 +37,6 @@ extern const lv_obj_class_t lv_spinner_class; lv_obj_t * lv_spinner_create(lv_obj_t * parent); - void lv_spinner_set_anim_params(lv_obj_t * obj, uint32_t t, uint32_t angle); /********************** diff --git a/project/gui/lvgl/src/widgets/switch/lv_switch.h b/project/gui/lvgl/src/widgets/switch/lv_switch.h index 57cb191eb..624c98346 100644 --- a/project/gui/lvgl/src/widgets/switch/lv_switch.h +++ b/project/gui/lvgl/src/widgets/switch/lv_switch.h @@ -35,7 +35,7 @@ typedef struct { int32_t anim_state; } lv_switch_t; -extern const lv_obj_class_t lv_switch_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_switch_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/table/lv_table.c b/project/gui/lvgl/src/widgets/table/lv_table.c index 5bf161ddb..afd591b62 100644 --- a/project/gui/lvgl/src/widgets/table/lv_table.c +++ b/project/gui/lvgl/src/widgets/table/lv_table.c @@ -41,7 +41,7 @@ static void refr_size_form_row(lv_obj_t * obj, uint32_t start_row); static void refr_cell_size(lv_obj_t * obj, uint32_t row, uint32_t col); static lv_result_t get_pressed_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col); static size_t get_cell_txt_len(const char * txt); -static void copy_cell_txt(char * dst, const char * txt); +static void copy_cell_txt(lv_table_cell_t * dst, const char * txt); static void get_cell_area(lv_obj_t * obj, uint32_t row, uint32_t col, lv_area_t * area); static void scroll_to_selected_cell(lv_obj_t * obj); @@ -100,7 +100,12 @@ void lv_table_set_cell_value(lv_obj_t * obj, uint32_t row, uint32_t col, const c lv_table_cell_ctrl_t ctrl = 0; /*Save the control byte*/ - if(table->cell_data[cell]) ctrl = table->cell_data[cell][0]; + if(table->cell_data[cell]) ctrl = table->cell_data[cell]->ctrl; + + void * user_data = NULL; + + /*Save the user data*/ + if(table->cell_data[cell]) user_data = table->cell_data[cell]->user_data; size_t to_allocate = get_cell_txt_len(txt); @@ -110,7 +115,8 @@ void lv_table_set_cell_value(lv_obj_t * obj, uint32_t row, uint32_t col, const c copy_cell_txt(table->cell_data[cell], txt); - table->cell_data[cell][0] = ctrl; + table->cell_data[cell]->ctrl = ctrl; + table->cell_data[cell]->user_data = user_data; refr_cell_size(obj, row, col); } @@ -133,7 +139,12 @@ void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint32_t row, uint32_t col, con lv_table_cell_ctrl_t ctrl = 0; /*Save the control byte*/ - if(table->cell_data[cell]) ctrl = table->cell_data[cell][0]; + if(table->cell_data[cell]) ctrl = table->cell_data[cell]->ctrl; + + void * user_data = NULL; + + /*Save the user_data*/ + if(table->cell_data[cell]) user_data = table->cell_data[cell]->user_data; va_list ap, ap2; va_start(ap, fmt); @@ -156,32 +167,33 @@ void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint32_t row, uint32_t col, con /*Get the size of the Arabic text and process it*/ size_t len_ap = _lv_text_ap_calc_bytes_cnt(raw_txt); - table->cell_data[cell] = lv_realloc(table->cell_data[cell], len_ap + 1); + table->cell_data[cell] = lv_realloc(table->cell_data[cell], sizeof(lv_table_cell_t) + len_ap + 1); LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) { va_end(ap2); return; } - _lv_text_ap_proc(raw_txt, &table->cell_data[cell][1]); + _lv_text_ap_proc(raw_txt, table->cell_data[cell]->txt); lv_free(raw_txt); #else - table->cell_data[cell] = lv_realloc(table->cell_data[cell], len + 2); /*+1: trailing '\0; +1: format byte*/ + table->cell_data[cell] = lv_realloc(table->cell_data[cell], + sizeof(lv_table_cell_t) + len + 1); /*+1: trailing '\0; */ LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) { va_end(ap2); return; } - table->cell_data[cell][len + 1] = 0; /*Ensure NULL termination*/ + table->cell_data[cell]->txt[len] = 0; /*Ensure NULL termination*/ - lv_vsnprintf(&table->cell_data[cell][1], len + 1, fmt, ap2); + lv_vsnprintf(table->cell_data[cell]->txt, len, fmt, ap2); #endif va_end(ap2); - table->cell_data[cell][0] = ctrl; - + table->cell_data[cell]->ctrl = ctrl; + table->cell_data[cell]->user_data = user_data; refr_cell_size(obj, row, col); } @@ -206,11 +218,15 @@ void lv_table_set_row_cnt(lv_obj_t * obj, uint32_t row_cnt) uint32_t new_cell_cnt = table->col_cnt * table->row_cnt; uint32_t i; for(i = new_cell_cnt; i < old_cell_cnt; i++) { + if(table->cell_data[i]->user_data) { + lv_free(table->cell_data[i]->user_data); + table->cell_data[i]->user_data = NULL; + } lv_free(table->cell_data[i]); } } - table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(char *)); + table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(lv_table_cell_t *)); LV_ASSERT_MALLOC(table->cell_data); if(table->cell_data == NULL) return; @@ -235,7 +251,7 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint32_t col_cnt) uint32_t old_col_cnt = table->col_cnt; table->col_cnt = col_cnt; - char ** new_cell_data = lv_malloc(table->row_cnt * table->col_cnt * sizeof(char *)); + lv_table_cell_t ** new_cell_data = lv_malloc(table->row_cnt * table->col_cnt * sizeof(lv_table_cell_t *)); LV_ASSERT_MALLOC(new_cell_data); if(new_cell_data == NULL) return; uint32_t new_cell_cnt = table->col_cnt * table->row_cnt; @@ -258,6 +274,10 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint32_t col_cnt) int32_t i; for(i = 0; i < (int32_t)old_col_cnt - (int32_t)col_cnt; i++) { uint32_t idx = old_col_start + min_col_cnt + i; + if(table->cell_data[idx]->user_data) { + lv_free(table->cell_data[idx]->user_data); + table->cell_data[idx]->user_data = NULL; + } lv_free(table->cell_data[idx]); table->cell_data[idx] = NULL; } @@ -276,7 +296,6 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint32_t col_cnt) table->col_w[col] = LV_DPI_DEF; } - refr_size_form_row(obj, 0) ; } @@ -306,15 +325,16 @@ void lv_table_add_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table uint32_t cell = row * table->col_cnt + col; if(is_cell_empty(table->cell_data[cell])) { - table->cell_data[cell] = lv_malloc(2); /*+1: trailing '\0; +1: format byte*/ + table->cell_data[cell] = lv_malloc(sizeof(lv_table_cell_t) + 1); /*+1: trailing '\0 */ LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) return; - table->cell_data[cell][0] = 0; - table->cell_data[cell][1] = '\0'; + table->cell_data[cell]->ctrl = 0; + table->cell_data[cell]->user_data = NULL; + table->cell_data[cell]->txt[0] = '\0'; } - table->cell_data[cell][0] |= ctrl; + table->cell_data[cell]->ctrl |= ctrl; } void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table_cell_ctrl_t ctrl) @@ -330,15 +350,45 @@ void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_tab uint32_t cell = row * table->col_cnt + col; if(is_cell_empty(table->cell_data[cell])) { - table->cell_data[cell] = lv_malloc(2); /*+1: trailing '\0; +1: format byte*/ + table->cell_data[cell] = lv_malloc(sizeof(lv_table_cell_t) + 1); /*+1: trailing '\0 */ LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) return; - table->cell_data[cell][0] = 0; - table->cell_data[cell][1] = '\0'; + table->cell_data[cell]->ctrl = 0; + table->cell_data[cell]->user_data = NULL; + table->cell_data[cell]->txt[0] = '\0'; } - table->cell_data[cell][0] &= (~ctrl); + table->cell_data[cell]->ctrl &= (~ctrl); +} + +void lv_table_set_cell_user_data(lv_obj_t * obj, uint16_t row, uint16_t col, void * user_data) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_table_t * table = (lv_table_t *)obj; + + /*Auto expand*/ + if(col >= table->col_cnt) lv_table_set_col_cnt(obj, col + 1); + if(row >= table->row_cnt) lv_table_set_row_cnt(obj, row + 1); + + uint32_t cell = row * table->col_cnt + col; + + if(is_cell_empty(table->cell_data[cell])) { + table->cell_data[cell] = lv_malloc(sizeof(lv_table_cell_t) + 1); /*+1: trailing '\0 */ + LV_ASSERT_MALLOC(table->cell_data[cell]); + if(table->cell_data[cell] == NULL) return; + + table->cell_data[cell]->ctrl = 0; + table->cell_data[cell]->user_data = NULL; + table->cell_data[cell]->txt[0] = '\0'; + } + + if(table->cell_data[cell]->user_data) { + lv_free(table->cell_data[cell]->user_data); + } + + table->cell_data[cell]->user_data = user_data; } /*===================== @@ -358,7 +408,7 @@ const char * lv_table_get_cell_value(lv_obj_t * obj, uint32_t row, uint32_t col) if(is_cell_empty(table->cell_data[cell])) return ""; - return &table->cell_data[cell][1]; /*Skip the format byte*/ + return table->cell_data[cell]->txt; } uint32_t lv_table_get_row_cnt(lv_obj_t * obj) @@ -403,7 +453,7 @@ bool lv_table_has_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table uint32_t cell = row * table->col_cnt + col; if(is_cell_empty(table->cell_data[cell])) return false; - else return (table->cell_data[cell][0] & ctrl) == ctrl; + else return (table->cell_data[cell]->ctrl & ctrl) == ctrl; } void lv_table_get_selected_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col) @@ -413,6 +463,22 @@ void lv_table_get_selected_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col) *col = table->col_act; } +void * lv_table_get_cell_user_data(lv_obj_t * obj, uint16_t row, uint16_t col) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_table_t * table = (lv_table_t *)obj; + if(row >= table->row_cnt || col >= table->col_cnt) { + LV_LOG_WARN("invalid row or column"); + return NULL; + } + uint32_t cell = row * table->col_cnt + col; + + if(is_cell_empty(table->cell_data[cell])) return NULL; + + return table->cell_data[cell]->user_data; +} + /********************** * STATIC FUNCTIONS **********************/ @@ -430,7 +496,7 @@ static void lv_table_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) table->row_h = lv_malloc(table->row_cnt * sizeof(table->row_h[0])); table->col_w[0] = LV_DPI_DEF; table->row_h[0] = LV_DPI_DEF; - table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(char *)); + table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(lv_table_cell_t *)); table->cell_data[0] = NULL; LV_TRACE_OBJ_CREATE("finished"); @@ -444,6 +510,10 @@ static void lv_table_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) uint32_t i; for(i = 0; i < table->col_cnt * table->row_cnt; i++) { if(table->cell_data[i]) { + if(table->cell_data[i]->user_data) { + lv_free(table->cell_data[i]->user_data); + table->cell_data[i]->user_data = NULL; + } lv_free(table->cell_data[i]); table->cell_data[i] = NULL; } @@ -575,7 +645,6 @@ static void lv_table_event(const lv_obj_class_t * class_p, lv_event_t * e) } } - static void draw_main(lv_event_t * e) { lv_obj_t * obj = lv_event_get_target(e); @@ -635,7 +704,7 @@ static void draw_main(lv_event_t * e) for(col = 0; col < table->col_cnt; col++) { lv_table_cell_ctrl_t ctrl = 0; - if(table->cell_data[cell]) ctrl = table->cell_data[cell][0]; + if(table->cell_data[cell]) ctrl = table->cell_data[cell]->ctrl; if(rtl) { cell_area.x2 = cell_area.x1 - 1; @@ -648,11 +717,11 @@ static void draw_main(lv_event_t * e) uint32_t col_merge = 0; for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) { - char * next_cell_data = table->cell_data[cell + col_merge]; + lv_table_cell_t * next_cell_data = table->cell_data[cell + col_merge]; if(is_cell_empty(next_cell_data)) break; - lv_table_cell_ctrl_t merge_ctrl = (lv_table_cell_ctrl_t) next_cell_data[0]; + lv_table_cell_ctrl_t merge_ctrl = (lv_table_cell_ctrl_t) next_cell_data->ctrl; if(merge_ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) { int32_t offset = table->col_w[col + col_merge + 1]; @@ -736,7 +805,7 @@ static void draw_main(lv_event_t * e) bool crop = ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP; if(crop) txt_flags = LV_TEXT_FLAG_EXPAND; - lv_text_get_size(&txt_size, table->cell_data[cell] + 1, label_dsc_def.font, + lv_text_get_size(&txt_size, table->cell_data[cell]->txt, label_dsc_def.font, label_dsc_act.letter_space, label_dsc_act.line_space, lv_area_get_width(&txt_area), txt_flags); @@ -751,7 +820,7 @@ static void draw_main(lv_event_t * e) label_mask_ok = _lv_area_intersect(&label_clip_area, &clip_area, &cell_area); if(label_mask_ok) { layer->clip_area = label_clip_area; - label_dsc_act.text = table->cell_data[cell] + 1; + label_dsc_act.text = table->cell_data[cell]->txt; lv_draw_label(layer, &label_dsc_act, &txt_area); layer->clip_area = clip_area; } @@ -792,7 +861,6 @@ static void refr_size_form_row(lv_obj_t * obj, uint32_t start_row) lv_obj_invalidate(obj); } - static void refr_cell_size(lv_obj_t * obj, uint32_t row, uint32_t col) { const int32_t cell_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); @@ -841,7 +909,7 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t * uint32_t cell; uint32_t col; for(cell = row_start, col = 0; cell < row_start + table->col_cnt; cell++, col++) { - char * cell_data = table->cell_data[cell]; + lv_table_cell_t * cell_data = table->cell_data[cell]; if(is_cell_empty(cell_data)) { continue; @@ -854,11 +922,11 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t * * exit the traversal when the current cell control is not LV_TABLE_CELL_CTRL_MERGE_RIGHT */ uint32_t col_merge = 0; for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) { - char * next_cell_data = table->cell_data[cell + col_merge]; + lv_table_cell_t * next_cell_data = table->cell_data[cell + col_merge]; if(is_cell_empty(next_cell_data)) break; - lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) next_cell_data[0]; + lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) next_cell_data->ctrl; if(ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) { txt_w += table->col_w[col + col_merge + 1]; } @@ -867,7 +935,7 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t * } } - lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) cell_data[0]; + lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) cell_data->ctrl; /*When cropping the text we can assume the row height is equal to the line height*/ if(ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP) { @@ -879,7 +947,7 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t * lv_point_t txt_size; txt_w -= cell_left + cell_right; - lv_text_get_size(&txt_size, table->cell_data[cell] + 1, font, + lv_text_get_size(&txt_size, table->cell_data[cell]->txt, font, letter_space, line_space, txt_w, LV_TEXT_FLAG_NONE); h_max = LV_MAX(txt_size.y + cell_top + cell_bottom, h_max); @@ -949,23 +1017,21 @@ static size_t get_cell_txt_len(const char * txt) size_t retval = 0; #if LV_USE_ARABIC_PERSIAN_CHARS - retval = _lv_text_ap_calc_bytes_cnt(txt) + 1; + retval = sizeof(lv_table_cell_t) + _lv_text_ap_calc_bytes_cnt(txt) + 1; #else - /* cell_data layout: [ctrl][txt][trailing '\0' terminator] - * +2 because of the trailing '\0' and the ctrl */ - retval = lv_strlen(txt) + 2; + retval = sizeof(lv_table_cell_t) + strlen(txt) + 1; #endif return retval; } /* Copy txt into dst skipping the format byte */ -static void copy_cell_txt(char * dst, const char * txt) +static void copy_cell_txt(lv_table_cell_t * dst, const char * txt) { #if LV_USE_ARABIC_PERSIAN_CHARS - _lv_text_ap_proc(txt, &dst[1]); + _lv_text_ap_proc(txt, dst->txt); #else - lv_strcpy(&dst[1], txt); + strcpy(dst->txt, txt); #endif } @@ -1004,7 +1070,6 @@ static void get_cell_area(lv_obj_t * obj, uint32_t row, uint32_t col, lv_area_t } - static void scroll_to_selected_cell(lv_obj_t * obj) { lv_table_t * table = (lv_table_t *)obj; diff --git a/project/gui/lvgl/src/widgets/table/lv_table.h b/project/gui/lvgl/src/widgets/table/lv_table.h index d74c697d2..ccae204e3 100644 --- a/project/gui/lvgl/src/widgets/table/lv_table.h +++ b/project/gui/lvgl/src/widgets/table/lv_table.h @@ -48,20 +48,26 @@ typedef _lv_table_cell_ctrl_t lv_table_cell_ctrl_t; typedef uint32_t lv_table_cell_ctrl_t; #endif /*DOXYGEN*/ +/*Data of cell*/ +typedef struct { + lv_table_cell_ctrl_t ctrl; + void * user_data; /**< Custom user data*/ + char txt[]; +} lv_table_cell_t; /*Data of table*/ typedef struct { lv_obj_t obj; uint32_t col_cnt; uint32_t row_cnt; - char ** cell_data; + lv_table_cell_t ** cell_data; int32_t * row_h; int32_t * col_w; uint32_t col_act; uint32_t row_act; } lv_table_t; -extern const lv_obj_class_t lv_table_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_table_class; /********************** * GLOBAL PROTOTYPES @@ -130,7 +136,6 @@ void lv_table_set_col_width(lv_obj_t * obj, uint32_t col_id, int32_t w); */ void lv_table_add_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table_cell_ctrl_t ctrl); - /** * Clear control bits of the cell. * @param obj pointer to a Table object @@ -140,6 +145,18 @@ void lv_table_add_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table */ void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table_cell_ctrl_t ctrl); +/** + * Add custom user data to the cell. + * @param obj pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param user_data pointer to the new user_data. + * Should be allocated by `lv_malloc`, + * and it will be freed automatically when the table is deleted or + * when the cell is dropped due to lower row or column count. + */ +void lv_table_set_cell_user_data(lv_obj_t * obj, uint16_t row, uint16_t col, void * user_data); + /*===================== * Getter functions *====================*/ @@ -193,6 +210,14 @@ bool lv_table_has_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table */ void lv_table_get_selected_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col); +/** + * Get custom user data to the cell. + * @param obj pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + */ +void * lv_table_get_cell_user_data(lv_obj_t * obj, uint16_t row, uint16_t col); + /********************** * MACROS **********************/ diff --git a/project/gui/lvgl/src/widgets/tabview/lv_tabview.h b/project/gui/lvgl/src/widgets/tabview/lv_tabview.h index 38d010bca..3bfe738be 100644 --- a/project/gui/lvgl/src/widgets/tabview/lv_tabview.h +++ b/project/gui/lvgl/src/widgets/tabview/lv_tabview.h @@ -33,7 +33,7 @@ typedef struct { lv_dir_t tab_pos; } lv_tabview_t; -extern const lv_obj_class_t lv_tabview_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_tabview_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/textarea/lv_textarea.h b/project/gui/lvgl/src/widgets/textarea/lv_textarea.h index 38ea77519..336585c34 100644 --- a/project/gui/lvgl/src/widgets/textarea/lv_textarea.h +++ b/project/gui/lvgl/src/widgets/textarea/lv_textarea.h @@ -63,7 +63,7 @@ typedef struct { uint8_t one_line : 1; /*One line mode (ignore line breaks)*/ } lv_textarea_t; -extern const lv_obj_class_t lv_textarea_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_textarea_class; enum { LV_PART_TEXTAREA_PLACEHOLDER = LV_PART_CUSTOM_FIRST, diff --git a/project/gui/lvgl/src/widgets/tileview/lv_tileview.h b/project/gui/lvgl/src/widgets/tileview/lv_tileview.h index 5f3525173..783e02565 100644 --- a/project/gui/lvgl/src/widgets/tileview/lv_tileview.h +++ b/project/gui/lvgl/src/widgets/tileview/lv_tileview.h @@ -34,8 +34,8 @@ typedef struct { lv_dir_t dir; } lv_tileview_tile_t; -extern const lv_obj_class_t lv_tileview_class; -extern const lv_obj_class_t lv_tileview_tile_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_tileview_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_tileview_tile_class; /********************** * GLOBAL PROTOTYPES diff --git a/project/gui/lvgl/src/widgets/win/lv_win.h b/project/gui/lvgl/src/widgets/win/lv_win.h index bcebed841..c57d0c376 100644 --- a/project/gui/lvgl/src/widgets/win/lv_win.h +++ b/project/gui/lvgl/src/widgets/win/lv_win.h @@ -26,7 +26,7 @@ typedef struct { lv_obj_t obj; } lv_win_t; -extern const lv_obj_class_t lv_win_class; +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_win_class; /********************** * GLOBAL PROTOTYPES @@ -34,7 +34,6 @@ extern const lv_obj_class_t lv_win_class; lv_obj_t * lv_win_create(lv_obj_t * parent); - lv_obj_t * lv_win_add_title(lv_obj_t * win, const char * txt); lv_obj_t * lv_win_add_button(lv_obj_t * win, const void * icon, int32_t btn_w);