diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 3aa481a02..b64beb368 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -36,6 +36,14 @@ jobs: run: scripts/install-prerequisites.sh - name: Run tests run: python tests/main.py --report test + - name: Archive screenshot errors + if: failure() + uses: actions/upload-artifact@v3 + with: + name: screenshot-errors-amd64 + path: | + tests/ref_imgs/**/*_err.png + test_screenshot_error.h - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 if: github.event_name == 'push' @@ -91,4 +99,13 @@ jobs: echo 'export PATH="/usr/lib/ccache:$PATH"' | tee -a ~/.bashrc run: | - env PATH="/usr/lib/ccache:$PATH" ASAN_OPTIONS=detect_leaks=0 python3 tests/main.py test + env PATH="/usr/lib/ccache:$PATH" NON_AMD64_BUILD=1 ASAN_OPTIONS=detect_leaks=0 python3 tests/main.py test + - name: Archive screenshot errors + if: failure() + uses: actions/upload-artifact@v3 + with: + name: screenshot-errors-${{ matrix.arch }} + path: | + tests/ref_imgs/**/*_err.png + test_screenshot_error.h + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af8547244..dc26d1256 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,3 +34,7 @@ repos: rev: v1.16.20 hooks: - id: typos + exclude: | + (?x)^( + src/libs/ + ) diff --git a/Kconfig b/Kconfig index 0babfd84a..6a1b15df5 100644 --- a/Kconfig +++ b/Kconfig @@ -311,6 +311,12 @@ menu "LVGL configuration" depends on LV_USE_DRAW_SW help Only used if software rotation is enabled in the display driver. + + config LV_USE_VECTOR_GRAPHIC + bool "Use Vector Graphic APIs" + default n + help + Enable drawing support vector graphic APIs. endmenu menu "GPU" @@ -1108,6 +1114,17 @@ menu "LVGL configuration" config LV_USE_RLOTTIE bool "Lottie library" + config LV_USE_THORVG + bool "ThorVG library" + choice + prompt "Use ThorVG config" + depends on LV_USE_THORVG + default LV_USE_THORVG_INTERNAL + config LV_USE_THORVG_INTERNAL + bool "Use ThorVG internal" + config LV_USE_THORVG_EXTERNAL + bool "Use ThorVG external" + endchoice config LV_USE_FFMPEG bool "FFmpeg library" @@ -1408,6 +1425,9 @@ menu "LVGL configuration" config LV_USE_DEMO_MULTILANG bool "multi-language demo" default n + config LV_USE_DEMO_VECTOR_GRAPHIC + bool "vector graphic demo" + default n endmenu endmenu diff --git a/component.mk b/component.mk index 16e38458c..ee6e131ae 100644 --- a/component.mk +++ b/component.mk @@ -59,5 +59,8 @@ COMPONENT_SRCDIRS := . \ src/extra/widgets/tileview \ src/extra/widgets/win +ifeq ($(CONFIG_LV_USE_THORVG_INTERNAL),y) +COMPONENT_SRCDIRS += src/extra/libs/thorvg +endif COMPONENT_ADD_INCLUDEDIRS := $(COMPONENT_SRCDIRS) . diff --git a/demos/lv_demos.c b/demos/lv_demos.c index e86ebe4ea..7765a018f 100644 --- a/demos/lv_demos.c +++ b/demos/lv_demos.c @@ -92,10 +92,15 @@ static const demo_entry_info_t demos_entry_info[] = { { "scroll", .entry_cb = lv_demo_scroll }, #endif +#if LV_USE_DEMO_VECTOR_GRAPHIC && LV_USE_VECTOR_GRAPHIC + { "vector_graphic", .entry_cb = lv_demo_vector_graphic }, +#endif + //#if LV_USE_DEMO_BENCHMARK // { DEMO_BENCHMARK_NAME, .entry_benchmark_cb = lv_demo_benchmark, 1 }, // { DEMO_BENCHMARK_SCENE_NAME, .entry_benchmark_scene_cb = lv_demo_benchmark_run_scene, 2 }, //#endif + { "", .entry_cb = NULL } }; diff --git a/demos/lv_demos.h b/demos/lv_demos.h index 63f34dfc7..34e5c20a4 100644 --- a/demos/lv_demos.h +++ b/demos/lv_demos.h @@ -51,6 +51,10 @@ extern "C" { #include "multilang/lv_demo_multilang.h" #endif +#if LV_USE_DEMO_VECTOR_GRAPHIC && LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL) +#include "vector_graphic/lv_demo_vector_graphic.h" +#endif + #if LV_USE_DEMO_RENDER #include "render/lv_demo_render.h" #endif diff --git a/demos/vector_graphic/assets/avatar.png b/demos/vector_graphic/assets/avatar.png new file mode 100644 index 000000000..e69de29bb diff --git a/demos/vector_graphic/assets/img_demo_vector_avatar.c b/demos/vector_graphic/assets/img_demo_vector_avatar.c new file mode 100644 index 000000000..b35fad11c --- /dev/null +++ b/demos/vector_graphic/assets/img_demo_vector_avatar.c @@ -0,0 +1,188 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN + #define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_IMG_DEMO_VECTOR_AVATAR + #define LV_ATTRIBUTE_IMAGE_IMG_DEMO_VECTOR_AVATAR +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_IMG_DEMO_VECTOR_AVATAR uint8_t +img_demo_vector_avatar_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf5, 0xeb, 0xeb, 0x19, 0xf7, 0xf0, 0xe8, 0x21, 0xf9, 0xed, 0xe7, 0x2a, 0xf9, 0xed, 0xe7, 0x2a, 0xf9, 0xed, 0xe7, 0x2a, 0xf9, 0xed, 0xe7, 0x2a, 0xf7, 0xf0, 0xe8, 0x21, 0xf0, 0xf0, 0xe1, 0x11, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf7, 0xec, 0xe8, 0x42, 0xf6, 0xed, 0xe6, 0x53, 0xf5, 0xec, 0xe7, 0x6b, 0xf7, 0xec, 0xe6, 0x7c, 0xf5, 0xed, 0xe7, 0x9d, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xe7, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xdf, 0xf6, 0xed, 0xe7, 0xc6, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0x74, 0xf7, 0xed, 0xe8, 0x63, 0xf5, 0xee, 0xe7, 0x4b, 0xf6, 0xed, 0xe9, 0x3a, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xf8, 0xf2, 0xed, 0xff, 0xf9, 0xf4, 0xf0, 0xff, 0xfa, 0xf6, 0xf3, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xfd, 0xfa, 0xf9, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfd, 0xfa, 0xf8, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfa, 0xf5, 0xf2, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0x74, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe7, 0xb5, 0xf5, 0xec, 0xe7, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe6, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xec, 0xe7, 0x6b, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xec, 0xe7, 0x6b, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfc, 0xfd, 0xfd, 0xff, 0xfa, 0xfc, 0xfc, 0xff, 0xf8, 0xfb, 0xfb, 0xff, 0xf5, 0xf9, 0xf9, 0xff, 0xf2, 0xf8, 0xf7, 0xff, 0xef, 0xf5, 0xf4, 0xff, 0xeb, 0xf2, 0xf2, 0xff, 0xe8, 0xf0, 0xef, 0xff, 0xe7, 0xef, 0xee, 0xff, 0xe6, 0xef, 0xf0, 0xff, 0xe8, 0xf1, 0xf1, 0xff, 0xe7, 0xf0, 0xf0, 0xff, 0xe6, 0xee, 0xef, 0xff, 0xe7, 0xef, 0xf0, 0xff, 0xe6, 0xee, 0xef, 0xff, 0xe8, 0xf0, 0xf1, 0xff, 0xe9, 0xf2, 0xf2, 0xff, 0xea, 0xf2, 0xf0, 0xff, 0xea, 0xf2, 0xf1, 0xff, 0xed, 0xf3, 0xf3, 0xff, 0xee, 0xf3, 0xf5, 0xff, 0xf1, 0xf6, 0xf7, 0xff, 0xf4, 0xf7, 0xf7, 0xff, 0xf6, 0xf9, 0xf9, 0xff, 0xf9, 0xfa, 0xfb, 0xff, 0xfb, 0xfc, 0xfc, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xec, 0xe7, 0x6b, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0xfd, 0xff, 0xfb, 0xfd, 0xfd, 0xff, 0xf5, 0xfa, 0xf9, 0xff, 0xf2, 0xf7, 0xf6, 0xff, 0xee, 0xf5, 0xf3, 0xff, 0xec, 0xf3, 0xf2, 0xff, 0xea, 0xf2, 0xf1, 0xff, 0xe8, 0xf1, 0xf0, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe2, 0xec, 0xec, 0xff, 0xe1, 0xec, 0xeb, 0xff, 0xe0, 0xea, 0xea, 0xff, 0xe1, 0xeb, 0xeb, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xe6, 0xef, 0xef, 0xff, 0xe2, 0xed, 0xed, 0xff, 0xe0, 0xed, 0xee, 0xff, 0xe1, 0xed, 0xee, 0xff, 0xe2, 0xed, 0xef, 0xff, 0xe2, 0xee, 0xef, 0xff, 0xe4, 0xef, 0xed, 0xff, 0xe5, 0xef, 0xee, 0xff, 0xe3, 0xee, 0xed, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe2, 0xec, 0xed, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe2, 0xeb, 0xeb, 0xff, 0xe4, 0xea, 0xeb, 0xff, 0xeb, 0xf0, 0xf0, 0xff, 0xeb, 0xef, 0xef, 0xff, 0xf2, 0xf4, 0xf4, 0xff, 0xf9, 0xf9, 0xf9, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfa, 0xfa, 0xf8, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xfc, 0xfb, 0xff, 0xed, 0xf6, 0xf7, 0xff, 0xe9, 0xf2, 0xf2, 0xff, 0xe5, 0xf0, 0xef, 0xff, 0xe3, 0xef, 0xed, 0xff, 0xe3, 0xee, 0xec, 0xff, 0xe2, 0xee, 0xeb, 0xff, 0xe5, 0xf0, 0xef, 0xff, 0xe8, 0xf2, 0xf3, 0xff, 0xe8, 0xf2, 0xf3, 0xff, 0xe7, 0xf1, 0xf2, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe2, 0xec, 0xec, 0xff, 0xe2, 0xec, 0xec, 0xff, 0xe0, 0xea, 0xea, 0xff, 0xe4, 0xef, 0xef, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe5, 0xee, 0xee, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe0, 0xed, 0xec, 0xff, 0xe4, 0xef, 0xef, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe4, 0xef, 0xee, 0xff, 0xe6, 0xf1, 0xf0, 0xff, 0xe7, 0xf0, 0xef, 0xff, 0xe8, 0xef, 0xee, 0xff, 0xe8, 0xf0, 0xef, 0xff, 0xe8, 0xef, 0xee, 0xff, 0xe7, 0xef, 0xee, 0xff, 0xe0, 0xe9, 0xe9, 0xff, 0xdc, 0xe8, 0xe7, 0xff, 0xda, 0xe2, 0xe3, 0xff, 0xcf, 0xd2, 0xd2, 0xff, 0xc9, 0xc9, 0xc9, 0xff, 0xed, 0xef, 0xef, 0xff, 0xfa, 0xfc, 0xfc, 0xff, 0xfc, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf8, 0xf6, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xfb, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xeb, 0xef, 0xe9, 0xff, 0xdb, 0xe1, 0xd6, 0xff, 0xde, 0xe5, 0xe1, 0xff, 0xe8, 0xf3, 0xf7, 0xff, 0xe4, 0xef, 0xf3, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe2, 0xed, 0xec, 0xff, 0xe3, 0xee, 0xec, 0xff, 0xe7, 0xf2, 0xf0, 0xff, 0xe6, 0xf1, 0xef, 0xff, 0xe3, 0xed, 0xec, 0xff, 0xe6, 0xf0, 0xf1, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe2, 0xec, 0xeb, 0xff, 0xe4, 0xef, 0xee, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe3, 0xee, 0xed, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe6, 0xef, 0xf0, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe1, 0xec, 0xec, 0xff, 0xdf, 0xec, 0xed, 0xff, 0xe1, 0xed, 0xef, 0xff, 0xe1, 0xee, 0xef, 0xff, 0xe1, 0xee, 0xef, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe5, 0xee, 0xed, 0xff, 0xe4, 0xee, 0xed, 0xff, 0xe5, 0xee, 0xed, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe1, 0xed, 0xed, 0xff, 0xda, 0xe6, 0xe4, 0xff, 0xca, 0xd2, 0xd0, 0xff, 0xd3, 0xd9, 0xd8, 0xff, 0xd4, 0xd4, 0xd5, 0xff, 0xa9, 0xaa, 0xa8, 0xff, 0xd8, 0xe1, 0xdf, 0xff, 0xe9, 0xee, 0xf0, 0xff, 0xeb, 0xf0, 0xf2, 0xff, 0xf4, 0xf7, 0xf8, 0xff, 0xfc, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0xd6, 0xf6, 0xed, 0xe7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xfa, 0xfa, 0xff, 0xef, 0xf3, 0xf0, 0xff, 0xe0, 0xe6, 0xdb, 0xff, 0xd4, 0xd8, 0xca, 0xff, 0xd2, 0xd8, 0xcd, 0xff, 0xe3, 0xec, 0xe4, 0xff, 0xe4, 0xf1, 0xf0, 0xff, 0xe2, 0xf0, 0xf0, 0xff, 0xe4, 0xef, 0xee, 0xff, 0xe6, 0xf0, 0xef, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe4, 0xee, 0xee, 0xff, 0xe8, 0xf2, 0xf1, 0xff, 0xeb, 0xf6, 0xf4, 0xff, 0xe5, 0xf0, 0xef, 0xff, 0xe4, 0xee, 0xef, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe7, 0xf1, 0xf2, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe4, 0xed, 0xee, 0xff, 0xe2, 0xec, 0xee, 0xff, 0xe2, 0xeb, 0xee, 0xff, 0xe3, 0xec, 0xee, 0xff, 0xe0, 0xe9, 0xec, 0xff, 0xd9, 0xe4, 0xe7, 0xff, 0xde, 0xe9, 0xec, 0xff, 0xdd, 0xe9, 0xec, 0xff, 0xdb, 0xe7, 0xea, 0xff, 0xe1, 0xec, 0xee, 0xff, 0xdd, 0xe9, 0xeb, 0xff, 0xdf, 0xec, 0xee, 0xff, 0xdd, 0xea, 0xec, 0xff, 0xdc, 0xe8, 0xea, 0xff, 0xdc, 0xe9, 0xeb, 0xff, 0xdc, 0xe8, 0xea, 0xff, 0xda, 0xe7, 0xe8, 0xff, 0xdb, 0xe7, 0xe9, 0xff, 0xdc, 0xe8, 0xea, 0xff, 0xdb, 0xe6, 0xe7, 0xff, 0xd5, 0xdb, 0xdb, 0xff, 0xd1, 0xd6, 0xd7, 0xff, 0x9a, 0x94, 0x96, 0xff, 0x99, 0x94, 0x93, 0xff, 0xdb, 0xe9, 0xe5, 0xff, 0xdd, 0xe6, 0xe8, 0xff, 0xd7, 0xde, 0xe2, 0xff, 0xdc, 0xe5, 0xe8, 0xff, 0xe9, 0xf1, 0xf2, 0xff, 0xf1, 0xf5, 0xf6, 0xff, 0xf9, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf7, 0xf1, 0xeb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xfa, 0xff, 0xf5, 0xf8, 0xf4, 0xff, 0xe8, 0xf1, 0xed, 0xff, 0xde, 0xeb, 0xec, 0xff, 0xd6, 0xe2, 0xe0, 0xff, 0xd6, 0xe1, 0xdb, 0xff, 0xde, 0xe9, 0xe2, 0xff, 0xdf, 0xe7, 0xe4, 0xff, 0xe5, 0xee, 0xee, 0xff, 0xe4, 0xef, 0xf0, 0xff, 0xe5, 0xf1, 0xf1, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xe7, 0xf2, 0xf0, 0xff, 0xe4, 0xee, 0xed, 0xff, 0xe5, 0xee, 0xef, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xed, 0xf8, 0xf5, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe4, 0xec, 0xec, 0xff, 0xe4, 0xef, 0xed, 0xff, 0xe5, 0xf0, 0xf0, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xdf, 0xe9, 0xea, 0xff, 0xe1, 0xeb, 0xed, 0xff, 0xe2, 0xec, 0xee, 0xff, 0xe2, 0xeb, 0xed, 0xff, 0xe0, 0xea, 0xec, 0xff, 0xdd, 0xe7, 0xe9, 0xff, 0xda, 0xe3, 0xe5, 0xff, 0xde, 0xe7, 0xea, 0xff, 0xd8, 0xe4, 0xe7, 0xff, 0xda, 0xe9, 0xec, 0xff, 0xdc, 0xe8, 0xec, 0xff, 0xd6, 0xe0, 0xe6, 0xff, 0xd4, 0xdd, 0xe3, 0xff, 0xdb, 0xe5, 0xe9, 0xff, 0xd1, 0xdc, 0xe0, 0xff, 0xce, 0xda, 0xde, 0xff, 0xd8, 0xe3, 0xe7, 0xff, 0xd6, 0xe3, 0xe6, 0xff, 0xd5, 0xe2, 0xe4, 0xff, 0xd6, 0xdf, 0xe8, 0xff, 0xda, 0xe3, 0xe6, 0xff, 0xda, 0xe3, 0xe4, 0xff, 0xd1, 0xd9, 0xdc, 0xff, 0x91, 0x8f, 0x8f, 0xff, 0xb1, 0xad, 0xab, 0xff, 0xe3, 0xec, 0xe9, 0xff, 0xd6, 0xe1, 0xe0, 0xff, 0xd9, 0xe4, 0xe4, 0xff, 0xdb, 0xe8, 0xe8, 0xff, 0xd8, 0xe6, 0xe6, 0xff, 0xda, 0xe4, 0xe6, 0xff, 0xe0, 0xe9, 0xe9, 0xff, 0xe9, 0xef, 0xef, 0xff, 0xf6, 0xf8, 0xf8, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xfc, 0xff, 0xf3, 0xf8, 0xf4, 0xff, 0xe7, 0xef, 0xe8, 0xff, 0xe3, 0xee, 0xe8, 0xff, 0xda, 0xe7, 0xe2, 0xff, 0xdd, 0xea, 0xe9, 0xff, 0xe0, 0xed, 0xec, 0xff, 0xe2, 0xf0, 0xee, 0xff, 0xe3, 0xf1, 0xf0, 0xff, 0xe7, 0xf3, 0xf2, 0xff, 0xe6, 0xf2, 0xf0, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xe9, 0xf3, 0xf1, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xe7, 0xf2, 0xf0, 0xff, 0xe4, 0xee, 0xef, 0xff, 0xe4, 0xed, 0xee, 0xff, 0xe7, 0xf2, 0xf1, 0xff, 0xed, 0xf8, 0xf6, 0xff, 0xeb, 0xf6, 0xf4, 0xff, 0xe5, 0xef, 0xee, 0xff, 0xe5, 0xed, 0xed, 0xff, 0xe2, 0xeb, 0xe9, 0xff, 0xdf, 0xe8, 0xe6, 0xff, 0xe2, 0xea, 0xea, 0xff, 0xdf, 0xe7, 0xe9, 0xff, 0xd4, 0xdd, 0xde, 0xff, 0xe4, 0xed, 0xec, 0xff, 0xe3, 0xed, 0xec, 0xff, 0xe3, 0xed, 0xec, 0xff, 0xe3, 0xec, 0xeb, 0xff, 0xe3, 0xeb, 0xea, 0xff, 0xdf, 0xe6, 0xe7, 0xff, 0xdf, 0xea, 0xec, 0xff, 0xdc, 0xea, 0xed, 0xff, 0xd2, 0xdd, 0xe5, 0xff, 0xd3, 0xde, 0xe3, 0xff, 0xd6, 0xe1, 0xe4, 0xff, 0xd5, 0xe0, 0xe4, 0xff, 0xcc, 0xd8, 0xde, 0xff, 0xcf, 0xda, 0xe1, 0xff, 0xd0, 0xdb, 0xe2, 0xff, 0xcd, 0xd8, 0xde, 0xff, 0xcb, 0xd6, 0xe0, 0xff, 0xc8, 0xd8, 0xe1, 0xff, 0xbc, 0xcb, 0xdb, 0xff, 0xca, 0xd7, 0xde, 0xff, 0xd7, 0xe2, 0xe0, 0xff, 0xc4, 0xce, 0xce, 0xff, 0xc4, 0xc9, 0xc9, 0xff, 0xda, 0xe0, 0xe1, 0xff, 0xd0, 0xdd, 0xdf, 0xff, 0xdd, 0xe8, 0xeb, 0xff, 0xdd, 0xea, 0xec, 0xff, 0xd5, 0xe3, 0xe4, 0xff, 0xd8, 0xe4, 0xe5, 0xff, 0xda, 0xe7, 0xea, 0xff, 0xd7, 0xe4, 0xe6, 0xff, 0xe2, 0xea, 0xea, 0xff, 0xd9, 0xdb, 0xdb, 0xff, 0xe3, 0xe2, 0xe3, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xfb, 0xf9, 0xff, 0xf0, 0xf7, 0xf5, 0xff, 0xe6, 0xf2, 0xf1, 0xff, 0xe3, 0xf3, 0xf4, 0xff, 0xe7, 0xf5, 0xf4, 0xff, 0xe1, 0xef, 0xea, 0xff, 0xe0, 0xed, 0xe7, 0xff, 0xe2, 0xef, 0xef, 0xff, 0xe4, 0xf0, 0xf1, 0xff, 0xe5, 0xf2, 0xf0, 0xff, 0xe7, 0xf3, 0xf1, 0xff, 0xe9, 0xf3, 0xf1, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe8, 0xf2, 0xf3, 0xff, 0xe3, 0xef, 0xee, 0xff, 0xe5, 0xf2, 0xf1, 0xff, 0xed, 0xf7, 0xf7, 0xff, 0xea, 0xf4, 0xf5, 0xff, 0xea, 0xf4, 0xf4, 0xff, 0xea, 0xf1, 0xf0, 0xff, 0xcd, 0xd4, 0xd3, 0xff, 0xd4, 0xda, 0xda, 0xff, 0xd5, 0xda, 0xda, 0xff, 0xdb, 0xe1, 0xe0, 0xff, 0xeb, 0xf2, 0xf1, 0xff, 0xe8, 0xef, 0xee, 0xff, 0xde, 0xe4, 0xe3, 0xff, 0xe2, 0xe8, 0xe7, 0xff, 0xdb, 0xe4, 0xe3, 0xff, 0xdc, 0xe6, 0xe7, 0xff, 0xdf, 0xeb, 0xed, 0xff, 0xd9, 0xe4, 0xe8, 0xff, 0xd9, 0xe1, 0xe2, 0xff, 0xdc, 0xe9, 0xe6, 0xff, 0xc1, 0xcd, 0xd0, 0xff, 0xbd, 0xc5, 0xd1, 0xff, 0xc8, 0xd1, 0xde, 0xff, 0xc7, 0xd3, 0xe0, 0xff, 0xc4, 0xd1, 0xdf, 0xff, 0xc3, 0xce, 0xda, 0xff, 0xc9, 0xd2, 0xe0, 0xff, 0xb8, 0xc9, 0xde, 0xff, 0x9f, 0xb3, 0xcd, 0xff, 0xb4, 0xbe, 0xcf, 0xff, 0xc6, 0xca, 0xd0, 0xff, 0xc3, 0xc7, 0xcb, 0xff, 0xd4, 0xd6, 0xda, 0xff, 0xc2, 0xc5, 0xcc, 0xff, 0xbd, 0xc3, 0xcb, 0xff, 0xc9, 0xcf, 0xd2, 0xff, 0xd4, 0xdb, 0xdd, 0xff, 0xe1, 0xeb, 0xee, 0xff, 0xde, 0xeb, 0xed, 0xff, 0xd9, 0xe6, 0xe9, 0xff, 0xd9, 0xe7, 0xe9, 0xff, 0xdb, 0xe6, 0xe5, 0xff, 0xbb, 0xc0, 0xbf, 0xff, 0xc0, 0xc4, 0xc2, 0xff, 0xec, 0xf0, 0xef, 0xff, 0xf4, 0xf7, 0xf8, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xf0, 0xe8, 0x21, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfa, 0xff, 0xe7, 0xe6, 0xdc, 0xff, 0xde, 0xe3, 0xd6, 0xff, 0xda, 0xea, 0xe8, 0xff, 0xde, 0xeb, 0xea, 0xff, 0xde, 0xed, 0xee, 0xff, 0xdf, 0xef, 0xf2, 0xff, 0xe3, 0xf1, 0xf2, 0xff, 0xe6, 0xf3, 0xf1, 0xff, 0xe6, 0xf3, 0xf2, 0xff, 0xe6, 0xf2, 0xf2, 0xff, 0xe6, 0xf3, 0xf3, 0xff, 0xe8, 0xf4, 0xf2, 0xff, 0xe9, 0xf4, 0xf1, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xea, 0xf5, 0xf3, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe7, 0xf2, 0xf1, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe5, 0xef, 0xf0, 0xff, 0xe2, 0xef, 0xf0, 0xff, 0xe6, 0xf2, 0xf2, 0xff, 0xeb, 0xf6, 0xf5, 0xff, 0xeb, 0xf5, 0xf5, 0xff, 0xeb, 0xf5, 0xf4, 0xff, 0xe0, 0xe9, 0xe7, 0xff, 0xe2, 0xe8, 0xe8, 0xff, 0xd7, 0xdc, 0xda, 0xff, 0xe4, 0xea, 0xe8, 0xff, 0xeb, 0xf2, 0xf2, 0xff, 0xd8, 0xdf, 0xde, 0xff, 0xda, 0xe1, 0xdf, 0xff, 0xe3, 0xea, 0xe9, 0xff, 0xdf, 0xe8, 0xe7, 0xff, 0xe2, 0xef, 0xee, 0xff, 0xe2, 0xef, 0xef, 0xff, 0xd3, 0xdd, 0xdf, 0xff, 0xbd, 0xc9, 0xd4, 0xff, 0xc2, 0xd3, 0xe5, 0xff, 0xb4, 0xc3, 0xda, 0xff, 0xa5, 0xaf, 0xc6, 0xff, 0xb5, 0xc0, 0xd0, 0xff, 0xb5, 0xc4, 0xd5, 0xff, 0x99, 0xaa, 0xbd, 0xff, 0xb3, 0xc0, 0xd0, 0xff, 0xd0, 0xda, 0xe6, 0xff, 0xa7, 0xb2, 0xc8, 0xff, 0x92, 0xa1, 0xb0, 0xff, 0x90, 0x9b, 0xaa, 0xff, 0x92, 0x94, 0xa2, 0xff, 0x9e, 0x99, 0xa0, 0xff, 0x96, 0x92, 0x96, 0xff, 0x9f, 0x9d, 0xa0, 0xff, 0xa4, 0xa6, 0xaa, 0xff, 0xb2, 0xb5, 0xb7, 0xff, 0xae, 0xb0, 0xb1, 0xff, 0xbc, 0xbe, 0xbf, 0xff, 0xcf, 0xd6, 0xd7, 0xff, 0xd7, 0xdf, 0xe1, 0xff, 0xda, 0xe3, 0xe4, 0xff, 0xda, 0xe4, 0xe4, 0xff, 0xd0, 0xd7, 0xd8, 0xff, 0xd3, 0xdb, 0xdd, 0xff, 0xd6, 0xe1, 0xe1, 0xff, 0xd8, 0xe3, 0xe5, 0xff, 0xe5, 0xed, 0xee, 0xff, 0xf5, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xdf, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf3, 0xf4, 0xed, 0xff, 0xd4, 0xd1, 0xbb, 0xff, 0xc2, 0xc4, 0xa9, 0xff, 0xd2, 0xdb, 0xcb, 0xff, 0xe0, 0xea, 0xea, 0xff, 0xdc, 0xe5, 0xe5, 0xff, 0xdb, 0xe9, 0xe7, 0xff, 0xde, 0xed, 0xee, 0xff, 0xe0, 0xef, 0xf1, 0xff, 0xe5, 0xf2, 0xf3, 0xff, 0xe6, 0xf2, 0xf2, 0xff, 0xe6, 0xf3, 0xf2, 0xff, 0xe5, 0xf2, 0xf2, 0xff, 0xe7, 0xf2, 0xf1, 0xff, 0xe9, 0xf3, 0xf2, 0xff, 0xe8, 0xf3, 0xf1, 0xff, 0xea, 0xf5, 0xf3, 0xff, 0xeb, 0xf6, 0xf4, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe8, 0xf2, 0xf1, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xe1, 0xeb, 0xec, 0xff, 0xe3, 0xee, 0xf0, 0xff, 0xe6, 0xf1, 0xf3, 0xff, 0xe8, 0xf2, 0xf3, 0xff, 0xeb, 0xf5, 0xf5, 0xff, 0xea, 0xf5, 0xf4, 0xff, 0xeb, 0xf6, 0xf3, 0xff, 0xe9, 0xf2, 0xf0, 0xff, 0xec, 0xf5, 0xf2, 0xff, 0xe9, 0xf1, 0xef, 0xff, 0xe4, 0xed, 0xec, 0xff, 0xe2, 0xeb, 0xe9, 0xff, 0xe4, 0xed, 0xec, 0xff, 0xe5, 0xee, 0xed, 0xff, 0xe7, 0xf1, 0xf2, 0xff, 0xd6, 0xe2, 0xe5, 0xff, 0xce, 0xd8, 0xdc, 0xff, 0xd3, 0xdd, 0xe2, 0xff, 0x8a, 0x99, 0xb7, 0xff, 0x59, 0x68, 0x98, 0xff, 0x8b, 0x9c, 0xbe, 0xff, 0xb1, 0xc1, 0xd1, 0xff, 0xbc, 0xc8, 0xd2, 0xff, 0xb6, 0xc3, 0xd0, 0xff, 0xa8, 0xb6, 0xc6, 0xff, 0xaa, 0xb8, 0xc4, 0xff, 0xc5, 0xd3, 0xda, 0xff, 0xd5, 0xe5, 0xe3, 0xff, 0xe1, 0xec, 0xee, 0xff, 0xd3, 0xdf, 0xe5, 0xff, 0xc0, 0xcb, 0xd2, 0xff, 0xb3, 0xb9, 0xbb, 0xff, 0xb0, 0xb2, 0xb3, 0xff, 0xaa, 0xaa, 0xaa, 0xff, 0xb0, 0xb0, 0xb0, 0xff, 0xbb, 0xc0, 0xc0, 0xff, 0xc3, 0xc7, 0xc7, 0xff, 0xc1, 0xc3, 0xc3, 0xff, 0xbc, 0xbc, 0xbd, 0xff, 0xc4, 0xc6, 0xc5, 0xff, 0xc9, 0xcd, 0xcc, 0xff, 0xce, 0xd6, 0xd5, 0xff, 0xda, 0xe2, 0xe3, 0xff, 0xdd, 0xe9, 0xec, 0xff, 0xcc, 0xda, 0xde, 0xff, 0xd5, 0xe1, 0xe4, 0xff, 0xdd, 0xea, 0xea, 0xff, 0xdf, 0xeb, 0xea, 0xff, 0xeb, 0xf2, 0xf3, 0xff, 0xfa, 0xfb, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0xf7, 0xf0, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xed, 0xe7, 0x9d, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xde, 0xe1, 0xd5, 0xff, 0xcf, 0xd5, 0xc6, 0xff, 0xd6, 0xdb, 0xc2, 0xff, 0xd4, 0xdd, 0xcc, 0xff, 0xdc, 0xe6, 0xda, 0xff, 0xde, 0xe9, 0xe2, 0xff, 0xdc, 0xe8, 0xe5, 0xff, 0xdf, 0xec, 0xea, 0xff, 0xe2, 0xef, 0xed, 0xff, 0xe2, 0xef, 0xef, 0xff, 0xe4, 0xf1, 0xf3, 0xff, 0xe6, 0xf2, 0xf2, 0xff, 0xe5, 0xf1, 0xf1, 0xff, 0xe6, 0xf1, 0xf1, 0xff, 0xe8, 0xf1, 0xf2, 0xff, 0xe8, 0xf2, 0xf1, 0xff, 0xea, 0xf5, 0xf2, 0xff, 0xeb, 0xf6, 0xf4, 0xff, 0xea, 0xf4, 0xf4, 0xff, 0xea, 0xf3, 0xf4, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xe8, 0xf1, 0xf1, 0xff, 0xe5, 0xf1, 0xf1, 0xff, 0xe0, 0xec, 0xed, 0xff, 0xe6, 0xf0, 0xef, 0xff, 0xea, 0xf2, 0xf6, 0xff, 0xe9, 0xf1, 0xf6, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xeb, 0xf5, 0xf3, 0xff, 0xea, 0xf4, 0xf3, 0xff, 0xe6, 0xf1, 0xef, 0xff, 0xe6, 0xf1, 0xef, 0xff, 0xe7, 0xf1, 0xef, 0xff, 0xe7, 0xf1, 0xef, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe6, 0xf2, 0xf2, 0xff, 0xe3, 0xef, 0xee, 0xff, 0xdd, 0xe8, 0xec, 0xff, 0xca, 0xd5, 0xe1, 0xff, 0xbf, 0xc9, 0xd7, 0xff, 0xc6, 0xd1, 0xdb, 0xff, 0xa2, 0xb0, 0xc0, 0xff, 0x7f, 0x8c, 0xaa, 0xff, 0xa5, 0xb5, 0xcb, 0xff, 0xcb, 0xdc, 0xe4, 0xff, 0xd1, 0xdf, 0xe6, 0xff, 0xcb, 0xd5, 0xdc, 0xff, 0xd9, 0xe2, 0xe7, 0xff, 0xe2, 0xef, 0xf4, 0xff, 0xd2, 0xe1, 0xe9, 0xff, 0xc7, 0xd1, 0xd9, 0xff, 0xc2, 0xc7, 0xca, 0xff, 0xd0, 0xd5, 0xd3, 0xff, 0xea, 0xf5, 0xf1, 0xff, 0xe8, 0xfa, 0xf7, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xbb, 0xbf, 0xbf, 0xff, 0xa4, 0xa2, 0xa1, 0xff, 0xbc, 0xbb, 0xb9, 0xff, 0xcf, 0xd4, 0xd5, 0xff, 0xcb, 0xd2, 0xd2, 0xff, 0xc3, 0xc5, 0xc3, 0xff, 0xc1, 0xc1, 0xc1, 0xff, 0xc0, 0xc2, 0xc2, 0xff, 0xc3, 0xc8, 0xc6, 0xff, 0xc0, 0xc6, 0xc4, 0xff, 0xc8, 0xd1, 0xd2, 0xff, 0xc4, 0xcd, 0xd2, 0xff, 0xbf, 0xc5, 0xcb, 0xff, 0xcf, 0xd9, 0xdc, 0xff, 0xdc, 0xe8, 0xe7, 0xff, 0xdb, 0xe6, 0xe5, 0xff, 0xe0, 0xe8, 0xe7, 0xff, 0xef, 0xf2, 0xf3, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xdf, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe6, 0xde, 0xff, 0xc0, 0xb8, 0xa1, 0xff, 0xcd, 0xd3, 0xbf, 0xff, 0xe4, 0xee, 0xe2, 0xff, 0xdc, 0xe7, 0xdd, 0xff, 0xe1, 0xed, 0xe8, 0xff, 0xde, 0xec, 0xe6, 0xff, 0xe0, 0xee, 0xe4, 0xff, 0xe3, 0xf1, 0xe8, 0xff, 0xe4, 0xf1, 0xef, 0xff, 0xe4, 0xf0, 0xee, 0xff, 0xe3, 0xef, 0xef, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xe7, 0xf2, 0xf2, 0xff, 0xea, 0xf3, 0xf3, 0xff, 0xe9, 0xf3, 0xf4, 0xff, 0xea, 0xf5, 0xf4, 0xff, 0xec, 0xf8, 0xf5, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xea, 0xf4, 0xf4, 0xff, 0xea, 0xf4, 0xf4, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xe5, 0xf0, 0xf3, 0xff, 0xe4, 0xf0, 0xf2, 0xff, 0xe7, 0xf1, 0xf2, 0xff, 0xea, 0xf3, 0xf5, 0xff, 0xe9, 0xf2, 0xf4, 0xff, 0xe8, 0xf2, 0xf3, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe5, 0xf0, 0xef, 0xff, 0xe4, 0xef, 0xef, 0xff, 0xe4, 0xef, 0xee, 0xff, 0xe5, 0xf0, 0xf0, 0xff, 0xe4, 0xef, 0xef, 0xff, 0xe5, 0xf1, 0xf1, 0xff, 0xe1, 0xed, 0xec, 0xff, 0xd8, 0xe4, 0xe7, 0xff, 0xc9, 0xd6, 0xe1, 0xff, 0xc6, 0xd3, 0xdf, 0xff, 0xc0, 0xcd, 0xd5, 0xff, 0xb3, 0xc0, 0xce, 0xff, 0xb4, 0xc5, 0xdc, 0xff, 0xad, 0xc2, 0xd6, 0xff, 0x92, 0xa3, 0xbe, 0xff, 0xa1, 0xaf, 0xc4, 0xff, 0xc6, 0xd5, 0xdc, 0xff, 0xe3, 0xf0, 0xf3, 0xff, 0xad, 0xb6, 0xc3, 0xff, 0x80, 0x84, 0x97, 0xff, 0x66, 0x64, 0x73, 0xff, 0x88, 0x82, 0x88, 0xff, 0xa3, 0x9c, 0x99, 0xff, 0xaf, 0xb0, 0xa8, 0xff, 0xda, 0xe6, 0xe4, 0xff, 0xe1, 0xee, 0xed, 0xff, 0xe7, 0xf2, 0xf0, 0xff, 0xcc, 0xd1, 0xcc, 0xff, 0xa5, 0x9e, 0x99, 0xff, 0xb8, 0xb4, 0xb3, 0xff, 0xbe, 0xbf, 0xbe, 0xff, 0xb8, 0xbd, 0xba, 0xff, 0xbc, 0xc0, 0xc0, 0xff, 0xbc, 0xbe, 0xbf, 0xff, 0xc9, 0xc9, 0xc7, 0xff, 0xb5, 0xb4, 0xb3, 0xff, 0xb0, 0xac, 0xaf, 0xff, 0xa7, 0xa7, 0xac, 0xff, 0xa4, 0xac, 0xaf, 0xff, 0xc0, 0xc8, 0xcc, 0xff, 0xc9, 0xcf, 0xd0, 0xff, 0xc4, 0xcb, 0xc8, 0xff, 0xcc, 0xd5, 0xd4, 0xff, 0xce, 0xda, 0xdb, 0xff, 0xde, 0xe6, 0xe8, 0xff, 0xf6, 0xf8, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf8, 0xf1, 0xec, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xf8, 0xff, 0xe4, 0xe6, 0xdb, 0xff, 0xad, 0xa8, 0x8c, 0xff, 0xb3, 0xac, 0x8e, 0xff, 0xc8, 0xca, 0xaa, 0xff, 0xba, 0xb8, 0x9b, 0xff, 0xcd, 0xd7, 0xc2, 0xff, 0xde, 0xeb, 0xdf, 0xff, 0xde, 0xeb, 0xe1, 0xff, 0xe4, 0xec, 0xe5, 0xff, 0xe0, 0xe8, 0xe3, 0xff, 0xdf, 0xee, 0xea, 0xff, 0xe2, 0xf0, 0xed, 0xff, 0xe5, 0xf2, 0xf1, 0xff, 0xe8, 0xf3, 0xf4, 0xff, 0xe9, 0xf5, 0xf3, 0xff, 0xeb, 0xf6, 0xf4, 0xff, 0xeb, 0xf6, 0xf4, 0xff, 0xec, 0xf5, 0xf4, 0xff, 0xed, 0xf7, 0xf5, 0xff, 0xed, 0xf9, 0xf7, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xeb, 0xf5, 0xf5, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe8, 0xf1, 0xf4, 0xff, 0xe4, 0xef, 0xf2, 0xff, 0xe1, 0xec, 0xef, 0xff, 0xe3, 0xec, 0xef, 0xff, 0xe8, 0xf2, 0xf1, 0xff, 0xea, 0xf3, 0xf3, 0xff, 0xe8, 0xf1, 0xf4, 0xff, 0xe7, 0xf2, 0xf4, 0xff, 0xe3, 0xf0, 0xf0, 0xff, 0xe1, 0xed, 0xee, 0xff, 0xe2, 0xee, 0xef, 0xff, 0xe3, 0xef, 0xf1, 0xff, 0xe0, 0xed, 0xee, 0xff, 0xe2, 0xef, 0xef, 0xff, 0xe6, 0xf3, 0xf3, 0xff, 0xdd, 0xe8, 0xe7, 0xff, 0xde, 0xe8, 0xe9, 0xff, 0xc7, 0xd2, 0xda, 0xff, 0xc2, 0xd0, 0xd8, 0xff, 0xcb, 0xd9, 0xdf, 0xff, 0xbd, 0xc9, 0xd5, 0xff, 0xa7, 0xb5, 0xc7, 0xff, 0x88, 0x98, 0xaa, 0xff, 0x9d, 0xa9, 0xb9, 0xff, 0xbf, 0xcc, 0xd6, 0xff, 0xd4, 0xe2, 0xe7, 0xff, 0xad, 0xb4, 0xb9, 0xff, 0x82, 0x7f, 0x89, 0xff, 0x8c, 0x86, 0x8b, 0xff, 0x7f, 0x7c, 0x79, 0xff, 0x7a, 0x77, 0x76, 0xff, 0xa3, 0x9f, 0x9e, 0xff, 0xc1, 0xbf, 0xbc, 0xff, 0xdb, 0xdf, 0xdc, 0xff, 0xdc, 0xe7, 0xe4, 0xff, 0xe1, 0xef, 0xeb, 0xff, 0xe7, 0xf0, 0xea, 0xff, 0xbb, 0xb8, 0xb1, 0xff, 0x9e, 0x97, 0x95, 0xff, 0xad, 0xa6, 0xa8, 0xff, 0xb9, 0xb8, 0xb8, 0xff, 0xbc, 0xbe, 0xbe, 0xff, 0xbc, 0xbd, 0xbb, 0xff, 0xb7, 0xb3, 0xaf, 0xff, 0xb3, 0xad, 0xa9, 0xff, 0xa3, 0x9c, 0x9e, 0xff, 0xb2, 0xb0, 0xb5, 0xff, 0xb4, 0xb6, 0xb8, 0xff, 0xb8, 0xbe, 0xc1, 0xff, 0xc0, 0xc8, 0xcc, 0xff, 0xc5, 0xcd, 0xce, 0xff, 0xc7, 0xce, 0xd0, 0xff, 0xc8, 0xd2, 0xd4, 0xff, 0xbc, 0xc7, 0xcb, 0xff, 0xd1, 0xda, 0xe1, 0xff, 0xeb, 0xef, 0xf3, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xeb, 0xeb, 0x19, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0xe6, 0xe1, 0xff, 0xe3, 0xe6, 0xce, 0xff, 0xca, 0xd3, 0xb5, 0xff, 0xa6, 0xa0, 0x89, 0xff, 0xac, 0xa4, 0x8b, 0xff, 0xca, 0xcd, 0xb4, 0xff, 0xc8, 0xc9, 0xb0, 0xff, 0xde, 0xe8, 0xda, 0xff, 0xdf, 0xeb, 0xe5, 0xff, 0xe0, 0xed, 0xe8, 0xff, 0xe4, 0xef, 0xea, 0xff, 0xe8, 0xf3, 0xed, 0xff, 0xe8, 0xf4, 0xf0, 0xff, 0xea, 0xf7, 0xf4, 0xff, 0xec, 0xf8, 0xf7, 0xff, 0xed, 0xf8, 0xf7, 0xff, 0xee, 0xf9, 0xf7, 0xff, 0xef, 0xfa, 0xf8, 0xff, 0xed, 0xf8, 0xf6, 0xff, 0xec, 0xf4, 0xf3, 0xff, 0xe2, 0xec, 0xea, 0xff, 0xd9, 0xe5, 0xe2, 0xff, 0xe2, 0xed, 0xea, 0xff, 0xea, 0xf4, 0xf3, 0xff, 0xec, 0xf6, 0xf7, 0xff, 0xeb, 0xf5, 0xf7, 0xff, 0xe8, 0xf0, 0xf4, 0xff, 0xe1, 0xed, 0xef, 0xff, 0xda, 0xe6, 0xe9, 0xff, 0xe2, 0xeb, 0xed, 0xff, 0xe7, 0xf0, 0xf0, 0xff, 0xea, 0xf4, 0xf5, 0xff, 0xe4, 0xed, 0xf0, 0xff, 0xe0, 0xed, 0xef, 0xff, 0xe6, 0xf3, 0xf6, 0xff, 0xe0, 0xec, 0xef, 0xff, 0xde, 0xeb, 0xec, 0xff, 0xdd, 0xea, 0xec, 0xff, 0xde, 0xeb, 0xee, 0xff, 0xe7, 0xf3, 0xf5, 0xff, 0xe1, 0xee, 0xee, 0xff, 0xd8, 0xe2, 0xe0, 0xff, 0xde, 0xe5, 0xe4, 0xff, 0xcc, 0xd7, 0xdc, 0xff, 0xcb, 0xd7, 0xdd, 0xff, 0xd9, 0xe7, 0xe7, 0xff, 0xc3, 0xce, 0xd4, 0xff, 0xa1, 0xac, 0xb9, 0xff, 0xbf, 0xc9, 0xcf, 0xff, 0xdb, 0xe2, 0xde, 0xff, 0xf7, 0xff, 0xf9, 0xff, 0xbd, 0xc0, 0xc2, 0xff, 0x81, 0x7e, 0x82, 0xff, 0x8b, 0x84, 0x84, 0xff, 0x90, 0x83, 0x7f, 0xff, 0x9a, 0x8a, 0x87, 0xff, 0x8c, 0x82, 0x81, 0xff, 0x8b, 0x86, 0x87, 0xff, 0xa9, 0xa4, 0xa5, 0xff, 0xb9, 0xb8, 0xb6, 0xff, 0xdc, 0xe6, 0xe3, 0xff, 0xd9, 0xe4, 0xdf, 0xff, 0xdd, 0xe5, 0xdc, 0xff, 0xdf, 0xe3, 0xd9, 0xff, 0xb5, 0xaf, 0xac, 0xff, 0x96, 0x8c, 0x8f, 0xff, 0x95, 0x8f, 0x92, 0xff, 0xa4, 0x9f, 0x9f, 0xff, 0xbc, 0xb8, 0xb2, 0xff, 0xaa, 0xa8, 0x9d, 0xff, 0xba, 0xb6, 0xad, 0xff, 0x99, 0x94, 0x94, 0xff, 0xa8, 0x9d, 0xa1, 0xff, 0x9e, 0x8e, 0x91, 0xff, 0x8c, 0x84, 0x88, 0xff, 0xac, 0xae, 0xb5, 0xff, 0xaf, 0xb6, 0xc0, 0xff, 0xab, 0xb2, 0xba, 0xff, 0xb7, 0xbe, 0xc3, 0xff, 0xba, 0xc3, 0xc5, 0xff, 0xb2, 0xb9, 0xbe, 0xff, 0xb6, 0xbe, 0xc8, 0xff, 0xd8, 0xde, 0xe3, 0xff, 0xfb, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xeb, 0xeb, 0x19, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xd5, 0xcc, 0xff, 0x9e, 0x94, 0x7a, 0xff, 0x96, 0x86, 0x64, 0xff, 0xbd, 0xba, 0x9f, 0xff, 0xd3, 0xd7, 0xbc, 0xff, 0xcb, 0xd1, 0xba, 0xff, 0xd9, 0xe0, 0xd3, 0xff, 0xdb, 0xe4, 0xd2, 0xff, 0xe0, 0xec, 0xe4, 0xff, 0xe3, 0xee, 0xec, 0xff, 0xe4, 0xf1, 0xf1, 0xff, 0xe8, 0xf5, 0xf4, 0xff, 0xec, 0xf7, 0xf4, 0xff, 0xee, 0xf9, 0xf8, 0xff, 0xe8, 0xf5, 0xf6, 0xff, 0xe7, 0xf4, 0xf4, 0xff, 0xeb, 0xf5, 0xf3, 0xff, 0xea, 0xf4, 0xf3, 0xff, 0xeb, 0xf5, 0xf5, 0xff, 0xec, 0xf6, 0xf6, 0xff, 0xec, 0xf5, 0xf5, 0xff, 0xe3, 0xec, 0xeb, 0xff, 0xd5, 0xde, 0xdc, 0xff, 0xdd, 0xe5, 0xe4, 0xff, 0xed, 0xf5, 0xf4, 0xff, 0xea, 0xf3, 0xf3, 0xff, 0xec, 0xf5, 0xf6, 0xff, 0xec, 0xf6, 0xf6, 0xff, 0xe7, 0xf3, 0xf3, 0xff, 0xdd, 0xe8, 0xe8, 0xff, 0xde, 0xe6, 0xe6, 0xff, 0xe3, 0xeb, 0xee, 0xff, 0xe7, 0xf1, 0xf3, 0xff, 0xe1, 0xee, 0xef, 0xff, 0xd8, 0xe2, 0xe9, 0xff, 0xdf, 0xea, 0xf1, 0xff, 0xe3, 0xef, 0xf3, 0xff, 0xde, 0xea, 0xec, 0xff, 0xdf, 0xed, 0xed, 0xff, 0xe7, 0xf4, 0xf7, 0xff, 0xe0, 0xed, 0xef, 0xff, 0xdc, 0xe7, 0xe6, 0xff, 0xdb, 0xe5, 0xe2, 0xff, 0xde, 0xe8, 0xe8, 0xff, 0xd9, 0xe5, 0xe7, 0xff, 0xdb, 0xe8, 0xe9, 0xff, 0xdd, 0xe9, 0xe5, 0xff, 0xd6, 0xe3, 0xe6, 0xff, 0xc4, 0xd2, 0xdc, 0xff, 0xb1, 0xb5, 0xbd, 0xff, 0x8c, 0x87, 0x8e, 0xff, 0x70, 0x68, 0x65, 0xff, 0x72, 0x63, 0x67, 0xff, 0x96, 0x86, 0x85, 0xff, 0x9c, 0x8f, 0x86, 0xff, 0xb4, 0xb0, 0xa5, 0xff, 0xab, 0x9e, 0x95, 0xff, 0x78, 0x61, 0x5e, 0xff, 0x88, 0x78, 0x78, 0xff, 0x89, 0x7d, 0x80, 0xff, 0x6d, 0x60, 0x65, 0xff, 0xd8, 0xdc, 0xda, 0xff, 0xdb, 0xe7, 0xde, 0xff, 0xc3, 0xc9, 0xbd, 0xff, 0xe2, 0xe7, 0xdb, 0xff, 0xc2, 0xc2, 0xb9, 0xff, 0xa1, 0x96, 0x94, 0xff, 0x94, 0x8c, 0x8e, 0xff, 0x8a, 0x83, 0x85, 0xff, 0xb2, 0xaa, 0xa4, 0xff, 0xba, 0xbb, 0xad, 0xff, 0xae, 0xab, 0xa1, 0xff, 0xad, 0xa1, 0x9a, 0xff, 0x79, 0x67, 0x63, 0xff, 0x7c, 0x66, 0x66, 0xff, 0x7b, 0x69, 0x68, 0xff, 0x81, 0x75, 0x7a, 0xff, 0x6d, 0x65, 0x74, 0xff, 0x76, 0x73, 0x84, 0xff, 0x95, 0x99, 0xa3, 0xff, 0xb8, 0xbd, 0xc3, 0xff, 0xbd, 0xc4, 0xc4, 0xff, 0xa9, 0xaf, 0xb4, 0xff, 0xa7, 0xae, 0xb8, 0xff, 0xcc, 0xd4, 0xd8, 0xff, 0xf4, 0xf6, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xeb, 0xeb, 0x19, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf9, 0xf8, 0xff, 0xcd, 0xc2, 0xb3, 0xff, 0x97, 0x82, 0x61, 0xff, 0x8a, 0x75, 0x52, 0xff, 0x84, 0x6d, 0x4c, 0xff, 0x96, 0x85, 0x60, 0xff, 0xc7, 0xc6, 0xa5, 0xff, 0xdd, 0xe8, 0xd8, 0xff, 0xe4, 0xef, 0xe3, 0xff, 0xe0, 0xef, 0xec, 0xff, 0xdf, 0xed, 0xe9, 0xff, 0xe3, 0xf0, 0xef, 0xff, 0xe3, 0xf1, 0xf1, 0xff, 0xe4, 0xf2, 0xf3, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xdd, 0xe9, 0xec, 0xff, 0xdb, 0xe8, 0xec, 0xff, 0xe8, 0xf4, 0xf5, 0xff, 0xea, 0xf5, 0xf3, 0xff, 0xe9, 0xf4, 0xf1, 0xff, 0xe5, 0xf0, 0xee, 0xff, 0xe3, 0xee, 0xec, 0xff, 0xe5, 0xf0, 0xee, 0xff, 0xe9, 0xf3, 0xf2, 0xff, 0xea, 0xf3, 0xf3, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe9, 0xf3, 0xf2, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xec, 0xf5, 0xf5, 0xff, 0xec, 0xf8, 0xf8, 0xff, 0xdf, 0xea, 0xea, 0xff, 0xcc, 0xd4, 0xd5, 0xff, 0xe0, 0xe9, 0xec, 0xff, 0xdf, 0xeb, 0xed, 0xff, 0xe4, 0xf1, 0xf3, 0xff, 0xdd, 0xe9, 0xee, 0xff, 0xd7, 0xe3, 0xe9, 0xff, 0xde, 0xea, 0xf0, 0xff, 0xd3, 0xdf, 0xe5, 0xff, 0xcd, 0xda, 0xde, 0xff, 0xd2, 0xdc, 0xdf, 0xff, 0xd2, 0xdb, 0xdc, 0xff, 0xd8, 0xe1, 0xdf, 0xff, 0xdd, 0xe9, 0xe4, 0xff, 0xdf, 0xee, 0xeb, 0xff, 0xe0, 0xee, 0xed, 0xff, 0xe3, 0xee, 0xef, 0xff, 0xdb, 0xe7, 0xe7, 0xff, 0xd8, 0xe4, 0xeb, 0xff, 0xd8, 0xdf, 0xe0, 0xff, 0x89, 0x89, 0x89, 0xff, 0x70, 0x6a, 0x6f, 0xff, 0x85, 0x80, 0x83, 0xff, 0x5c, 0x50, 0x54, 0xff, 0xa1, 0x91, 0x8a, 0xff, 0x98, 0x87, 0x74, 0xff, 0x92, 0x81, 0x70, 0xff, 0xb5, 0xaf, 0xa0, 0xff, 0xaf, 0xa4, 0x9a, 0xff, 0x7b, 0x68, 0x65, 0xff, 0x8e, 0x83, 0x80, 0xff, 0x88, 0x7f, 0x7e, 0xff, 0x9e, 0x9c, 0x9a, 0xff, 0xd8, 0xde, 0xd8, 0xff, 0xbb, 0xbb, 0xb2, 0xff, 0xd2, 0xd7, 0xcd, 0xff, 0xc8, 0xcc, 0xc1, 0xff, 0x9b, 0x91, 0x8b, 0xff, 0x99, 0x90, 0x8e, 0xff, 0xa0, 0x9a, 0x9a, 0xff, 0x89, 0x7d, 0x7e, 0xff, 0xa0, 0x94, 0x90, 0xff, 0xad, 0xa0, 0x96, 0xff, 0xb9, 0xab, 0x95, 0xff, 0x6d, 0x5b, 0x50, 0xff, 0x59, 0x3f, 0x39, 0xff, 0x7c, 0x65, 0x57, 0xff, 0x6a, 0x53, 0x53, 0xff, 0x66, 0x53, 0x57, 0xff, 0x67, 0x58, 0x62, 0xff, 0x66, 0x5f, 0x6c, 0xff, 0x63, 0x5e, 0x70, 0xff, 0x8e, 0x90, 0x9c, 0xff, 0xc2, 0xc8, 0xca, 0xff, 0xba, 0xc1, 0xc3, 0xff, 0x9d, 0xa6, 0xb0, 0xff, 0xb2, 0xb9, 0xc2, 0xff, 0xec, 0xed, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xe1, 0x11, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf8, 0xf5, 0xff, 0xc2, 0xb8, 0xa4, 0xff, 0x76, 0x63, 0x42, 0xff, 0x90, 0x79, 0x4e, 0xff, 0xab, 0x9a, 0x73, 0xff, 0xd9, 0xdb, 0xbb, 0xff, 0xd3, 0xdb, 0xc0, 0xff, 0xbe, 0xbf, 0xa2, 0xff, 0xd5, 0xdf, 0xca, 0xff, 0xd4, 0xe1, 0xd1, 0xff, 0xd9, 0xe5, 0xde, 0xff, 0xe1, 0xed, 0xe9, 0xff, 0xe2, 0xf1, 0xf1, 0xff, 0xe3, 0xf1, 0xf0, 0xff, 0xe3, 0xef, 0xf0, 0xff, 0xdb, 0xea, 0xee, 0xff, 0xcb, 0xda, 0xe2, 0xff, 0xde, 0xec, 0xef, 0xff, 0xe8, 0xf3, 0xf3, 0xff, 0xe7, 0xf1, 0xf0, 0xff, 0xe6, 0xf1, 0xef, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe9, 0xf4, 0xf2, 0xff, 0xe7, 0xf1, 0xf0, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xe6, 0xf0, 0xf1, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xea, 0xf4, 0xf4, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe3, 0xed, 0xed, 0xff, 0xe8, 0xf3, 0xf3, 0xff, 0xe9, 0xf5, 0xf5, 0xff, 0xe8, 0xf3, 0xf3, 0xff, 0xe5, 0xf0, 0xf2, 0xff, 0xe1, 0xee, 0xef, 0xff, 0xde, 0xeb, 0xed, 0xff, 0xdd, 0xeb, 0xed, 0xff, 0xe0, 0xed, 0xf0, 0xff, 0xe4, 0xf2, 0xf4, 0xff, 0xdb, 0xe8, 0xeb, 0xff, 0xc3, 0xcd, 0xd1, 0xff, 0xb5, 0xbb, 0xbd, 0xff, 0xb3, 0xb5, 0xb5, 0xff, 0xda, 0xdc, 0xdb, 0xff, 0xe8, 0xf2, 0xef, 0xff, 0xe8, 0xf7, 0xf6, 0xff, 0xe4, 0xf0, 0xf2, 0xff, 0xca, 0xd1, 0xd5, 0xff, 0xc6, 0xca, 0xce, 0xff, 0xb2, 0xb0, 0xaf, 0xff, 0x7b, 0x71, 0x6c, 0xff, 0x93, 0x8e, 0x86, 0xff, 0x8b, 0x83, 0x7d, 0xff, 0xbb, 0xb9, 0xb3, 0xff, 0xb2, 0xb2, 0xa8, 0xff, 0x70, 0x68, 0x5a, 0xff, 0xa7, 0x97, 0x89, 0xff, 0xa0, 0x91, 0x82, 0xff, 0x9d, 0x8e, 0x7d, 0xff, 0xa6, 0x9d, 0x91, 0xff, 0xc4, 0xbf, 0xb9, 0xff, 0x88, 0x7a, 0x75, 0xff, 0x98, 0x8d, 0x86, 0xff, 0x92, 0x8a, 0x8b, 0xff, 0xb9, 0xb9, 0xb6, 0xff, 0xcf, 0xcf, 0xc7, 0xff, 0xcb, 0xce, 0xc4, 0xff, 0xcf, 0xd1, 0xc7, 0xff, 0xa8, 0x9e, 0x96, 0xff, 0x95, 0x8b, 0x86, 0xff, 0x91, 0x81, 0x7f, 0xff, 0x5a, 0x3d, 0x3d, 0xff, 0x5b, 0x45, 0x43, 0xff, 0x99, 0x8d, 0x86, 0xff, 0xb4, 0xa7, 0x92, 0xff, 0xa4, 0x91, 0x80, 0xff, 0x78, 0x5f, 0x55, 0xff, 0x61, 0x49, 0x38, 0xff, 0x60, 0x45, 0x3c, 0xff, 0x6a, 0x52, 0x4f, 0xff, 0x6e, 0x59, 0x5b, 0xff, 0x49, 0x3b, 0x44, 0xff, 0x2c, 0x21, 0x34, 0xff, 0x57, 0x49, 0x59, 0xff, 0x7b, 0x70, 0x78, 0xff, 0xb5, 0xb6, 0xb8, 0xff, 0xb8, 0xc5, 0xca, 0xff, 0x7a, 0x82, 0x8f, 0xff, 0xa3, 0xa3, 0xa9, 0xff, 0xe8, 0xe9, 0xe6, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xec, 0xe9, 0xff, 0xae, 0xa0, 0x8a, 0xff, 0x96, 0x7f, 0x5b, 0xff, 0x87, 0x72, 0x4e, 0xff, 0x87, 0x71, 0x4b, 0xff, 0xa0, 0x86, 0x58, 0xff, 0xba, 0xb6, 0x8d, 0xff, 0xd2, 0xd7, 0xbf, 0xff, 0xbc, 0xba, 0x9b, 0xff, 0xc4, 0xc7, 0xb0, 0xff, 0xe1, 0xed, 0xe3, 0xff, 0xe3, 0xeb, 0xe4, 0xff, 0xdf, 0xed, 0xf1, 0xff, 0xdf, 0xee, 0xee, 0xff, 0xe3, 0xef, 0xed, 0xff, 0xe5, 0xf0, 0xee, 0xff, 0xd7, 0xe7, 0xeb, 0xff, 0xc9, 0xdb, 0xe5, 0xff, 0xdd, 0xea, 0xef, 0xff, 0xe6, 0xf0, 0xef, 0xff, 0xdf, 0xe9, 0xe8, 0xff, 0xd8, 0xe2, 0xe3, 0xff, 0xe4, 0xed, 0xef, 0xff, 0xea, 0xf4, 0xf5, 0xff, 0xe9, 0xf2, 0xf4, 0xff, 0xe7, 0xf2, 0xf2, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xe5, 0xef, 0xef, 0xff, 0xe8, 0xf2, 0xf2, 0xff, 0xee, 0xf8, 0xf8, 0xff, 0xe6, 0xf0, 0xef, 0xff, 0xe1, 0xed, 0xec, 0xff, 0xe8, 0xf5, 0xf4, 0xff, 0xeb, 0xf8, 0xf8, 0xff, 0xea, 0xf6, 0xf8, 0xff, 0xe6, 0xf2, 0xf5, 0xff, 0xe4, 0xf1, 0xf3, 0xff, 0xee, 0xfc, 0xfe, 0xff, 0xea, 0xf7, 0xf9, 0xff, 0xec, 0xf8, 0xf8, 0xff, 0xe8, 0xf5, 0xf4, 0xff, 0xe2, 0xe9, 0xea, 0xff, 0xc7, 0xc8, 0xc7, 0xff, 0xc5, 0xc2, 0xc1, 0xff, 0xcc, 0xca, 0xcb, 0xff, 0xc0, 0xc1, 0xc2, 0xff, 0xbd, 0xbd, 0xbc, 0xff, 0xb9, 0xb6, 0xb6, 0xff, 0xa5, 0x9c, 0x9b, 0xff, 0x9a, 0x8c, 0x89, 0xff, 0x9c, 0x86, 0x71, 0xff, 0x3b, 0x23, 0x1a, 0xff, 0x6a, 0x64, 0x5d, 0xff, 0x93, 0x87, 0x77, 0xff, 0x4b, 0x39, 0x31, 0xff, 0xc4, 0xbc, 0xaf, 0xff, 0xac, 0xa7, 0x95, 0xff, 0x68, 0x58, 0x4c, 0xff, 0x9d, 0x8d, 0x80, 0xff, 0xa2, 0x95, 0x85, 0xff, 0x84, 0x73, 0x6a, 0xff, 0x9d, 0x94, 0x8e, 0xff, 0xc1, 0xb9, 0xb4, 0xff, 0x97, 0x85, 0x85, 0xff, 0x8e, 0x7e, 0x83, 0xff, 0xa4, 0xa4, 0xa3, 0xff, 0xb1, 0xb4, 0xab, 0xff, 0xb9, 0xb6, 0xad, 0xff, 0xbd, 0xbb, 0xb1, 0xff, 0x91, 0x87, 0x7d, 0xff, 0xa7, 0x9e, 0x96, 0xff, 0x7b, 0x6f, 0x67, 0xff, 0x51, 0x35, 0x2a, 0xff, 0x62, 0x45, 0x3c, 0xff, 0x63, 0x4a, 0x4a, 0xff, 0x7c, 0x65, 0x61, 0xff, 0xa5, 0x94, 0x80, 0xff, 0x82, 0x6d, 0x62, 0xff, 0x72, 0x5b, 0x50, 0xff, 0x7b, 0x66, 0x53, 0xff, 0x4c, 0x36, 0x35, 0xff, 0x51, 0x3b, 0x3d, 0xff, 0x61, 0x51, 0x53, 0xff, 0x3d, 0x31, 0x3c, 0xff, 0x41, 0x33, 0x40, 0xff, 0x46, 0x34, 0x41, 0xff, 0x56, 0x4b, 0x52, 0xff, 0xbf, 0xc1, 0xbf, 0xff, 0xaf, 0xb2, 0xb4, 0xff, 0x77, 0x73, 0x7b, 0xff, 0x8b, 0x8a, 0x8b, 0xff, 0xde, 0xdc, 0xd9, 0xff, 0xfd, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xec, 0xe8, 0xff, 0xb8, 0xa6, 0x8e, 0xff, 0x90, 0x7c, 0x54, 0xff, 0x84, 0x6c, 0x44, 0xff, 0x74, 0x5b, 0x33, 0xff, 0x8c, 0x75, 0x49, 0xff, 0x97, 0x7e, 0x5a, 0xff, 0x6c, 0x52, 0x2e, 0xff, 0xac, 0xa3, 0x7b, 0xff, 0xe1, 0xe9, 0xd0, 0xff, 0xe1, 0xed, 0xe0, 0xff, 0xe0, 0xee, 0xed, 0xff, 0xe0, 0xed, 0xeb, 0xff, 0xde, 0xee, 0xee, 0xff, 0xdd, 0xeb, 0xec, 0xff, 0xe2, 0xee, 0xeb, 0xff, 0xe9, 0xf2, 0xef, 0xff, 0xe5, 0xef, 0xf0, 0xff, 0xd6, 0xe4, 0xeb, 0xff, 0xda, 0xe5, 0xeb, 0xff, 0xe5, 0xef, 0xf3, 0xff, 0xe4, 0xf0, 0xf1, 0xff, 0xe7, 0xf1, 0xf2, 0xff, 0xe3, 0xed, 0xee, 0xff, 0xe4, 0xee, 0xef, 0xff, 0xe9, 0xf3, 0xf4, 0xff, 0xe6, 0xf0, 0xef, 0xff, 0xe5, 0xf0, 0xee, 0xff, 0xe5, 0xee, 0xee, 0xff, 0xe2, 0xec, 0xee, 0xff, 0xde, 0xe7, 0xe9, 0xff, 0xdc, 0xe5, 0xe5, 0xff, 0xe1, 0xed, 0xec, 0xff, 0xee, 0xf9, 0xf8, 0xff, 0xed, 0xf6, 0xf6, 0xff, 0xea, 0xf6, 0xf7, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xe9, 0xf5, 0xf5, 0xff, 0xe8, 0xf0, 0xf2, 0xff, 0xbb, 0xbc, 0xc3, 0xff, 0x99, 0x93, 0xa0, 0xff, 0x92, 0x87, 0x95, 0xff, 0xa1, 0x97, 0x9e, 0xff, 0x99, 0x91, 0x93, 0xff, 0xae, 0xa3, 0xa6, 0xff, 0x9f, 0x94, 0x97, 0xff, 0x89, 0x7f, 0x81, 0xff, 0x9d, 0x90, 0x90, 0xff, 0x97, 0x8c, 0x8b, 0xff, 0xb0, 0xa7, 0xa6, 0xff, 0x9c, 0x93, 0x91, 0xff, 0x93, 0x8b, 0x80, 0xff, 0xb8, 0xa4, 0x8f, 0xff, 0x63, 0x4d, 0x40, 0xff, 0x62, 0x5a, 0x52, 0xff, 0x93, 0x8a, 0x7e, 0xff, 0x58, 0x41, 0x38, 0xff, 0x60, 0x4a, 0x48, 0xff, 0xe1, 0xdd, 0xce, 0xff, 0x8a, 0x7f, 0x6d, 0xff, 0x7e, 0x6f, 0x60, 0xff, 0xb4, 0xa9, 0x9c, 0xff, 0x9b, 0x8e, 0x85, 0xff, 0x6a, 0x54, 0x4c, 0xff, 0x6d, 0x5f, 0x5d, 0xff, 0x94, 0x88, 0x89, 0xff, 0x71, 0x63, 0x67, 0xff, 0x8b, 0x80, 0x82, 0xff, 0x8c, 0x82, 0x83, 0xff, 0xa2, 0x9b, 0x96, 0xff, 0x9d, 0x99, 0x90, 0xff, 0x82, 0x76, 0x71, 0xff, 0x92, 0x83, 0x7b, 0xff, 0x94, 0x88, 0x7b, 0xff, 0x63, 0x4e, 0x48, 0xff, 0x50, 0x33, 0x2c, 0xff, 0x4a, 0x2c, 0x2d, 0xff, 0x54, 0x38, 0x39, 0xff, 0x7d, 0x64, 0x58, 0xff, 0x6a, 0x52, 0x46, 0xff, 0x43, 0x2b, 0x25, 0xff, 0x76, 0x63, 0x5c, 0xff, 0x58, 0x42, 0x40, 0xff, 0x33, 0x1a, 0x1d, 0xff, 0x4b, 0x37, 0x3a, 0xff, 0x5e, 0x4f, 0x53, 0xff, 0x38, 0x2b, 0x32, 0xff, 0x4a, 0x3d, 0x45, 0xff, 0x45, 0x34, 0x39, 0xff, 0x63, 0x54, 0x55, 0xff, 0xc7, 0xc4, 0xc0, 0xff, 0x9d, 0x9d, 0x9a, 0xff, 0x6d, 0x68, 0x72, 0xff, 0xa9, 0xa5, 0xa6, 0xff, 0xd6, 0xd4, 0xcc, 0xff, 0xfa, 0xfa, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xed, 0xe7, 0x9d, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xee, 0xe9, 0xff, 0xa4, 0x95, 0x7a, 0xff, 0x8b, 0x7c, 0x4f, 0xff, 0x98, 0x8d, 0x5e, 0xff, 0xb0, 0xa0, 0x71, 0xff, 0xb5, 0xa5, 0x79, 0xff, 0xce, 0xc8, 0x98, 0xff, 0xb3, 0xae, 0x8a, 0xff, 0x92, 0x80, 0x63, 0xff, 0xdc, 0xd8, 0xb9, 0xff, 0xd3, 0xd7, 0xc1, 0xff, 0xcd, 0xcf, 0xb6, 0xff, 0xe4, 0xed, 0xe0, 0xff, 0xdc, 0xeb, 0xec, 0xff, 0xdd, 0xea, 0xed, 0xff, 0xdd, 0xeb, 0xee, 0xff, 0xe0, 0xed, 0xec, 0xff, 0xe7, 0xf2, 0xef, 0xff, 0xea, 0xf4, 0xf3, 0xff, 0xdf, 0xec, 0xef, 0xff, 0xde, 0xea, 0xee, 0xff, 0xe0, 0xec, 0xf0, 0xff, 0xe1, 0xed, 0xef, 0xff, 0xe6, 0xf0, 0xef, 0xff, 0xe9, 0xf3, 0xf2, 0xff, 0xeb, 0xf5, 0xf4, 0xff, 0xea, 0xf4, 0xf3, 0xff, 0xe8, 0xf1, 0xf2, 0xff, 0xe7, 0xf0, 0xf2, 0xff, 0xe4, 0xef, 0xf2, 0xff, 0xe4, 0xef, 0xf3, 0xff, 0xd2, 0xdd, 0xe3, 0xff, 0xb7, 0xc1, 0xcb, 0xff, 0xab, 0xb4, 0xc4, 0xff, 0xb6, 0xbb, 0xca, 0xff, 0xb9, 0xbd, 0xc5, 0xff, 0xd3, 0xda, 0xdb, 0xff, 0xe7, 0xf0, 0xef, 0xff, 0xde, 0xe6, 0xe5, 0xff, 0xd3, 0xd5, 0xd7, 0xff, 0xa8, 0xa2, 0xa0, 0xff, 0x8a, 0x7f, 0x7e, 0xff, 0x90, 0x81, 0x83, 0xff, 0x80, 0x6f, 0x70, 0xff, 0x78, 0x69, 0x6c, 0xff, 0x9b, 0x8d, 0x92, 0xff, 0xb3, 0xa9, 0xac, 0xff, 0xab, 0xa3, 0xa2, 0xff, 0x90, 0x85, 0x84, 0xff, 0x6b, 0x5c, 0x60, 0xff, 0x8a, 0x7e, 0x81, 0xff, 0x84, 0x7a, 0x7a, 0xff, 0x9d, 0x93, 0x8d, 0xff, 0xc7, 0xba, 0xae, 0xff, 0x6b, 0x58, 0x4c, 0xff, 0x8b, 0x82, 0x74, 0xff, 0xb2, 0xa6, 0x95, 0xff, 0x78, 0x67, 0x5b, 0xff, 0x2d, 0x12, 0x11, 0xff, 0xa3, 0x99, 0x8a, 0xff, 0xda, 0xdb, 0xc9, 0xff, 0x88, 0x79, 0x6a, 0xff, 0x97, 0x86, 0x7a, 0xff, 0xad, 0xa1, 0x9b, 0xff, 0x8f, 0x7b, 0x77, 0xff, 0x36, 0x1e, 0x1f, 0xff, 0x49, 0x33, 0x36, 0xff, 0x68, 0x56, 0x59, 0xff, 0x6b, 0x5c, 0x5e, 0xff, 0x6b, 0x5f, 0x63, 0xff, 0x84, 0x79, 0x7a, 0xff, 0x8a, 0x81, 0x78, 0xff, 0x83, 0x73, 0x6c, 0xff, 0x6f, 0x59, 0x54, 0xff, 0x73, 0x64, 0x59, 0xff, 0x50, 0x3f, 0x3c, 0xff, 0x4a, 0x32, 0x30, 0xff, 0x3f, 0x29, 0x29, 0xff, 0x4f, 0x37, 0x36, 0xff, 0x59, 0x3c, 0x39, 0xff, 0x71, 0x56, 0x4d, 0xff, 0x40, 0x28, 0x23, 0xff, 0x3c, 0x25, 0x24, 0xff, 0x5a, 0x43, 0x40, 0xff, 0x42, 0x2a, 0x2b, 0xff, 0x37, 0x22, 0x25, 0xff, 0x3e, 0x2b, 0x2b, 0xff, 0x77, 0x69, 0x63, 0xff, 0x49, 0x3c, 0x3e, 0xff, 0x35, 0x20, 0x29, 0xff, 0x4d, 0x3b, 0x3c, 0xff, 0x76, 0x6a, 0x65, 0xff, 0xa7, 0xa1, 0x92, 0xff, 0x9f, 0x99, 0x95, 0xff, 0x91, 0x8c, 0x8c, 0xff, 0x9b, 0x98, 0x8f, 0xff, 0xcc, 0xc9, 0xc4, 0xff, 0xf9, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xf7, 0xf7, 0xec, 0xe8, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0xea, 0xe5, 0xff, 0xb1, 0xa2, 0x88, 0xff, 0x74, 0x5c, 0x38, 0xff, 0x87, 0x73, 0x4d, 0xff, 0x99, 0x86, 0x5f, 0xff, 0x77, 0x62, 0x3b, 0xff, 0xa4, 0x98, 0x73, 0xff, 0xb2, 0xa5, 0x84, 0xff, 0x9a, 0x8b, 0x67, 0xff, 0xc1, 0xb4, 0x88, 0xff, 0xc0, 0xb8, 0x92, 0xff, 0xd9, 0xdb, 0xc7, 0xff, 0xd5, 0xda, 0xc4, 0xff, 0xd9, 0xdf, 0xcc, 0xff, 0xe1, 0xeb, 0xe8, 0xff, 0xdf, 0xeb, 0xf1, 0xff, 0xe0, 0xed, 0xf0, 0xff, 0xe1, 0xee, 0xef, 0xff, 0xe4, 0xef, 0xf1, 0xff, 0xe5, 0xf1, 0xf3, 0xff, 0xe1, 0xed, 0xef, 0xff, 0xdf, 0xea, 0xef, 0xff, 0xe2, 0xed, 0xf0, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xe7, 0xf0, 0xf2, 0xff, 0xe6, 0xf0, 0xf2, 0xff, 0xe7, 0xf1, 0xf3, 0xff, 0xe5, 0xef, 0xf1, 0xff, 0xe6, 0xf1, 0xef, 0xff, 0xe8, 0xf5, 0xf4, 0xff, 0xe5, 0xf3, 0xf5, 0xff, 0xe8, 0xf4, 0xf4, 0xff, 0xd6, 0xdf, 0xe0, 0xff, 0xad, 0xb1, 0xbc, 0xff, 0x7e, 0x7b, 0x8f, 0xff, 0x70, 0x6a, 0x7b, 0xff, 0x87, 0x82, 0x89, 0xff, 0xa9, 0xa6, 0xa7, 0xff, 0xbf, 0xbd, 0xbd, 0xff, 0xbd, 0xba, 0xba, 0xff, 0xad, 0xaa, 0xab, 0xff, 0xa7, 0xa5, 0xa5, 0xff, 0xc7, 0xc6, 0xc1, 0xff, 0xc1, 0xc1, 0xbd, 0xff, 0xa3, 0xa0, 0xa1, 0xff, 0x86, 0x7c, 0x84, 0xff, 0x63, 0x52, 0x5a, 0xff, 0x56, 0x47, 0x4b, 0xff, 0x68, 0x5f, 0x60, 0xff, 0x68, 0x5e, 0x5f, 0xff, 0x4b, 0x3d, 0x3e, 0xff, 0x41, 0x33, 0x33, 0xff, 0x4c, 0x38, 0x38, 0xff, 0x62, 0x4d, 0x4b, 0xff, 0x9a, 0x89, 0x82, 0xff, 0x6b, 0x56, 0x4c, 0xff, 0x75, 0x69, 0x59, 0xff, 0xa6, 0x93, 0x7f, 0xff, 0x6a, 0x57, 0x4a, 0xff, 0x72, 0x5f, 0x59, 0xff, 0x89, 0x70, 0x5f, 0xff, 0xc3, 0xc7, 0xb7, 0xff, 0xa4, 0x9a, 0x8b, 0xff, 0x7b, 0x69, 0x58, 0xff, 0x96, 0x87, 0x7f, 0xff, 0x9f, 0x92, 0x89, 0xff, 0x61, 0x4c, 0x49, 0xff, 0x32, 0x18, 0x1a, 0xff, 0x43, 0x2c, 0x30, 0xff, 0x59, 0x46, 0x49, 0xff, 0x4a, 0x3a, 0x3d, 0xff, 0x4f, 0x3e, 0x42, 0xff, 0x88, 0x78, 0x6e, 0xff, 0x81, 0x6e, 0x64, 0xff, 0x5f, 0x4a, 0x45, 0xff, 0x72, 0x5f, 0x56, 0xff, 0x45, 0x30, 0x2e, 0xff, 0x40, 0x26, 0x26, 0xff, 0x49, 0x30, 0x30, 0xff, 0x38, 0x20, 0x1e, 0xff, 0x45, 0x2e, 0x2b, 0xff, 0x55, 0x3f, 0x3a, 0xff, 0x43, 0x2a, 0x26, 0xff, 0x46, 0x2e, 0x29, 0xff, 0x4c, 0x36, 0x32, 0xff, 0x40, 0x2b, 0x29, 0xff, 0x53, 0x3f, 0x3d, 0xff, 0x36, 0x1f, 0x1d, 0xff, 0x49, 0x35, 0x30, 0xff, 0x69, 0x5a, 0x51, 0xff, 0x37, 0x24, 0x1f, 0xff, 0x4b, 0x36, 0x3c, 0xff, 0x62, 0x4a, 0x4a, 0xff, 0x87, 0x74, 0x66, 0xff, 0xa5, 0x9d, 0x8c, 0xff, 0x9d, 0x96, 0x8f, 0xff, 0x8e, 0x8a, 0x88, 0xff, 0x9d, 0x9e, 0x95, 0xff, 0xd1, 0xcf, 0xc8, 0xff, 0xfb, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xdf, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xee, 0xea, 0xff, 0xa5, 0x96, 0x7d, 0xff, 0x73, 0x5c, 0x36, 0xff, 0x95, 0x7d, 0x59, 0xff, 0x80, 0x6b, 0x46, 0xff, 0x7e, 0x64, 0x3f, 0xff, 0x85, 0x6d, 0x4b, 0xff, 0xa3, 0x95, 0x73, 0xff, 0x84, 0x6d, 0x43, 0xff, 0x7c, 0x5b, 0x32, 0xff, 0x8b, 0x6f, 0x41, 0xff, 0x84, 0x6c, 0x39, 0xff, 0xbd, 0xb3, 0x97, 0xff, 0xe2, 0xe6, 0xd0, 0xff, 0xd6, 0xd9, 0xc9, 0xff, 0xe3, 0xee, 0xed, 0xff, 0xdb, 0xe9, 0xee, 0xff, 0xe0, 0xec, 0xf0, 0xff, 0xe3, 0xf0, 0xf2, 0xff, 0xe0, 0xed, 0xf1, 0xff, 0xe1, 0xec, 0xf1, 0xff, 0xe4, 0xf0, 0xf2, 0xff, 0xe4, 0xef, 0xf3, 0xff, 0xe7, 0xef, 0xf2, 0xff, 0xea, 0xf4, 0xf3, 0xff, 0xe4, 0xf1, 0xf3, 0xff, 0xe1, 0xee, 0xf1, 0xff, 0xe4, 0xf0, 0xf3, 0xff, 0xe6, 0xf2, 0xf4, 0xff, 0xe6, 0xf1, 0xf3, 0xff, 0xdd, 0xe9, 0xed, 0xff, 0xe0, 0xed, 0xf2, 0xff, 0xe2, 0xed, 0xf1, 0xff, 0xd8, 0xdd, 0xde, 0xff, 0xad, 0xad, 0xad, 0xff, 0x9c, 0x98, 0x98, 0xff, 0x9d, 0x99, 0x99, 0xff, 0x8b, 0x84, 0x88, 0xff, 0x73, 0x66, 0x6b, 0xff, 0x59, 0x4b, 0x4d, 0xff, 0x69, 0x5c, 0x5d, 0xff, 0x86, 0x7c, 0x7d, 0xff, 0x83, 0x7a, 0x7e, 0xff, 0x86, 0x7d, 0x85, 0xff, 0x5b, 0x4e, 0x58, 0xff, 0x44, 0x36, 0x3c, 0xff, 0x34, 0x23, 0x25, 0xff, 0x36, 0x21, 0x20, 0xff, 0x40, 0x29, 0x24, 0xff, 0x40, 0x2b, 0x23, 0xff, 0x6a, 0x59, 0x52, 0xff, 0x97, 0x88, 0x7f, 0xff, 0x7f, 0x70, 0x68, 0xff, 0x59, 0x44, 0x40, 0xff, 0x3e, 0x25, 0x23, 0xff, 0x63, 0x4f, 0x47, 0xff, 0x92, 0x7b, 0x6d, 0xff, 0x96, 0x89, 0x78, 0xff, 0x7f, 0x6e, 0x60, 0xff, 0x7c, 0x68, 0x5e, 0xff, 0x66, 0x51, 0x48, 0xff, 0x73, 0x58, 0x43, 0xff, 0xa7, 0x9a, 0x89, 0xff, 0xbe, 0xbd, 0xb0, 0xff, 0x73, 0x5e, 0x53, 0xff, 0x7b, 0x67, 0x5f, 0xff, 0x93, 0x86, 0x7a, 0xff, 0x81, 0x6c, 0x67, 0xff, 0x41, 0x2a, 0x2b, 0xff, 0x1a, 0x03, 0x07, 0xff, 0x52, 0x3c, 0x3f, 0xff, 0x5f, 0x4a, 0x4d, 0xff, 0x38, 0x26, 0x28, 0xff, 0x63, 0x4f, 0x4a, 0xff, 0x6d, 0x58, 0x53, 0xff, 0x56, 0x45, 0x3b, 0xff, 0x73, 0x62, 0x55, 0xff, 0x40, 0x2a, 0x2a, 0xff, 0x33, 0x1c, 0x1d, 0xff, 0x3c, 0x22, 0x23, 0xff, 0x3f, 0x28, 0x26, 0xff, 0x2c, 0x18, 0x17, 0xff, 0x40, 0x2c, 0x2b, 0xff, 0x5a, 0x43, 0x3d, 0xff, 0x53, 0x3a, 0x34, 0xff, 0x44, 0x2e, 0x2a, 0xff, 0x3e, 0x2a, 0x28, 0xff, 0x4b, 0x36, 0x31, 0xff, 0x5b, 0x40, 0x3a, 0xff, 0x39, 0x1e, 0x1b, 0xff, 0x43, 0x31, 0x29, 0xff, 0x54, 0x3f, 0x37, 0xff, 0x36, 0x20, 0x22, 0xff, 0x50, 0x39, 0x38, 0xff, 0x72, 0x5d, 0x56, 0xff, 0x9b, 0x8e, 0x83, 0xff, 0x84, 0x79, 0x72, 0xff, 0x80, 0x72, 0x73, 0xff, 0x8e, 0x85, 0x80, 0xff, 0xad, 0xaa, 0x9d, 0xff, 0xd1, 0xce, 0xc6, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xc6, 0xf0, 0xf0, 0xe1, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xf2, 0xf0, 0xff, 0xb3, 0xa5, 0x8e, 0xff, 0x87, 0x71, 0x4d, 0xff, 0x66, 0x4e, 0x2c, 0xff, 0x95, 0x7e, 0x58, 0xff, 0x8b, 0x74, 0x4b, 0xff, 0x71, 0x59, 0x30, 0xff, 0xc2, 0xba, 0x89, 0xff, 0xc7, 0xc6, 0x9c, 0xff, 0x89, 0x70, 0x49, 0xff, 0x73, 0x4e, 0x27, 0xff, 0x94, 0x7a, 0x4b, 0xff, 0xa8, 0x97, 0x6c, 0xff, 0xc4, 0xc4, 0xa6, 0xff, 0xd7, 0xd8, 0xba, 0xff, 0xd8, 0xdb, 0xc4, 0xff, 0xe4, 0xf3, 0xf0, 0xff, 0xda, 0xea, 0xee, 0xff, 0xe2, 0xed, 0xf0, 0xff, 0xe8, 0xf4, 0xf7, 0xff, 0xe1, 0xec, 0xf0, 0xff, 0xe2, 0xee, 0xf1, 0xff, 0xe5, 0xf1, 0xf4, 0xff, 0xe3, 0xef, 0xf1, 0xff, 0xe4, 0xef, 0xf1, 0xff, 0xe7, 0xf1, 0xf2, 0xff, 0xe4, 0xf0, 0xf2, 0xff, 0xe8, 0xf4, 0xf5, 0xff, 0xe7, 0xf3, 0xf4, 0xff, 0xe8, 0xf5, 0xf6, 0xff, 0xe7, 0xf0, 0xf3, 0xff, 0xd6, 0xe2, 0xec, 0xff, 0xc9, 0xdb, 0xec, 0xff, 0xbd, 0xc8, 0xd5, 0xff, 0xa8, 0xaa, 0xb0, 0xff, 0x99, 0x97, 0x97, 0xff, 0xa7, 0xa2, 0x9f, 0xff, 0x8e, 0x87, 0x86, 0xff, 0x81, 0x77, 0x77, 0xff, 0x7b, 0x6b, 0x6b, 0xff, 0x7c, 0x69, 0x65, 0xff, 0x79, 0x67, 0x62, 0xff, 0x6c, 0x57, 0x54, 0xff, 0x48, 0x2f, 0x2e, 0xff, 0x3a, 0x23, 0x23, 0xff, 0x41, 0x29, 0x2d, 0xff, 0x4b, 0x36, 0x34, 0xff, 0x56, 0x45, 0x40, 0xff, 0x6f, 0x59, 0x53, 0xff, 0x92, 0x7b, 0x6f, 0xff, 0x8f, 0x7d, 0x70, 0xff, 0x8a, 0x7c, 0x72, 0xff, 0x82, 0x71, 0x68, 0xff, 0x85, 0x71, 0x66, 0xff, 0x88, 0x72, 0x67, 0xff, 0x75, 0x5f, 0x59, 0xff, 0x5a, 0x42, 0x38, 0xff, 0x3a, 0x20, 0x15, 0xff, 0x9a, 0x8b, 0x80, 0xff, 0x83, 0x74, 0x6a, 0xff, 0x69, 0x57, 0x4a, 0xff, 0x33, 0x1c, 0x1b, 0xff, 0x62, 0x4b, 0x3d, 0xff, 0xb5, 0x9e, 0x87, 0xff, 0xc4, 0xbf, 0xad, 0xff, 0x86, 0x78, 0x70, 0xff, 0x5c, 0x43, 0x3e, 0xff, 0x6e, 0x56, 0x53, 0xff, 0x65, 0x4f, 0x4c, 0xff, 0x81, 0x6f, 0x6a, 0xff, 0x35, 0x22, 0x24, 0xff, 0x3c, 0x28, 0x25, 0xff, 0x60, 0x49, 0x4a, 0xff, 0x42, 0x32, 0x32, 0xff, 0x4b, 0x39, 0x35, 0xff, 0x67, 0x53, 0x50, 0xff, 0x50, 0x3d, 0x37, 0xff, 0x5a, 0x4a, 0x3e, 0xff, 0x4b, 0x39, 0x35, 0xff, 0x2c, 0x19, 0x1a, 0xff, 0x27, 0x13, 0x15, 0xff, 0x41, 0x2c, 0x2b, 0xff, 0x40, 0x2b, 0x29, 0xff, 0x2e, 0x19, 0x18, 0xff, 0x4a, 0x33, 0x2e, 0xff, 0x5b, 0x42, 0x3b, 0xff, 0x47, 0x31, 0x2b, 0xff, 0x38, 0x24, 0x21, 0xff, 0x3e, 0x28, 0x26, 0xff, 0x52, 0x39, 0x34, 0xff, 0x41, 0x29, 0x27, 0xff, 0x27, 0x13, 0x12, 0xff, 0x3e, 0x28, 0x27, 0xff, 0x45, 0x31, 0x2d, 0xff, 0x3d, 0x28, 0x21, 0xff, 0x4e, 0x39, 0x37, 0xff, 0x8b, 0x7b, 0x79, 0xff, 0x78, 0x6b, 0x63, 0xff, 0x51, 0x41, 0x39, 0xff, 0x75, 0x61, 0x5d, 0xff, 0x6f, 0x5f, 0x58, 0xff, 0x9b, 0x91, 0x84, 0xff, 0xd1, 0xce, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf7, 0xf6, 0xff, 0xa4, 0x96, 0x85, 0xff, 0x83, 0x6f, 0x4b, 0xff, 0x9b, 0x88, 0x5f, 0xff, 0x6b, 0x53, 0x30, 0xff, 0x71, 0x57, 0x37, 0xff, 0xa2, 0x8f, 0x65, 0xff, 0x9d, 0x8a, 0x5b, 0xff, 0xa7, 0x8e, 0x62, 0xff, 0xb6, 0xa9, 0x7b, 0xff, 0x8a, 0x77, 0x52, 0xff, 0x8d, 0x77, 0x52, 0xff, 0xc9, 0xbc, 0x8b, 0xff, 0xde, 0xde, 0xbc, 0xff, 0xe0, 0xe3, 0xc5, 0xff, 0xcb, 0xc7, 0xa8, 0xff, 0xd8, 0xdc, 0xc9, 0xff, 0xe5, 0xf1, 0xef, 0xff, 0xe0, 0xed, 0xf1, 0xff, 0xe3, 0xef, 0xf2, 0xff, 0xdc, 0xe9, 0xee, 0xff, 0xde, 0xea, 0xef, 0xff, 0xe5, 0xf1, 0xf3, 0xff, 0xe4, 0xef, 0xf2, 0xff, 0xe3, 0xef, 0xf1, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xe7, 0xf1, 0xf4, 0xff, 0xe8, 0xf5, 0xf4, 0xff, 0xe8, 0xf3, 0xf3, 0xff, 0xe8, 0xf4, 0xf6, 0xff, 0xde, 0xed, 0xf4, 0xff, 0xc5, 0xd2, 0xe0, 0xff, 0xa8, 0xac, 0xc0, 0xff, 0x96, 0x94, 0xa7, 0xff, 0x82, 0x7a, 0x84, 0xff, 0x6d, 0x5c, 0x5e, 0xff, 0x55, 0x41, 0x44, 0xff, 0x6c, 0x59, 0x5c, 0xff, 0x76, 0x62, 0x60, 0xff, 0x8d, 0x78, 0x72, 0xff, 0x98, 0x84, 0x7b, 0xff, 0x9c, 0x91, 0x86, 0xff, 0xae, 0x9f, 0x96, 0xff, 0x8e, 0x7e, 0x77, 0xff, 0x8e, 0x80, 0x76, 0xff, 0x7c, 0x67, 0x5c, 0xff, 0x85, 0x76, 0x68, 0xff, 0xae, 0xa2, 0x91, 0xff, 0xa1, 0x91, 0x7d, 0xff, 0x8c, 0x7b, 0x6b, 0xff, 0x87, 0x74, 0x69, 0xff, 0x88, 0x74, 0x6e, 0xff, 0x63, 0x4c, 0x41, 0xff, 0x56, 0x3c, 0x31, 0xff, 0x60, 0x45, 0x42, 0xff, 0x78, 0x64, 0x56, 0xff, 0x6e, 0x53, 0x47, 0xff, 0x47, 0x29, 0x25, 0xff, 0x3a, 0x25, 0x23, 0xff, 0x8a, 0x79, 0x6e, 0xff, 0x7f, 0x6a, 0x5f, 0xff, 0x65, 0x51, 0x4d, 0xff, 0x38, 0x21, 0x1c, 0xff, 0x86, 0x6b, 0x57, 0xff, 0x94, 0x81, 0x6b, 0xff, 0xa7, 0xa1, 0x97, 0xff, 0x6f, 0x5e, 0x56, 0xff, 0x6e, 0x55, 0x51, 0xff, 0x50, 0x39, 0x3a, 0xff, 0x65, 0x52, 0x49, 0xff, 0x66, 0x56, 0x4f, 0xff, 0x2c, 0x17, 0x19, 0xff, 0x41, 0x2b, 0x2e, 0xff, 0x66, 0x55, 0x56, 0xff, 0x54, 0x44, 0x41, 0xff, 0x5e, 0x4d, 0x48, 0xff, 0x4a, 0x35, 0x34, 0xff, 0x44, 0x30, 0x29, 0xff, 0x46, 0x32, 0x2d, 0xff, 0x2c, 0x17, 0x19, 0xff, 0x2b, 0x16, 0x1a, 0xff, 0x30, 0x1a, 0x1b, 0xff, 0x31, 0x1c, 0x19, 0xff, 0x42, 0x2e, 0x2d, 0xff, 0x42, 0x2e, 0x29, 0xff, 0x47, 0x30, 0x2b, 0xff, 0x57, 0x42, 0x3b, 0xff, 0x3e, 0x29, 0x27, 0xff, 0x2f, 0x18, 0x1c, 0xff, 0x3c, 0x26, 0x25, 0xff, 0x30, 0x1c, 0x1d, 0xff, 0x34, 0x20, 0x20, 0xff, 0x2f, 0x1d, 0x19, 0xff, 0x30, 0x1b, 0x1a, 0xff, 0x38, 0x21, 0x21, 0xff, 0x5c, 0x47, 0x48, 0xff, 0x64, 0x52, 0x52, 0xff, 0x73, 0x60, 0x52, 0xff, 0x74, 0x63, 0x51, 0xff, 0x65, 0x51, 0x4b, 0xff, 0x52, 0x3d, 0x34, 0xff, 0x6f, 0x5c, 0x4d, 0xff, 0x8c, 0x79, 0x72, 0xff, 0xc9, 0xc4, 0xc2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xb8, 0xac, 0x9f, 0xff, 0x6e, 0x55, 0x3d, 0xff, 0x62, 0x4c, 0x2c, 0xff, 0x93, 0x7e, 0x50, 0xff, 0x99, 0x84, 0x5a, 0xff, 0x6e, 0x53, 0x2f, 0xff, 0xa0, 0x8c, 0x61, 0xff, 0xa7, 0x93, 0x66, 0xff, 0x65, 0x3c, 0x16, 0xff, 0x76, 0x5a, 0x2d, 0xff, 0xbc, 0xb2, 0x84, 0xff, 0xcd, 0xc6, 0xa1, 0xff, 0xc2, 0xbd, 0x95, 0xff, 0xd3, 0xd3, 0xb5, 0xff, 0xcf, 0xcd, 0xb2, 0xff, 0xc6, 0xc6, 0xad, 0xff, 0xe3, 0xec, 0xe3, 0xff, 0xdf, 0xe9, 0xe7, 0xff, 0xe6, 0xf0, 0xf3, 0xff, 0xda, 0xe8, 0xef, 0xff, 0xd6, 0xe4, 0xeb, 0xff, 0xdf, 0xec, 0xf1, 0xff, 0xe5, 0xf0, 0xf3, 0xff, 0xe7, 0xf0, 0xf3, 0xff, 0xe7, 0xef, 0xf3, 0xff, 0xe7, 0xf1, 0xf1, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe9, 0xf4, 0xf5, 0xff, 0xe8, 0xf5, 0xf4, 0xff, 0xe9, 0xf5, 0xf6, 0xff, 0xd8, 0xe4, 0xe8, 0xff, 0xab, 0xaf, 0xc0, 0xff, 0x78, 0x7b, 0x91, 0xff, 0x6f, 0x72, 0x86, 0xff, 0x97, 0x95, 0xa6, 0xff, 0x61, 0x53, 0x5b, 0xff, 0x46, 0x30, 0x30, 0xff, 0x6e, 0x58, 0x57, 0xff, 0x8a, 0x77, 0x7a, 0xff, 0x91, 0x80, 0x7d, 0xff, 0x75, 0x64, 0x5e, 0xff, 0x8c, 0x77, 0x71, 0xff, 0xa3, 0x94, 0x8b, 0xff, 0x8e, 0x82, 0x78, 0xff, 0xc3, 0xbd, 0xb6, 0xff, 0xcf, 0xc8, 0xc2, 0xff, 0xad, 0x9d, 0x95, 0xff, 0x88, 0x75, 0x6a, 0xff, 0x95, 0x87, 0x78, 0xff, 0xaa, 0xa2, 0x93, 0xff, 0xc6, 0xbe, 0xab, 0xff, 0x95, 0x81, 0x74, 0xff, 0x63, 0x47, 0x39, 0xff, 0x60, 0x42, 0x37, 0xff, 0x45, 0x28, 0x23, 0xff, 0x3c, 0x23, 0x21, 0xff, 0x36, 0x20, 0x1d, 0xff, 0x4b, 0x2f, 0x2b, 0xff, 0x61, 0x42, 0x41, 0xff, 0x3b, 0x26, 0x1f, 0xff, 0x60, 0x4d, 0x3d, 0xff, 0x70, 0x5a, 0x4e, 0xff, 0xa3, 0x90, 0x7f, 0xff, 0x45, 0x2a, 0x27, 0xff, 0x47, 0x29, 0x21, 0xff, 0x63, 0x48, 0x37, 0xff, 0x7e, 0x6b, 0x66, 0xff, 0x8a, 0x7d, 0x76, 0xff, 0x5e, 0x4a, 0x3f, 0xff, 0x6d, 0x53, 0x4f, 0xff, 0x53, 0x3a, 0x39, 0xff, 0x56, 0x41, 0x39, 0xff, 0x34, 0x21, 0x1f, 0xff, 0x1c, 0x05, 0x0f, 0xff, 0x3a, 0x26, 0x2b, 0xff, 0x5d, 0x4a, 0x4b, 0xff, 0x59, 0x48, 0x46, 0xff, 0x6a, 0x5b, 0x56, 0xff, 0x4e, 0x3d, 0x39, 0xff, 0x29, 0x15, 0x13, 0xff, 0x40, 0x2b, 0x2c, 0xff, 0x34, 0x1f, 0x23, 0xff, 0x2d, 0x19, 0x1a, 0xff, 0x2d, 0x19, 0x18, 0xff, 0x38, 0x23, 0x25, 0xff, 0x3e, 0x29, 0x28, 0xff, 0x36, 0x20, 0x1e, 0xff, 0x43, 0x2e, 0x2d, 0xff, 0x30, 0x1b, 0x1b, 0xff, 0x2f, 0x18, 0x1d, 0xff, 0x3b, 0x25, 0x25, 0xff, 0x2c, 0x19, 0x17, 0xff, 0x26, 0x12, 0x11, 0xff, 0x26, 0x12, 0x11, 0xff, 0x2b, 0x16, 0x14, 0xff, 0x33, 0x1f, 0x1c, 0xff, 0x69, 0x57, 0x56, 0xff, 0x4f, 0x3c, 0x3c, 0xff, 0x40, 0x25, 0x1d, 0xff, 0x54, 0x3b, 0x31, 0xff, 0x92, 0x83, 0x78, 0xff, 0x84, 0x78, 0x66, 0xff, 0x5f, 0x47, 0x3b, 0xff, 0x55, 0x36, 0x30, 0xff, 0x7c, 0x6b, 0x5f, 0xff, 0xd8, 0xd1, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xc9, 0xc1, 0xb8, 0xff, 0x71, 0x5b, 0x43, 0xff, 0x68, 0x4f, 0x35, 0xff, 0x62, 0x48, 0x2e, 0xff, 0x65, 0x4b, 0x2b, 0xff, 0x99, 0x83, 0x59, 0xff, 0xb8, 0xa4, 0x75, 0xff, 0x8f, 0x78, 0x48, 0xff, 0x82, 0x6b, 0x40, 0xff, 0x91, 0x84, 0x5f, 0xff, 0xaf, 0x9e, 0x72, 0xff, 0xcd, 0xc0, 0x8e, 0xff, 0xe4, 0xe3, 0xbe, 0xff, 0xca, 0xc7, 0xa8, 0xff, 0xc7, 0xc1, 0xa2, 0xff, 0xcb, 0xc4, 0xa5, 0xff, 0xca, 0xc8, 0xa9, 0xff, 0xdb, 0xe0, 0xca, 0xff, 0xe7, 0xf3, 0xf0, 0xff, 0xe2, 0xef, 0xf5, 0xff, 0xda, 0xe8, 0xef, 0xff, 0xdb, 0xe9, 0xf0, 0xff, 0xe2, 0xef, 0xf2, 0xff, 0xe8, 0xf2, 0xf3, 0xff, 0xea, 0xf2, 0xf6, 0xff, 0xe7, 0xf0, 0xf1, 0xff, 0xe7, 0xf2, 0xf2, 0xff, 0xea, 0xf3, 0xf3, 0xff, 0xe8, 0xf3, 0xf2, 0xff, 0xe6, 0xf3, 0xf2, 0xff, 0xea, 0xf6, 0xf9, 0xff, 0xcb, 0xd6, 0xdd, 0xff, 0x97, 0x95, 0xa0, 0xff, 0x78, 0x75, 0x91, 0xff, 0x79, 0x7f, 0xa6, 0xff, 0x88, 0x87, 0x9b, 0xff, 0x61, 0x58, 0x5c, 0xff, 0x71, 0x63, 0x68, 0xff, 0x7a, 0x69, 0x6c, 0xff, 0x45, 0x32, 0x36, 0xff, 0x5c, 0x4c, 0x47, 0xff, 0x6d, 0x63, 0x5b, 0xff, 0x78, 0x63, 0x5e, 0xff, 0x9b, 0x87, 0x82, 0xff, 0x8f, 0x7b, 0x72, 0xff, 0x75, 0x61, 0x56, 0xff, 0x7b, 0x6d, 0x67, 0xff, 0x8d, 0x7c, 0x78, 0xff, 0xab, 0x95, 0x90, 0xff, 0x81, 0x70, 0x68, 0xff, 0x59, 0x4b, 0x44, 0xff, 0x6e, 0x5e, 0x58, 0xff, 0x8d, 0x7c, 0x6d, 0xff, 0x82, 0x6c, 0x5d, 0xff, 0x7e, 0x63, 0x53, 0xff, 0x46, 0x2b, 0x24, 0xff, 0x2a, 0x11, 0x14, 0xff, 0x3b, 0x27, 0x25, 0xff, 0x52, 0x3b, 0x33, 0xff, 0x58, 0x3e, 0x34, 0xff, 0x49, 0x37, 0x2f, 0xff, 0x2f, 0x17, 0x1a, 0xff, 0x4b, 0x2f, 0x2c, 0xff, 0x67, 0x50, 0x47, 0xff, 0x7d, 0x68, 0x60, 0xff, 0x63, 0x4b, 0x45, 0xff, 0x63, 0x46, 0x3d, 0xff, 0x65, 0x4d, 0x47, 0xff, 0x86, 0x75, 0x71, 0xff, 0x66, 0x54, 0x4b, 0xff, 0x61, 0x4f, 0x45, 0xff, 0x59, 0x41, 0x3f, 0xff, 0x49, 0x2f, 0x2d, 0xff, 0x66, 0x50, 0x48, 0xff, 0x30, 0x1a, 0x1c, 0xff, 0x22, 0x0b, 0x11, 0xff, 0x38, 0x23, 0x27, 0xff, 0x59, 0x4b, 0x4b, 0xff, 0x7a, 0x6f, 0x6c, 0xff, 0x66, 0x56, 0x55, 0xff, 0x3a, 0x26, 0x24, 0xff, 0x31, 0x1b, 0x19, 0xff, 0x31, 0x1d, 0x1e, 0xff, 0x20, 0x0d, 0x0f, 0xff, 0x31, 0x1e, 0x20, 0xff, 0x20, 0x0e, 0x12, 0xff, 0x2b, 0x16, 0x18, 0xff, 0x37, 0x20, 0x21, 0xff, 0x3c, 0x27, 0x26, 0xff, 0x3b, 0x28, 0x25, 0xff, 0x2f, 0x19, 0x1b, 0xff, 0x2d, 0x18, 0x17, 0xff, 0x38, 0x24, 0x22, 0xff, 0x30, 0x1b, 0x1a, 0xff, 0x2f, 0x19, 0x1b, 0xff, 0x38, 0x23, 0x20, 0xff, 0x3a, 0x27, 0x1f, 0xff, 0x65, 0x54, 0x51, 0xff, 0x47, 0x36, 0x38, 0xff, 0x35, 0x21, 0x1f, 0xff, 0x66, 0x50, 0x4c, 0xff, 0x7c, 0x6c, 0x5c, 0xff, 0x82, 0x6f, 0x58, 0xff, 0x64, 0x4d, 0x43, 0xff, 0x3d, 0x26, 0x22, 0xff, 0x61, 0x4a, 0x41, 0xff, 0x85, 0x72, 0x6a, 0xff, 0xe1, 0xdc, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xd6, 0xd1, 0xff, 0x7b, 0x64, 0x50, 0xff, 0x61, 0x49, 0x2e, 0xff, 0x64, 0x4a, 0x31, 0xff, 0x68, 0x4b, 0x33, 0xff, 0x65, 0x4a, 0x31, 0xff, 0x70, 0x57, 0x33, 0xff, 0xa4, 0x91, 0x61, 0xff, 0xa6, 0x92, 0x5e, 0xff, 0xa4, 0x91, 0x5f, 0xff, 0xce, 0xc2, 0x9f, 0xff, 0xc1, 0xb1, 0x7e, 0xff, 0xbb, 0xa8, 0x71, 0xff, 0xbb, 0xab, 0x89, 0xff, 0xb7, 0xb1, 0x8b, 0xff, 0xbc, 0xb6, 0x90, 0xff, 0xe2, 0xdc, 0xc6, 0xff, 0xe0, 0xe5, 0xcf, 0xff, 0xd8, 0xdd, 0xce, 0xff, 0xea, 0xf5, 0xf8, 0xff, 0xe3, 0xef, 0xf5, 0xff, 0xe2, 0xef, 0xf2, 0xff, 0xe4, 0xf0, 0xf4, 0xff, 0xe8, 0xf2, 0xf4, 0xff, 0xe8, 0xf1, 0xf1, 0xff, 0xe7, 0xf2, 0xf2, 0xff, 0xe6, 0xf2, 0xf1, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe9, 0xf3, 0xf2, 0xff, 0xe7, 0xf3, 0xf4, 0xff, 0xe5, 0xf3, 0xf3, 0xff, 0xe4, 0xf0, 0xf4, 0xff, 0xc6, 0xd0, 0xda, 0xff, 0x95, 0x9a, 0xb0, 0xff, 0x89, 0x87, 0xa0, 0xff, 0x82, 0x79, 0x8d, 0xff, 0x61, 0x55, 0x65, 0xff, 0x79, 0x6e, 0x7b, 0xff, 0x8d, 0x7f, 0x84, 0xff, 0x5c, 0x44, 0x45, 0xff, 0x54, 0x3a, 0x3b, 0xff, 0x56, 0x40, 0x3c, 0xff, 0x5d, 0x49, 0x46, 0xff, 0x54, 0x3b, 0x37, 0xff, 0x52, 0x3d, 0x36, 0xff, 0x8b, 0x78, 0x6d, 0xff, 0x6f, 0x54, 0x45, 0xff, 0x56, 0x40, 0x34, 0xff, 0x65, 0x4e, 0x46, 0xff, 0x5e, 0x45, 0x3c, 0xff, 0x7d, 0x68, 0x5e, 0xff, 0x76, 0x5c, 0x55, 0xff, 0x54, 0x36, 0x31, 0xff, 0x49, 0x2e, 0x28, 0xff, 0x63, 0x4c, 0x43, 0xff, 0x6a, 0x50, 0x43, 0xff, 0x5f, 0x44, 0x3c, 0xff, 0x3d, 0x26, 0x23, 0xff, 0x3c, 0x29, 0x25, 0xff, 0x28, 0x11, 0x13, 0xff, 0x54, 0x3e, 0x37, 0xff, 0x68, 0x56, 0x4e, 0xff, 0x3a, 0x25, 0x25, 0xff, 0x39, 0x1e, 0x1e, 0xff, 0x35, 0x1c, 0x1b, 0xff, 0x4b, 0x37, 0x30, 0xff, 0x5e, 0x4a, 0x43, 0xff, 0x73, 0x5c, 0x54, 0xff, 0x49, 0x34, 0x2d, 0xff, 0x5f, 0x4d, 0x4c, 0xff, 0x92, 0x87, 0x80, 0xff, 0x48, 0x3e, 0x37, 0xff, 0x54, 0x3f, 0x3a, 0xff, 0x41, 0x27, 0x25, 0xff, 0x4a, 0x31, 0x30, 0xff, 0x5e, 0x4a, 0x40, 0xff, 0x2d, 0x17, 0x14, 0xff, 0x17, 0x01, 0x06, 0xff, 0x40, 0x2e, 0x33, 0xff, 0x79, 0x6e, 0x6b, 0xff, 0x62, 0x56, 0x52, 0xff, 0x42, 0x30, 0x2c, 0xff, 0x47, 0x2f, 0x2b, 0xff, 0x3d, 0x29, 0x26, 0xff, 0x2e, 0x1b, 0x1a, 0xff, 0x1f, 0x0c, 0x0d, 0xff, 0x24, 0x11, 0x13, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x37, 0x21, 0x24, 0xff, 0x38, 0x23, 0x23, 0xff, 0x38, 0x25, 0x22, 0xff, 0x34, 0x1e, 0x20, 0xff, 0x34, 0x1f, 0x1d, 0xff, 0x30, 0x1c, 0x1a, 0xff, 0x27, 0x13, 0x15, 0xff, 0x2e, 0x1b, 0x1b, 0xff, 0x2d, 0x19, 0x17, 0xff, 0x32, 0x1b, 0x1c, 0xff, 0x5d, 0x48, 0x48, 0xff, 0x42, 0x31, 0x2f, 0xff, 0x38, 0x29, 0x22, 0xff, 0x6c, 0x5f, 0x56, 0xff, 0x6a, 0x59, 0x49, 0xff, 0x91, 0x7a, 0x61, 0xff, 0x6a, 0x56, 0x4c, 0xff, 0x57, 0x47, 0x3f, 0xff, 0x5b, 0x48, 0x43, 0xff, 0x40, 0x2e, 0x2a, 0xff, 0x93, 0x84, 0x7d, 0xff, 0xf1, 0xef, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xeb, 0xe9, 0xff, 0x89, 0x77, 0x66, 0xff, 0x5f, 0x43, 0x2d, 0xff, 0x5e, 0x46, 0x2b, 0xff, 0x62, 0x48, 0x2f, 0xff, 0x66, 0x4c, 0x30, 0xff, 0x68, 0x4e, 0x30, 0xff, 0x6b, 0x51, 0x31, 0xff, 0x8b, 0x76, 0x4a, 0xff, 0xb7, 0xa9, 0x7c, 0xff, 0xbe, 0xb6, 0x89, 0xff, 0xae, 0xa2, 0x74, 0xff, 0xab, 0x9c, 0x70, 0xff, 0xc8, 0xc1, 0x9c, 0xff, 0xa1, 0x86, 0x5b, 0xff, 0xb5, 0xab, 0x7f, 0xff, 0xec, 0xf5, 0xde, 0xff, 0xd5, 0xd4, 0xba, 0xff, 0xd7, 0xd8, 0xbb, 0xff, 0xe9, 0xf2, 0xeb, 0xff, 0xe7, 0xf2, 0xf6, 0xff, 0xe5, 0xf2, 0xf3, 0xff, 0xe6, 0xf3, 0xf4, 0xff, 0xea, 0xf3, 0xf6, 0xff, 0xe9, 0xf2, 0xf4, 0xff, 0xeb, 0xf4, 0xf4, 0xff, 0xe9, 0xf4, 0xf4, 0xff, 0xe9, 0xf3, 0xf3, 0xff, 0xe9, 0xee, 0xed, 0xff, 0xdd, 0xe3, 0xe3, 0xff, 0xe6, 0xf0, 0xf0, 0xff, 0xed, 0xf8, 0xf8, 0xff, 0xeb, 0xf6, 0xf8, 0xff, 0xc9, 0xd1, 0xdc, 0xff, 0xb2, 0xbc, 0xd1, 0xff, 0x8c, 0x90, 0xa2, 0xff, 0x70, 0x67, 0x73, 0xff, 0x7b, 0x70, 0x7d, 0xff, 0x78, 0x69, 0x73, 0xff, 0x57, 0x42, 0x42, 0xff, 0x6e, 0x55, 0x4f, 0xff, 0x74, 0x5b, 0x55, 0xff, 0x65, 0x50, 0x4d, 0xff, 0x67, 0x51, 0x4c, 0xff, 0x77, 0x60, 0x56, 0xff, 0x69, 0x50, 0x46, 0xff, 0x46, 0x2e, 0x29, 0xff, 0x60, 0x48, 0x43, 0xff, 0x5a, 0x3e, 0x38, 0xff, 0x54, 0x38, 0x34, 0xff, 0x42, 0x27, 0x26, 0xff, 0x3c, 0x23, 0x23, 0xff, 0x4f, 0x36, 0x35, 0xff, 0x3d, 0x22, 0x20, 0xff, 0x4b, 0x31, 0x2f, 0xff, 0x3f, 0x25, 0x22, 0xff, 0x4b, 0x30, 0x2b, 0xff, 0x54, 0x39, 0x32, 0xff, 0x47, 0x2e, 0x28, 0xff, 0x39, 0x23, 0x22, 0xff, 0x35, 0x20, 0x21, 0xff, 0x21, 0x0a, 0x10, 0xff, 0x4a, 0x36, 0x34, 0xff, 0x56, 0x44, 0x3c, 0xff, 0x3d, 0x27, 0x23, 0xff, 0x42, 0x2c, 0x2a, 0xff, 0x30, 0x19, 0x1a, 0xff, 0x40, 0x27, 0x23, 0xff, 0x5c, 0x45, 0x3b, 0xff, 0x6c, 0x56, 0x4f, 0xff, 0x3a, 0x25, 0x24, 0xff, 0x76, 0x68, 0x63, 0xff, 0x7d, 0x72, 0x6a, 0xff, 0x48, 0x34, 0x30, 0xff, 0x50, 0x39, 0x38, 0xff, 0x33, 0x1b, 0x19, 0xff, 0x46, 0x32, 0x29, 0xff, 0x52, 0x3f, 0x37, 0xff, 0x2d, 0x17, 0x1b, 0xff, 0x1d, 0x07, 0x0d, 0xff, 0x61, 0x51, 0x4f, 0xff, 0x88, 0x7e, 0x79, 0xff, 0x5f, 0x50, 0x4b, 0xff, 0x60, 0x49, 0x42, 0xff, 0x4a, 0x33, 0x2c, 0xff, 0x3d, 0x28, 0x22, 0xff, 0x43, 0x2f, 0x2b, 0xff, 0x2d, 0x1a, 0x18, 0xff, 0x32, 0x1e, 0x1f, 0xff, 0x30, 0x1a, 0x1c, 0xff, 0x37, 0x21, 0x22, 0xff, 0x29, 0x16, 0x17, 0xff, 0x27, 0x13, 0x16, 0xff, 0x3c, 0x27, 0x27, 0xff, 0x3d, 0x29, 0x26, 0xff, 0x1f, 0x0b, 0x10, 0xff, 0x27, 0x17, 0x13, 0xff, 0x2e, 0x1b, 0x1b, 0xff, 0x2f, 0x19, 0x1c, 0xff, 0x60, 0x4d, 0x46, 0xff, 0x34, 0x20, 0x1a, 0xff, 0x3e, 0x2c, 0x24, 0xff, 0x66, 0x5a, 0x50, 0xff, 0x65, 0x52, 0x45, 0xff, 0x70, 0x5a, 0x47, 0xff, 0x64, 0x52, 0x47, 0xff, 0x40, 0x2d, 0x22, 0xff, 0x46, 0x32, 0x2c, 0xff, 0x4a, 0x33, 0x32, 0xff, 0x4e, 0x39, 0x2e, 0xff, 0x99, 0x8d, 0x89, 0xff, 0xfb, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfa, 0xff, 0xa8, 0x9b, 0x8d, 0xff, 0x5e, 0x44, 0x2c, 0xff, 0x5e, 0x43, 0x30, 0xff, 0x61, 0x44, 0x30, 0xff, 0x5d, 0x42, 0x2a, 0xff, 0x59, 0x3f, 0x27, 0xff, 0x67, 0x4b, 0x35, 0xff, 0x84, 0x69, 0x4b, 0xff, 0x8f, 0x7b, 0x52, 0xff, 0xba, 0xa8, 0x7e, 0xff, 0xab, 0x98, 0x68, 0xff, 0xac, 0x97, 0x60, 0xff, 0xa7, 0x92, 0x62, 0xff, 0xbe, 0xb4, 0x89, 0xff, 0xab, 0x8e, 0x52, 0xff, 0xcb, 0xb8, 0x84, 0xff, 0xe7, 0xea, 0xd7, 0xff, 0xdc, 0xdf, 0xcf, 0xff, 0xe0, 0xe3, 0xcd, 0xff, 0xe8, 0xf2, 0xec, 0xff, 0xe8, 0xf3, 0xf7, 0xff, 0xe7, 0xf4, 0xf5, 0xff, 0xe8, 0xf4, 0xf7, 0xff, 0xe9, 0xf2, 0xf5, 0xff, 0xeb, 0xf5, 0xf4, 0xff, 0xeb, 0xf6, 0xf6, 0xff, 0xeb, 0xf4, 0xf6, 0xff, 0xe5, 0xeb, 0xeb, 0xff, 0xd7, 0xde, 0xdb, 0xff, 0xe0, 0xe3, 0xdf, 0xff, 0xbd, 0xb8, 0xb5, 0xff, 0xc3, 0xc0, 0xbe, 0xff, 0xbf, 0xbe, 0xc1, 0xff, 0x94, 0x93, 0x9f, 0xff, 0xa6, 0xa9, 0xb7, 0xff, 0xb3, 0xb2, 0xbc, 0xff, 0x78, 0x6c, 0x77, 0xff, 0x51, 0x40, 0x49, 0xff, 0x44, 0x2b, 0x32, 0xff, 0x48, 0x30, 0x30, 0xff, 0x62, 0x53, 0x4c, 0xff, 0x74, 0x63, 0x5b, 0xff, 0x56, 0x3d, 0x3a, 0xff, 0x55, 0x3a, 0x3b, 0xff, 0x6c, 0x51, 0x4a, 0xff, 0x7c, 0x61, 0x57, 0xff, 0x5f, 0x46, 0x3f, 0xff, 0x51, 0x39, 0x37, 0xff, 0x47, 0x2f, 0x2e, 0xff, 0x36, 0x1f, 0x20, 0xff, 0x2b, 0x14, 0x1a, 0xff, 0x25, 0x10, 0x14, 0xff, 0x29, 0x14, 0x16, 0xff, 0x31, 0x1b, 0x1e, 0xff, 0x34, 0x1e, 0x1f, 0xff, 0x3a, 0x24, 0x25, 0xff, 0x34, 0x1e, 0x20, 0xff, 0x36, 0x1e, 0x1e, 0xff, 0x41, 0x27, 0x24, 0xff, 0x44, 0x2a, 0x27, 0xff, 0x36, 0x21, 0x1f, 0xff, 0x25, 0x0e, 0x13, 0xff, 0x31, 0x1c, 0x1b, 0xff, 0x3f, 0x2b, 0x25, 0xff, 0x2d, 0x17, 0x17, 0xff, 0x4a, 0x36, 0x35, 0xff, 0x51, 0x3c, 0x3c, 0xff, 0x2f, 0x19, 0x16, 0xff, 0x43, 0x2b, 0x28, 0xff, 0x49, 0x34, 0x2f, 0xff, 0x5d, 0x4a, 0x3f, 0xff, 0x5b, 0x48, 0x43, 0xff, 0x78, 0x66, 0x65, 0xff, 0x76, 0x65, 0x62, 0xff, 0x54, 0x40, 0x3d, 0xff, 0x4b, 0x35, 0x31, 0xff, 0x40, 0x29, 0x24, 0xff, 0x47, 0x31, 0x2e, 0xff, 0x48, 0x35, 0x30, 0xff, 0x2b, 0x17, 0x15, 0xff, 0x28, 0x14, 0x19, 0xff, 0x6c, 0x5e, 0x5f, 0xff, 0x7f, 0x72, 0x6e, 0xff, 0x40, 0x2c, 0x26, 0xff, 0x53, 0x3b, 0x35, 0xff, 0x33, 0x1d, 0x17, 0xff, 0x45, 0x34, 0x2c, 0xff, 0x42, 0x30, 0x29, 0xff, 0x27, 0x15, 0x12, 0xff, 0x34, 0x1e, 0x1d, 0xff, 0x3c, 0x26, 0x24, 0xff, 0x33, 0x20, 0x1f, 0xff, 0x2f, 0x1c, 0x1e, 0xff, 0x35, 0x21, 0x20, 0xff, 0x3a, 0x27, 0x22, 0xff, 0x20, 0x0a, 0x0f, 0xff, 0x28, 0x14, 0x15, 0xff, 0x31, 0x1d, 0x1b, 0xff, 0x37, 0x25, 0x1e, 0xff, 0x68, 0x57, 0x4b, 0xff, 0x2a, 0x16, 0x14, 0xff, 0x3b, 0x28, 0x20, 0xff, 0x56, 0x42, 0x34, 0xff, 0x54, 0x3c, 0x34, 0xff, 0x4d, 0x34, 0x2a, 0xff, 0x8d, 0x7c, 0x6a, 0xff, 0x46, 0x33, 0x31, 0xff, 0x38, 0x24, 0x1f, 0xff, 0x48, 0x34, 0x2f, 0xff, 0x4d, 0x38, 0x32, 0xff, 0x55, 0x3e, 0x37, 0xff, 0xc9, 0xc2, 0xbe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe7, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xc0, 0xb7, 0xff, 0x69, 0x50, 0x3b, 0xff, 0x5a, 0x3e, 0x2c, 0xff, 0x5a, 0x3e, 0x2a, 0xff, 0x65, 0x48, 0x30, 0xff, 0x6b, 0x53, 0x34, 0xff, 0x70, 0x56, 0x39, 0xff, 0x82, 0x67, 0x47, 0xff, 0x96, 0x82, 0x5f, 0xff, 0x66, 0x4e, 0x2b, 0xff, 0x7d, 0x62, 0x32, 0xff, 0xaa, 0x97, 0x61, 0xff, 0xba, 0xa8, 0x7a, 0xff, 0xf2, 0xf4, 0xd0, 0xff, 0xd2, 0xcf, 0xa7, 0xff, 0xc2, 0xb1, 0x87, 0xff, 0xc1, 0xb3, 0x7a, 0xff, 0xc8, 0xba, 0x8d, 0xff, 0xf0, 0xf2, 0xe9, 0xff, 0xe1, 0xeb, 0xde, 0xff, 0xe8, 0xf0, 0xed, 0xff, 0xeb, 0xf4, 0xf8, 0xff, 0xea, 0xf6, 0xf7, 0xff, 0xe8, 0xf5, 0xf8, 0xff, 0xe9, 0xf3, 0xf4, 0xff, 0xed, 0xf6, 0xf6, 0xff, 0xeb, 0xf8, 0xf8, 0xff, 0xe6, 0xf0, 0xed, 0xff, 0xd0, 0xd3, 0xd1, 0xff, 0xe1, 0xed, 0xea, 0xff, 0xe8, 0xea, 0xe5, 0xff, 0x89, 0x76, 0x6f, 0xff, 0x62, 0x50, 0x4d, 0xff, 0x80, 0x71, 0x75, 0xff, 0x6b, 0x5d, 0x67, 0xff, 0x7c, 0x6b, 0x75, 0xff, 0x78, 0x61, 0x6b, 0xff, 0x42, 0x29, 0x30, 0xff, 0x2b, 0x12, 0x13, 0xff, 0x42, 0x23, 0x24, 0xff, 0x42, 0x23, 0x25, 0xff, 0x50, 0x39, 0x36, 0xff, 0x5e, 0x46, 0x44, 0xff, 0x4b, 0x30, 0x30, 0xff, 0x3c, 0x22, 0x20, 0xff, 0x39, 0x20, 0x1a, 0xff, 0x41, 0x27, 0x24, 0xff, 0x46, 0x2d, 0x30, 0xff, 0x41, 0x2c, 0x2c, 0xff, 0x3a, 0x24, 0x21, 0xff, 0x3b, 0x25, 0x23, 0xff, 0x32, 0x1e, 0x1f, 0xff, 0x26, 0x12, 0x15, 0xff, 0x29, 0x14, 0x18, 0xff, 0x2c, 0x18, 0x1b, 0xff, 0x2c, 0x16, 0x18, 0xff, 0x31, 0x1b, 0x1e, 0xff, 0x31, 0x1c, 0x21, 0xff, 0x2a, 0x15, 0x1a, 0xff, 0x32, 0x1b, 0x1a, 0xff, 0x4f, 0x35, 0x31, 0xff, 0x32, 0x1b, 0x1a, 0xff, 0x2a, 0x14, 0x19, 0xff, 0x2b, 0x16, 0x16, 0xff, 0x34, 0x20, 0x1d, 0xff, 0x1f, 0x0b, 0x0f, 0xff, 0x29, 0x15, 0x16, 0xff, 0x3f, 0x2a, 0x2a, 0xff, 0x3f, 0x2a, 0x2a, 0xff, 0x2d, 0x1a, 0x1b, 0xff, 0x41, 0x2e, 0x2b, 0xff, 0x5d, 0x4a, 0x44, 0xff, 0x35, 0x20, 0x1e, 0xff, 0x43, 0x2d, 0x2f, 0xff, 0x75, 0x63, 0x60, 0xff, 0x8a, 0x7a, 0x74, 0xff, 0x6d, 0x5c, 0x56, 0xff, 0x42, 0x2c, 0x29, 0xff, 0x2e, 0x18, 0x16, 0xff, 0x40, 0x2d, 0x28, 0xff, 0x4b, 0x37, 0x34, 0xff, 0x28, 0x12, 0x15, 0xff, 0x27, 0x13, 0x16, 0xff, 0x85, 0x77, 0x74, 0xff, 0x59, 0x4a, 0x45, 0xff, 0x3d, 0x28, 0x25, 0xff, 0x65, 0x4f, 0x4a, 0xff, 0x2b, 0x19, 0x10, 0xff, 0x34, 0x22, 0x1c, 0xff, 0x31, 0x1e, 0x1c, 0xff, 0x2b, 0x16, 0x17, 0xff, 0x35, 0x1e, 0x21, 0xff, 0x2d, 0x1a, 0x1a, 0xff, 0x2e, 0x1c, 0x1a, 0xff, 0x35, 0x21, 0x1e, 0xff, 0x37, 0x23, 0x22, 0xff, 0x32, 0x1d, 0x1d, 0xff, 0x2f, 0x1a, 0x1a, 0xff, 0x24, 0x10, 0x0f, 0xff, 0x54, 0x40, 0x37, 0xff, 0x62, 0x4f, 0x46, 0xff, 0x40, 0x30, 0x2c, 0xff, 0x3d, 0x2c, 0x27, 0xff, 0x49, 0x31, 0x26, 0xff, 0x4c, 0x33, 0x2b, 0xff, 0x67, 0x50, 0x43, 0xff, 0x85, 0x72, 0x5f, 0xff, 0x2d, 0x1c, 0x1a, 0xff, 0x39, 0x23, 0x20, 0xff, 0x2f, 0x1c, 0x19, 0xff, 0x37, 0x21, 0x23, 0xff, 0x60, 0x45, 0x3d, 0xff, 0x80, 0x6a, 0x62, 0xff, 0xed, 0xec, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xec, 0xe6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xec, 0xe7, 0x6b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0xe7, 0xe3, 0xff, 0x82, 0x6c, 0x57, 0xff, 0x58, 0x3e, 0x27, 0xff, 0x5f, 0x45, 0x2c, 0xff, 0x7d, 0x63, 0x44, 0xff, 0x7b, 0x63, 0x3f, 0xff, 0x79, 0x63, 0x3b, 0xff, 0x92, 0x79, 0x52, 0xff, 0x97, 0x81, 0x51, 0xff, 0xa2, 0x92, 0x61, 0xff, 0xc6, 0xb8, 0x94, 0xff, 0xb2, 0xa4, 0x79, 0xff, 0xc9, 0xbf, 0x93, 0xff, 0xd0, 0xcb, 0xa3, 0xff, 0xb8, 0xae, 0x8e, 0xff, 0xd2, 0xcc, 0xa4, 0xff, 0xe4, 0xea, 0xd1, 0xff, 0xf0, 0xf5, 0xe3, 0xff, 0xd7, 0xd0, 0xb1, 0xff, 0xe1, 0xe9, 0xde, 0xff, 0xe3, 0xed, 0xe6, 0xff, 0xec, 0xf3, 0xf0, 0xff, 0xec, 0xf4, 0xf7, 0xff, 0xea, 0xf6, 0xf7, 0xff, 0xea, 0xf7, 0xf6, 0xff, 0xee, 0xf7, 0xf6, 0xff, 0xec, 0xf6, 0xf7, 0xff, 0xef, 0xf9, 0xf7, 0xff, 0xd3, 0xd0, 0xc9, 0xff, 0xd3, 0xcb, 0xc8, 0xff, 0xef, 0xf8, 0xf6, 0xff, 0xc4, 0xc0, 0xbf, 0xff, 0x87, 0x6f, 0x64, 0xff, 0x78, 0x5e, 0x51, 0xff, 0x60, 0x49, 0x44, 0xff, 0x7f, 0x6a, 0x67, 0xff, 0x65, 0x4a, 0x45, 0xff, 0x2f, 0x13, 0x11, 0xff, 0x31, 0x16, 0x18, 0xff, 0x41, 0x27, 0x2a, 0xff, 0x3f, 0x25, 0x27, 0xff, 0x57, 0x3c, 0x3c, 0xff, 0x34, 0x1a, 0x1a, 0xff, 0x1b, 0x04, 0x06, 0xff, 0x48, 0x33, 0x36, 0xff, 0x52, 0x3b, 0x3b, 0xff, 0x46, 0x2e, 0x2c, 0xff, 0x35, 0x1d, 0x1f, 0xff, 0x24, 0x0d, 0x14, 0xff, 0x23, 0x0e, 0x14, 0xff, 0x32, 0x1b, 0x1d, 0xff, 0x31, 0x18, 0x1b, 0xff, 0x2e, 0x17, 0x1a, 0xff, 0x2a, 0x14, 0x17, 0xff, 0x28, 0x13, 0x16, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x27, 0x12, 0x14, 0xff, 0x32, 0x1d, 0x1f, 0xff, 0x29, 0x15, 0x18, 0xff, 0x25, 0x12, 0x14, 0xff, 0x2d, 0x18, 0x17, 0xff, 0x46, 0x30, 0x2c, 0xff, 0x33, 0x1f, 0x1d, 0xff, 0x2f, 0x1a, 0x1e, 0xff, 0x1c, 0x08, 0x07, 0xff, 0x2d, 0x1b, 0x16, 0xff, 0x22, 0x0f, 0x14, 0xff, 0x22, 0x0e, 0x12, 0xff, 0x1c, 0x06, 0x09, 0xff, 0x23, 0x0e, 0x11, 0xff, 0x3c, 0x2d, 0x2c, 0xff, 0x53, 0x45, 0x43, 0xff, 0x3a, 0x27, 0x29, 0xff, 0x31, 0x1c, 0x1f, 0xff, 0x30, 0x1a, 0x1a, 0xff, 0x3b, 0x25, 0x22, 0xff, 0x73, 0x64, 0x5e, 0xff, 0x93, 0x85, 0x7f, 0xff, 0x66, 0x55, 0x50, 0xff, 0x3c, 0x29, 0x22, 0xff, 0x32, 0x1c, 0x1c, 0xff, 0x3c, 0x25, 0x27, 0xff, 0x46, 0x30, 0x2d, 0xff, 0x28, 0x12, 0x13, 0xff, 0x3e, 0x2c, 0x2d, 0xff, 0x78, 0x6c, 0x68, 0xff, 0x43, 0x31, 0x2e, 0xff, 0x53, 0x3f, 0x39, 0xff, 0x59, 0x47, 0x3d, 0xff, 0x26, 0x14, 0x0f, 0xff, 0x2f, 0x1b, 0x1b, 0xff, 0x2a, 0x15, 0x19, 0xff, 0x2f, 0x1b, 0x1f, 0xff, 0x29, 0x18, 0x19, 0xff, 0x37, 0x23, 0x1e, 0xff, 0x3c, 0x26, 0x22, 0xff, 0x27, 0x11, 0x15, 0xff, 0x3a, 0x26, 0x21, 0xff, 0x3a, 0x24, 0x21, 0xff, 0x2e, 0x1a, 0x1b, 0xff, 0x6d, 0x59, 0x4c, 0xff, 0x4a, 0x35, 0x2f, 0xff, 0x3d, 0x2f, 0x27, 0xff, 0x42, 0x34, 0x2f, 0xff, 0x47, 0x31, 0x2a, 0xff, 0x4a, 0x36, 0x27, 0xff, 0x78, 0x65, 0x4f, 0xff, 0x81, 0x6a, 0x5a, 0xff, 0x30, 0x1e, 0x1a, 0xff, 0x31, 0x1b, 0x1b, 0xff, 0x32, 0x1e, 0x20, 0xff, 0x30, 0x19, 0x20, 0xff, 0x4a, 0x33, 0x2c, 0xff, 0x51, 0x38, 0x2d, 0xff, 0xb7, 0xa8, 0x9e, 0xff, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xc6, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfb, 0xfa, 0xff, 0xaa, 0x9c, 0x8d, 0xff, 0x5c, 0x40, 0x29, 0xff, 0x67, 0x4d, 0x30, 0xff, 0x83, 0x6b, 0x48, 0xff, 0x72, 0x59, 0x38, 0xff, 0x64, 0x47, 0x2c, 0xff, 0x60, 0x43, 0x28, 0xff, 0x58, 0x3c, 0x25, 0xff, 0x5f, 0x44, 0x29, 0xff, 0x88, 0x6f, 0x46, 0xff, 0xb2, 0x9a, 0x6e, 0xff, 0xb7, 0xa6, 0x7a, 0xff, 0xad, 0x9d, 0x76, 0xff, 0xb1, 0x9e, 0x76, 0xff, 0xb9, 0xb0, 0x79, 0xff, 0xda, 0xdd, 0xbf, 0xff, 0xf0, 0xf6, 0xf5, 0xff, 0xf0, 0xfc, 0xf9, 0xff, 0xec, 0xf9, 0xf5, 0xff, 0xe6, 0xf2, 0xed, 0xff, 0xe9, 0xf1, 0xed, 0xff, 0xee, 0xf7, 0xf5, 0xff, 0xeb, 0xf5, 0xf6, 0xff, 0xec, 0xf6, 0xf7, 0xff, 0xef, 0xf8, 0xf7, 0xff, 0xee, 0xf6, 0xf9, 0xff, 0xee, 0xfd, 0xff, 0xff, 0xde, 0xe4, 0xe3, 0xff, 0x91, 0x81, 0x7d, 0xff, 0xd0, 0xcd, 0xc9, 0xff, 0xe1, 0xeb, 0xeb, 0xff, 0xbc, 0xb6, 0xb9, 0xff, 0x88, 0x6f, 0x6d, 0xff, 0x6b, 0x51, 0x4b, 0xff, 0x69, 0x4b, 0x42, 0xff, 0x73, 0x59, 0x52, 0xff, 0x5e, 0x44, 0x42, 0xff, 0x4d, 0x38, 0x36, 0xff, 0x4d, 0x3a, 0x38, 0xff, 0x3c, 0x23, 0x26, 0xff, 0x24, 0x0a, 0x0e, 0xff, 0x39, 0x22, 0x22, 0xff, 0x50, 0x3c, 0x3a, 0xff, 0x45, 0x34, 0x30, 0xff, 0x3c, 0x29, 0x28, 0xff, 0x3b, 0x25, 0x27, 0xff, 0x3d, 0x28, 0x29, 0xff, 0x3b, 0x27, 0x25, 0xff, 0x2d, 0x1b, 0x19, 0xff, 0x2b, 0x16, 0x19, 0xff, 0x22, 0x0d, 0x11, 0xff, 0x28, 0x13, 0x17, 0xff, 0x29, 0x13, 0x17, 0xff, 0x2c, 0x16, 0x1a, 0xff, 0x29, 0x14, 0x17, 0xff, 0x2c, 0x18, 0x1b, 0xff, 0x27, 0x13, 0x15, 0xff, 0x2a, 0x15, 0x19, 0xff, 0x24, 0x0f, 0x11, 0xff, 0x28, 0x16, 0x12, 0xff, 0x45, 0x32, 0x2e, 0xff, 0x2e, 0x1a, 0x1a, 0xff, 0x24, 0x11, 0x14, 0xff, 0x25, 0x11, 0x13, 0xff, 0x37, 0x24, 0x20, 0xff, 0x45, 0x32, 0x2c, 0xff, 0x3d, 0x2c, 0x2a, 0xff, 0x2d, 0x1b, 0x1e, 0xff, 0x32, 0x21, 0x22, 0xff, 0x35, 0x23, 0x21, 0xff, 0x48, 0x38, 0x35, 0xff, 0x4c, 0x3b, 0x3a, 0xff, 0x4e, 0x3a, 0x3a, 0xff, 0x49, 0x34, 0x36, 0xff, 0x40, 0x2b, 0x2c, 0xff, 0x39, 0x24, 0x23, 0xff, 0x43, 0x2f, 0x2e, 0xff, 0x63, 0x53, 0x51, 0xff, 0x89, 0x7b, 0x75, 0xff, 0x72, 0x65, 0x5b, 0xff, 0x35, 0x22, 0x20, 0xff, 0x23, 0x0b, 0x10, 0xff, 0x33, 0x1e, 0x1c, 0xff, 0x32, 0x1d, 0x1f, 0xff, 0x2c, 0x16, 0x1b, 0xff, 0x4a, 0x37, 0x36, 0xff, 0x71, 0x5f, 0x59, 0xff, 0x43, 0x30, 0x29, 0xff, 0x54, 0x43, 0x3c, 0xff, 0x51, 0x41, 0x3a, 0xff, 0x31, 0x1e, 0x18, 0xff, 0x31, 0x1b, 0x1a, 0xff, 0x2e, 0x1b, 0x1e, 0xff, 0x26, 0x15, 0x16, 0xff, 0x3b, 0x25, 0x20, 0xff, 0x44, 0x2b, 0x23, 0xff, 0x22, 0x0f, 0x11, 0xff, 0x21, 0x13, 0x12, 0xff, 0x1e, 0x0a, 0x0e, 0xff, 0x49, 0x34, 0x31, 0xff, 0x68, 0x55, 0x44, 0xff, 0x50, 0x3d, 0x33, 0xff, 0x3b, 0x2b, 0x21, 0xff, 0x20, 0x0d, 0x0c, 0xff, 0x45, 0x32, 0x28, 0xff, 0x58, 0x45, 0x35, 0xff, 0x6f, 0x59, 0x4a, 0xff, 0x7b, 0x65, 0x54, 0xff, 0x4a, 0x37, 0x33, 0xff, 0x26, 0x11, 0x11, 0xff, 0x2d, 0x18, 0x1b, 0xff, 0x2c, 0x17, 0x1c, 0xff, 0x3e, 0x2a, 0x27, 0xff, 0x5f, 0x47, 0x3f, 0xff, 0x78, 0x5e, 0x4e, 0xff, 0xd2, 0xca, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0xe6, 0x7c, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xd4, 0xcc, 0xc3, 0xff, 0x6d, 0x55, 0x39, 0xff, 0x63, 0x48, 0x2c, 0xff, 0x93, 0x7b, 0x54, 0xff, 0x75, 0x5c, 0x3b, 0xff, 0x53, 0x37, 0x20, 0xff, 0x66, 0x47, 0x30, 0xff, 0x67, 0x48, 0x30, 0xff, 0x66, 0x4b, 0x2f, 0xff, 0x90, 0x77, 0x53, 0xff, 0x94, 0x7a, 0x53, 0xff, 0x84, 0x65, 0x3d, 0xff, 0x8b, 0x6f, 0x44, 0xff, 0x93, 0x77, 0x46, 0xff, 0xa9, 0x8d, 0x5c, 0xff, 0xde, 0xd4, 0xa6, 0xff, 0xe2, 0xe5, 0xc4, 0xff, 0xed, 0xf4, 0xf1, 0xff, 0xed, 0xff, 0xff, 0xff, 0xed, 0xfc, 0xf9, 0xff, 0xef, 0xf6, 0xf7, 0xff, 0xee, 0xf5, 0xf6, 0xff, 0xee, 0xf8, 0xf9, 0xff, 0xed, 0xf8, 0xf7, 0xff, 0xef, 0xf6, 0xf8, 0xff, 0xf0, 0xf6, 0xfb, 0xff, 0xe9, 0xf8, 0xfc, 0xff, 0xdf, 0xe9, 0xe3, 0xff, 0xcf, 0xc4, 0xbd, 0xff, 0xb0, 0xa4, 0xa0, 0xff, 0xcd, 0xd0, 0xc7, 0xff, 0xd0, 0xce, 0xcb, 0xff, 0xab, 0xa6, 0xaa, 0xff, 0x79, 0x66, 0x65, 0xff, 0x5a, 0x3e, 0x3a, 0xff, 0x52, 0x33, 0x2f, 0xff, 0x47, 0x2b, 0x27, 0xff, 0x51, 0x36, 0x34, 0xff, 0x52, 0x3d, 0x3a, 0xff, 0x38, 0x25, 0x22, 0xff, 0x2c, 0x15, 0x15, 0xff, 0x49, 0x35, 0x34, 0xff, 0x4d, 0x3c, 0x38, 0xff, 0x63, 0x51, 0x4b, 0xff, 0x4c, 0x38, 0x33, 0xff, 0x25, 0x0e, 0x0b, 0xff, 0x2d, 0x17, 0x14, 0xff, 0x46, 0x32, 0x2e, 0xff, 0x4d, 0x3a, 0x35, 0xff, 0x3e, 0x2c, 0x28, 0xff, 0x33, 0x1e, 0x1e, 0xff, 0x2c, 0x15, 0x18, 0xff, 0x2a, 0x14, 0x17, 0xff, 0x31, 0x1a, 0x1d, 0xff, 0x33, 0x1c, 0x20, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x2b, 0x15, 0x19, 0xff, 0x29, 0x14, 0x17, 0xff, 0x31, 0x1c, 0x1d, 0xff, 0x3e, 0x2b, 0x28, 0xff, 0x36, 0x22, 0x21, 0xff, 0x21, 0x0d, 0x11, 0xff, 0x2a, 0x14, 0x19, 0xff, 0x1f, 0x0a, 0x0d, 0xff, 0x29, 0x16, 0x17, 0xff, 0x36, 0x24, 0x24, 0xff, 0x48, 0x38, 0x34, 0xff, 0x48, 0x38, 0x35, 0xff, 0x3d, 0x2d, 0x2b, 0xff, 0x38, 0x28, 0x25, 0xff, 0x23, 0x10, 0x11, 0xff, 0x1c, 0x07, 0x09, 0xff, 0x44, 0x31, 0x2d, 0xff, 0x3f, 0x2b, 0x29, 0xff, 0x38, 0x23, 0x24, 0xff, 0x41, 0x2c, 0x2e, 0xff, 0x28, 0x14, 0x17, 0xff, 0x2a, 0x17, 0x19, 0xff, 0x5a, 0x49, 0x49, 0xff, 0x84, 0x79, 0x77, 0xff, 0x5e, 0x51, 0x4b, 0xff, 0x29, 0x18, 0x14, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x2b, 0x19, 0x1b, 0xff, 0x2c, 0x18, 0x1b, 0xff, 0x33, 0x1f, 0x21, 0xff, 0x68, 0x56, 0x52, 0xff, 0x64, 0x50, 0x4d, 0xff, 0x44, 0x30, 0x2d, 0xff, 0x57, 0x45, 0x3f, 0xff, 0x4a, 0x37, 0x2e, 0xff, 0x30, 0x1c, 0x16, 0xff, 0x29, 0x15, 0x19, 0xff, 0x2a, 0x19, 0x1d, 0xff, 0x2e, 0x1b, 0x16, 0xff, 0x45, 0x2e, 0x26, 0xff, 0x22, 0x10, 0x12, 0xff, 0x24, 0x17, 0x16, 0xff, 0x1e, 0x0b, 0x10, 0xff, 0x47, 0x34, 0x29, 0xff, 0x59, 0x45, 0x38, 0xff, 0x69, 0x57, 0x4c, 0xff, 0x3c, 0x2a, 0x24, 0xff, 0x1b, 0x06, 0x06, 0xff, 0x4b, 0x35, 0x2a, 0xff, 0x6b, 0x56, 0x48, 0xff, 0x65, 0x50, 0x44, 0xff, 0x5d, 0x47, 0x36, 0xff, 0x5b, 0x48, 0x42, 0xff, 0x2a, 0x15, 0x16, 0xff, 0x2b, 0x16, 0x19, 0xff, 0x26, 0x12, 0x15, 0xff, 0x3d, 0x28, 0x27, 0xff, 0x46, 0x2c, 0x26, 0xff, 0x68, 0x50, 0x3f, 0xff, 0xa2, 0x90, 0x81, 0xff, 0xf2, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xed, 0xe7, 0xd6, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf1, 0xee, 0xff, 0x95, 0x82, 0x6b, 0xff, 0x60, 0x47, 0x28, 0xff, 0x7f, 0x67, 0x43, 0xff, 0x7f, 0x68, 0x42, 0xff, 0x58, 0x3e, 0x21, 0xff, 0x64, 0x47, 0x32, 0xff, 0x6a, 0x4c, 0x32, 0xff, 0x63, 0x46, 0x2c, 0xff, 0x88, 0x6c, 0x4b, 0xff, 0x8b, 0x72, 0x4a, 0xff, 0x63, 0x44, 0x28, 0xff, 0x88, 0x6a, 0x44, 0xff, 0xb0, 0x97, 0x62, 0xff, 0xbc, 0x9d, 0x66, 0xff, 0x9d, 0x82, 0x48, 0xff, 0xd0, 0xc3, 0x9e, 0xff, 0xe2, 0xe2, 0xc2, 0xff, 0xdd, 0xe4, 0xd1, 0xff, 0xea, 0xf8, 0xf4, 0xff, 0xe9, 0xf6, 0xf2, 0xff, 0xef, 0xf6, 0xfa, 0xff, 0xf0, 0xf8, 0xfc, 0xff, 0xef, 0xf9, 0xfa, 0xff, 0xed, 0xf8, 0xf8, 0xff, 0xeb, 0xf7, 0xf5, 0xff, 0xe6, 0xf5, 0xf4, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xbf, 0xb3, 0xa4, 0xff, 0x85, 0x5e, 0x48, 0xff, 0xa5, 0x96, 0x87, 0xff, 0xbd, 0xba, 0xae, 0xff, 0xc5, 0xb6, 0xb0, 0xff, 0xbd, 0xad, 0xa8, 0xff, 0x9b, 0x85, 0x7f, 0xff, 0x5a, 0x3b, 0x38, 0xff, 0x46, 0x29, 0x28, 0xff, 0x47, 0x2e, 0x2b, 0xff, 0x31, 0x1b, 0x15, 0xff, 0x28, 0x17, 0x12, 0xff, 0x44, 0x32, 0x2f, 0xff, 0x51, 0x3f, 0x3d, 0xff, 0x58, 0x4b, 0x48, 0xff, 0x57, 0x4a, 0x45, 0xff, 0x44, 0x32, 0x2b, 0xff, 0x42, 0x2a, 0x26, 0xff, 0x3f, 0x25, 0x24, 0xff, 0x35, 0x1c, 0x1d, 0xff, 0x28, 0x11, 0x12, 0xff, 0x28, 0x12, 0x15, 0xff, 0x2e, 0x19, 0x1b, 0xff, 0x29, 0x14, 0x14, 0xff, 0x2a, 0x14, 0x16, 0xff, 0x24, 0x0f, 0x11, 0xff, 0x29, 0x13, 0x15, 0xff, 0x2e, 0x1a, 0x1c, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x25, 0x11, 0x14, 0xff, 0x29, 0x15, 0x17, 0xff, 0x43, 0x2e, 0x2f, 0xff, 0x31, 0x1c, 0x1e, 0xff, 0x1f, 0x0c, 0x10, 0xff, 0x25, 0x11, 0x16, 0xff, 0x26, 0x12, 0x15, 0xff, 0x27, 0x14, 0x16, 0xff, 0x1e, 0x0b, 0x0f, 0xff, 0x18, 0x05, 0x09, 0xff, 0x23, 0x11, 0x12, 0xff, 0x21, 0x0d, 0x0e, 0xff, 0x21, 0x0c, 0x10, 0xff, 0x1f, 0x0c, 0x10, 0xff, 0x24, 0x0e, 0x13, 0xff, 0x24, 0x0f, 0x10, 0xff, 0x42, 0x34, 0x2e, 0xff, 0x39, 0x27, 0x24, 0xff, 0x2b, 0x15, 0x17, 0xff, 0x32, 0x1e, 0x20, 0xff, 0x2c, 0x19, 0x1d, 0xff, 0x1d, 0x0a, 0x0d, 0xff, 0x1e, 0x0b, 0x0f, 0xff, 0x3c, 0x29, 0x2f, 0xff, 0x77, 0x63, 0x5f, 0xff, 0x5b, 0x46, 0x3e, 0xff, 0x2c, 0x14, 0x16, 0xff, 0x24, 0x11, 0x13, 0xff, 0x24, 0x12, 0x14, 0xff, 0x29, 0x16, 0x19, 0xff, 0x4b, 0x39, 0x3a, 0xff, 0x62, 0x50, 0x4f, 0xff, 0x43, 0x2f, 0x2d, 0xff, 0x53, 0x3e, 0x3b, 0xff, 0x54, 0x42, 0x38, 0xff, 0x4c, 0x39, 0x31, 0xff, 0x21, 0x0c, 0x10, 0xff, 0x28, 0x17, 0x1c, 0xff, 0x29, 0x18, 0x17, 0xff, 0x3b, 0x26, 0x23, 0xff, 0x24, 0x12, 0x14, 0xff, 0x11, 0x02, 0x04, 0xff, 0x2e, 0x1d, 0x1e, 0xff, 0x42, 0x31, 0x23, 0xff, 0x6b, 0x55, 0x48, 0xff, 0x5d, 0x4a, 0x41, 0xff, 0x34, 0x20, 0x22, 0xff, 0x39, 0x23, 0x20, 0xff, 0x4d, 0x33, 0x26, 0xff, 0x72, 0x5c, 0x4b, 0xff, 0x70, 0x5d, 0x50, 0xff, 0x61, 0x4b, 0x40, 0xff, 0x54, 0x40, 0x38, 0xff, 0x20, 0x0b, 0x0d, 0xff, 0x29, 0x16, 0x18, 0xff, 0x24, 0x12, 0x14, 0xff, 0x3e, 0x28, 0x2a, 0xff, 0x38, 0x20, 0x1e, 0xff, 0x67, 0x51, 0x40, 0xff, 0x7a, 0x64, 0x52, 0xff, 0xb8, 0xad, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0xe6, 0x7c, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xbe, 0xad, 0xff, 0x71, 0x59, 0x37, 0xff, 0x6f, 0x56, 0x37, 0xff, 0x83, 0x6c, 0x48, 0xff, 0x64, 0x4a, 0x2c, 0xff, 0x62, 0x47, 0x2d, 0xff, 0x68, 0x4d, 0x30, 0xff, 0x6b, 0x4e, 0x33, 0xff, 0x67, 0x4a, 0x2e, 0xff, 0x8c, 0x72, 0x4a, 0xff, 0x77, 0x5d, 0x3d, 0xff, 0x68, 0x48, 0x32, 0xff, 0x90, 0x72, 0x49, 0xff, 0x9f, 0x81, 0x54, 0xff, 0x91, 0x6f, 0x43, 0xff, 0xaa, 0x8d, 0x4f, 0xff, 0xc1, 0xb5, 0x82, 0xff, 0xf3, 0xf7, 0xe8, 0xff, 0xe2, 0xe8, 0xd4, 0xff, 0xdf, 0xe4, 0xd5, 0xff, 0xef, 0xf6, 0xf8, 0xff, 0xee, 0xfa, 0xf9, 0xff, 0xef, 0xfa, 0xf9, 0xff, 0xed, 0xf8, 0xf7, 0xff, 0xeb, 0xf7, 0xf7, 0xff, 0xe9, 0xf6, 0xf5, 0xff, 0xe8, 0xf5, 0xf7, 0xff, 0xf2, 0xfa, 0xfe, 0xff, 0xc1, 0xb6, 0xa6, 0xff, 0x93, 0x75, 0x57, 0xff, 0x9e, 0x8a, 0x7b, 0xff, 0xae, 0xa2, 0xa4, 0xff, 0xae, 0xa6, 0xaa, 0xff, 0x7b, 0x5f, 0x55, 0xff, 0x57, 0x37, 0x2d, 0xff, 0x54, 0x36, 0x33, 0xff, 0x46, 0x2b, 0x26, 0xff, 0x52, 0x40, 0x38, 0xff, 0x71, 0x65, 0x5e, 0xff, 0x67, 0x5e, 0x55, 0xff, 0x5d, 0x4f, 0x4b, 0xff, 0x4d, 0x3e, 0x3b, 0xff, 0x35, 0x28, 0x26, 0xff, 0x43, 0x2d, 0x2e, 0xff, 0x42, 0x2a, 0x27, 0xff, 0x4b, 0x31, 0x2b, 0xff, 0x53, 0x3b, 0x36, 0xff, 0x40, 0x29, 0x26, 0xff, 0x37, 0x1f, 0x1e, 0xff, 0x2f, 0x19, 0x19, 0xff, 0x30, 0x1b, 0x1e, 0xff, 0x28, 0x15, 0x17, 0xff, 0x28, 0x14, 0x18, 0xff, 0x23, 0x10, 0x13, 0xff, 0x26, 0x12, 0x16, 0xff, 0x24, 0x11, 0x14, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x27, 0x14, 0x18, 0xff, 0x24, 0x11, 0x14, 0xff, 0x29, 0x16, 0x19, 0xff, 0x2b, 0x15, 0x18, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x28, 0x16, 0x19, 0xff, 0x22, 0x11, 0x14, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x23, 0x10, 0x13, 0xff, 0x23, 0x10, 0x13, 0xff, 0x25, 0x11, 0x14, 0xff, 0x2c, 0x16, 0x18, 0xff, 0x27, 0x11, 0x14, 0xff, 0x29, 0x13, 0x16, 0xff, 0x2e, 0x1a, 0x1a, 0xff, 0x32, 0x1c, 0x1e, 0xff, 0x2a, 0x18, 0x17, 0xff, 0x37, 0x2e, 0x27, 0xff, 0x31, 0x22, 0x22, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x2f, 0x1d, 0x1c, 0xff, 0x31, 0x1f, 0x21, 0xff, 0x28, 0x14, 0x18, 0xff, 0x26, 0x13, 0x15, 0xff, 0x1f, 0x0b, 0x0f, 0xff, 0x31, 0x1b, 0x1c, 0xff, 0x66, 0x4e, 0x4c, 0xff, 0x54, 0x3e, 0x3b, 0xff, 0x21, 0x0e, 0x0d, 0xff, 0x1e, 0x0c, 0x0f, 0xff, 0x23, 0x10, 0x14, 0xff, 0x21, 0x0e, 0x12, 0xff, 0x62, 0x52, 0x51, 0xff, 0x5b, 0x4c, 0x48, 0xff, 0x54, 0x3f, 0x3e, 0xff, 0x43, 0x30, 0x2a, 0xff, 0x57, 0x46, 0x3f, 0xff, 0x3f, 0x2c, 0x2a, 0xff, 0x17, 0x08, 0x0b, 0xff, 0x20, 0x10, 0x14, 0xff, 0x29, 0x15, 0x16, 0xff, 0x25, 0x13, 0x16, 0xff, 0x10, 0x00, 0x06, 0xff, 0x2b, 0x1b, 0x18, 0xff, 0x56, 0x44, 0x38, 0xff, 0x74, 0x61, 0x4e, 0xff, 0x2b, 0x17, 0x13, 0xff, 0x26, 0x12, 0x18, 0xff, 0x45, 0x30, 0x28, 0xff, 0x5c, 0x42, 0x2f, 0xff, 0x74, 0x5f, 0x4c, 0xff, 0x5e, 0x4c, 0x40, 0xff, 0x54, 0x3e, 0x37, 0xff, 0x52, 0x3e, 0x38, 0xff, 0x26, 0x12, 0x14, 0xff, 0x26, 0x14, 0x16, 0xff, 0x26, 0x15, 0x15, 0xff, 0x30, 0x1b, 0x1f, 0xff, 0x27, 0x12, 0x15, 0xff, 0x63, 0x4f, 0x42, 0xff, 0x6c, 0x55, 0x45, 0xff, 0x79, 0x61, 0x57, 0xff, 0xe3, 0xde, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xdf, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xeb, 0xe5, 0xff, 0x95, 0x83, 0x62, 0xff, 0x74, 0x5b, 0x37, 0xff, 0x8c, 0x71, 0x4e, 0xff, 0x7a, 0x5f, 0x3c, 0xff, 0x61, 0x47, 0x29, 0xff, 0x68, 0x4d, 0x33, 0xff, 0x6d, 0x51, 0x35, 0xff, 0x6d, 0x50, 0x36, 0xff, 0x68, 0x4b, 0x2c, 0xff, 0x8b, 0x72, 0x48, 0xff, 0x8b, 0x6f, 0x4c, 0xff, 0x6b, 0x4b, 0x30, 0xff, 0x6c, 0x4e, 0x2c, 0xff, 0x7a, 0x57, 0x37, 0xff, 0x8b, 0x6b, 0x46, 0xff, 0x99, 0x83, 0x4f, 0xff, 0xd3, 0xc1, 0x96, 0xff, 0xe4, 0xe0, 0xc6, 0xff, 0xd2, 0xd7, 0xbe, 0xff, 0xea, 0xf4, 0xea, 0xff, 0xf4, 0xfd, 0xff, 0xff, 0xf0, 0xfa, 0xfa, 0xff, 0xee, 0xf9, 0xf7, 0xff, 0xeb, 0xf7, 0xf7, 0xff, 0xe8, 0xf5, 0xf5, 0xff, 0xe6, 0xf3, 0xf1, 0xff, 0xec, 0xf6, 0xfa, 0xff, 0xf1, 0xfa, 0xfd, 0xff, 0x87, 0x72, 0x64, 0xff, 0x7f, 0x6a, 0x59, 0xff, 0xc1, 0xb8, 0xb2, 0xff, 0xa8, 0x9e, 0x9c, 0xff, 0x80, 0x74, 0x6a, 0xff, 0x7e, 0x64, 0x50, 0xff, 0x62, 0x47, 0x38, 0xff, 0x6e, 0x57, 0x4c, 0xff, 0x6b, 0x57, 0x50, 0xff, 0xae, 0xa3, 0x98, 0xff, 0xba, 0xb2, 0xa6, 0xff, 0x8d, 0x84, 0x7a, 0xff, 0x5f, 0x53, 0x4b, 0xff, 0x59, 0x4e, 0x47, 0xff, 0x55, 0x46, 0x40, 0xff, 0x4e, 0x39, 0x33, 0xff, 0x48, 0x32, 0x2b, 0xff, 0x5e, 0x4a, 0x43, 0xff, 0x53, 0x3f, 0x35, 0xff, 0x3e, 0x29, 0x22, 0xff, 0x4f, 0x39, 0x33, 0xff, 0x43, 0x2e, 0x2c, 0xff, 0x32, 0x1c, 0x1e, 0xff, 0x33, 0x1e, 0x21, 0xff, 0x34, 0x1e, 0x20, 0xff, 0x2a, 0x15, 0x17, 0xff, 0x26, 0x12, 0x16, 0xff, 0x24, 0x10, 0x13, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x27, 0x14, 0x17, 0xff, 0x2b, 0x19, 0x1c, 0xff, 0x27, 0x14, 0x17, 0xff, 0x26, 0x13, 0x16, 0xff, 0x27, 0x13, 0x17, 0xff, 0x28, 0x16, 0x18, 0xff, 0x26, 0x15, 0x17, 0xff, 0x22, 0x11, 0x13, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x29, 0x16, 0x16, 0xff, 0x27, 0x14, 0x14, 0xff, 0x24, 0x12, 0x13, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x27, 0x15, 0x15, 0xff, 0x21, 0x0c, 0x0e, 0xff, 0x20, 0x12, 0x11, 0xff, 0x44, 0x39, 0x35, 0xff, 0x2d, 0x1b, 0x1c, 0xff, 0x26, 0x12, 0x17, 0xff, 0x30, 0x1c, 0x1d, 0xff, 0x2d, 0x1a, 0x1b, 0xff, 0x29, 0x15, 0x18, 0xff, 0x29, 0x14, 0x18, 0xff, 0x25, 0x13, 0x15, 0xff, 0x24, 0x12, 0x15, 0xff, 0x36, 0x24, 0x24, 0xff, 0x52, 0x3f, 0x3b, 0xff, 0x36, 0x23, 0x22, 0xff, 0x1e, 0x0c, 0x0e, 0xff, 0x22, 0x0e, 0x11, 0xff, 0x1e, 0x0b, 0x0e, 0xff, 0x34, 0x25, 0x25, 0xff, 0x69, 0x59, 0x56, 0xff, 0x49, 0x36, 0x35, 0xff, 0x30, 0x1e, 0x1d, 0xff, 0x4e, 0x3f, 0x39, 0xff, 0x57, 0x48, 0x3f, 0xff, 0x27, 0x16, 0x14, 0xff, 0x26, 0x12, 0x16, 0xff, 0x1c, 0x0a, 0x0e, 0xff, 0x12, 0x03, 0x09, 0xff, 0x26, 0x17, 0x1a, 0xff, 0x5b, 0x4b, 0x41, 0xff, 0x6a, 0x57, 0x47, 0xff, 0x50, 0x3c, 0x31, 0xff, 0x25, 0x12, 0x14, 0xff, 0x23, 0x11, 0x14, 0xff, 0x3e, 0x29, 0x20, 0xff, 0x69, 0x54, 0x3d, 0xff, 0x6e, 0x59, 0x48, 0xff, 0x41, 0x2c, 0x21, 0xff, 0x57, 0x40, 0x35, 0xff, 0x53, 0x3c, 0x37, 0xff, 0x2a, 0x16, 0x18, 0xff, 0x2d, 0x19, 0x1c, 0xff, 0x29, 0x16, 0x19, 0xff, 0x25, 0x15, 0x14, 0xff, 0x1c, 0x08, 0x0d, 0xff, 0x65, 0x50, 0x48, 0xff, 0x70, 0x58, 0x45, 0xff, 0x58, 0x3c, 0x32, 0xff, 0xb0, 0xa1, 0x95, 0xff, 0xfc, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0xe7, 0x7c, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xbd, 0xa7, 0xff, 0x7f, 0x69, 0x41, 0xff, 0x8f, 0x77, 0x51, 0xff, 0x8a, 0x6f, 0x4a, 0xff, 0x6a, 0x4f, 0x2c, 0xff, 0x6a, 0x50, 0x30, 0xff, 0x69, 0x4d, 0x32, 0xff, 0x6d, 0x4f, 0x35, 0xff, 0x70, 0x54, 0x37, 0xff, 0x70, 0x53, 0x36, 0xff, 0x7a, 0x5e, 0x3c, 0xff, 0x95, 0x78, 0x51, 0xff, 0x8e, 0x71, 0x46, 0xff, 0x93, 0x7f, 0x52, 0xff, 0xa1, 0x83, 0x5d, 0xff, 0xa3, 0x83, 0x58, 0xff, 0xbe, 0xb0, 0x80, 0xff, 0xdc, 0xd2, 0xa6, 0xff, 0xc2, 0xbc, 0x8a, 0xff, 0xd8, 0xd8, 0xc4, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xeb, 0xfa, 0xf9, 0xff, 0xec, 0xf8, 0xf5, 0xff, 0xed, 0xf6, 0xf7, 0xff, 0xe9, 0xf5, 0xf5, 0xff, 0xe7, 0xf3, 0xf3, 0xff, 0xe3, 0xf0, 0xf2, 0xff, 0xf2, 0xfc, 0xfd, 0xff, 0xe4, 0xe5, 0xdf, 0xff, 0x78, 0x61, 0x55, 0xff, 0x8d, 0x7d, 0x72, 0xff, 0xaf, 0xa8, 0x99, 0xff, 0x8f, 0x85, 0x76, 0xff, 0x8c, 0x7c, 0x6e, 0xff, 0x88, 0x76, 0x68, 0xff, 0xa1, 0x8e, 0x81, 0xff, 0x93, 0x8b, 0x80, 0xff, 0xc2, 0xb9, 0xaf, 0xff, 0xc5, 0xb7, 0xad, 0xff, 0x7d, 0x6c, 0x63, 0xff, 0x80, 0x6b, 0x64, 0xff, 0x7d, 0x6b, 0x63, 0xff, 0x69, 0x57, 0x51, 0xff, 0x62, 0x50, 0x49, 0xff, 0x51, 0x3f, 0x36, 0xff, 0x53, 0x3d, 0x36, 0xff, 0x46, 0x32, 0x2c, 0xff, 0x55, 0x43, 0x3d, 0xff, 0x4c, 0x37, 0x32, 0xff, 0x4c, 0x39, 0x33, 0xff, 0x55, 0x41, 0x3d, 0xff, 0x4b, 0x35, 0x34, 0xff, 0x47, 0x32, 0x31, 0xff, 0x34, 0x20, 0x1c, 0xff, 0x34, 0x20, 0x1e, 0xff, 0x32, 0x1d, 0x20, 0xff, 0x2a, 0x14, 0x17, 0xff, 0x29, 0x14, 0x17, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x27, 0x14, 0x17, 0xff, 0x26, 0x13, 0x16, 0xff, 0x25, 0x12, 0x15, 0xff, 0x24, 0x11, 0x14, 0xff, 0x25, 0x11, 0x15, 0xff, 0x25, 0x12, 0x15, 0xff, 0x22, 0x10, 0x12, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x20, 0x0d, 0x10, 0xff, 0x22, 0x0f, 0x10, 0xff, 0x26, 0x14, 0x13, 0xff, 0x24, 0x13, 0x15, 0xff, 0x1d, 0x0a, 0x0e, 0xff, 0x1f, 0x0c, 0x10, 0xff, 0x1c, 0x07, 0x0b, 0xff, 0x33, 0x26, 0x25, 0xff, 0x56, 0x4a, 0x47, 0xff, 0x28, 0x13, 0x16, 0xff, 0x24, 0x10, 0x15, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x26, 0x14, 0x14, 0xff, 0x23, 0x11, 0x12, 0xff, 0x25, 0x12, 0x16, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x28, 0x15, 0x18, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x31, 0x1f, 0x1f, 0xff, 0x44, 0x30, 0x31, 0xff, 0x2c, 0x1a, 0x1b, 0xff, 0x1f, 0x0c, 0x0f, 0xff, 0x23, 0x10, 0x13, 0xff, 0x27, 0x16, 0x19, 0xff, 0x57, 0x46, 0x46, 0xff, 0x53, 0x42, 0x41, 0xff, 0x36, 0x23, 0x23, 0xff, 0x3a, 0x28, 0x24, 0xff, 0x41, 0x33, 0x2d, 0xff, 0x2e, 0x19, 0x19, 0xff, 0x34, 0x1f, 0x21, 0xff, 0x1e, 0x0e, 0x10, 0xff, 0x15, 0x06, 0x05, 0xff, 0x51, 0x41, 0x39, 0xff, 0x5e, 0x4d, 0x40, 0xff, 0x5d, 0x4a, 0x3c, 0xff, 0x44, 0x2f, 0x29, 0xff, 0x30, 0x1b, 0x1b, 0xff, 0x2f, 0x1b, 0x18, 0xff, 0x4f, 0x3a, 0x2d, 0xff, 0x7e, 0x68, 0x52, 0xff, 0x6a, 0x52, 0x43, 0xff, 0x41, 0x29, 0x21, 0xff, 0x4a, 0x34, 0x2b, 0xff, 0x47, 0x31, 0x2d, 0xff, 0x30, 0x1c, 0x1c, 0xff, 0x31, 0x1c, 0x1f, 0xff, 0x2b, 0x16, 0x19, 0xff, 0x29, 0x18, 0x16, 0xff, 0x1c, 0x08, 0x0f, 0xff, 0x57, 0x41, 0x3d, 0xff, 0x65, 0x4c, 0x3e, 0xff, 0x51, 0x36, 0x2f, 0xff, 0x7f, 0x68, 0x59, 0xff, 0xcf, 0xc8, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xec, 0xe5, 0xff, 0x99, 0x86, 0x60, 0xff, 0xa0, 0x8a, 0x60, 0xff, 0x98, 0x81, 0x58, 0xff, 0x70, 0x55, 0x31, 0xff, 0x70, 0x55, 0x33, 0xff, 0x6e, 0x52, 0x33, 0xff, 0x6c, 0x4f, 0x34, 0xff, 0x6e, 0x50, 0x35, 0xff, 0x76, 0x59, 0x3b, 0xff, 0x7a, 0x5c, 0x3e, 0xff, 0x75, 0x56, 0x35, 0xff, 0x74, 0x55, 0x33, 0xff, 0x83, 0x65, 0x42, 0xff, 0x9b, 0x7d, 0x54, 0xff, 0x9b, 0x7a, 0x50, 0xff, 0xa7, 0x8b, 0x57, 0xff, 0xba, 0x99, 0x60, 0xff, 0xba, 0xaf, 0x87, 0xff, 0xf1, 0xfe, 0xf9, 0xff, 0xf6, 0xff, 0xff, 0xff, 0xec, 0xf8, 0xfd, 0xff, 0xec, 0xf6, 0xf5, 0xff, 0xeb, 0xf5, 0xf5, 0xff, 0xe6, 0xf3, 0xf5, 0xff, 0xe7, 0xf3, 0xf5, 0xff, 0xe7, 0xf3, 0xf4, 0xff, 0xe3, 0xee, 0xf0, 0xff, 0xf3, 0xff, 0xfd, 0xff, 0xbe, 0xba, 0xae, 0xff, 0x97, 0x87, 0x79, 0xff, 0x97, 0x88, 0x80, 0xff, 0x84, 0x6a, 0x62, 0xff, 0x80, 0x65, 0x5d, 0xff, 0x8a, 0x75, 0x6f, 0xff, 0x80, 0x71, 0x6c, 0xff, 0x8f, 0x82, 0x82, 0xff, 0xb1, 0xad, 0xad, 0xff, 0x8d, 0x83, 0x84, 0xff, 0x52, 0x41, 0x41, 0xff, 0x52, 0x38, 0x34, 0xff, 0x4f, 0x39, 0x32, 0xff, 0x4e, 0x3e, 0x38, 0xff, 0x4a, 0x3b, 0x37, 0xff, 0x55, 0x47, 0x42, 0xff, 0x70, 0x60, 0x59, 0xff, 0x5f, 0x4c, 0x45, 0xff, 0x4a, 0x34, 0x31, 0xff, 0x3c, 0x26, 0x29, 0xff, 0x22, 0x0c, 0x0f, 0xff, 0x27, 0x12, 0x14, 0xff, 0x34, 0x1f, 0x20, 0xff, 0x3b, 0x27, 0x26, 0xff, 0x4e, 0x3c, 0x39, 0xff, 0x48, 0x34, 0x30, 0xff, 0x46, 0x33, 0x2f, 0xff, 0x39, 0x24, 0x26, 0xff, 0x2a, 0x15, 0x19, 0xff, 0x29, 0x14, 0x17, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x27, 0x14, 0x17, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x25, 0x12, 0x15, 0xff, 0x28, 0x15, 0x18, 0xff, 0x22, 0x0e, 0x12, 0xff, 0x20, 0x0e, 0x10, 0xff, 0x22, 0x10, 0x12, 0xff, 0x21, 0x0e, 0x11, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x35, 0x22, 0x22, 0xff, 0x2c, 0x1a, 0x19, 0xff, 0x23, 0x11, 0x13, 0xff, 0x28, 0x15, 0x18, 0xff, 0x27, 0x14, 0x17, 0xff, 0x1c, 0x08, 0x0a, 0xff, 0x4f, 0x41, 0x41, 0xff, 0x4f, 0x41, 0x42, 0xff, 0x15, 0x01, 0x04, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x2b, 0x18, 0x1a, 0xff, 0x21, 0x0e, 0x10, 0xff, 0x27, 0x14, 0x16, 0xff, 0x27, 0x14, 0x17, 0xff, 0x22, 0x11, 0x14, 0xff, 0x1e, 0x0c, 0x11, 0xff, 0x28, 0x15, 0x1a, 0xff, 0x24, 0x12, 0x14, 0xff, 0x31, 0x1e, 0x1f, 0xff, 0x39, 0x26, 0x28, 0xff, 0x23, 0x10, 0x13, 0xff, 0x23, 0x10, 0x14, 0xff, 0x28, 0x17, 0x1a, 0xff, 0x3c, 0x2a, 0x2c, 0xff, 0x4d, 0x3b, 0x3c, 0xff, 0x36, 0x24, 0x24, 0xff, 0x28, 0x14, 0x15, 0xff, 0x34, 0x21, 0x24, 0xff, 0x28, 0x15, 0x1a, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x2d, 0x1c, 0x19, 0xff, 0x54, 0x42, 0x39, 0xff, 0x4e, 0x3c, 0x30, 0xff, 0x4d, 0x3a, 0x30, 0xff, 0x57, 0x42, 0x38, 0xff, 0x4d, 0x37, 0x30, 0xff, 0x36, 0x20, 0x1a, 0xff, 0x48, 0x33, 0x28, 0xff, 0x63, 0x4a, 0x3b, 0xff, 0x70, 0x58, 0x41, 0xff, 0x64, 0x4d, 0x3a, 0xff, 0x4a, 0x33, 0x28, 0xff, 0x5c, 0x4a, 0x3f, 0xff, 0x3b, 0x28, 0x23, 0xff, 0x30, 0x1a, 0x1a, 0xff, 0x3b, 0x27, 0x25, 0xff, 0x2a, 0x15, 0x16, 0xff, 0x2a, 0x16, 0x18, 0xff, 0x20, 0x0b, 0x12, 0xff, 0x4b, 0x34, 0x31, 0xff, 0x53, 0x3a, 0x34, 0xff, 0x3f, 0x27, 0x26, 0xff, 0x60, 0x48, 0x3f, 0xff, 0x9e, 0x8e, 0x89, 0xff, 0xf7, 0xf6, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0x74, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xbd, 0xa5, 0xff, 0xa2, 0x8e, 0x5f, 0xff, 0xb8, 0xa4, 0x76, 0xff, 0x80, 0x68, 0x3f, 0xff, 0x78, 0x5d, 0x39, 0xff, 0x73, 0x58, 0x36, 0xff, 0x6e, 0x52, 0x33, 0xff, 0x6f, 0x52, 0x37, 0xff, 0x73, 0x54, 0x38, 0xff, 0x73, 0x57, 0x36, 0xff, 0x78, 0x5b, 0x38, 0xff, 0x7e, 0x5f, 0x3c, 0xff, 0x7c, 0x5c, 0x3e, 0xff, 0x7f, 0x5b, 0x41, 0xff, 0x7b, 0x59, 0x36, 0xff, 0x88, 0x60, 0x35, 0xff, 0xa8, 0x8d, 0x5f, 0xff, 0xbc, 0xae, 0x8d, 0xff, 0xeb, 0xf2, 0xe8, 0xff, 0xf2, 0xff, 0xff, 0xff, 0xee, 0xfb, 0xf8, 0xff, 0xe7, 0xf7, 0xf5, 0xff, 0xe6, 0xf4, 0xf8, 0xff, 0xe8, 0xf4, 0xf8, 0xff, 0xe6, 0xf2, 0xf5, 0xff, 0xe2, 0xed, 0xf0, 0xff, 0xe1, 0xe9, 0xee, 0xff, 0xea, 0xf2, 0xf5, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xb8, 0xb1, 0xac, 0xff, 0x75, 0x5c, 0x52, 0xff, 0x67, 0x4e, 0x48, 0xff, 0x62, 0x44, 0x3c, 0xff, 0x69, 0x47, 0x3d, 0xff, 0x7b, 0x64, 0x59, 0xff, 0xad, 0xa3, 0x9a, 0xff, 0xb1, 0xa8, 0xa7, 0xff, 0x75, 0x67, 0x6a, 0xff, 0x47, 0x2e, 0x30, 0xff, 0x42, 0x2c, 0x2a, 0xff, 0x4b, 0x36, 0x30, 0xff, 0x3c, 0x2a, 0x22, 0xff, 0x4e, 0x43, 0x3c, 0xff, 0x68, 0x5e, 0x58, 0xff, 0x98, 0x8e, 0x88, 0xff, 0x89, 0x80, 0x79, 0xff, 0x78, 0x6b, 0x60, 0xff, 0x78, 0x68, 0x5f, 0xff, 0x35, 0x23, 0x22, 0xff, 0x2b, 0x18, 0x19, 0xff, 0x34, 0x20, 0x22, 0xff, 0x25, 0x0f, 0x13, 0xff, 0x20, 0x0b, 0x0d, 0xff, 0x1b, 0x08, 0x0a, 0xff, 0x2d, 0x1a, 0x1a, 0xff, 0x3c, 0x28, 0x26, 0xff, 0x39, 0x25, 0x24, 0xff, 0x2f, 0x1a, 0x1d, 0xff, 0x23, 0x0d, 0x11, 0xff, 0x27, 0x14, 0x17, 0xff, 0x29, 0x15, 0x18, 0xff, 0x23, 0x10, 0x13, 0xff, 0x25, 0x12, 0x15, 0xff, 0x24, 0x11, 0x14, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x23, 0x10, 0x13, 0xff, 0x1e, 0x0a, 0x0e, 0xff, 0x2c, 0x19, 0x1d, 0xff, 0x38, 0x24, 0x25, 0xff, 0x25, 0x12, 0x11, 0xff, 0x29, 0x17, 0x19, 0xff, 0x2d, 0x1a, 0x1d, 0xff, 0x26, 0x13, 0x17, 0xff, 0x24, 0x0f, 0x11, 0xff, 0x49, 0x3b, 0x3b, 0xff, 0x44, 0x37, 0x38, 0xff, 0x21, 0x0c, 0x10, 0xff, 0x29, 0x17, 0x19, 0xff, 0x27, 0x16, 0x16, 0xff, 0x1e, 0x0c, 0x10, 0xff, 0x22, 0x10, 0x12, 0xff, 0x22, 0x11, 0x11, 0xff, 0x23, 0x13, 0x14, 0xff, 0x1e, 0x0e, 0x11, 0xff, 0x23, 0x12, 0x15, 0xff, 0x29, 0x16, 0x18, 0xff, 0x29, 0x15, 0x16, 0xff, 0x34, 0x21, 0x22, 0xff, 0x29, 0x16, 0x19, 0xff, 0x27, 0x15, 0x18, 0xff, 0x28, 0x18, 0x1b, 0xff, 0x32, 0x1f, 0x22, 0xff, 0x3e, 0x2b, 0x2e, 0xff, 0x35, 0x23, 0x24, 0xff, 0x28, 0x13, 0x14, 0xff, 0x31, 0x1d, 0x1f, 0xff, 0x22, 0x12, 0x14, 0xff, 0x18, 0x08, 0x08, 0xff, 0x42, 0x2f, 0x2b, 0xff, 0x4c, 0x39, 0x33, 0xff, 0x39, 0x26, 0x22, 0xff, 0x42, 0x2e, 0x29, 0xff, 0x4d, 0x37, 0x2f, 0xff, 0x44, 0x2e, 0x28, 0xff, 0x3d, 0x28, 0x22, 0xff, 0x50, 0x3b, 0x2e, 0xff, 0x65, 0x4d, 0x3d, 0xff, 0x5a, 0x43, 0x32, 0xff, 0x50, 0x3a, 0x31, 0xff, 0x41, 0x2c, 0x23, 0xff, 0x54, 0x41, 0x35, 0xff, 0x3f, 0x2a, 0x26, 0xff, 0x39, 0x23, 0x23, 0xff, 0x3e, 0x2a, 0x27, 0xff, 0x24, 0x10, 0x10, 0xff, 0x2b, 0x16, 0x1a, 0xff, 0x25, 0x10, 0x14, 0xff, 0x4b, 0x36, 0x30, 0xff, 0x4a, 0x31, 0x2d, 0xff, 0x35, 0x1f, 0x1f, 0xff, 0x4c, 0x35, 0x31, 0xff, 0x5f, 0x49, 0x40, 0xff, 0xd0, 0xc9, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xee, 0xe7, 0xff, 0xb4, 0xa3, 0x7d, 0xff, 0xbf, 0xab, 0x7c, 0xff, 0x90, 0x7b, 0x4e, 0xff, 0x82, 0x6a, 0x40, 0xff, 0x7e, 0x64, 0x3f, 0xff, 0x74, 0x59, 0x36, 0xff, 0x72, 0x55, 0x36, 0xff, 0x71, 0x54, 0x37, 0xff, 0x71, 0x54, 0x37, 0xff, 0x73, 0x57, 0x37, 0xff, 0x7a, 0x5c, 0x3c, 0xff, 0x7f, 0x61, 0x40, 0xff, 0x81, 0x5f, 0x40, 0xff, 0x73, 0x51, 0x2e, 0xff, 0x6e, 0x48, 0x23, 0xff, 0xa7, 0x93, 0x72, 0xff, 0xba, 0xb4, 0x8b, 0xff, 0xbe, 0xbc, 0xa1, 0xff, 0xf5, 0xff, 0xff, 0xff, 0xec, 0xfa, 0xfe, 0xff, 0xef, 0xf6, 0xf9, 0xff, 0xec, 0xf4, 0xf5, 0xff, 0xe8, 0xf2, 0xf4, 0xff, 0xe6, 0xf1, 0xf4, 0xff, 0xe6, 0xf3, 0xf4, 0xff, 0xdd, 0xe8, 0xea, 0xff, 0xd9, 0xe2, 0xe4, 0xff, 0xf0, 0xfa, 0xfa, 0xff, 0xe7, 0xee, 0xee, 0xff, 0x8c, 0x7e, 0x81, 0xff, 0x42, 0x29, 0x29, 0xff, 0x4e, 0x37, 0x33, 0xff, 0x64, 0x4a, 0x46, 0xff, 0x76, 0x5b, 0x58, 0xff, 0x89, 0x73, 0x71, 0xff, 0x8a, 0x7a, 0x7c, 0xff, 0x57, 0x49, 0x49, 0xff, 0x41, 0x2d, 0x2c, 0xff, 0x46, 0x2c, 0x29, 0xff, 0x51, 0x3b, 0x34, 0xff, 0x60, 0x4d, 0x45, 0xff, 0x75, 0x66, 0x5f, 0xff, 0x9e, 0x97, 0x92, 0xff, 0x94, 0x86, 0x80, 0xff, 0x73, 0x64, 0x5f, 0xff, 0x77, 0x70, 0x6b, 0xff, 0x82, 0x78, 0x71, 0xff, 0x52, 0x44, 0x40, 0xff, 0x2c, 0x1f, 0x1f, 0xff, 0x42, 0x31, 0x30, 0xff, 0x35, 0x23, 0x24, 0xff, 0x21, 0x0f, 0x0f, 0xff, 0x27, 0x14, 0x13, 0xff, 0x27, 0x15, 0x18, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x24, 0x10, 0x12, 0xff, 0x2a, 0x16, 0x17, 0xff, 0x3a, 0x26, 0x29, 0xff, 0x2f, 0x1b, 0x1e, 0xff, 0x1c, 0x09, 0x0d, 0xff, 0x22, 0x10, 0x14, 0xff, 0x25, 0x12, 0x16, 0xff, 0x26, 0x11, 0x15, 0xff, 0x24, 0x11, 0x13, 0xff, 0x22, 0x0f, 0x11, 0xff, 0x21, 0x0d, 0x11, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x27, 0x15, 0x16, 0xff, 0x31, 0x1f, 0x20, 0xff, 0x23, 0x11, 0x11, 0xff, 0x24, 0x11, 0x13, 0xff, 0x2d, 0x1b, 0x1d, 0xff, 0x33, 0x20, 0x23, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x2d, 0x1a, 0x1c, 0xff, 0x4e, 0x40, 0x40, 0xff, 0x45, 0x38, 0x39, 0xff, 0x1d, 0x0b, 0x0d, 0xff, 0x2b, 0x1a, 0x1b, 0xff, 0x28, 0x17, 0x18, 0xff, 0x20, 0x0d, 0x12, 0xff, 0x20, 0x0e, 0x12, 0xff, 0x1e, 0x0c, 0x0e, 0xff, 0x22, 0x12, 0x14, 0xff, 0x20, 0x10, 0x12, 0xff, 0x21, 0x10, 0x13, 0xff, 0x27, 0x15, 0x18, 0xff, 0x25, 0x14, 0x16, 0xff, 0x29, 0x18, 0x1a, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x29, 0x17, 0x1a, 0xff, 0x2e, 0x1c, 0x1f, 0xff, 0x2d, 0x1a, 0x1d, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x2c, 0x18, 0x1a, 0xff, 0x2f, 0x1b, 0x1d, 0xff, 0x23, 0x11, 0x13, 0xff, 0x26, 0x15, 0x16, 0xff, 0x2a, 0x18, 0x18, 0xff, 0x28, 0x17, 0x17, 0xff, 0x30, 0x1d, 0x1f, 0xff, 0x35, 0x20, 0x20, 0xff, 0x45, 0x2f, 0x2c, 0xff, 0x3f, 0x2a, 0x29, 0xff, 0x47, 0x31, 0x28, 0xff, 0x54, 0x3c, 0x2a, 0xff, 0x5b, 0x44, 0x37, 0xff, 0x55, 0x3f, 0x36, 0xff, 0x43, 0x2d, 0x24, 0xff, 0x42, 0x2a, 0x24, 0xff, 0x4d, 0x37, 0x32, 0xff, 0x33, 0x1f, 0x1e, 0xff, 0x3a, 0x26, 0x24, 0xff, 0x3e, 0x29, 0x25, 0xff, 0x21, 0x0d, 0x0e, 0xff, 0x27, 0x13, 0x18, 0xff, 0x24, 0x12, 0x13, 0xff, 0x43, 0x2d, 0x29, 0xff, 0x3d, 0x26, 0x26, 0xff, 0x37, 0x21, 0x22, 0xff, 0x3d, 0x26, 0x22, 0xff, 0x53, 0x38, 0x33, 0xff, 0xa7, 0x97, 0x8d, 0xff, 0xf7, 0xf6, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0xc7, 0xb1, 0xff, 0xb3, 0xa0, 0x72, 0xff, 0xa4, 0x90, 0x61, 0xff, 0x8e, 0x77, 0x4a, 0xff, 0x8f, 0x77, 0x4d, 0xff, 0x82, 0x69, 0x42, 0xff, 0x79, 0x5d, 0x3a, 0xff, 0x72, 0x56, 0x35, 0xff, 0x72, 0x56, 0x38, 0xff, 0x73, 0x56, 0x39, 0xff, 0x78, 0x5a, 0x3c, 0xff, 0x7b, 0x62, 0x3f, 0xff, 0x76, 0x53, 0x37, 0xff, 0x76, 0x53, 0x36, 0xff, 0x9a, 0x87, 0x5d, 0xff, 0xc2, 0xae, 0x85, 0xff, 0xdd, 0xd6, 0xad, 0xff, 0xcf, 0xcb, 0xa3, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xed, 0xfc, 0xff, 0xff, 0xe8, 0xf7, 0xf6, 0xff, 0xeb, 0xf4, 0xf6, 0xff, 0xea, 0xf5, 0xf2, 0xff, 0xdf, 0xed, 0xf2, 0xff, 0xde, 0xea, 0xf0, 0xff, 0xe6, 0xf2, 0xf0, 0xff, 0xe1, 0xec, 0xed, 0xff, 0xdd, 0xe5, 0xe6, 0xff, 0xef, 0xfc, 0xf9, 0xff, 0xce, 0xd0, 0xcc, 0xff, 0x36, 0x1d, 0x1d, 0xff, 0x4a, 0x31, 0x31, 0xff, 0x65, 0x54, 0x4f, 0xff, 0x5e, 0x4b, 0x4b, 0xff, 0x6b, 0x5c, 0x5a, 0xff, 0x7f, 0x70, 0x6e, 0xff, 0x64, 0x50, 0x52, 0xff, 0x3d, 0x2a, 0x2a, 0xff, 0x43, 0x2f, 0x2d, 0xff, 0x3f, 0x2b, 0x29, 0xff, 0x53, 0x40, 0x3c, 0xff, 0x7a, 0x6c, 0x64, 0xff, 0x86, 0x7d, 0x77, 0xff, 0x6d, 0x62, 0x60, 0xff, 0x52, 0x41, 0x3e, 0xff, 0x62, 0x50, 0x4e, 0xff, 0x73, 0x67, 0x65, 0xff, 0x3a, 0x2a, 0x2b, 0xff, 0x23, 0x0f, 0x11, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x27, 0x15, 0x17, 0xff, 0x27, 0x16, 0x16, 0xff, 0x32, 0x20, 0x21, 0xff, 0x34, 0x22, 0x23, 0xff, 0x29, 0x17, 0x19, 0xff, 0x26, 0x13, 0x15, 0xff, 0x26, 0x13, 0x16, 0xff, 0x1e, 0x0c, 0x0e, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x37, 0x24, 0x27, 0xff, 0x30, 0x1d, 0x20, 0xff, 0x21, 0x0e, 0x10, 0xff, 0x24, 0x11, 0x16, 0xff, 0x23, 0x10, 0x18, 0xff, 0x23, 0x14, 0x16, 0xff, 0x21, 0x10, 0x13, 0xff, 0x24, 0x0f, 0x15, 0xff, 0x36, 0x22, 0x26, 0xff, 0x3f, 0x2d, 0x2d, 0xff, 0x27, 0x14, 0x15, 0xff, 0x23, 0x0f, 0x13, 0xff, 0x2b, 0x17, 0x1b, 0xff, 0x30, 0x1d, 0x1f, 0xff, 0x2d, 0x1a, 0x1d, 0xff, 0x34, 0x21, 0x23, 0xff, 0x36, 0x24, 0x26, 0xff, 0x3d, 0x2d, 0x30, 0xff, 0x2e, 0x1f, 0x22, 0xff, 0x1d, 0x0b, 0x0e, 0xff, 0x32, 0x1f, 0x21, 0xff, 0x26, 0x13, 0x15, 0xff, 0x22, 0x0e, 0x13, 0xff, 0x23, 0x0e, 0x13, 0xff, 0x21, 0x0f, 0x10, 0xff, 0x22, 0x11, 0x14, 0xff, 0x21, 0x10, 0x13, 0xff, 0x26, 0x15, 0x18, 0xff, 0x23, 0x12, 0x15, 0xff, 0x26, 0x16, 0x19, 0xff, 0x25, 0x14, 0x18, 0xff, 0x26, 0x13, 0x16, 0xff, 0x29, 0x15, 0x18, 0xff, 0x2b, 0x17, 0x1a, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x28, 0x15, 0x18, 0xff, 0x28, 0x15, 0x18, 0xff, 0x29, 0x16, 0x1a, 0xff, 0x29, 0x16, 0x19, 0xff, 0x29, 0x16, 0x19, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x22, 0x0f, 0x13, 0xff, 0x25, 0x15, 0x18, 0xff, 0x27, 0x16, 0x17, 0xff, 0x36, 0x22, 0x21, 0xff, 0x3e, 0x29, 0x27, 0xff, 0x35, 0x21, 0x1d, 0xff, 0x49, 0x32, 0x26, 0xff, 0x61, 0x48, 0x35, 0xff, 0x4f, 0x39, 0x2d, 0xff, 0x41, 0x2c, 0x23, 0xff, 0x4c, 0x34, 0x27, 0xff, 0x5a, 0x41, 0x37, 0xff, 0x48, 0x32, 0x2f, 0xff, 0x2c, 0x19, 0x1a, 0xff, 0x3c, 0x28, 0x23, 0xff, 0x48, 0x32, 0x2c, 0xff, 0x28, 0x15, 0x17, 0xff, 0x24, 0x12, 0x16, 0xff, 0x26, 0x15, 0x15, 0xff, 0x40, 0x29, 0x27, 0xff, 0x38, 0x22, 0x24, 0xff, 0x32, 0x1c, 0x1e, 0xff, 0x2b, 0x15, 0x15, 0xff, 0x4d, 0x35, 0x31, 0xff, 0x70, 0x5a, 0x4d, 0xff, 0xda, 0xd4, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf4, 0xee, 0xff, 0xc5, 0xb6, 0x91, 0xff, 0x9d, 0x8a, 0x5b, 0xff, 0x98, 0x84, 0x55, 0xff, 0x9d, 0x86, 0x58, 0xff, 0x90, 0x78, 0x4d, 0xff, 0x86, 0x6c, 0x46, 0xff, 0x7e, 0x63, 0x3f, 0xff, 0x78, 0x5d, 0x3a, 0xff, 0x75, 0x58, 0x39, 0xff, 0x79, 0x5c, 0x3e, 0xff, 0x7a, 0x5d, 0x3d, 0xff, 0x73, 0x57, 0x37, 0xff, 0x7e, 0x5e, 0x39, 0xff, 0xaf, 0x9a, 0x6d, 0xff, 0xab, 0x99, 0x74, 0xff, 0xbb, 0xb3, 0x94, 0xff, 0xaa, 0x97, 0x65, 0xff, 0xdf, 0xda, 0xc6, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xed, 0xf8, 0xfb, 0xff, 0xec, 0xf6, 0xf3, 0xff, 0xe7, 0xf3, 0xf4, 0xff, 0xe3, 0xf3, 0xf5, 0xff, 0xc4, 0xd7, 0xe7, 0xff, 0xd0, 0xde, 0xe7, 0xff, 0xea, 0xf6, 0xf2, 0xff, 0xe0, 0xea, 0xe9, 0xff, 0xe1, 0xeb, 0xeb, 0xff, 0xe1, 0xec, 0xe8, 0xff, 0xc5, 0xba, 0xb3, 0xff, 0x69, 0x46, 0x40, 0xff, 0x2b, 0x09, 0x08, 0xff, 0x3f, 0x23, 0x23, 0xff, 0x4e, 0x38, 0x38, 0xff, 0x6a, 0x57, 0x55, 0xff, 0x61, 0x50, 0x50, 0xff, 0x54, 0x42, 0x43, 0xff, 0x6f, 0x5b, 0x56, 0xff, 0x59, 0x45, 0x41, 0xff, 0x5d, 0x4c, 0x4a, 0xff, 0x75, 0x66, 0x66, 0xff, 0x64, 0x59, 0x54, 0xff, 0x68, 0x5a, 0x57, 0xff, 0x5b, 0x48, 0x49, 0xff, 0x5a, 0x48, 0x4b, 0xff, 0x60, 0x4f, 0x50, 0xff, 0x2d, 0x19, 0x1a, 0xff, 0x25, 0x11, 0x10, 0xff, 0x37, 0x22, 0x20, 0xff, 0x38, 0x25, 0x23, 0xff, 0x2f, 0x1d, 0x1d, 0xff, 0x26, 0x14, 0x16, 0xff, 0x2f, 0x1c, 0x20, 0xff, 0x26, 0x13, 0x17, 0xff, 0x25, 0x13, 0x14, 0xff, 0x32, 0x20, 0x20, 0xff, 0x2f, 0x1e, 0x1e, 0xff, 0x24, 0x13, 0x13, 0xff, 0x20, 0x0e, 0x10, 0xff, 0x24, 0x10, 0x14, 0xff, 0x3c, 0x29, 0x2b, 0xff, 0x2f, 0x1b, 0x1d, 0xff, 0x2d, 0x1a, 0x1b, 0xff, 0x37, 0x26, 0x28, 0xff, 0x31, 0x21, 0x23, 0xff, 0x29, 0x16, 0x18, 0xff, 0x28, 0x12, 0x15, 0xff, 0x31, 0x21, 0x23, 0xff, 0x3d, 0x2f, 0x30, 0xff, 0x2e, 0x1e, 0x1f, 0xff, 0x2d, 0x1d, 0x1f, 0xff, 0x34, 0x23, 0x25, 0xff, 0x34, 0x21, 0x25, 0xff, 0x33, 0x21, 0x23, 0xff, 0x3b, 0x28, 0x2a, 0xff, 0x2c, 0x19, 0x1b, 0xff, 0x31, 0x1e, 0x22, 0xff, 0x2f, 0x1d, 0x21, 0xff, 0x2e, 0x1a, 0x1e, 0xff, 0x2e, 0x19, 0x1c, 0xff, 0x21, 0x0e, 0x11, 0xff, 0x28, 0x15, 0x18, 0xff, 0x27, 0x13, 0x17, 0xff, 0x1f, 0x0c, 0x0f, 0xff, 0x21, 0x10, 0x13, 0xff, 0x23, 0x12, 0x15, 0xff, 0x26, 0x15, 0x18, 0xff, 0x25, 0x14, 0x17, 0xff, 0x27, 0x15, 0x18, 0xff, 0x26, 0x13, 0x16, 0xff, 0x28, 0x14, 0x17, 0xff, 0x28, 0x15, 0x18, 0xff, 0x28, 0x15, 0x18, 0xff, 0x27, 0x14, 0x17, 0xff, 0x27, 0x14, 0x17, 0xff, 0x27, 0x15, 0x17, 0xff, 0x27, 0x14, 0x17, 0xff, 0x25, 0x12, 0x15, 0xff, 0x26, 0x13, 0x16, 0xff, 0x23, 0x10, 0x13, 0xff, 0x23, 0x11, 0x14, 0xff, 0x29, 0x18, 0x1b, 0xff, 0x2e, 0x1c, 0x1c, 0xff, 0x34, 0x22, 0x1f, 0xff, 0x3f, 0x2c, 0x26, 0xff, 0x35, 0x21, 0x1a, 0xff, 0x47, 0x30, 0x2d, 0xff, 0x41, 0x28, 0x27, 0xff, 0x3a, 0x24, 0x1d, 0xff, 0x45, 0x2f, 0x25, 0xff, 0x4e, 0x38, 0x2e, 0xff, 0x51, 0x39, 0x31, 0xff, 0x39, 0x22, 0x20, 0xff, 0x34, 0x1f, 0x1f, 0xff, 0x4e, 0x36, 0x2f, 0xff, 0x44, 0x2b, 0x24, 0xff, 0x29, 0x16, 0x15, 0xff, 0x28, 0x14, 0x16, 0xff, 0x2d, 0x1a, 0x1b, 0xff, 0x36, 0x21, 0x1f, 0xff, 0x30, 0x1c, 0x1e, 0xff, 0x30, 0x1b, 0x1e, 0xff, 0x27, 0x12, 0x17, 0xff, 0x50, 0x3e, 0x34, 0xff, 0x4f, 0x36, 0x2d, 0xff, 0x95, 0x87, 0x86, 0xff, 0xf9, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xdb, 0xca, 0xff, 0xbf, 0xac, 0x80, 0xff, 0xba, 0xa6, 0x77, 0xff, 0xaa, 0x95, 0x66, 0xff, 0x98, 0x81, 0x52, 0xff, 0x91, 0x79, 0x4e, 0xff, 0x89, 0x70, 0x48, 0xff, 0x82, 0x67, 0x42, 0xff, 0x7b, 0x5f, 0x3c, 0xff, 0x79, 0x5d, 0x3c, 0xff, 0x79, 0x5b, 0x3d, 0xff, 0x7a, 0x5c, 0x3c, 0xff, 0x74, 0x53, 0x34, 0xff, 0x9a, 0x84, 0x57, 0xff, 0x99, 0x82, 0x50, 0xff, 0xa5, 0x8d, 0x6c, 0xff, 0xae, 0xa0, 0x7a, 0xff, 0xba, 0xad, 0x8b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xfc, 0xf7, 0xff, 0xed, 0xf7, 0xf7, 0xff, 0xea, 0xf3, 0xf7, 0xff, 0xe9, 0xf6, 0xf5, 0xff, 0xda, 0xeb, 0xf0, 0xff, 0xb2, 0xc7, 0xdf, 0xff, 0xcf, 0xdc, 0xea, 0xff, 0xea, 0xf4, 0xef, 0xff, 0xda, 0xe3, 0xe2, 0xff, 0xe3, 0xec, 0xeb, 0xff, 0xe7, 0xf3, 0xed, 0xff, 0xad, 0x9c, 0x91, 0xff, 0x6a, 0x42, 0x37, 0xff, 0x50, 0x36, 0x36, 0xff, 0x4e, 0x36, 0x39, 0xff, 0x56, 0x40, 0x3e, 0xff, 0x61, 0x4a, 0x4c, 0xff, 0x3b, 0x27, 0x29, 0xff, 0x39, 0x2a, 0x2a, 0xff, 0x3d, 0x2b, 0x27, 0xff, 0x62, 0x52, 0x4f, 0xff, 0x78, 0x6c, 0x6c, 0xff, 0x69, 0x5a, 0x5d, 0xff, 0x4d, 0x39, 0x3c, 0xff, 0x45, 0x31, 0x34, 0xff, 0x62, 0x51, 0x55, 0xff, 0x56, 0x45, 0x49, 0xff, 0x28, 0x12, 0x16, 0xff, 0x1d, 0x07, 0x0a, 0xff, 0x27, 0x13, 0x14, 0xff, 0x23, 0x10, 0x11, 0xff, 0x29, 0x17, 0x19, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x2b, 0x17, 0x1b, 0xff, 0x27, 0x14, 0x17, 0xff, 0x26, 0x12, 0x15, 0xff, 0x29, 0x15, 0x19, 0xff, 0x2b, 0x18, 0x1a, 0xff, 0x2f, 0x1c, 0x1e, 0xff, 0x2e, 0x1a, 0x1d, 0xff, 0x2c, 0x18, 0x1b, 0xff, 0x21, 0x0d, 0x11, 0xff, 0x39, 0x27, 0x2b, 0xff, 0x3d, 0x2b, 0x30, 0xff, 0x2b, 0x18, 0x1d, 0xff, 0x39, 0x26, 0x2a, 0xff, 0x35, 0x23, 0x2a, 0xff, 0x31, 0x20, 0x26, 0xff, 0x35, 0x24, 0x28, 0xff, 0x31, 0x22, 0x27, 0xff, 0x32, 0x23, 0x29, 0xff, 0x3f, 0x30, 0x36, 0xff, 0x42, 0x34, 0x39, 0xff, 0x3f, 0x31, 0x36, 0xff, 0x3b, 0x2b, 0x31, 0xff, 0x39, 0x29, 0x2e, 0xff, 0x35, 0x25, 0x28, 0xff, 0x2d, 0x1b, 0x1d, 0xff, 0x38, 0x27, 0x29, 0xff, 0x36, 0x27, 0x29, 0xff, 0x25, 0x13, 0x16, 0xff, 0x26, 0x11, 0x16, 0xff, 0x27, 0x13, 0x17, 0xff, 0x2f, 0x1d, 0x1e, 0xff, 0x2a, 0x17, 0x19, 0xff, 0x21, 0x0d, 0x12, 0xff, 0x22, 0x10, 0x14, 0xff, 0x24, 0x15, 0x17, 0xff, 0x2a, 0x1a, 0x1d, 0xff, 0x28, 0x18, 0x1b, 0xff, 0x26, 0x13, 0x16, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x26, 0x15, 0x18, 0xff, 0x24, 0x13, 0x16, 0xff, 0x24, 0x13, 0x16, 0xff, 0x25, 0x14, 0x16, 0xff, 0x24, 0x13, 0x16, 0xff, 0x23, 0x12, 0x15, 0xff, 0x25, 0x12, 0x15, 0xff, 0x25, 0x12, 0x15, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x27, 0x14, 0x17, 0xff, 0x2f, 0x1e, 0x1f, 0xff, 0x31, 0x1e, 0x1f, 0xff, 0x2b, 0x19, 0x19, 0xff, 0x36, 0x23, 0x23, 0xff, 0x3e, 0x29, 0x28, 0xff, 0x2b, 0x14, 0x16, 0xff, 0x36, 0x1e, 0x21, 0xff, 0x30, 0x19, 0x19, 0xff, 0x41, 0x2d, 0x25, 0xff, 0x5a, 0x44, 0x39, 0xff, 0x48, 0x31, 0x27, 0xff, 0x3d, 0x27, 0x22, 0xff, 0x37, 0x21, 0x20, 0xff, 0x49, 0x2f, 0x2a, 0xff, 0x3a, 0x21, 0x1a, 0xff, 0x2d, 0x1b, 0x1b, 0xff, 0x24, 0x11, 0x14, 0xff, 0x2e, 0x19, 0x1b, 0xff, 0x39, 0x23, 0x23, 0xff, 0x2a, 0x19, 0x1c, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x33, 0x1e, 0x1e, 0xff, 0x3c, 0x27, 0x26, 0xff, 0x3a, 0x23, 0x1e, 0xff, 0x59, 0x47, 0x47, 0xff, 0xdb, 0xd7, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf9, 0xf7, 0xff, 0xc0, 0xb4, 0x95, 0xff, 0xa0, 0x8c, 0x5c, 0xff, 0xad, 0x99, 0x6a, 0xff, 0xb8, 0xa5, 0x75, 0xff, 0xb0, 0x9a, 0x6b, 0xff, 0x95, 0x7d, 0x51, 0xff, 0x85, 0x6e, 0x43, 0xff, 0x83, 0x69, 0x42, 0xff, 0x7a, 0x5e, 0x3b, 0xff, 0x7a, 0x5e, 0x3e, 0xff, 0x77, 0x5a, 0x3b, 0xff, 0x77, 0x56, 0x35, 0xff, 0x8c, 0x6c, 0x49, 0xff, 0xa2, 0x83, 0x5a, 0xff, 0x73, 0x4b, 0x20, 0xff, 0xac, 0x99, 0x6c, 0xff, 0xdf, 0xde, 0xb5, 0xff, 0xe6, 0xf2, 0xe8, 0xff, 0xec, 0xfc, 0xfe, 0xff, 0xee, 0xf9, 0xf7, 0xff, 0xea, 0xf4, 0xf5, 0xff, 0xe6, 0xf2, 0xf6, 0xff, 0xe4, 0xef, 0xf3, 0xff, 0xd1, 0xe0, 0xe8, 0xff, 0xb1, 0xc4, 0xdc, 0xff, 0xd4, 0xdf, 0xe8, 0xff, 0xe9, 0xf3, 0xee, 0xff, 0xda, 0xe5, 0xe4, 0xff, 0xdf, 0xe7, 0xe6, 0xff, 0xf1, 0xfa, 0xf6, 0xff, 0x9f, 0x8e, 0x84, 0xff, 0x4f, 0x2c, 0x22, 0xff, 0x41, 0x2c, 0x2f, 0xff, 0x42, 0x2b, 0x2f, 0xff, 0x50, 0x34, 0x34, 0xff, 0x38, 0x1b, 0x1f, 0xff, 0x2d, 0x16, 0x1a, 0xff, 0x4f, 0x3e, 0x3e, 0xff, 0x5a, 0x48, 0x49, 0xff, 0x74, 0x64, 0x66, 0xff, 0x51, 0x42, 0x45, 0xff, 0x35, 0x24, 0x28, 0xff, 0x38, 0x21, 0x26, 0xff, 0x50, 0x3e, 0x43, 0xff, 0x51, 0x45, 0x49, 0xff, 0x25, 0x13, 0x16, 0xff, 0x2e, 0x17, 0x16, 0xff, 0x37, 0x22, 0x22, 0xff, 0x26, 0x12, 0x15, 0xff, 0x1c, 0x09, 0x0e, 0xff, 0x1e, 0x0d, 0x11, 0xff, 0x2a, 0x19, 0x1c, 0xff, 0x30, 0x1f, 0x21, 0xff, 0x38, 0x26, 0x28, 0xff, 0x38, 0x26, 0x28, 0xff, 0x32, 0x1f, 0x24, 0xff, 0x30, 0x1c, 0x24, 0xff, 0x32, 0x1e, 0x25, 0xff, 0x32, 0x1f, 0x25, 0xff, 0x30, 0x1d, 0x21, 0xff, 0x32, 0x1f, 0x24, 0xff, 0x37, 0x26, 0x2d, 0xff, 0x3a, 0x2b, 0x36, 0xff, 0x34, 0x25, 0x33, 0xff, 0x3b, 0x2d, 0x40, 0xff, 0x39, 0x2d, 0x43, 0xff, 0x39, 0x2f, 0x44, 0xff, 0x39, 0x2f, 0x42, 0xff, 0x32, 0x26, 0x3a, 0xff, 0x3b, 0x2e, 0x49, 0xff, 0x4f, 0x40, 0x5b, 0xff, 0x3d, 0x2f, 0x42, 0xff, 0x43, 0x36, 0x44, 0xff, 0x42, 0x36, 0x3f, 0xff, 0x35, 0x27, 0x2f, 0xff, 0x3f, 0x2e, 0x37, 0xff, 0x36, 0x24, 0x2c, 0xff, 0x30, 0x22, 0x24, 0xff, 0x2e, 0x21, 0x23, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x25, 0x12, 0x15, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x34, 0x23, 0x24, 0xff, 0x24, 0x13, 0x14, 0xff, 0x24, 0x12, 0x16, 0xff, 0x25, 0x14, 0x17, 0xff, 0x2e, 0x1c, 0x1f, 0xff, 0x2d, 0x1b, 0x1f, 0xff, 0x28, 0x16, 0x19, 0xff, 0x28, 0x15, 0x18, 0xff, 0x33, 0x20, 0x23, 0xff, 0x28, 0x17, 0x1a, 0xff, 0x20, 0x10, 0x13, 0xff, 0x25, 0x14, 0x17, 0xff, 0x25, 0x14, 0x17, 0xff, 0x21, 0x11, 0x13, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x27, 0x15, 0x18, 0xff, 0x24, 0x11, 0x14, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x26, 0x13, 0x16, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x28, 0x14, 0x17, 0xff, 0x25, 0x12, 0x16, 0xff, 0x25, 0x13, 0x17, 0xff, 0x1d, 0x0a, 0x0e, 0xff, 0x20, 0x0d, 0x0e, 0xff, 0x3b, 0x2d, 0x27, 0xff, 0x43, 0x36, 0x2b, 0xff, 0x4e, 0x3e, 0x33, 0xff, 0x46, 0x32, 0x2b, 0xff, 0x45, 0x2f, 0x27, 0xff, 0x49, 0x33, 0x28, 0xff, 0x3c, 0x26, 0x1f, 0xff, 0x32, 0x1c, 0x1b, 0xff, 0x45, 0x2e, 0x2a, 0xff, 0x48, 0x31, 0x29, 0xff, 0x2a, 0x18, 0x18, 0xff, 0x1f, 0x0c, 0x10, 0xff, 0x2d, 0x17, 0x19, 0xff, 0x3b, 0x26, 0x27, 0xff, 0x2a, 0x17, 0x1b, 0xff, 0x2d, 0x1a, 0x1d, 0xff, 0x33, 0x1e, 0x1c, 0xff, 0x45, 0x2d, 0x2a, 0xff, 0x3a, 0x22, 0x1e, 0xff, 0x33, 0x1e, 0x1b, 0xff, 0xa4, 0x9a, 0x9c, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xe7, 0xde, 0xff, 0xab, 0x9a, 0x71, 0xff, 0xa3, 0x8f, 0x5f, 0xff, 0x9e, 0x8b, 0x5a, 0xff, 0x9f, 0x8b, 0x5b, 0xff, 0xab, 0x95, 0x67, 0xff, 0xaa, 0x92, 0x65, 0xff, 0x98, 0x81, 0x55, 0xff, 0x7f, 0x68, 0x3d, 0xff, 0x79, 0x5c, 0x37, 0xff, 0x79, 0x5c, 0x3c, 0xff, 0x78, 0x5c, 0x3b, 0xff, 0x75, 0x53, 0x32, 0xff, 0x91, 0x76, 0x4c, 0xff, 0xa7, 0x93, 0x66, 0xff, 0x98, 0x80, 0x5d, 0xff, 0xb7, 0xb3, 0x93, 0xff, 0xf4, 0xff, 0xf9, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xf9, 0xf8, 0xff, 0xeb, 0xf5, 0xf8, 0xff, 0xe7, 0xf1, 0xf3, 0xff, 0xe6, 0xf2, 0xf3, 0xff, 0xd6, 0xe5, 0xee, 0xff, 0xc5, 0xd7, 0xe6, 0xff, 0xbd, 0xce, 0xdc, 0xff, 0xe2, 0xeb, 0xed, 0xff, 0xe8, 0xf4, 0xf1, 0xff, 0xd6, 0xe1, 0xe0, 0xff, 0xdf, 0xe5, 0xe1, 0xff, 0xf2, 0xfa, 0xf5, 0xff, 0xa5, 0x95, 0x8e, 0xff, 0x49, 0x28, 0x22, 0xff, 0x2b, 0x0f, 0x15, 0xff, 0x2e, 0x12, 0x15, 0xff, 0x38, 0x1f, 0x1f, 0xff, 0x4e, 0x37, 0x39, 0xff, 0x5c, 0x49, 0x4c, 0xff, 0x5b, 0x4c, 0x4c, 0xff, 0x51, 0x3f, 0x42, 0xff, 0x3c, 0x29, 0x2c, 0xff, 0x3e, 0x2a, 0x2e, 0xff, 0x47, 0x34, 0x39, 0xff, 0x51, 0x40, 0x43, 0xff, 0x40, 0x31, 0x34, 0xff, 0x17, 0x07, 0x0c, 0xff, 0x1e, 0x0a, 0x0d, 0xff, 0x37, 0x23, 0x1f, 0xff, 0x3a, 0x28, 0x25, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x29, 0x19, 0x1d, 0xff, 0x3c, 0x2f, 0x31, 0xff, 0x3c, 0x2e, 0x2f, 0xff, 0x3d, 0x2f, 0x30, 0xff, 0x3d, 0x2e, 0x33, 0xff, 0x38, 0x28, 0x2e, 0xff, 0x37, 0x25, 0x2d, 0xff, 0x35, 0x21, 0x2a, 0xff, 0x35, 0x24, 0x2d, 0xff, 0x34, 0x25, 0x31, 0xff, 0x38, 0x26, 0x32, 0xff, 0x3e, 0x2d, 0x3b, 0xff, 0x3b, 0x30, 0x46, 0xff, 0x3d, 0x39, 0x56, 0xff, 0x48, 0x44, 0x67, 0xff, 0x4a, 0x47, 0x6d, 0xff, 0x48, 0x47, 0x6f, 0xff, 0x46, 0x43, 0x6f, 0xff, 0x49, 0x46, 0x6f, 0xff, 0x47, 0x44, 0x6e, 0xff, 0x57, 0x57, 0x86, 0xff, 0x5e, 0x5d, 0x8a, 0xff, 0x48, 0x43, 0x66, 0xff, 0x50, 0x4a, 0x6a, 0xff, 0x5a, 0x54, 0x6f, 0xff, 0x4b, 0x44, 0x55, 0xff, 0x3c, 0x30, 0x3c, 0xff, 0x39, 0x28, 0x32, 0xff, 0x30, 0x1f, 0x26, 0xff, 0x36, 0x25, 0x28, 0xff, 0x2d, 0x18, 0x1e, 0xff, 0x2b, 0x16, 0x1a, 0xff, 0x30, 0x1e, 0x1f, 0xff, 0x2d, 0x1c, 0x1e, 0xff, 0x21, 0x10, 0x13, 0xff, 0x25, 0x14, 0x16, 0xff, 0x2d, 0x1b, 0x1c, 0xff, 0x2f, 0x1c, 0x1f, 0xff, 0x25, 0x11, 0x15, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x2b, 0x1b, 0x1e, 0xff, 0x25, 0x14, 0x17, 0xff, 0x24, 0x13, 0x16, 0xff, 0x24, 0x13, 0x16, 0xff, 0x23, 0x12, 0x15, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x26, 0x14, 0x17, 0xff, 0x25, 0x12, 0x15, 0xff, 0x23, 0x10, 0x13, 0xff, 0x27, 0x14, 0x17, 0xff, 0x24, 0x11, 0x14, 0xff, 0x24, 0x10, 0x14, 0xff, 0x1b, 0x0a, 0x0e, 0xff, 0x1f, 0x10, 0x11, 0xff, 0x3d, 0x2a, 0x2a, 0xff, 0x57, 0x47, 0x44, 0xff, 0x57, 0x4f, 0x47, 0xff, 0x4b, 0x44, 0x3c, 0xff, 0x3a, 0x2b, 0x26, 0xff, 0x2d, 0x15, 0x14, 0xff, 0x40, 0x29, 0x25, 0xff, 0x45, 0x30, 0x28, 0xff, 0x39, 0x22, 0x1e, 0xff, 0x36, 0x20, 0x22, 0xff, 0x39, 0x25, 0x21, 0xff, 0x4b, 0x36, 0x2b, 0xff, 0x2c, 0x1a, 0x17, 0xff, 0x17, 0x02, 0x07, 0xff, 0x34, 0x1e, 0x20, 0xff, 0x36, 0x21, 0x23, 0xff, 0x2d, 0x17, 0x1b, 0xff, 0x2f, 0x1a, 0x1e, 0xff, 0x32, 0x1b, 0x1f, 0xff, 0x5b, 0x42, 0x34, 0xff, 0x3b, 0x20, 0x1e, 0xff, 0x30, 0x19, 0x1b, 0xff, 0x80, 0x72, 0x6e, 0xff, 0xf4, 0xf3, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xd5, 0xcc, 0xb7, 0xff, 0xa0, 0x8b, 0x5b, 0xff, 0xa4, 0x8f, 0x5f, 0xff, 0xa6, 0x91, 0x61, 0xff, 0xa4, 0x8f, 0x5f, 0xff, 0x9d, 0x85, 0x57, 0xff, 0x9a, 0x83, 0x55, 0xff, 0x9f, 0x87, 0x5b, 0xff, 0x95, 0x7d, 0x52, 0xff, 0x89, 0x70, 0x45, 0xff, 0x83, 0x64, 0x3d, 0xff, 0x6f, 0x4f, 0x2b, 0xff, 0x89, 0x71, 0x4a, 0xff, 0x9b, 0x83, 0x5f, 0xff, 0x79, 0x62, 0x40, 0xff, 0xbe, 0xb1, 0x90, 0xff, 0xf2, 0xf7, 0xe5, 0xff, 0xe5, 0xf3, 0xf2, 0xff, 0xe8, 0xf6, 0xf6, 0xff, 0xed, 0xfa, 0xf9, 0xff, 0xe7, 0xf3, 0xf4, 0xff, 0xe5, 0xf0, 0xf1, 0xff, 0xe5, 0xf1, 0xf2, 0xff, 0xcb, 0xde, 0xec, 0xff, 0xbc, 0xcf, 0xe0, 0xff, 0xd0, 0xd9, 0xdc, 0xff, 0xe9, 0xf2, 0xf0, 0xff, 0xe3, 0xf2, 0xf1, 0xff, 0xd8, 0xe0, 0xdd, 0xff, 0xd5, 0xd6, 0xce, 0xff, 0xda, 0xe3, 0xdc, 0xff, 0xcb, 0xc5, 0xc0, 0xff, 0x68, 0x49, 0x46, 0xff, 0x3e, 0x21, 0x21, 0xff, 0x55, 0x40, 0x40, 0xff, 0x5f, 0x4a, 0x4c, 0xff, 0x5d, 0x48, 0x4c, 0xff, 0x50, 0x3b, 0x3e, 0xff, 0x39, 0x25, 0x28, 0xff, 0x3e, 0x29, 0x2c, 0xff, 0x3d, 0x29, 0x2c, 0xff, 0x41, 0x2e, 0x32, 0xff, 0x47, 0x34, 0x38, 0xff, 0x3b, 0x27, 0x2b, 0xff, 0x24, 0x10, 0x13, 0xff, 0x25, 0x10, 0x14, 0xff, 0x2f, 0x19, 0x1d, 0xff, 0x27, 0x13, 0x15, 0xff, 0x29, 0x1a, 0x1b, 0xff, 0x3d, 0x2e, 0x34, 0xff, 0x47, 0x3c, 0x42, 0xff, 0x3c, 0x31, 0x3a, 0xff, 0x30, 0x21, 0x2f, 0xff, 0x31, 0x23, 0x34, 0xff, 0x2b, 0x20, 0x34, 0xff, 0x2e, 0x22, 0x38, 0xff, 0x37, 0x28, 0x3c, 0xff, 0x38, 0x2a, 0x39, 0xff, 0x39, 0x2f, 0x40, 0xff, 0x41, 0x38, 0x54, 0xff, 0x46, 0x3f, 0x61, 0xff, 0x50, 0x4c, 0x74, 0xff, 0x56, 0x54, 0x87, 0xff, 0x60, 0x63, 0x96, 0xff, 0x66, 0x6c, 0x9d, 0xff, 0x66, 0x6d, 0xa0, 0xff, 0x65, 0x6e, 0xa2, 0xff, 0x68, 0x70, 0xa5, 0xff, 0x6d, 0x72, 0xa6, 0xff, 0x6a, 0x72, 0xa5, 0xff, 0x70, 0x7b, 0xa9, 0xff, 0x6c, 0x77, 0xa3, 0xff, 0x6d, 0x72, 0xa1, 0xff, 0x72, 0x78, 0xa3, 0xff, 0x74, 0x79, 0xa4, 0xff, 0x6d, 0x6c, 0x94, 0xff, 0x49, 0x45, 0x60, 0xff, 0x3c, 0x32, 0x41, 0xff, 0x34, 0x23, 0x2e, 0xff, 0x38, 0x26, 0x2d, 0xff, 0x34, 0x21, 0x27, 0xff, 0x31, 0x1f, 0x24, 0xff, 0x36, 0x23, 0x28, 0xff, 0x2d, 0x19, 0x1e, 0xff, 0x2c, 0x18, 0x1c, 0xff, 0x31, 0x1e, 0x22, 0xff, 0x34, 0x21, 0x24, 0xff, 0x2b, 0x18, 0x1a, 0xff, 0x2a, 0x16, 0x1a, 0xff, 0x2e, 0x1a, 0x1f, 0xff, 0x2a, 0x18, 0x1c, 0xff, 0x27, 0x16, 0x19, 0xff, 0x23, 0x13, 0x15, 0xff, 0x25, 0x14, 0x17, 0xff, 0x24, 0x12, 0x16, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x22, 0x11, 0x14, 0xff, 0x25, 0x13, 0x16, 0xff, 0x27, 0x14, 0x17, 0xff, 0x26, 0x13, 0x16, 0xff, 0x27, 0x14, 0x17, 0xff, 0x27, 0x14, 0x17, 0xff, 0x2a, 0x1a, 0x19, 0xff, 0x3a, 0x2d, 0x2b, 0xff, 0x43, 0x37, 0x34, 0xff, 0x54, 0x46, 0x42, 0xff, 0x51, 0x42, 0x3e, 0xff, 0x2e, 0x1f, 0x20, 0xff, 0x1a, 0x09, 0x10, 0xff, 0x19, 0x03, 0x0a, 0xff, 0x3a, 0x23, 0x22, 0xff, 0x4a, 0x34, 0x2d, 0xff, 0x48, 0x34, 0x2d, 0xff, 0x38, 0x24, 0x22, 0xff, 0x2a, 0x16, 0x17, 0xff, 0x39, 0x25, 0x1f, 0xff, 0x44, 0x2f, 0x27, 0xff, 0x23, 0x11, 0x12, 0xff, 0x1a, 0x07, 0x0b, 0xff, 0x34, 0x21, 0x22, 0xff, 0x2e, 0x1a, 0x1d, 0xff, 0x2e, 0x19, 0x1c, 0xff, 0x2c, 0x18, 0x1a, 0xff, 0x35, 0x20, 0x20, 0xff, 0x49, 0x30, 0x2a, 0xff, 0x2b, 0x14, 0x15, 0xff, 0x29, 0x16, 0x1b, 0xff, 0x5b, 0x48, 0x41, 0xff, 0xd9, 0xd4, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xbb, 0xac, 0x8b, 0xff, 0xa0, 0x89, 0x59, 0xff, 0xa5, 0x8e, 0x5e, 0xff, 0xa5, 0x8f, 0x5f, 0xff, 0xa5, 0x8f, 0x5f, 0xff, 0xa2, 0x8b, 0x5d, 0xff, 0x97, 0x7f, 0x52, 0xff, 0x8d, 0x74, 0x49, 0xff, 0x8d, 0x75, 0x4b, 0xff, 0x93, 0x7a, 0x4e, 0xff, 0x96, 0x79, 0x50, 0xff, 0x8a, 0x6d, 0x45, 0xff, 0xb5, 0xa0, 0x76, 0xff, 0x98, 0x80, 0x57, 0xff, 0x8c, 0x6b, 0x3a, 0xff, 0xda, 0xd6, 0xb9, 0xff, 0xe6, 0xf4, 0xe4, 0xff, 0xe7, 0xf2, 0xf3, 0xff, 0xed, 0xf9, 0xfc, 0xff, 0xea, 0xf6, 0xf6, 0xff, 0xe7, 0xf3, 0xf3, 0xff, 0xe5, 0xf0, 0xf1, 0xff, 0xe0, 0xee, 0xf1, 0xff, 0xca, 0xdb, 0xea, 0xff, 0xbb, 0xca, 0xd8, 0xff, 0xd8, 0xe1, 0xdd, 0xff, 0xed, 0xf4, 0xf2, 0xff, 0xde, 0xed, 0xec, 0xff, 0xe0, 0xe3, 0xe0, 0xff, 0xb5, 0xb0, 0xac, 0xff, 0xb6, 0xb9, 0xb2, 0xff, 0xdf, 0xde, 0xdc, 0xff, 0x91, 0x80, 0x80, 0xff, 0x5e, 0x46, 0x44, 0xff, 0x64, 0x4e, 0x51, 0xff, 0x51, 0x3c, 0x41, 0xff, 0x3f, 0x2a, 0x2f, 0xff, 0x3a, 0x23, 0x29, 0xff, 0x39, 0x24, 0x27, 0xff, 0x37, 0x20, 0x25, 0xff, 0x38, 0x22, 0x26, 0xff, 0x32, 0x1e, 0x20, 0xff, 0x2d, 0x1b, 0x1d, 0xff, 0x27, 0x12, 0x17, 0xff, 0x26, 0x0f, 0x13, 0xff, 0x29, 0x14, 0x16, 0xff, 0x2e, 0x1c, 0x1f, 0xff, 0x29, 0x15, 0x1e, 0xff, 0x3c, 0x2a, 0x35, 0xff, 0x52, 0x44, 0x51, 0xff, 0x31, 0x26, 0x37, 0xff, 0x30, 0x26, 0x41, 0xff, 0x3f, 0x39, 0x60, 0xff, 0x42, 0x3f, 0x6a, 0xff, 0x45, 0x44, 0x71, 0xff, 0x44, 0x43, 0x6d, 0xff, 0x40, 0x3d, 0x67, 0xff, 0x45, 0x44, 0x6b, 0xff, 0x46, 0x46, 0x6f, 0xff, 0x47, 0x48, 0x77, 0xff, 0x59, 0x63, 0x99, 0xff, 0x69, 0x75, 0xb0, 0xff, 0x72, 0x7a, 0xb2, 0xff, 0x80, 0x88, 0xb5, 0xff, 0x88, 0x91, 0xba, 0xff, 0x80, 0x8c, 0xba, 0xff, 0x7f, 0x8d, 0xba, 0xff, 0x88, 0x96, 0xbf, 0xff, 0x93, 0x9d, 0xc4, 0xff, 0x8b, 0x98, 0xbe, 0xff, 0x89, 0x97, 0xb8, 0xff, 0x89, 0x94, 0xb6, 0xff, 0x8b, 0x94, 0xb8, 0xff, 0x8a, 0x98, 0xb6, 0xff, 0x82, 0x91, 0xb5, 0xff, 0x7a, 0x82, 0xb0, 0xff, 0x67, 0x6c, 0x9b, 0xff, 0x52, 0x52, 0x77, 0xff, 0x39, 0x32, 0x48, 0xff, 0x33, 0x24, 0x32, 0xff, 0x3a, 0x2b, 0x31, 0xff, 0x35, 0x27, 0x2c, 0xff, 0x31, 0x20, 0x29, 0xff, 0x36, 0x26, 0x2d, 0xff, 0x3a, 0x28, 0x2d, 0xff, 0x32, 0x20, 0x24, 0xff, 0x2b, 0x19, 0x1b, 0xff, 0x2b, 0x19, 0x1d, 0xff, 0x2f, 0x1d, 0x22, 0xff, 0x2d, 0x1b, 0x1f, 0xff, 0x2b, 0x1a, 0x1d, 0xff, 0x28, 0x17, 0x1a, 0xff, 0x26, 0x15, 0x18, 0xff, 0x26, 0x15, 0x18, 0xff, 0x20, 0x10, 0x12, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x23, 0x12, 0x15, 0xff, 0x22, 0x11, 0x14, 0xff, 0x24, 0x12, 0x15, 0xff, 0x26, 0x12, 0x15, 0xff, 0x25, 0x12, 0x15, 0xff, 0x28, 0x15, 0x18, 0xff, 0x2c, 0x18, 0x1c, 0xff, 0x2e, 0x1e, 0x1e, 0xff, 0x37, 0x29, 0x29, 0xff, 0x2a, 0x1d, 0x1d, 0xff, 0x1c, 0x0b, 0x0c, 0xff, 0x10, 0x00, 0x04, 0xff, 0x16, 0x03, 0x09, 0xff, 0x1b, 0x07, 0x08, 0xff, 0x44, 0x31, 0x2a, 0xff, 0x4d, 0x39, 0x32, 0xff, 0x36, 0x21, 0x1f, 0xff, 0x29, 0x15, 0x12, 0xff, 0x2e, 0x19, 0x18, 0xff, 0x32, 0x1e, 0x1c, 0xff, 0x4b, 0x37, 0x31, 0xff, 0x40, 0x2c, 0x27, 0xff, 0x13, 0x03, 0x06, 0xff, 0x27, 0x13, 0x17, 0xff, 0x2b, 0x18, 0x1a, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x2f, 0x1a, 0x1d, 0xff, 0x2c, 0x18, 0x18, 0xff, 0x44, 0x2d, 0x26, 0xff, 0x3a, 0x1f, 0x21, 0xff, 0x23, 0x13, 0x14, 0xff, 0x29, 0x18, 0x1b, 0xff, 0x31, 0x19, 0x1a, 0xff, 0x9f, 0x93, 0x92, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe7, 0xdd, 0xff, 0xa7, 0x94, 0x69, 0xff, 0x9e, 0x88, 0x58, 0xff, 0xa2, 0x8c, 0x5c, 0xff, 0xa2, 0x8c, 0x5c, 0xff, 0xa4, 0x8e, 0x5e, 0xff, 0xa3, 0x8c, 0x5e, 0xff, 0x9d, 0x84, 0x57, 0xff, 0x95, 0x7b, 0x50, 0xff, 0x8d, 0x73, 0x49, 0xff, 0x86, 0x68, 0x40, 0xff, 0x74, 0x59, 0x33, 0xff, 0x9a, 0x83, 0x5b, 0xff, 0xae, 0x94, 0x6b, 0xff, 0x81, 0x61, 0x38, 0xff, 0x9a, 0x79, 0x48, 0xff, 0xd0, 0xce, 0xaf, 0xff, 0xe0, 0xf7, 0xef, 0xff, 0xed, 0xf8, 0xf9, 0xff, 0xeb, 0xf6, 0xfa, 0xff, 0xe7, 0xf4, 0xf6, 0xff, 0xe6, 0xf2, 0xf3, 0xff, 0xe1, 0xef, 0xef, 0xff, 0xdd, 0xeb, 0xf2, 0xff, 0xc6, 0xd4, 0xdf, 0xff, 0xca, 0xd5, 0xdd, 0xff, 0xe0, 0xeb, 0xeb, 0xff, 0xe2, 0xec, 0xeb, 0xff, 0xdf, 0xea, 0xe8, 0xff, 0xc9, 0xcb, 0xc9, 0xff, 0xb3, 0xae, 0xae, 0xff, 0xb6, 0xb3, 0xb1, 0xff, 0xb6, 0xb2, 0xb4, 0xff, 0x87, 0x7d, 0x80, 0xff, 0x4b, 0x36, 0x36, 0xff, 0x3d, 0x27, 0x2c, 0xff, 0x43, 0x2c, 0x30, 0xff, 0x42, 0x2b, 0x31, 0xff, 0x3b, 0x25, 0x2d, 0xff, 0x35, 0x21, 0x26, 0xff, 0x34, 0x1e, 0x23, 0xff, 0x37, 0x21, 0x25, 0xff, 0x34, 0x1f, 0x21, 0xff, 0x28, 0x14, 0x17, 0xff, 0x24, 0x11, 0x15, 0xff, 0x28, 0x12, 0x16, 0xff, 0x2a, 0x16, 0x17, 0xff, 0x2a, 0x1a, 0x1e, 0xff, 0x2f, 0x20, 0x2d, 0xff, 0x42, 0x36, 0x47, 0xff, 0x30, 0x29, 0x3d, 0xff, 0x2c, 0x28, 0x44, 0xff, 0x40, 0x3d, 0x69, 0xff, 0x4f, 0x51, 0x8b, 0xff, 0x5b, 0x61, 0x9c, 0xff, 0x5b, 0x63, 0x9d, 0xff, 0x56, 0x5e, 0x92, 0xff, 0x54, 0x5c, 0x93, 0xff, 0x51, 0x57, 0x95, 0xff, 0x59, 0x5d, 0x9c, 0xff, 0x67, 0x72, 0xaa, 0xff, 0x79, 0x84, 0xb6, 0xff, 0x83, 0x8e, 0xba, 0xff, 0x89, 0x98, 0xbd, 0xff, 0x8d, 0x9d, 0xbf, 0xff, 0x92, 0xa0, 0xc2, 0xff, 0x92, 0xa1, 0xc5, 0xff, 0x96, 0xa6, 0xc8, 0xff, 0x9e, 0xae, 0xcb, 0xff, 0xa0, 0xaf, 0xc7, 0xff, 0xa0, 0xb0, 0xc8, 0xff, 0x9f, 0xac, 0xc8, 0xff, 0x9e, 0xa9, 0xc4, 0xff, 0x9d, 0xaa, 0xc1, 0xff, 0x9b, 0xa7, 0xbf, 0xff, 0x91, 0x9e, 0xb8, 0xff, 0x82, 0x90, 0xaf, 0xff, 0x73, 0x7f, 0xa8, 0xff, 0x68, 0x6f, 0xa0, 0xff, 0x4d, 0x4c, 0x78, 0xff, 0x3c, 0x31, 0x50, 0xff, 0x3a, 0x2c, 0x40, 0xff, 0x33, 0x25, 0x32, 0xff, 0x37, 0x29, 0x32, 0xff, 0x39, 0x2a, 0x32, 0xff, 0x3a, 0x2a, 0x32, 0xff, 0x37, 0x27, 0x2c, 0xff, 0x36, 0x25, 0x29, 0xff, 0x31, 0x21, 0x25, 0xff, 0x2d, 0x1c, 0x20, 0xff, 0x2a, 0x1a, 0x1f, 0xff, 0x2a, 0x19, 0x1c, 0xff, 0x27, 0x16, 0x19, 0xff, 0x24, 0x13, 0x16, 0xff, 0x21, 0x10, 0x14, 0xff, 0x21, 0x10, 0x13, 0xff, 0x23, 0x12, 0x15, 0xff, 0x21, 0x10, 0x13, 0xff, 0x1e, 0x0e, 0x11, 0xff, 0x22, 0x11, 0x14, 0xff, 0x29, 0x16, 0x19, 0xff, 0x28, 0x15, 0x18, 0xff, 0x24, 0x10, 0x13, 0xff, 0x21, 0x0e, 0x11, 0xff, 0x1d, 0x08, 0x0d, 0xff, 0x18, 0x05, 0x0a, 0xff, 0x1d, 0x0b, 0x10, 0xff, 0x21, 0x0c, 0x11, 0xff, 0x22, 0x0e, 0x13, 0xff, 0x30, 0x1d, 0x1d, 0xff, 0x54, 0x40, 0x3b, 0xff, 0x4f, 0x3a, 0x35, 0xff, 0x36, 0x22, 0x1f, 0xff, 0x2b, 0x1a, 0x16, 0xff, 0x37, 0x22, 0x20, 0xff, 0x47, 0x30, 0x2d, 0xff, 0x31, 0x1c, 0x16, 0xff, 0x4a, 0x37, 0x2f, 0xff, 0x34, 0x21, 0x1f, 0xff, 0x19, 0x09, 0x0e, 0xff, 0x27, 0x13, 0x17, 0xff, 0x28, 0x15, 0x17, 0xff, 0x28, 0x15, 0x18, 0xff, 0x2a, 0x15, 0x19, 0xff, 0x34, 0x20, 0x1d, 0xff, 0x51, 0x37, 0x2c, 0xff, 0x3b, 0x1f, 0x20, 0xff, 0x2d, 0x1c, 0x1b, 0xff, 0x2f, 0x1c, 0x1d, 0xff, 0x35, 0x1d, 0x20, 0xff, 0x72, 0x62, 0x60, 0xff, 0xf5, 0xf4, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xec, 0xe8, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd7, 0xcf, 0xbc, 0xff, 0x9e, 0x87, 0x58, 0xff, 0x9e, 0x87, 0x59, 0xff, 0xa0, 0x89, 0x5b, 0xff, 0xa2, 0x8b, 0x5d, 0xff, 0xa5, 0x8e, 0x5f, 0xff, 0xa5, 0x8d, 0x5e, 0xff, 0x9c, 0x84, 0x56, 0xff, 0x97, 0x7c, 0x50, 0xff, 0x94, 0x77, 0x4e, 0xff, 0x91, 0x74, 0x4c, 0xff, 0x75, 0x59, 0x35, 0xff, 0x9c, 0x83, 0x5c, 0xff, 0x9c, 0x80, 0x57, 0xff, 0x65, 0x40, 0x22, 0xff, 0x6b, 0x41, 0x1d, 0xff, 0xb2, 0xa9, 0x8a, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xec, 0xf7, 0xfa, 0xff, 0xe9, 0xf6, 0xf9, 0xff, 0xe6, 0xf3, 0xf5, 0xff, 0xe3, 0xf1, 0xf0, 0xff, 0xe0, 0xee, 0xee, 0xff, 0xd5, 0xe2, 0xec, 0xff, 0xc9, 0xd6, 0xde, 0xff, 0xd5, 0xe1, 0xe4, 0xff, 0xe4, 0xee, 0xed, 0xff, 0xcd, 0xd8, 0xd8, 0xff, 0xd6, 0xe1, 0xe1, 0xff, 0xc6, 0xcc, 0xc8, 0xff, 0xc0, 0xbc, 0xb8, 0xff, 0x82, 0x74, 0x79, 0xff, 0x92, 0x8d, 0x93, 0xff, 0x8d, 0x88, 0x90, 0xff, 0x69, 0x55, 0x5e, 0xff, 0x49, 0x35, 0x3a, 0xff, 0x32, 0x1d, 0x20, 0xff, 0x38, 0x22, 0x25, 0xff, 0x40, 0x2d, 0x30, 0xff, 0x43, 0x30, 0x34, 0xff, 0x35, 0x20, 0x23, 0xff, 0x25, 0x11, 0x14, 0xff, 0x29, 0x16, 0x19, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x37, 0x21, 0x21, 0xff, 0x31, 0x1e, 0x21, 0xff, 0x29, 0x16, 0x1b, 0xff, 0x30, 0x1e, 0x26, 0xff, 0x42, 0x35, 0x47, 0xff, 0x2f, 0x24, 0x3a, 0xff, 0x2b, 0x27, 0x46, 0xff, 0x3e, 0x3f, 0x6c, 0xff, 0x4a, 0x4f, 0x89, 0xff, 0x56, 0x5d, 0x9d, 0xff, 0x64, 0x6b, 0xaa, 0xff, 0x6a, 0x73, 0xae, 0xff, 0x67, 0x72, 0xa9, 0xff, 0x6c, 0x7a, 0xb1, 0xff, 0x71, 0x7d, 0xb6, 0xff, 0x77, 0x81, 0xb8, 0xff, 0x84, 0x91, 0xc3, 0xff, 0x8c, 0x9a, 0xc2, 0xff, 0x94, 0xa3, 0xc4, 0xff, 0x99, 0xa9, 0xc8, 0xff, 0x9a, 0xaa, 0xca, 0xff, 0x9e, 0xaf, 0xcc, 0xff, 0xa8, 0xb9, 0xd1, 0xff, 0xab, 0xbb, 0xd2, 0xff, 0xac, 0xbd, 0xd2, 0xff, 0xad, 0xbd, 0xd2, 0xff, 0xad, 0xbd, 0xd2, 0xff, 0xab, 0xba, 0xd0, 0xff, 0xac, 0xb9, 0xd0, 0xff, 0xa8, 0xb7, 0xcc, 0xff, 0xa3, 0xb0, 0xc7, 0xff, 0xa1, 0xaf, 0xc4, 0xff, 0x93, 0xa0, 0xba, 0xff, 0x82, 0x8c, 0xb0, 0xff, 0x76, 0x7e, 0xaa, 0xff, 0x64, 0x6b, 0x9a, 0xff, 0x4d, 0x4f, 0x7d, 0xff, 0x41, 0x3b, 0x60, 0xff, 0x3b, 0x31, 0x48, 0xff, 0x39, 0x2d, 0x39, 0xff, 0x39, 0x28, 0x35, 0xff, 0x37, 0x25, 0x30, 0xff, 0x38, 0x29, 0x30, 0xff, 0x34, 0x21, 0x29, 0xff, 0x2d, 0x1b, 0x20, 0xff, 0x2b, 0x19, 0x1e, 0xff, 0x2e, 0x1c, 0x23, 0xff, 0x2a, 0x16, 0x1b, 0xff, 0x25, 0x13, 0x16, 0xff, 0x23, 0x12, 0x16, 0xff, 0x21, 0x10, 0x14, 0xff, 0x22, 0x11, 0x15, 0xff, 0x21, 0x10, 0x14, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x20, 0x0f, 0x13, 0xff, 0x22, 0x11, 0x14, 0xff, 0x23, 0x12, 0x15, 0xff, 0x2a, 0x19, 0x1c, 0xff, 0x2d, 0x1b, 0x1e, 0xff, 0x2b, 0x19, 0x1a, 0xff, 0x2d, 0x19, 0x1b, 0xff, 0x31, 0x1e, 0x20, 0xff, 0x36, 0x23, 0x23, 0xff, 0x3e, 0x2a, 0x28, 0xff, 0x3e, 0x2a, 0x27, 0xff, 0x49, 0x34, 0x32, 0xff, 0x4a, 0x35, 0x35, 0xff, 0x32, 0x1e, 0x1d, 0xff, 0x43, 0x2e, 0x2b, 0xff, 0x43, 0x2e, 0x27, 0xff, 0x42, 0x2b, 0x27, 0xff, 0x33, 0x1c, 0x1f, 0xff, 0x33, 0x22, 0x1b, 0xff, 0x4e, 0x3f, 0x33, 0xff, 0x19, 0x08, 0x0b, 0xff, 0x1c, 0x0b, 0x10, 0xff, 0x2b, 0x16, 0x1a, 0xff, 0x2e, 0x1a, 0x1e, 0xff, 0x2b, 0x17, 0x1b, 0xff, 0x28, 0x12, 0x15, 0xff, 0x35, 0x20, 0x1d, 0xff, 0x44, 0x2e, 0x29, 0xff, 0x34, 0x1c, 0x1c, 0xff, 0x34, 0x1d, 0x1e, 0xff, 0x31, 0x1d, 0x20, 0xff, 0x2c, 0x18, 0x1c, 0xff, 0x3c, 0x26, 0x2a, 0xff, 0xdd, 0xd8, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf1, 0xec, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xc0, 0xb1, 0x95, 0xff, 0x9c, 0x85, 0x58, 0xff, 0xa0, 0x89, 0x5b, 0xff, 0x9f, 0x88, 0x5b, 0xff, 0xa1, 0x8a, 0x5c, 0xff, 0xa4, 0x8d, 0x60, 0xff, 0xa4, 0x8c, 0x5e, 0xff, 0x9e, 0x86, 0x58, 0xff, 0x9a, 0x82, 0x55, 0xff, 0x95, 0x7b, 0x4e, 0xff, 0x90, 0x73, 0x4c, 0xff, 0x78, 0x5b, 0x38, 0xff, 0x95, 0x7d, 0x53, 0xff, 0xbf, 0xaa, 0x7e, 0xff, 0x80, 0x66, 0x3f, 0xff, 0x6d, 0x48, 0x1e, 0xff, 0xbb, 0xae, 0x8b, 0xff, 0xf2, 0xfe, 0xf7, 0xff, 0xe9, 0xf5, 0xfb, 0xff, 0xea, 0xf7, 0xfa, 0xff, 0xe7, 0xf4, 0xf5, 0xff, 0xe0, 0xed, 0xeb, 0xff, 0xdc, 0xe8, 0xe8, 0xff, 0xd8, 0xe4, 0xe8, 0xff, 0xd6, 0xe3, 0xe6, 0xff, 0xe5, 0xf0, 0xf2, 0xff, 0xcc, 0xd4, 0xd5, 0xff, 0xa2, 0xa8, 0xab, 0xff, 0xc1, 0xcb, 0xd6, 0xff, 0xbb, 0xc2, 0xc8, 0xff, 0x63, 0x58, 0x55, 0xff, 0x87, 0x79, 0x77, 0xff, 0xa1, 0xa3, 0xa7, 0xff, 0x87, 0x88, 0x95, 0xff, 0x57, 0x45, 0x53, 0xff, 0x33, 0x1b, 0x20, 0xff, 0x35, 0x1f, 0x22, 0xff, 0x3b, 0x26, 0x29, 0xff, 0x2e, 0x19, 0x1c, 0xff, 0x32, 0x1e, 0x21, 0xff, 0x32, 0x1d, 0x20, 0xff, 0x2d, 0x18, 0x1b, 0xff, 0x31, 0x1e, 0x21, 0xff, 0x28, 0x15, 0x17, 0xff, 0x28, 0x14, 0x17, 0xff, 0x20, 0x0f, 0x15, 0xff, 0x28, 0x16, 0x1e, 0xff, 0x3c, 0x2a, 0x36, 0xff, 0x39, 0x2f, 0x41, 0xff, 0x32, 0x2b, 0x47, 0xff, 0x3f, 0x3e, 0x70, 0xff, 0x50, 0x56, 0x95, 0xff, 0x5c, 0x67, 0xa6, 0xff, 0x61, 0x6c, 0xa9, 0xff, 0x66, 0x72, 0xac, 0xff, 0x74, 0x80, 0xb5, 0xff, 0x7e, 0x8b, 0xbb, 0xff, 0x82, 0x91, 0xbe, 0xff, 0x88, 0x98, 0xc2, 0xff, 0x92, 0xa2, 0xc7, 0xff, 0x94, 0xa5, 0xc6, 0xff, 0x99, 0xab, 0xcb, 0xff, 0x9e, 0xaf, 0xce, 0xff, 0xa1, 0xb1, 0xcd, 0xff, 0xa8, 0xba, 0xd3, 0xff, 0xad, 0xc0, 0xd5, 0xff, 0xb3, 0xc4, 0xd7, 0xff, 0xb3, 0xc3, 0xd6, 0xff, 0xb4, 0xc5, 0xd7, 0xff, 0xb5, 0xc5, 0xd7, 0xff, 0xb3, 0xc4, 0xd7, 0xff, 0xb4, 0xc4, 0xd6, 0xff, 0xb3, 0xc2, 0xd4, 0xff, 0xb4, 0xc2, 0xd5, 0xff, 0xb0, 0xbf, 0xd1, 0xff, 0xaa, 0xb9, 0xcc, 0xff, 0xa3, 0xb0, 0xc9, 0xff, 0x90, 0x9b, 0xba, 0xff, 0x87, 0x90, 0xb4, 0xff, 0x79, 0x82, 0xac, 0xff, 0x5a, 0x60, 0x8f, 0xff, 0x4d, 0x4e, 0x7d, 0xff, 0x4b, 0x45, 0x6e, 0xff, 0x46, 0x3c, 0x57, 0xff, 0x42, 0x36, 0x44, 0xff, 0x37, 0x28, 0x31, 0xff, 0x33, 0x21, 0x2a, 0xff, 0x33, 0x20, 0x29, 0xff, 0x32, 0x20, 0x24, 0xff, 0x30, 0x1d, 0x20, 0xff, 0x2d, 0x19, 0x1e, 0xff, 0x32, 0x1e, 0x22, 0xff, 0x32, 0x1f, 0x22, 0xff, 0x25, 0x14, 0x17, 0xff, 0x22, 0x12, 0x16, 0xff, 0x22, 0x11, 0x16, 0xff, 0x22, 0x10, 0x15, 0xff, 0x21, 0x10, 0x14, 0xff, 0x20, 0x0f, 0x13, 0xff, 0x22, 0x11, 0x15, 0xff, 0x20, 0x0f, 0x13, 0xff, 0x1f, 0x0e, 0x11, 0xff, 0x29, 0x17, 0x1a, 0xff, 0x40, 0x2d, 0x2f, 0xff, 0x42, 0x31, 0x2e, 0xff, 0x2e, 0x1c, 0x19, 0xff, 0x3d, 0x2b, 0x29, 0xff, 0x43, 0x30, 0x2e, 0xff, 0x3b, 0x28, 0x27, 0xff, 0x2d, 0x1a, 0x1b, 0xff, 0x21, 0x0e, 0x10, 0xff, 0x2a, 0x19, 0x18, 0xff, 0x2d, 0x1a, 0x1b, 0xff, 0x32, 0x1c, 0x1c, 0xff, 0x28, 0x12, 0x14, 0xff, 0x21, 0x0c, 0x13, 0xff, 0x3e, 0x2e, 0x2a, 0xff, 0x49, 0x37, 0x2f, 0xff, 0x1a, 0x06, 0x0b, 0xff, 0x1e, 0x0d, 0x11, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x29, 0x14, 0x1a, 0xff, 0x2a, 0x15, 0x1a, 0xff, 0x30, 0x1a, 0x1d, 0xff, 0x36, 0x20, 0x20, 0xff, 0x29, 0x16, 0x17, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x30, 0x1a, 0x1d, 0xff, 0x2a, 0x16, 0x18, 0xff, 0x2a, 0x17, 0x17, 0xff, 0x3d, 0x27, 0x26, 0xff, 0xc1, 0xb6, 0xb3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf0, 0xeb, 0xff, 0xae, 0x9b, 0x75, 0xff, 0x9f, 0x88, 0x5a, 0xff, 0x9f, 0x88, 0x5a, 0xff, 0xa2, 0x8b, 0x5d, 0xff, 0xa3, 0x8c, 0x5e, 0xff, 0xa4, 0x8d, 0x5f, 0xff, 0xa2, 0x8b, 0x5d, 0xff, 0x9f, 0x87, 0x59, 0xff, 0x9e, 0x86, 0x58, 0xff, 0x99, 0x81, 0x53, 0xff, 0x8f, 0x74, 0x4b, 0xff, 0x7c, 0x60, 0x3b, 0xff, 0x8f, 0x76, 0x4a, 0xff, 0xb3, 0x9e, 0x6e, 0xff, 0x9f, 0x89, 0x62, 0xff, 0x87, 0x6e, 0x4e, 0xff, 0xab, 0x9d, 0x7b, 0xff, 0xe8, 0xf2, 0xe5, 0xff, 0xe7, 0xf8, 0xff, 0xff, 0xea, 0xf7, 0xfb, 0xff, 0xe7, 0xf4, 0xf5, 0xff, 0xde, 0xe9, 0xe7, 0xff, 0xd8, 0xe3, 0xe0, 0xff, 0xe2, 0xee, 0xed, 0xff, 0xda, 0xe7, 0xe6, 0xff, 0xe7, 0xf2, 0xf4, 0xff, 0xc7, 0xce, 0xd1, 0xff, 0x9c, 0x9f, 0xa6, 0xff, 0xa0, 0xa5, 0xb6, 0xff, 0x9e, 0xa4, 0xb1, 0xff, 0x86, 0x7f, 0x7e, 0xff, 0xb7, 0xbd, 0xc1, 0xff, 0xb9, 0xc0, 0xcf, 0xff, 0x5f, 0x49, 0x58, 0xff, 0x22, 0x0a, 0x0d, 0xff, 0x28, 0x13, 0x11, 0xff, 0x61, 0x4d, 0x50, 0xff, 0x54, 0x3f, 0x42, 0xff, 0x37, 0x22, 0x25, 0xff, 0x2e, 0x19, 0x1c, 0xff, 0x2d, 0x18, 0x1b, 0xff, 0x32, 0x1d, 0x20, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x24, 0x11, 0x13, 0xff, 0x20, 0x0f, 0x13, 0xff, 0x28, 0x17, 0x21, 0xff, 0x39, 0x27, 0x31, 0xff, 0x34, 0x23, 0x34, 0xff, 0x2c, 0x28, 0x46, 0xff, 0x45, 0x47, 0x75, 0xff, 0x52, 0x59, 0x9b, 0xff, 0x57, 0x65, 0xaa, 0xff, 0x63, 0x73, 0xaf, 0xff, 0x78, 0x86, 0xbc, 0xff, 0x81, 0x91, 0xbf, 0xff, 0x87, 0x99, 0xc0, 0xff, 0x8d, 0x9e, 0xc5, 0xff, 0x90, 0xa1, 0xc8, 0xff, 0x97, 0xa8, 0xcc, 0xff, 0x9e, 0xb0, 0xcf, 0xff, 0x9f, 0xb1, 0xcf, 0xff, 0xa1, 0xb5, 0xd0, 0xff, 0xa8, 0xbb, 0xd3, 0xff, 0xb1, 0xc2, 0xd9, 0xff, 0xb2, 0xc7, 0xd9, 0xff, 0xb3, 0xc8, 0xd8, 0xff, 0xb7, 0xc9, 0xda, 0xff, 0xb7, 0xc9, 0xd9, 0xff, 0xb9, 0xcb, 0xdb, 0xff, 0xb8, 0xca, 0xda, 0xff, 0xb6, 0xc8, 0xd9, 0xff, 0xb6, 0xc9, 0xd9, 0xff, 0xb7, 0xc9, 0xd9, 0xff, 0xb9, 0xc9, 0xd9, 0xff, 0xb4, 0xc4, 0xd4, 0xff, 0xae, 0xbd, 0xcf, 0xff, 0xac, 0xba, 0xd0, 0xff, 0xa2, 0xae, 0xc8, 0xff, 0x95, 0xa0, 0xbf, 0xff, 0x8a, 0x94, 0xb6, 0xff, 0x7b, 0x84, 0xac, 0xff, 0x67, 0x6e, 0x9c, 0xff, 0x4a, 0x4d, 0x7f, 0xff, 0x40, 0x3e, 0x6a, 0xff, 0x41, 0x3d, 0x58, 0xff, 0x41, 0x37, 0x48, 0xff, 0x39, 0x29, 0x37, 0xff, 0x31, 0x1f, 0x2b, 0xff, 0x30, 0x20, 0x26, 0xff, 0x32, 0x21, 0x24, 0xff, 0x37, 0x24, 0x27, 0xff, 0x38, 0x25, 0x26, 0xff, 0x39, 0x27, 0x28, 0xff, 0x34, 0x21, 0x24, 0xff, 0x25, 0x13, 0x16, 0xff, 0x22, 0x12, 0x15, 0xff, 0x27, 0x16, 0x1a, 0xff, 0x22, 0x11, 0x15, 0xff, 0x1f, 0x0e, 0x12, 0xff, 0x1d, 0x0c, 0x10, 0xff, 0x1e, 0x0d, 0x11, 0xff, 0x1f, 0x0e, 0x11, 0xff, 0x1f, 0x0c, 0x0f, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x58, 0x49, 0x49, 0xff, 0x5a, 0x49, 0x47, 0xff, 0x18, 0x07, 0x05, 0xff, 0x21, 0x0e, 0x12, 0xff, 0x24, 0x11, 0x15, 0xff, 0x22, 0x10, 0x13, 0xff, 0x26, 0x14, 0x17, 0xff, 0x24, 0x10, 0x14, 0xff, 0x23, 0x12, 0x14, 0xff, 0x25, 0x14, 0x13, 0xff, 0x37, 0x23, 0x23, 0xff, 0x20, 0x0f, 0x0f, 0xff, 0x42, 0x35, 0x2e, 0xff, 0x33, 0x20, 0x20, 0xff, 0x26, 0x10, 0x16, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x23, 0x12, 0x15, 0xff, 0x25, 0x10, 0x15, 0xff, 0x34, 0x1e, 0x21, 0xff, 0x34, 0x1f, 0x22, 0xff, 0x2d, 0x17, 0x1b, 0xff, 0x29, 0x14, 0x15, 0xff, 0x36, 0x21, 0x20, 0xff, 0x40, 0x28, 0x25, 0xff, 0x3f, 0x27, 0x22, 0xff, 0x47, 0x30, 0x2d, 0xff, 0x50, 0x3b, 0x37, 0xff, 0x80, 0x70, 0x6d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xdd, 0xd1, 0xff, 0x9e, 0x86, 0x59, 0xff, 0x9e, 0x87, 0x59, 0xff, 0x9d, 0x85, 0x58, 0xff, 0xa1, 0x8a, 0x5c, 0xff, 0xa4, 0x8d, 0x5f, 0xff, 0xa5, 0x8e, 0x60, 0xff, 0xa3, 0x8c, 0x5e, 0xff, 0xa2, 0x8b, 0x5d, 0xff, 0xa1, 0x88, 0x5a, 0xff, 0x9c, 0x84, 0x56, 0xff, 0x94, 0x7c, 0x4f, 0xff, 0x86, 0x6a, 0x42, 0xff, 0x9b, 0x7e, 0x52, 0xff, 0x9f, 0x85, 0x58, 0xff, 0x77, 0x5c, 0x39, 0xff, 0x8d, 0x6f, 0x50, 0xff, 0x8a, 0x72, 0x4b, 0xff, 0xc6, 0xc7, 0xad, 0xff, 0xed, 0xfe, 0xff, 0xff, 0xe9, 0xf8, 0xfc, 0xff, 0xe9, 0xf6, 0xf6, 0xff, 0xe1, 0xed, 0xea, 0xff, 0xd0, 0xda, 0xd7, 0xff, 0xd0, 0xdc, 0xdd, 0xff, 0xdd, 0xea, 0xee, 0xff, 0xc9, 0xd5, 0xd8, 0xff, 0xc9, 0xd5, 0xd7, 0xff, 0xbe, 0xc5, 0xcc, 0xff, 0x8b, 0x8f, 0x9c, 0xff, 0xb5, 0xbc, 0xc3, 0xff, 0xea, 0xf6, 0xfb, 0xff, 0xb4, 0xb8, 0xc7, 0xff, 0x50, 0x3f, 0x4e, 0xff, 0x1e, 0x02, 0x07, 0xff, 0x25, 0x0e, 0x0e, 0xff, 0x5b, 0x45, 0x48, 0xff, 0x5f, 0x4d, 0x50, 0xff, 0x48, 0x34, 0x37, 0xff, 0x4a, 0x35, 0x38, 0xff, 0x2e, 0x19, 0x1c, 0xff, 0x33, 0x1d, 0x20, 0xff, 0x3a, 0x26, 0x29, 0xff, 0x27, 0x14, 0x17, 0xff, 0x29, 0x16, 0x18, 0xff, 0x32, 0x1f, 0x23, 0xff, 0x37, 0x25, 0x2f, 0xff, 0x31, 0x1e, 0x2a, 0xff, 0x32, 0x24, 0x3a, 0xff, 0x41, 0x41, 0x70, 0xff, 0x51, 0x5a, 0x98, 0xff, 0x58, 0x66, 0xa9, 0xff, 0x62, 0x72, 0xb0, 0xff, 0x79, 0x87, 0xba, 0xff, 0x84, 0x95, 0xc0, 0xff, 0x8f, 0xa1, 0xc5, 0xff, 0x95, 0xa7, 0xc6, 0xff, 0x99, 0xa9, 0xcb, 0xff, 0x9a, 0xae, 0xcf, 0xff, 0x9c, 0xb1, 0xcd, 0xff, 0xa1, 0xb4, 0xd0, 0xff, 0xa3, 0xb4, 0xd3, 0xff, 0xa7, 0xbc, 0xd4, 0xff, 0xad, 0xc1, 0xd5, 0xff, 0xb2, 0xc5, 0xd9, 0xff, 0xb3, 0xc7, 0xda, 0xff, 0xb4, 0xc8, 0xda, 0xff, 0xb8, 0xcb, 0xdb, 0xff, 0xba, 0xcc, 0xdc, 0xff, 0xb9, 0xcb, 0xdc, 0xff, 0xbb, 0xcc, 0xdd, 0xff, 0xb9, 0xcc, 0xdd, 0xff, 0xb8, 0xca, 0xdb, 0xff, 0xb7, 0xc9, 0xda, 0xff, 0xb5, 0xc8, 0xd8, 0xff, 0xb4, 0xc5, 0xd6, 0xff, 0xaf, 0xbc, 0xd2, 0xff, 0xa8, 0xb6, 0xce, 0xff, 0xa7, 0xb4, 0xce, 0xff, 0xa2, 0xaf, 0xca, 0xff, 0x99, 0xa5, 0xc0, 0xff, 0x93, 0x9f, 0xbb, 0xff, 0x88, 0x92, 0xb5, 0xff, 0x78, 0x81, 0xaa, 0xff, 0x5f, 0x64, 0x92, 0xff, 0x46, 0x46, 0x72, 0xff, 0x3e, 0x39, 0x59, 0xff, 0x42, 0x38, 0x4c, 0xff, 0x35, 0x27, 0x34, 0xff, 0x2e, 0x21, 0x28, 0xff, 0x35, 0x28, 0x2b, 0xff, 0x37, 0x27, 0x2b, 0xff, 0x3b, 0x26, 0x28, 0xff, 0x3a, 0x26, 0x27, 0xff, 0x36, 0x23, 0x26, 0xff, 0x37, 0x23, 0x26, 0xff, 0x2e, 0x1c, 0x1f, 0xff, 0x1f, 0x0e, 0x12, 0xff, 0x20, 0x0f, 0x13, 0xff, 0x21, 0x10, 0x14, 0xff, 0x1f, 0x0e, 0x12, 0xff, 0x1d, 0x0c, 0x10, 0xff, 0x22, 0x11, 0x16, 0xff, 0x22, 0x0f, 0x14, 0xff, 0x1d, 0x0a, 0x0d, 0xff, 0x2b, 0x1c, 0x1b, 0xff, 0x75, 0x69, 0x60, 0xff, 0x66, 0x58, 0x4f, 0xff, 0x32, 0x22, 0x1e, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x24, 0x10, 0x13, 0xff, 0x28, 0x16, 0x15, 0xff, 0x29, 0x14, 0x17, 0xff, 0x23, 0x10, 0x14, 0xff, 0x2a, 0x18, 0x19, 0xff, 0x27, 0x12, 0x15, 0xff, 0x33, 0x1f, 0x1f, 0xff, 0x40, 0x34, 0x2c, 0xff, 0x19, 0x0a, 0x0e, 0xff, 0x2e, 0x18, 0x1e, 0xff, 0x2d, 0x17, 0x19, 0xff, 0x28, 0x13, 0x17, 0xff, 0x31, 0x1a, 0x1f, 0xff, 0x36, 0x21, 0x21, 0xff, 0x2e, 0x19, 0x18, 0xff, 0x2c, 0x17, 0x19, 0xff, 0x2d, 0x18, 0x1b, 0xff, 0x32, 0x1c, 0x1e, 0xff, 0x31, 0x1a, 0x1b, 0xff, 0x30, 0x1b, 0x1b, 0xff, 0x35, 0x1f, 0x21, 0xff, 0x30, 0x19, 0x1b, 0xff, 0x52, 0x3c, 0x40, 0xff, 0xec, 0xe9, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xd1, 0xc6, 0xb1, 0xff, 0x9e, 0x87, 0x59, 0xff, 0x9d, 0x88, 0x58, 0xff, 0x9f, 0x87, 0x59, 0xff, 0x9e, 0x88, 0x5a, 0xff, 0xa1, 0x8c, 0x5e, 0xff, 0xa4, 0x8f, 0x60, 0xff, 0xa5, 0x91, 0x61, 0xff, 0xa7, 0x91, 0x61, 0xff, 0xa5, 0x8e, 0x5f, 0xff, 0xa0, 0x8a, 0x5a, 0xff, 0x9a, 0x83, 0x54, 0xff, 0x8e, 0x72, 0x49, 0xff, 0x82, 0x65, 0x3b, 0xff, 0xba, 0xa4, 0x72, 0xff, 0x8d, 0x74, 0x49, 0xff, 0x5d, 0x37, 0x19, 0xff, 0x83, 0x60, 0x35, 0xff, 0xcf, 0xc3, 0x95, 0xff, 0xe8, 0xef, 0xe2, 0xff, 0xed, 0xfb, 0xff, 0xff, 0xe8, 0xf6, 0xf9, 0xff, 0xe8, 0xf8, 0xfb, 0xff, 0xea, 0xf3, 0xf0, 0xff, 0xae, 0xb1, 0xb2, 0xff, 0xb5, 0xbb, 0xbc, 0xff, 0xd4, 0xde, 0xdd, 0xff, 0xbc, 0xc5, 0xc9, 0xff, 0xc5, 0xcd, 0xcf, 0xff, 0xcd, 0xd7, 0xd7, 0xff, 0xd9, 0xe6, 0xe9, 0xff, 0xb0, 0xb3, 0xbb, 0xff, 0x55, 0x42, 0x50, 0xff, 0x21, 0x06, 0x0a, 0xff, 0x27, 0x0f, 0x0d, 0xff, 0x5e, 0x4d, 0x4f, 0xff, 0x76, 0x63, 0x67, 0xff, 0x49, 0x32, 0x36, 0xff, 0x42, 0x2b, 0x31, 0xff, 0x31, 0x1b, 0x20, 0xff, 0x34, 0x1f, 0x21, 0xff, 0x3e, 0x29, 0x2b, 0xff, 0x32, 0x1d, 0x21, 0xff, 0x2d, 0x18, 0x1d, 0xff, 0x2d, 0x1b, 0x1d, 0xff, 0x32, 0x22, 0x23, 0xff, 0x31, 0x1f, 0x29, 0xff, 0x2f, 0x1f, 0x2e, 0xff, 0x3c, 0x37, 0x52, 0xff, 0x4f, 0x53, 0x8d, 0xff, 0x59, 0x64, 0xaa, 0xff, 0x61, 0x70, 0xb1, 0xff, 0x72, 0x83, 0xbb, 0xff, 0x84, 0x94, 0xc1, 0xff, 0x8c, 0x9e, 0xc4, 0xff, 0x99, 0xac, 0xcc, 0xff, 0x9f, 0xb0, 0xcd, 0xff, 0xa3, 0xb3, 0xd0, 0xff, 0xa5, 0xb6, 0xd1, 0xff, 0xa5, 0xb7, 0xd0, 0xff, 0xab, 0xbd, 0xd3, 0xff, 0xa9, 0xbd, 0xd2, 0xff, 0xac, 0xc0, 0xd5, 0xff, 0xb2, 0xc4, 0xd9, 0xff, 0xb2, 0xc5, 0xda, 0xff, 0xb3, 0xc5, 0xdb, 0xff, 0xb4, 0xc7, 0xdc, 0xff, 0xb5, 0xc9, 0xdb, 0xff, 0xb7, 0xcb, 0xdc, 0xff, 0xb9, 0xcc, 0xdd, 0xff, 0xbb, 0xcd, 0xdd, 0xff, 0xba, 0xcc, 0xdd, 0xff, 0xb9, 0xcb, 0xdc, 0xff, 0xb8, 0xc9, 0xdb, 0xff, 0xb6, 0xc6, 0xd9, 0xff, 0xb4, 0xc5, 0xd6, 0xff, 0xb1, 0xc2, 0xd5, 0xff, 0xaa, 0xbb, 0xd0, 0xff, 0xaa, 0xba, 0xd2, 0xff, 0xa9, 0xb8, 0xce, 0xff, 0xa0, 0xae, 0xc4, 0xff, 0x9a, 0xa7, 0xc0, 0xff, 0x94, 0xa1, 0xbd, 0xff, 0x8f, 0x9b, 0xb7, 0xff, 0x87, 0x91, 0xb3, 0xff, 0x74, 0x7a, 0xa7, 0xff, 0x5b, 0x5c, 0x87, 0xff, 0x49, 0x46, 0x66, 0xff, 0x4a, 0x40, 0x56, 0xff, 0x3b, 0x2c, 0x39, 0xff, 0x34, 0x25, 0x28, 0xff, 0x3a, 0x2b, 0x2c, 0xff, 0x3b, 0x28, 0x2b, 0xff, 0x3d, 0x29, 0x2c, 0xff, 0x2f, 0x1e, 0x20, 0xff, 0x2e, 0x1d, 0x20, 0xff, 0x31, 0x20, 0x23, 0xff, 0x2a, 0x19, 0x1c, 0xff, 0x24, 0x13, 0x16, 0xff, 0x25, 0x14, 0x17, 0xff, 0x23, 0x12, 0x16, 0xff, 0x20, 0x0f, 0x15, 0xff, 0x24, 0x13, 0x17, 0xff, 0x26, 0x12, 0x16, 0xff, 0x23, 0x10, 0x13, 0xff, 0x19, 0x08, 0x09, 0xff, 0x27, 0x16, 0x13, 0xff, 0x5f, 0x4e, 0x48, 0xff, 0x40, 0x2d, 0x27, 0xff, 0x33, 0x21, 0x1e, 0xff, 0x35, 0x23, 0x23, 0xff, 0x2b, 0x18, 0x19, 0xff, 0x2a, 0x18, 0x17, 0xff, 0x2b, 0x19, 0x19, 0xff, 0x28, 0x15, 0x18, 0xff, 0x26, 0x11, 0x15, 0xff, 0x3a, 0x26, 0x27, 0xff, 0x33, 0x24, 0x22, 0xff, 0x1f, 0x0f, 0x12, 0xff, 0x23, 0x0f, 0x13, 0xff, 0x27, 0x13, 0x16, 0xff, 0x2f, 0x1a, 0x1d, 0xff, 0x33, 0x1c, 0x20, 0xff, 0x26, 0x11, 0x12, 0xff, 0x2c, 0x17, 0x18, 0xff, 0x33, 0x1d, 0x1f, 0xff, 0x28, 0x16, 0x17, 0xff, 0x26, 0x12, 0x14, 0xff, 0x2a, 0x14, 0x1a, 0xff, 0x2f, 0x1b, 0x1e, 0xff, 0x32, 0x1d, 0x1d, 0xff, 0x4a, 0x2f, 0x29, 0xff, 0x53, 0x39, 0x32, 0xff, 0xcc, 0xc3, 0xc2, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfc, 0xff, 0xbe, 0xb0, 0x92, 0xff, 0x9d, 0x89, 0x5a, 0xff, 0xa0, 0x8c, 0x5c, 0xff, 0x9f, 0x8a, 0x5b, 0xff, 0x9e, 0x8a, 0x5b, 0xff, 0x9f, 0x8c, 0x5d, 0xff, 0xa2, 0x8f, 0x60, 0xff, 0xa7, 0x93, 0x64, 0xff, 0xac, 0x97, 0x67, 0xff, 0xa8, 0x91, 0x61, 0xff, 0xa3, 0x8c, 0x5c, 0xff, 0x9d, 0x87, 0x56, 0xff, 0x92, 0x76, 0x4b, 0xff, 0x78, 0x5b, 0x33, 0xff, 0xa5, 0x8d, 0x5f, 0xff, 0xcf, 0xb7, 0x87, 0xff, 0x82, 0x63, 0x40, 0xff, 0x79, 0x55, 0x31, 0xff, 0x98, 0x81, 0x51, 0xff, 0xbe, 0xb8, 0x97, 0xff, 0xee, 0xf9, 0xf8, 0xff, 0xef, 0xff, 0xff, 0xff, 0xe8, 0xf2, 0xf5, 0xff, 0xf1, 0xfc, 0xff, 0xff, 0xd7, 0xe0, 0xe0, 0xff, 0xcb, 0xd5, 0xd0, 0xff, 0xe6, 0xf2, 0xee, 0xff, 0xf1, 0xfe, 0xfc, 0xff, 0xed, 0xf9, 0xf7, 0xff, 0xe7, 0xf8, 0xf7, 0xff, 0x99, 0x97, 0x9b, 0xff, 0x33, 0x18, 0x1f, 0xff, 0x1e, 0x03, 0x08, 0xff, 0x41, 0x26, 0x25, 0xff, 0x67, 0x53, 0x4f, 0xff, 0x79, 0x69, 0x6b, 0xff, 0x52, 0x3d, 0x40, 0xff, 0x59, 0x42, 0x46, 0xff, 0x3b, 0x24, 0x2a, 0xff, 0x2b, 0x15, 0x1a, 0xff, 0x3b, 0x26, 0x29, 0xff, 0x36, 0x21, 0x23, 0xff, 0x2f, 0x19, 0x1e, 0xff, 0x2f, 0x1a, 0x20, 0xff, 0x2e, 0x1a, 0x1c, 0xff, 0x27, 0x16, 0x1a, 0xff, 0x2e, 0x1c, 0x27, 0xff, 0x38, 0x2d, 0x40, 0xff, 0x3d, 0x3e, 0x65, 0xff, 0x56, 0x5e, 0x9f, 0xff, 0x67, 0x73, 0xb6, 0xff, 0x73, 0x84, 0xbf, 0xff, 0x82, 0x93, 0xc5, 0xff, 0x8b, 0x9c, 0xc4, 0xff, 0x90, 0xa2, 0xc7, 0xff, 0x9b, 0xad, 0xce, 0xff, 0xa5, 0xb5, 0xd3, 0xff, 0xa6, 0xb6, 0xd3, 0xff, 0xa9, 0xb9, 0xd3, 0xff, 0xab, 0xbc, 0xd4, 0xff, 0xae, 0xbf, 0xd6, 0xff, 0xad, 0xc1, 0xd6, 0xff, 0xaf, 0xc2, 0xd7, 0xff, 0xb3, 0xc5, 0xdb, 0xff, 0xb5, 0xc8, 0xdd, 0xff, 0xb5, 0xc7, 0xdd, 0xff, 0xb6, 0xc9, 0xde, 0xff, 0xb6, 0xca, 0xdd, 0xff, 0xb6, 0xcb, 0xdc, 0xff, 0xbb, 0xcd, 0xde, 0xff, 0xbc, 0xce, 0xde, 0xff, 0xba, 0xcc, 0xdd, 0xff, 0xb9, 0xcb, 0xdc, 0xff, 0xb9, 0xca, 0xdb, 0xff, 0xb8, 0xc6, 0xd9, 0xff, 0xb4, 0xc5, 0xd6, 0xff, 0xaf, 0xc1, 0xd4, 0xff, 0xad, 0xbe, 0xd3, 0xff, 0xa8, 0xb9, 0xd0, 0xff, 0xa8, 0xb7, 0xce, 0xff, 0xa7, 0xb4, 0xcb, 0xff, 0x9f, 0xad, 0xc5, 0xff, 0x9c, 0xaa, 0xc3, 0xff, 0x93, 0x9f, 0xb9, 0xff, 0x91, 0x9c, 0xb7, 0xff, 0x89, 0x95, 0xb5, 0xff, 0x83, 0x8a, 0xad, 0xff, 0x6e, 0x6f, 0x92, 0xff, 0x5d, 0x59, 0x7a, 0xff, 0x50, 0x46, 0x5d, 0xff, 0x38, 0x27, 0x31, 0xff, 0x3b, 0x27, 0x2c, 0xff, 0x3d, 0x2a, 0x2c, 0xff, 0x31, 0x20, 0x23, 0xff, 0x32, 0x21, 0x24, 0xff, 0x2a, 0x19, 0x1c, 0xff, 0x29, 0x18, 0x1b, 0xff, 0x32, 0x21, 0x23, 0xff, 0x2b, 0x1a, 0x1d, 0xff, 0x23, 0x12, 0x15, 0xff, 0x22, 0x10, 0x14, 0xff, 0x20, 0x0d, 0x11, 0xff, 0x2e, 0x1b, 0x1f, 0xff, 0x2b, 0x19, 0x1b, 0xff, 0x1a, 0x09, 0x0c, 0xff, 0x1a, 0x08, 0x0e, 0xff, 0x1f, 0x0d, 0x11, 0xff, 0x24, 0x12, 0x13, 0xff, 0x34, 0x20, 0x1f, 0xff, 0x32, 0x21, 0x1e, 0xff, 0x2e, 0x1d, 0x1c, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x30, 0x1e, 0x20, 0xff, 0x2f, 0x1e, 0x1f, 0xff, 0x28, 0x16, 0x19, 0xff, 0x2b, 0x15, 0x1a, 0xff, 0x30, 0x1e, 0x1f, 0xff, 0x23, 0x14, 0x14, 0xff, 0x27, 0x15, 0x18, 0xff, 0x2e, 0x1b, 0x1e, 0xff, 0x2a, 0x17, 0x1b, 0xff, 0x2a, 0x16, 0x19, 0xff, 0x27, 0x13, 0x16, 0xff, 0x20, 0x0c, 0x10, 0xff, 0x2b, 0x18, 0x1b, 0xff, 0x26, 0x12, 0x14, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x30, 0x1b, 0x1e, 0xff, 0x2d, 0x18, 0x19, 0xff, 0x2d, 0x17, 0x1a, 0xff, 0x37, 0x1d, 0x20, 0xff, 0x58, 0x3b, 0x33, 0xff, 0x53, 0x38, 0x2a, 0xff, 0xa8, 0x9c, 0x98, 0xff, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf6, 0xf3, 0xff, 0xac, 0x9b, 0x76, 0xff, 0x9c, 0x89, 0x5a, 0xff, 0xa0, 0x8d, 0x5e, 0xff, 0xa0, 0x8e, 0x5f, 0xff, 0x9f, 0x8d, 0x5e, 0xff, 0x9f, 0x8e, 0x60, 0xff, 0xa3, 0x92, 0x63, 0xff, 0xa9, 0x97, 0x68, 0xff, 0xae, 0x9b, 0x6c, 0xff, 0xab, 0x98, 0x68, 0xff, 0xa7, 0x92, 0x63, 0xff, 0xa3, 0x8d, 0x5b, 0xff, 0x99, 0x80, 0x51, 0xff, 0x8d, 0x71, 0x48, 0xff, 0x7e, 0x60, 0x3b, 0xff, 0x9f, 0x85, 0x56, 0xff, 0xaa, 0x91, 0x62, 0xff, 0x92, 0x70, 0x4a, 0xff, 0x86, 0x67, 0x3a, 0xff, 0xb0, 0x9a, 0x68, 0xff, 0xb5, 0xa6, 0x78, 0xff, 0xd2, 0xd3, 0xc0, 0xff, 0xe9, 0xf8, 0xfa, 0xff, 0xdc, 0xe9, 0xec, 0xff, 0xdc, 0xeb, 0xe9, 0xff, 0xdb, 0xe6, 0xe8, 0xff, 0xb3, 0xb7, 0xbe, 0xff, 0xb6, 0xbc, 0xbf, 0xff, 0x9f, 0x9f, 0xa7, 0xff, 0x5f, 0x4e, 0x5c, 0xff, 0x32, 0x1a, 0x27, 0xff, 0x3c, 0x25, 0x2b, 0xff, 0x3f, 0x24, 0x26, 0xff, 0x5f, 0x44, 0x41, 0xff, 0x87, 0x75, 0x72, 0xff, 0x55, 0x43, 0x45, 0xff, 0x57, 0x3e, 0x40, 0xff, 0x42, 0x2d, 0x30, 0xff, 0x3f, 0x2a, 0x2d, 0xff, 0x3b, 0x25, 0x2a, 0xff, 0x31, 0x1d, 0x22, 0xff, 0x2a, 0x15, 0x1b, 0xff, 0x28, 0x13, 0x19, 0xff, 0x28, 0x15, 0x1a, 0xff, 0x22, 0x10, 0x13, 0xff, 0x29, 0x17, 0x21, 0xff, 0x37, 0x2a, 0x3a, 0xff, 0x33, 0x30, 0x4d, 0xff, 0x49, 0x4e, 0x86, 0xff, 0x68, 0x75, 0xb5, 0xff, 0x78, 0x89, 0xbe, 0xff, 0x84, 0x95, 0xc5, 0xff, 0x8e, 0xa0, 0xc7, 0xff, 0x95, 0xa7, 0xc9, 0xff, 0x9b, 0xae, 0xcf, 0xff, 0xa4, 0xb6, 0xd5, 0xff, 0xa8, 0xbb, 0xd5, 0xff, 0xa9, 0xbb, 0xd4, 0xff, 0xad, 0xbf, 0xd6, 0xff, 0xaf, 0xc1, 0xd8, 0xff, 0xb0, 0xc1, 0xd8, 0xff, 0xaf, 0xc3, 0xd9, 0xff, 0xb0, 0xc4, 0xd9, 0xff, 0xb2, 0xc6, 0xdb, 0xff, 0xb3, 0xc8, 0xdc, 0xff, 0xb2, 0xc7, 0xdc, 0xff, 0xb5, 0xc9, 0xde, 0xff, 0xb8, 0xcb, 0xde, 0xff, 0xb9, 0xcd, 0xdf, 0xff, 0xbd, 0xd0, 0xe1, 0xff, 0xbe, 0xd0, 0xe0, 0xff, 0xbc, 0xce, 0xdf, 0xff, 0xbb, 0xcd, 0xde, 0xff, 0xba, 0xcb, 0xdc, 0xff, 0xba, 0xca, 0xda, 0xff, 0xb5, 0xc5, 0xd6, 0xff, 0xb3, 0xc2, 0xd5, 0xff, 0xb0, 0xbf, 0xd3, 0xff, 0xad, 0xba, 0xd1, 0xff, 0xaa, 0xb9, 0xce, 0xff, 0xa9, 0xb8, 0xcc, 0xff, 0xa7, 0xb5, 0xcb, 0xff, 0xa2, 0xb0, 0xc7, 0xff, 0x9a, 0xa7, 0xc0, 0xff, 0x9a, 0xa6, 0xc0, 0xff, 0x96, 0xa5, 0xb7, 0xff, 0x93, 0xa0, 0xb3, 0xff, 0x8f, 0x96, 0xaf, 0xff, 0x82, 0x89, 0xa3, 0xff, 0x6a, 0x6a, 0x81, 0xff, 0x50, 0x45, 0x55, 0xff, 0x44, 0x32, 0x3a, 0xff, 0x34, 0x23, 0x28, 0xff, 0x2e, 0x1e, 0x23, 0xff, 0x32, 0x20, 0x24, 0xff, 0x2f, 0x1e, 0x21, 0xff, 0x2f, 0x1e, 0x21, 0xff, 0x29, 0x18, 0x1b, 0xff, 0x27, 0x16, 0x19, 0xff, 0x24, 0x14, 0x17, 0xff, 0x24, 0x12, 0x15, 0xff, 0x24, 0x10, 0x13, 0xff, 0x2f, 0x1c, 0x1e, 0xff, 0x29, 0x19, 0x1b, 0xff, 0x1d, 0x0d, 0x10, 0xff, 0x28, 0x17, 0x1b, 0xff, 0x24, 0x14, 0x16, 0xff, 0x24, 0x14, 0x15, 0xff, 0x29, 0x16, 0x19, 0xff, 0x2e, 0x1c, 0x1c, 0xff, 0x28, 0x16, 0x16, 0xff, 0x25, 0x13, 0x15, 0xff, 0x21, 0x0d, 0x12, 0xff, 0x26, 0x13, 0x18, 0xff, 0x25, 0x12, 0x14, 0xff, 0x28, 0x13, 0x14, 0xff, 0x2f, 0x1a, 0x1c, 0xff, 0x25, 0x13, 0x16, 0xff, 0x29, 0x17, 0x1a, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x25, 0x11, 0x14, 0xff, 0x1c, 0x09, 0x0d, 0xff, 0x28, 0x16, 0x19, 0xff, 0x2c, 0x1a, 0x1d, 0xff, 0x26, 0x12, 0x16, 0xff, 0x28, 0x13, 0x15, 0xff, 0x2f, 0x1b, 0x18, 0xff, 0x44, 0x30, 0x27, 0xff, 0x3e, 0x27, 0x20, 0xff, 0x42, 0x27, 0x23, 0xff, 0x52, 0x36, 0x28, 0xff, 0x59, 0x3f, 0x32, 0xff, 0x85, 0x75, 0x73, 0xff, 0xf1, 0xf0, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xee, 0xe7, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0xe6, 0xdc, 0xff, 0xa3, 0x92, 0x68, 0xff, 0x9e, 0x8a, 0x5e, 0xff, 0x9f, 0x8d, 0x5f, 0xff, 0x9f, 0x8f, 0x5f, 0xff, 0x9e, 0x8f, 0x60, 0xff, 0xa2, 0x93, 0x63, 0xff, 0xa6, 0x97, 0x67, 0xff, 0xab, 0x9d, 0x6d, 0xff, 0xb0, 0xa2, 0x72, 0xff, 0xaf, 0xa0, 0x70, 0xff, 0xac, 0x9c, 0x6c, 0xff, 0xab, 0x97, 0x64, 0xff, 0x9f, 0x89, 0x56, 0xff, 0x94, 0x7d, 0x4f, 0xff, 0x8f, 0x75, 0x4a, 0xff, 0x7f, 0x5d, 0x37, 0xff, 0x7f, 0x60, 0x39, 0xff, 0x87, 0x67, 0x41, 0xff, 0x7f, 0x59, 0x34, 0xff, 0x8f, 0x70, 0x41, 0xff, 0xa1, 0x87, 0x51, 0xff, 0xc5, 0xc1, 0xaa, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xe2, 0xec, 0xea, 0xff, 0xda, 0xe4, 0xdd, 0xff, 0xa6, 0xaa, 0xb1, 0xff, 0x5e, 0x53, 0x63, 0xff, 0x48, 0x37, 0x45, 0xff, 0x3a, 0x23, 0x2f, 0xff, 0x2f, 0x12, 0x1d, 0xff, 0x4c, 0x35, 0x3c, 0xff, 0x57, 0x46, 0x44, 0xff, 0x70, 0x60, 0x60, 0xff, 0x69, 0x56, 0x57, 0xff, 0x44, 0x2d, 0x2e, 0xff, 0x4d, 0x37, 0x3a, 0xff, 0x46, 0x2f, 0x30, 0xff, 0x37, 0x21, 0x24, 0xff, 0x44, 0x31, 0x31, 0xff, 0x3a, 0x27, 0x2b, 0xff, 0x2f, 0x1a, 0x23, 0xff, 0x2b, 0x16, 0x1f, 0xff, 0x2c, 0x18, 0x1d, 0xff, 0x22, 0x10, 0x12, 0xff, 0x1e, 0x0c, 0x11, 0xff, 0x37, 0x26, 0x33, 0xff, 0x37, 0x30, 0x46, 0xff, 0x39, 0x39, 0x67, 0xff, 0x61, 0x69, 0xa8, 0xff, 0x7d, 0x8f, 0xc2, 0xff, 0x85, 0x98, 0xc3, 0xff, 0x8d, 0x9e, 0xc6, 0xff, 0x97, 0xaa, 0xcc, 0xff, 0x9f, 0xb4, 0xd1, 0xff, 0xa4, 0xb9, 0xd4, 0xff, 0xa7, 0xbd, 0xd4, 0xff, 0xab, 0xc1, 0xd4, 0xff, 0xaf, 0xc2, 0xd6, 0xff, 0xb1, 0xc3, 0xd9, 0xff, 0xb0, 0xc2, 0xd8, 0xff, 0xb1, 0xc3, 0xd8, 0xff, 0xb1, 0xc5, 0xda, 0xff, 0xb1, 0xc6, 0xdb, 0xff, 0xb0, 0xc6, 0xdb, 0xff, 0xb1, 0xc7, 0xdc, 0xff, 0xb3, 0xca, 0xdf, 0xff, 0xb7, 0xcb, 0xe0, 0xff, 0xba, 0xce, 0xe0, 0xff, 0xb9, 0xcd, 0xdf, 0xff, 0xba, 0xcd, 0xde, 0xff, 0xbd, 0xcf, 0xdf, 0xff, 0xbd, 0xcf, 0xe0, 0xff, 0xbd, 0xd0, 0xe0, 0xff, 0xbb, 0xcd, 0xdc, 0xff, 0xbb, 0xca, 0xda, 0xff, 0xba, 0xca, 0xd9, 0xff, 0xb7, 0xc7, 0xd7, 0xff, 0xb5, 0xc5, 0xd6, 0xff, 0xb3, 0xc3, 0xd4, 0xff, 0xaf, 0xc0, 0xd0, 0xff, 0xad, 0xbc, 0xce, 0xff, 0xa7, 0xb6, 0xca, 0xff, 0xa4, 0xb1, 0xc7, 0xff, 0xa3, 0xb2, 0xc8, 0xff, 0xa1, 0xb1, 0xc4, 0xff, 0x9e, 0xab, 0xbe, 0xff, 0x9c, 0xa9, 0xba, 0xff, 0x9b, 0xa8, 0xb5, 0xff, 0x99, 0xa4, 0xb1, 0xff, 0x7f, 0x84, 0x99, 0xff, 0x5c, 0x58, 0x6f, 0xff, 0x4a, 0x40, 0x4f, 0xff, 0x3a, 0x2a, 0x32, 0xff, 0x2e, 0x1c, 0x23, 0xff, 0x31, 0x21, 0x26, 0xff, 0x2f, 0x1e, 0x20, 0xff, 0x28, 0x16, 0x19, 0xff, 0x2b, 0x19, 0x1c, 0xff, 0x27, 0x16, 0x19, 0xff, 0x2b, 0x1a, 0x1c, 0xff, 0x29, 0x17, 0x1b, 0xff, 0x23, 0x10, 0x16, 0xff, 0x2d, 0x1a, 0x1e, 0xff, 0x2c, 0x1a, 0x1a, 0xff, 0x3e, 0x2c, 0x2c, 0xff, 0x33, 0x21, 0x20, 0xff, 0x2e, 0x1c, 0x1a, 0xff, 0x33, 0x22, 0x21, 0xff, 0x1f, 0x0c, 0x0f, 0xff, 0x26, 0x13, 0x17, 0xff, 0x2d, 0x1b, 0x1d, 0xff, 0x2f, 0x1e, 0x1c, 0xff, 0x2e, 0x1c, 0x1c, 0xff, 0x2b, 0x19, 0x1b, 0xff, 0x38, 0x25, 0x22, 0xff, 0x36, 0x22, 0x1e, 0xff, 0x31, 0x1c, 0x1c, 0xff, 0x26, 0x14, 0x17, 0xff, 0x1e, 0x0c, 0x10, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x22, 0x10, 0x13, 0xff, 0x26, 0x12, 0x15, 0xff, 0x26, 0x13, 0x15, 0xff, 0x2b, 0x18, 0x1a, 0xff, 0x29, 0x14, 0x14, 0xff, 0x2c, 0x17, 0x17, 0xff, 0x2e, 0x19, 0x18, 0xff, 0x30, 0x1a, 0x18, 0xff, 0x3b, 0x21, 0x1e, 0xff, 0x4a, 0x2f, 0x27, 0xff, 0x4a, 0x2f, 0x24, 0xff, 0x5e, 0x42, 0x30, 0xff, 0x55, 0x3c, 0x2e, 0xff, 0x5f, 0x4e, 0x4e, 0xff, 0xd7, 0xd3, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xec, 0xe7, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfa, 0xff, 0xdb, 0xd4, 0xc3, 0xff, 0x9e, 0x8e, 0x61, 0xff, 0x9f, 0x8d, 0x5f, 0xff, 0xa2, 0x92, 0x63, 0xff, 0xa1, 0x92, 0x63, 0xff, 0xa1, 0x93, 0x64, 0xff, 0xa3, 0x96, 0x67, 0xff, 0xa9, 0x9b, 0x6c, 0xff, 0xaf, 0xa4, 0x74, 0xff, 0xb2, 0xa8, 0x77, 0xff, 0xb0, 0xa5, 0x75, 0xff, 0xb0, 0xa3, 0x73, 0xff, 0xaf, 0x9f, 0x6d, 0xff, 0xa2, 0x91, 0x5d, 0xff, 0x97, 0x83, 0x53, 0xff, 0x8f, 0x78, 0x4b, 0xff, 0x8c, 0x6c, 0x45, 0xff, 0x7e, 0x5d, 0x38, 0xff, 0x70, 0x53, 0x31, 0xff, 0x82, 0x69, 0x4f, 0xff, 0x93, 0x7c, 0x5a, 0xff, 0xcc, 0xc8, 0xa7, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xdf, 0xed, 0xee, 0xff, 0xdd, 0xeb, 0xe4, 0xff, 0xdf, 0xed, 0xe2, 0xff, 0xb8, 0xb6, 0xb1, 0xff, 0x69, 0x57, 0x5d, 0xff, 0x3b, 0x20, 0x28, 0xff, 0x47, 0x2d, 0x30, 0xff, 0x48, 0x36, 0x36, 0xff, 0x5d, 0x47, 0x45, 0xff, 0x75, 0x5d, 0x5b, 0xff, 0x68, 0x57, 0x58, 0xff, 0x33, 0x20, 0x23, 0xff, 0x38, 0x22, 0x25, 0xff, 0x48, 0x33, 0x35, 0xff, 0x44, 0x2f, 0x30, 0xff, 0x3c, 0x27, 0x2a, 0xff, 0x30, 0x1d, 0x20, 0xff, 0x31, 0x1e, 0x22, 0xff, 0x34, 0x20, 0x27, 0xff, 0x2f, 0x1a, 0x1f, 0xff, 0x2a, 0x17, 0x1b, 0xff, 0x20, 0x0d, 0x11, 0xff, 0x2a, 0x17, 0x1c, 0xff, 0x3f, 0x31, 0x43, 0xff, 0x32, 0x2d, 0x4f, 0xff, 0x44, 0x4b, 0x7f, 0xff, 0x71, 0x7f, 0xb6, 0xff, 0x88, 0x9a, 0xc6, 0xff, 0x90, 0xa3, 0xc8, 0xff, 0x98, 0xab, 0xcd, 0xff, 0x9e, 0xb1, 0xd0, 0xff, 0xa9, 0xbd, 0xd7, 0xff, 0xad, 0xc3, 0xd8, 0xff, 0xad, 0xc3, 0xd7, 0xff, 0xaf, 0xc4, 0xd8, 0xff, 0xb0, 0xc4, 0xd8, 0xff, 0xb2, 0xc5, 0xda, 0xff, 0xb2, 0xc5, 0xda, 0xff, 0xb3, 0xc6, 0xdb, 0xff, 0xb1, 0xc6, 0xdb, 0xff, 0xb0, 0xc5, 0xdb, 0xff, 0xb3, 0xc8, 0xde, 0xff, 0xb4, 0xc9, 0xde, 0xff, 0xb6, 0xca, 0xdf, 0xff, 0xb8, 0xcb, 0xe1, 0xff, 0xb8, 0xcc, 0xdf, 0xff, 0xb5, 0xca, 0xdb, 0xff, 0xb6, 0xc8, 0xda, 0xff, 0xba, 0xcb, 0xdd, 0xff, 0xbc, 0xce, 0xdf, 0xff, 0xbc, 0xce, 0xde, 0xff, 0xbc, 0xce, 0xde, 0xff, 0xbd, 0xce, 0xdd, 0xff, 0xbd, 0xce, 0xdd, 0xff, 0xbb, 0xcb, 0xdb, 0xff, 0xb9, 0xc9, 0xd9, 0xff, 0xb7, 0xc7, 0xd7, 0xff, 0xb4, 0xc4, 0xd4, 0xff, 0xaf, 0xbf, 0xd0, 0xff, 0xad, 0xbd, 0xce, 0xff, 0xaa, 0xba, 0xcc, 0xff, 0xa9, 0xb9, 0xcb, 0xff, 0xa6, 0xb5, 0xc8, 0xff, 0xa2, 0xae, 0xc5, 0xff, 0x9f, 0xad, 0xbf, 0xff, 0xa0, 0xad, 0xba, 0xff, 0x9e, 0xa7, 0xb2, 0xff, 0x8e, 0x95, 0xa7, 0xff, 0x72, 0x74, 0x8c, 0xff, 0x4d, 0x48, 0x62, 0xff, 0x3d, 0x31, 0x40, 0xff, 0x35, 0x25, 0x29, 0xff, 0x30, 0x20, 0x22, 0xff, 0x3a, 0x2a, 0x32, 0xff, 0x31, 0x22, 0x2a, 0xff, 0x27, 0x17, 0x1b, 0xff, 0x2b, 0x19, 0x1c, 0xff, 0x2d, 0x1a, 0x1c, 0xff, 0x2b, 0x1b, 0x1e, 0xff, 0x26, 0x17, 0x1d, 0xff, 0x2a, 0x19, 0x1d, 0xff, 0x3c, 0x2b, 0x28, 0xff, 0x56, 0x44, 0x3f, 0xff, 0x24, 0x10, 0x11, 0xff, 0x39, 0x26, 0x23, 0xff, 0x45, 0x32, 0x2e, 0xff, 0x23, 0x10, 0x12, 0xff, 0x21, 0x0d, 0x12, 0xff, 0x24, 0x11, 0x14, 0xff, 0x2b, 0x18, 0x19, 0xff, 0x2d, 0x1a, 0x1c, 0xff, 0x2d, 0x1b, 0x1c, 0xff, 0x2d, 0x1b, 0x19, 0xff, 0x2a, 0x17, 0x15, 0xff, 0x32, 0x1f, 0x1f, 0xff, 0x2a, 0x17, 0x18, 0xff, 0x2f, 0x1b, 0x1c, 0xff, 0x25, 0x12, 0x13, 0xff, 0x1e, 0x09, 0x0c, 0xff, 0x2c, 0x17, 0x19, 0xff, 0x2f, 0x1c, 0x1d, 0xff, 0x29, 0x16, 0x17, 0xff, 0x33, 0x1e, 0x1f, 0xff, 0x34, 0x1d, 0x1c, 0xff, 0x2c, 0x17, 0x16, 0xff, 0x2b, 0x14, 0x1a, 0xff, 0x2e, 0x15, 0x16, 0xff, 0x45, 0x2b, 0x21, 0xff, 0x44, 0x28, 0x20, 0xff, 0x5c, 0x41, 0x34, 0xff, 0x4d, 0x30, 0x28, 0xff, 0x4b, 0x36, 0x37, 0xff, 0xc0, 0xb9, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0xe8, 0x42, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf8, 0xf5, 0xff, 0xce, 0xc5, 0xae, 0xff, 0x9f, 0x8f, 0x61, 0xff, 0xa0, 0x90, 0x62, 0xff, 0xa1, 0x93, 0x64, 0xff, 0xa3, 0x95, 0x67, 0xff, 0xa4, 0x97, 0x69, 0xff, 0xa7, 0x9a, 0x6d, 0xff, 0xac, 0xa1, 0x73, 0xff, 0xb4, 0xab, 0x7c, 0xff, 0xb8, 0xaf, 0x80, 0xff, 0xb6, 0xac, 0x7c, 0xff, 0xb5, 0xab, 0x7b, 0xff, 0xac, 0x9f, 0x6e, 0xff, 0xa8, 0x99, 0x67, 0xff, 0x9b, 0x88, 0x58, 0xff, 0x9b, 0x82, 0x57, 0xff, 0x9e, 0x89, 0x5f, 0xff, 0x98, 0x82, 0x56, 0xff, 0xb1, 0x99, 0x6c, 0xff, 0xb1, 0xa0, 0x72, 0xff, 0xac, 0x9a, 0x6c, 0xff, 0xd1, 0xd5, 0xc2, 0xff, 0xe2, 0xf1, 0xf2, 0xff, 0xe1, 0xed, 0xee, 0xff, 0xd9, 0xe5, 0xd9, 0xff, 0xdc, 0xe5, 0xdc, 0xff, 0xab, 0xa4, 0x97, 0xff, 0x87, 0x73, 0x6d, 0xff, 0x63, 0x4b, 0x49, 0xff, 0x4f, 0x37, 0x37, 0xff, 0x51, 0x38, 0x38, 0xff, 0x54, 0x3b, 0x3a, 0xff, 0x43, 0x2d, 0x30, 0xff, 0x30, 0x19, 0x1d, 0xff, 0x4c, 0x36, 0x39, 0xff, 0x5d, 0x4b, 0x4d, 0xff, 0x45, 0x31, 0x33, 0xff, 0x3b, 0x25, 0x29, 0xff, 0x33, 0x1e, 0x23, 0xff, 0x36, 0x21, 0x27, 0xff, 0x33, 0x1f, 0x25, 0xff, 0x30, 0x1d, 0x25, 0xff, 0x28, 0x15, 0x17, 0xff, 0x28, 0x14, 0x19, 0xff, 0x22, 0x0d, 0x13, 0xff, 0x2d, 0x1a, 0x1e, 0xff, 0x3b, 0x31, 0x49, 0xff, 0x3e, 0x3c, 0x6a, 0xff, 0x5c, 0x68, 0xa0, 0xff, 0x7e, 0x90, 0xbe, 0xff, 0x8f, 0xa2, 0xc6, 0xff, 0x9b, 0xb0, 0xcf, 0xff, 0xa1, 0xb5, 0xd3, 0xff, 0xa7, 0xbb, 0xd5, 0xff, 0xae, 0xc2, 0xd9, 0xff, 0xb0, 0xc5, 0xd9, 0xff, 0xaf, 0xc4, 0xd9, 0xff, 0xaf, 0xc4, 0xda, 0xff, 0xaf, 0xc4, 0xda, 0xff, 0xb3, 0xc6, 0xdb, 0xff, 0xb6, 0xc9, 0xde, 0xff, 0xb5, 0xca, 0xdf, 0xff, 0xb4, 0xc9, 0xde, 0xff, 0xb3, 0xc8, 0xde, 0xff, 0xb5, 0xca, 0xe1, 0xff, 0xb5, 0xca, 0xe0, 0xff, 0xb7, 0xcb, 0xdf, 0xff, 0xba, 0xcd, 0xe0, 0xff, 0xb9, 0xcd, 0xdf, 0xff, 0xb9, 0xce, 0xdf, 0xff, 0xba, 0xcb, 0xdf, 0xff, 0xbd, 0xce, 0xe0, 0xff, 0xbd, 0xcf, 0xe0, 0xff, 0xbc, 0xcf, 0xdf, 0xff, 0xbc, 0xcf, 0xde, 0xff, 0xbc, 0xd0, 0xdf, 0xff, 0xbe, 0xcf, 0xdf, 0xff, 0xbe, 0xcd, 0xdd, 0xff, 0xbb, 0xcb, 0xdb, 0xff, 0xb9, 0xc8, 0xd9, 0xff, 0xb6, 0xc6, 0xd6, 0xff, 0xb4, 0xc4, 0xd4, 0xff, 0xb3, 0xc3, 0xd3, 0xff, 0xb0, 0xc1, 0xd1, 0xff, 0xae, 0xbd, 0xce, 0xff, 0xa9, 0xb8, 0xcc, 0xff, 0xa7, 0xb4, 0xca, 0xff, 0xa4, 0xb3, 0xc6, 0xff, 0xa0, 0xaf, 0xbf, 0xff, 0xa1, 0xab, 0xb8, 0xff, 0x98, 0xa1, 0xaf, 0xff, 0x85, 0x8b, 0x9e, 0xff, 0x60, 0x5e, 0x79, 0xff, 0x47, 0x40, 0x59, 0xff, 0x3e, 0x31, 0x3e, 0xff, 0x2c, 0x19, 0x1d, 0xff, 0x35, 0x24, 0x2b, 0xff, 0x54, 0x46, 0x52, 0xff, 0x4d, 0x42, 0x4b, 0xff, 0x36, 0x27, 0x2c, 0xff, 0x2b, 0x1a, 0x1f, 0xff, 0x2b, 0x1b, 0x20, 0xff, 0x2b, 0x1b, 0x1d, 0xff, 0x24, 0x12, 0x16, 0xff, 0x4c, 0x3d, 0x3a, 0xff, 0x51, 0x43, 0x3d, 0xff, 0x12, 0x00, 0x04, 0xff, 0x3d, 0x29, 0x27, 0xff, 0x53, 0x41, 0x3a, 0xff, 0x23, 0x10, 0x11, 0xff, 0x25, 0x11, 0x16, 0xff, 0x23, 0x10, 0x13, 0xff, 0x24, 0x11, 0x15, 0xff, 0x26, 0x12, 0x16, 0xff, 0x29, 0x17, 0x1b, 0xff, 0x1c, 0x0c, 0x0d, 0xff, 0x1d, 0x0c, 0x0d, 0xff, 0x23, 0x12, 0x14, 0xff, 0x28, 0x14, 0x14, 0xff, 0x37, 0x24, 0x1f, 0xff, 0x3f, 0x2c, 0x28, 0xff, 0x3f, 0x2c, 0x28, 0xff, 0x38, 0x24, 0x1f, 0xff, 0x39, 0x24, 0x1d, 0xff, 0x36, 0x1f, 0x1a, 0xff, 0x3c, 0x24, 0x21, 0xff, 0x3a, 0x20, 0x1c, 0xff, 0x2c, 0x17, 0x14, 0xff, 0x20, 0x0c, 0x13, 0xff, 0x33, 0x1f, 0x1a, 0xff, 0x50, 0x37, 0x2a, 0xff, 0x44, 0x27, 0x22, 0xff, 0x63, 0x48, 0x3a, 0xff, 0x57, 0x38, 0x2b, 0xff, 0x4d, 0x34, 0x2f, 0xff, 0xaa, 0x9f, 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf5, 0xf1, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xed, 0xe6, 0xff, 0xc0, 0xb7, 0x9b, 0xff, 0xa2, 0x94, 0x66, 0xff, 0xa2, 0x95, 0x66, 0xff, 0xa2, 0x97, 0x68, 0xff, 0xa4, 0x9a, 0x6b, 0xff, 0xa7, 0x9e, 0x6f, 0xff, 0xaa, 0xa1, 0x72, 0xff, 0xab, 0xa4, 0x75, 0xff, 0xb6, 0xb1, 0x82, 0xff, 0xbe, 0xb6, 0x88, 0xff, 0xb9, 0xb2, 0x81, 0xff, 0xb1, 0xab, 0x7b, 0xff, 0xaf, 0xa5, 0x75, 0xff, 0xad, 0x9f, 0x6e, 0xff, 0xa4, 0x93, 0x62, 0xff, 0x9c, 0x87, 0x58, 0xff, 0x9c, 0x85, 0x5b, 0xff, 0x95, 0x7e, 0x52, 0xff, 0x9e, 0x84, 0x51, 0xff, 0xb2, 0x95, 0x5a, 0xff, 0xbd, 0xaf, 0x7c, 0xff, 0xe4, 0xf0, 0xe3, 0xff, 0xdd, 0xea, 0xec, 0xff, 0xe0, 0xed, 0xea, 0xff, 0xd6, 0xe1, 0xd7, 0xff, 0xd5, 0xd5, 0xcb, 0xff, 0xa3, 0x93, 0x81, 0xff, 0x80, 0x66, 0x5b, 0xff, 0x76, 0x5a, 0x54, 0xff, 0x65, 0x4b, 0x48, 0xff, 0x5f, 0x48, 0x47, 0xff, 0x4a, 0x33, 0x34, 0xff, 0x43, 0x2d, 0x30, 0xff, 0x56, 0x40, 0x45, 0xff, 0x57, 0x44, 0x49, 0xff, 0x3c, 0x27, 0x2b, 0xff, 0x28, 0x11, 0x16, 0xff, 0x31, 0x1b, 0x20, 0xff, 0x38, 0x24, 0x29, 0xff, 0x33, 0x1f, 0x24, 0xff, 0x34, 0x20, 0x27, 0xff, 0x27, 0x16, 0x1f, 0xff, 0x25, 0x13, 0x1a, 0xff, 0x26, 0x13, 0x17, 0xff, 0x24, 0x10, 0x12, 0xff, 0x2c, 0x19, 0x21, 0xff, 0x37, 0x33, 0x53, 0xff, 0x52, 0x57, 0x8a, 0xff, 0x7d, 0x8b, 0xc2, 0xff, 0x8c, 0xa2, 0xc7, 0xff, 0x9b, 0xb1, 0xca, 0xff, 0xa4, 0xba, 0xd3, 0xff, 0xa9, 0xc0, 0xd7, 0xff, 0xae, 0xc3, 0xd8, 0xff, 0xaf, 0xc5, 0xd8, 0xff, 0xb1, 0xc7, 0xdc, 0xff, 0xb2, 0xc7, 0xdc, 0xff, 0xb1, 0xc6, 0xdb, 0xff, 0xb1, 0xc6, 0xdb, 0xff, 0xb2, 0xc6, 0xdb, 0xff, 0xb4, 0xc8, 0xde, 0xff, 0xb5, 0xca, 0xdf, 0xff, 0xb7, 0xcc, 0xe1, 0xff, 0xb8, 0xcd, 0xe2, 0xff, 0xb9, 0xce, 0xe2, 0xff, 0xb9, 0xce, 0xe2, 0xff, 0xba, 0xcf, 0xe1, 0xff, 0xbc, 0xcf, 0xe1, 0xff, 0xbd, 0xd1, 0xe3, 0xff, 0xbe, 0xd2, 0xe3, 0xff, 0xc0, 0xd2, 0xe3, 0xff, 0xbf, 0xd1, 0xe1, 0xff, 0xc0, 0xd3, 0xe3, 0xff, 0xc1, 0xd4, 0xe3, 0xff, 0xbf, 0xd3, 0xe2, 0xff, 0xbf, 0xd2, 0xe0, 0xff, 0xc1, 0xd2, 0xe1, 0xff, 0xc1, 0xd1, 0xe1, 0xff, 0xbe, 0xce, 0xde, 0xff, 0xbd, 0xcc, 0xdd, 0xff, 0xbb, 0xcb, 0xda, 0xff, 0xb9, 0xc9, 0xd8, 0xff, 0xb6, 0xc6, 0xd6, 0xff, 0xb3, 0xc3, 0xd3, 0xff, 0xb1, 0xc1, 0xd2, 0xff, 0xb0, 0xc0, 0xd1, 0xff, 0xab, 0xbb, 0xcc, 0xff, 0xa9, 0xb7, 0xc8, 0xff, 0xa4, 0xb3, 0xc3, 0xff, 0xa1, 0xad, 0xbd, 0xff, 0x9d, 0xa7, 0xb6, 0xff, 0x92, 0x99, 0xa9, 0xff, 0x79, 0x7b, 0x90, 0xff, 0x65, 0x63, 0x82, 0xff, 0x4e, 0x47, 0x64, 0xff, 0x3a, 0x2c, 0x3c, 0xff, 0x24, 0x12, 0x1a, 0xff, 0x31, 0x23, 0x2b, 0xff, 0x55, 0x46, 0x50, 0xff, 0x48, 0x37, 0x3d, 0xff, 0x2d, 0x1b, 0x1f, 0xff, 0x27, 0x15, 0x1a, 0xff, 0x2f, 0x1c, 0x1e, 0xff, 0x24, 0x15, 0x17, 0xff, 0x4e, 0x42, 0x42, 0xff, 0x3c, 0x2e, 0x2e, 0xff, 0x21, 0x0e, 0x11, 0xff, 0x50, 0x3d, 0x38, 0xff, 0x51, 0x3f, 0x37, 0xff, 0x22, 0x11, 0x0e, 0xff, 0x2b, 0x19, 0x1c, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x24, 0x11, 0x14, 0xff, 0x28, 0x14, 0x18, 0xff, 0x20, 0x0e, 0x11, 0xff, 0x1e, 0x0c, 0x0f, 0xff, 0x27, 0x14, 0x17, 0xff, 0x29, 0x16, 0x18, 0xff, 0x29, 0x15, 0x18, 0xff, 0x20, 0x0d, 0x0f, 0xff, 0x2a, 0x16, 0x17, 0xff, 0x33, 0x1e, 0x1d, 0xff, 0x34, 0x21, 0x1d, 0xff, 0x3e, 0x29, 0x22, 0xff, 0x3f, 0x28, 0x20, 0xff, 0x30, 0x19, 0x13, 0xff, 0x31, 0x1b, 0x18, 0xff, 0x29, 0x16, 0x15, 0xff, 0x2e, 0x18, 0x1a, 0xff, 0x38, 0x20, 0x1b, 0xff, 0x4d, 0x30, 0x27, 0xff, 0x56, 0x37, 0x2c, 0xff, 0x7a, 0x5b, 0x45, 0xff, 0x6a, 0x4f, 0x36, 0xff, 0x56, 0x3c, 0x2f, 0xff, 0x99, 0x8a, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf5, 0xee, 0xe8, 0x84, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xf2, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xdf, 0xd2, 0xff, 0xb7, 0xae, 0x8c, 0xff, 0xa2, 0x97, 0x6c, 0xff, 0xa5, 0x9b, 0x6d, 0xff, 0xa7, 0x9f, 0x70, 0xff, 0xa6, 0xa0, 0x74, 0xff, 0xa6, 0xa1, 0x74, 0xff, 0xa9, 0xa3, 0x75, 0xff, 0xaf, 0xaa, 0x7d, 0xff, 0xb8, 0xb4, 0x88, 0xff, 0xbf, 0xbb, 0x8e, 0xff, 0xbb, 0xb7, 0x89, 0xff, 0xb7, 0xb2, 0x83, 0xff, 0xb5, 0xae, 0x7f, 0xff, 0xad, 0xa2, 0x71, 0xff, 0xa8, 0x9a, 0x68, 0xff, 0xa0, 0x8c, 0x5d, 0xff, 0x96, 0x81, 0x53, 0xff, 0x8e, 0x7a, 0x4b, 0xff, 0xa5, 0x8c, 0x5e, 0xff, 0x7d, 0x57, 0x2b, 0xff, 0x8a, 0x76, 0x50, 0xff, 0xde, 0xdf, 0xbe, 0xff, 0xeb, 0xf4, 0xee, 0xff, 0xd6, 0xe9, 0xe2, 0xff, 0xd6, 0xdd, 0xd4, 0xff, 0xca, 0xca, 0xb9, 0xff, 0xaa, 0x97, 0x7f, 0xff, 0x7b, 0x5f, 0x4e, 0xff, 0x66, 0x4b, 0x40, 0xff, 0x60, 0x44, 0x3e, 0xff, 0x54, 0x3c, 0x3c, 0xff, 0x4a, 0x35, 0x38, 0xff, 0x4e, 0x38, 0x3a, 0xff, 0x46, 0x30, 0x34, 0xff, 0x31, 0x1b, 0x20, 0xff, 0x2b, 0x14, 0x19, 0xff, 0x42, 0x2f, 0x33, 0xff, 0x4c, 0x3b, 0x3f, 0xff, 0x32, 0x1e, 0x23, 0xff, 0x29, 0x17, 0x1d, 0xff, 0x2d, 0x1c, 0x22, 0xff, 0x28, 0x17, 0x20, 0xff, 0x29, 0x16, 0x1d, 0xff, 0x24, 0x12, 0x12, 0xff, 0x22, 0x11, 0x10, 0xff, 0x30, 0x22, 0x30, 0xff, 0x44, 0x48, 0x74, 0xff, 0x6b, 0x76, 0xac, 0xff, 0x8e, 0xa1, 0xca, 0xff, 0x9b, 0xb1, 0xcd, 0xff, 0xaa, 0xbf, 0xd3, 0xff, 0xad, 0xc3, 0xd6, 0xff, 0xb1, 0xc8, 0xda, 0xff, 0xb5, 0xca, 0xdc, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb6, 0xcc, 0xde, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb4, 0xca, 0xdc, 0xff, 0xb3, 0xc8, 0xde, 0xff, 0xb5, 0xc9, 0xdf, 0xff, 0xb6, 0xcc, 0xe1, 0xff, 0xb5, 0xcd, 0xe2, 0xff, 0xb8, 0xcf, 0xe2, 0xff, 0xba, 0xd0, 0xe1, 0xff, 0xbb, 0xd2, 0xe2, 0xff, 0xbc, 0xd1, 0xe2, 0xff, 0xbe, 0xd1, 0xe2, 0xff, 0xbf, 0xd3, 0xe4, 0xff, 0xbf, 0xd4, 0xe4, 0xff, 0xc2, 0xd6, 0xe4, 0xff, 0xc2, 0xd4, 0xe3, 0xff, 0xc3, 0xd6, 0xe5, 0xff, 0xc3, 0xd6, 0xe6, 0xff, 0xc3, 0xd6, 0xe4, 0xff, 0xc2, 0xd5, 0xe1, 0xff, 0xc2, 0xd5, 0xe2, 0xff, 0xc2, 0xd5, 0xe2, 0xff, 0xc0, 0xd3, 0xe0, 0xff, 0xbd, 0xd0, 0xde, 0xff, 0xbc, 0xce, 0xdb, 0xff, 0xbb, 0xcc, 0xd8, 0xff, 0xba, 0xc9, 0xd9, 0xff, 0xb8, 0xc7, 0xd8, 0xff, 0xb5, 0xc5, 0xd6, 0xff, 0xb4, 0xc4, 0xd5, 0xff, 0xb1, 0xc2, 0xd0, 0xff, 0xac, 0xbc, 0xcb, 0xff, 0xa9, 0xb8, 0xca, 0xff, 0xa2, 0xb1, 0xc4, 0xff, 0x9f, 0xab, 0xba, 0xff, 0x9a, 0xa1, 0xb0, 0xff, 0x91, 0x98, 0xaa, 0xff, 0x85, 0x8b, 0xa2, 0xff, 0x6c, 0x6d, 0x8b, 0xff, 0x42, 0x3d, 0x5c, 0xff, 0x41, 0x36, 0x4d, 0xff, 0x35, 0x28, 0x34, 0xff, 0x24, 0x14, 0x1c, 0xff, 0x2d, 0x1b, 0x21, 0xff, 0x2f, 0x1d, 0x21, 0xff, 0x2e, 0x1b, 0x23, 0xff, 0x31, 0x1f, 0x25, 0xff, 0x33, 0x27, 0x28, 0xff, 0x3e, 0x31, 0x33, 0xff, 0x1e, 0x0e, 0x11, 0xff, 0x48, 0x37, 0x32, 0xff, 0x6b, 0x5a, 0x51, 0xff, 0x53, 0x41, 0x39, 0xff, 0x2b, 0x19, 0x18, 0xff, 0x1e, 0x0c, 0x0f, 0xff, 0x22, 0x0e, 0x12, 0xff, 0x28, 0x14, 0x17, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x24, 0x11, 0x14, 0xff, 0x27, 0x14, 0x17, 0xff, 0x24, 0x11, 0x14, 0xff, 0x25, 0x11, 0x14, 0xff, 0x23, 0x10, 0x15, 0xff, 0x23, 0x10, 0x15, 0xff, 0x24, 0x0f, 0x13, 0xff, 0x25, 0x11, 0x14, 0xff, 0x26, 0x14, 0x15, 0xff, 0x38, 0x22, 0x1f, 0xff, 0x42, 0x2b, 0x27, 0xff, 0x34, 0x1f, 0x1e, 0xff, 0x1d, 0x0a, 0x0d, 0xff, 0x28, 0x12, 0x10, 0xff, 0x51, 0x35, 0x2d, 0xff, 0x62, 0x43, 0x33, 0xff, 0x61, 0x42, 0x2c, 0xff, 0x6a, 0x49, 0x34, 0xff, 0x49, 0x2d, 0x1f, 0xff, 0x3e, 0x24, 0x20, 0xff, 0x8d, 0x7b, 0x74, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf5, 0xed, 0xe7, 0x9d, 0xf6, 0xed, 0xe7, 0xff, 0xfa, 0xf4, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xd5, 0xc3, 0xff, 0xb7, 0xae, 0x8a, 0xff, 0xa6, 0x9d, 0x71, 0xff, 0xa7, 0xa0, 0x72, 0xff, 0xa8, 0xa1, 0x73, 0xff, 0xa8, 0xa2, 0x76, 0xff, 0xa7, 0xa2, 0x76, 0xff, 0xaa, 0xa6, 0x78, 0xff, 0xb3, 0xaf, 0x82, 0xff, 0xb9, 0xb6, 0x89, 0xff, 0xbd, 0xbb, 0x8e, 0xff, 0xbb, 0xb8, 0x8b, 0xff, 0xb9, 0xb6, 0x88, 0xff, 0xb6, 0xb1, 0x83, 0xff, 0xb2, 0xa9, 0x7a, 0xff, 0xa9, 0x9e, 0x6c, 0xff, 0xa7, 0x97, 0x65, 0xff, 0xa4, 0x91, 0x61, 0xff, 0xab, 0x93, 0x63, 0xff, 0x9e, 0x84, 0x52, 0xff, 0x7f, 0x66, 0x36, 0xff, 0xb9, 0xa5, 0x73, 0xff, 0xbb, 0xaf, 0x82, 0xff, 0xcd, 0xd3, 0xc9, 0xff, 0xe0, 0xed, 0xe8, 0xff, 0xc4, 0xc7, 0xb7, 0xff, 0xd1, 0xd5, 0xc5, 0xff, 0xb1, 0xa5, 0x8d, 0xff, 0x84, 0x65, 0x52, 0xff, 0x71, 0x53, 0x48, 0xff, 0x54, 0x39, 0x37, 0xff, 0x48, 0x2f, 0x30, 0xff, 0x47, 0x31, 0x34, 0xff, 0x41, 0x2b, 0x2e, 0xff, 0x39, 0x23, 0x25, 0xff, 0x3f, 0x29, 0x2c, 0xff, 0x4c, 0x35, 0x3a, 0xff, 0x4f, 0x3e, 0x42, 0xff, 0x3e, 0x2e, 0x32, 0xff, 0x2d, 0x19, 0x1f, 0xff, 0x33, 0x20, 0x28, 0xff, 0x31, 0x1f, 0x26, 0xff, 0x2b, 0x1a, 0x21, 0xff, 0x29, 0x15, 0x1b, 0xff, 0x24, 0x10, 0x13, 0xff, 0x1d, 0x0b, 0x0b, 0xff, 0x35, 0x27, 0x39, 0xff, 0x5d, 0x62, 0x95, 0xff, 0x80, 0x8f, 0xbe, 0xff, 0x98, 0xaf, 0xcb, 0xff, 0xa8, 0xbd, 0xd2, 0xff, 0xb0, 0xc2, 0xd6, 0xff, 0xb3, 0xca, 0xda, 0xff, 0xb6, 0xcd, 0xdd, 0xff, 0xb7, 0xcd, 0xdf, 0xff, 0xb7, 0xce, 0xdf, 0xff, 0xb8, 0xce, 0xe0, 0xff, 0xb7, 0xce, 0xde, 0xff, 0xb6, 0xcd, 0xde, 0xff, 0xb6, 0xcc, 0xdf, 0xff, 0xb6, 0xcb, 0xdf, 0xff, 0xb6, 0xcc, 0xe0, 0xff, 0xb7, 0xce, 0xe1, 0xff, 0xb6, 0xce, 0xe1, 0xff, 0xb7, 0xce, 0xe0, 0xff, 0xb9, 0xd0, 0xe1, 0xff, 0xbd, 0xd5, 0xe4, 0xff, 0xbc, 0xd3, 0xe2, 0xff, 0xbf, 0xd2, 0xe3, 0xff, 0xc1, 0xd5, 0xe6, 0xff, 0xc1, 0xd7, 0xe6, 0xff, 0xc3, 0xd7, 0xe6, 0xff, 0xc4, 0xd6, 0xe6, 0xff, 0xc5, 0xd8, 0xe6, 0xff, 0xc4, 0xd7, 0xe6, 0xff, 0xc4, 0xd7, 0xe4, 0xff, 0xc4, 0xd7, 0xe3, 0xff, 0xc3, 0xd7, 0xe3, 0xff, 0xc1, 0xd5, 0xe1, 0xff, 0xc0, 0xd3, 0xe0, 0xff, 0xc0, 0xd3, 0xdf, 0xff, 0xbc, 0xce, 0xdb, 0xff, 0xbc, 0xcd, 0xd9, 0xff, 0xbb, 0xcc, 0xda, 0xff, 0xba, 0xca, 0xd9, 0xff, 0xb8, 0xc8, 0xd8, 0xff, 0xb5, 0xc7, 0xd5, 0xff, 0xb4, 0xc5, 0xd1, 0xff, 0xb1, 0xc2, 0xcf, 0xff, 0xab, 0xbb, 0xcb, 0xff, 0xa6, 0xb5, 0xc6, 0xff, 0xa3, 0xaf, 0xbe, 0xff, 0x9f, 0xa9, 0xb6, 0xff, 0x99, 0xa4, 0xb3, 0xff, 0x96, 0xa0, 0xae, 0xff, 0x98, 0xa0, 0xb2, 0xff, 0x74, 0x77, 0x95, 0xff, 0x53, 0x52, 0x6f, 0xff, 0x45, 0x3f, 0x56, 0xff, 0x2d, 0x23, 0x35, 0xff, 0x2e, 0x20, 0x2f, 0xff, 0x33, 0x23, 0x2d, 0xff, 0x31, 0x21, 0x2a, 0xff, 0x2f, 0x20, 0x26, 0xff, 0x33, 0x27, 0x29, 0xff, 0x34, 0x26, 0x28, 0xff, 0x39, 0x26, 0x27, 0xff, 0x69, 0x5a, 0x4f, 0xff, 0x66, 0x57, 0x4c, 0xff, 0x57, 0x46, 0x41, 0xff, 0x1e, 0x0c, 0x0d, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x24, 0x12, 0x15, 0xff, 0x24, 0x11, 0x14, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x1f, 0x0d, 0x10, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x22, 0x10, 0x13, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x26, 0x12, 0x12, 0xff, 0x25, 0x13, 0x13, 0xff, 0x28, 0x14, 0x15, 0xff, 0x29, 0x12, 0x15, 0xff, 0x24, 0x0f, 0x13, 0xff, 0x2e, 0x17, 0x15, 0xff, 0x39, 0x1f, 0x1b, 0xff, 0x35, 0x1d, 0x1b, 0xff, 0x2e, 0x18, 0x17, 0xff, 0x45, 0x2e, 0x25, 0xff, 0x4f, 0x33, 0x27, 0xff, 0x58, 0x3a, 0x2a, 0xff, 0x86, 0x6b, 0x53, 0xff, 0x5c, 0x41, 0x2f, 0xff, 0x2f, 0x10, 0x0f, 0xff, 0x38, 0x1e, 0x1f, 0xff, 0x6d, 0x5c, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfa, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf6, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xcd, 0xb7, 0xff, 0xb4, 0xab, 0x84, 0xff, 0xa7, 0xa0, 0x73, 0xff, 0xa7, 0xa3, 0x76, 0xff, 0xa7, 0xa2, 0x75, 0xff, 0xa8, 0xa2, 0x75, 0xff, 0xa9, 0xa5, 0x78, 0xff, 0xac, 0xa9, 0x7c, 0xff, 0xb3, 0xaf, 0x83, 0xff, 0xba, 0xb7, 0x8a, 0xff, 0xbc, 0xbc, 0x8e, 0xff, 0xba, 0xba, 0x8d, 0xff, 0xba, 0xb8, 0x8b, 0xff, 0xb8, 0xb6, 0x8a, 0xff, 0xb3, 0xad, 0x80, 0xff, 0xad, 0xa3, 0x73, 0xff, 0xa8, 0x9b, 0x68, 0xff, 0xa4, 0x95, 0x65, 0xff, 0xa4, 0x91, 0x60, 0xff, 0x9c, 0x86, 0x53, 0xff, 0xbb, 0xa9, 0x77, 0xff, 0xa4, 0x8b, 0x56, 0xff, 0x9c, 0x8b, 0x64, 0xff, 0xda, 0xe9, 0xe0, 0xff, 0xe9, 0xf4, 0xf1, 0xff, 0xbc, 0xbb, 0xa6, 0xff, 0xc2, 0xc4, 0xae, 0xff, 0xbc, 0xb5, 0x9a, 0xff, 0x91, 0x77, 0x69, 0xff, 0x67, 0x4f, 0x4e, 0xff, 0x51, 0x3e, 0x44, 0xff, 0x51, 0x39, 0x3c, 0xff, 0x4b, 0x34, 0x36, 0xff, 0x40, 0x2c, 0x2e, 0xff, 0x4b, 0x37, 0x38, 0xff, 0x46, 0x31, 0x35, 0xff, 0x4d, 0x36, 0x3f, 0xff, 0x39, 0x24, 0x2c, 0xff, 0x2a, 0x17, 0x1a, 0xff, 0x2d, 0x1b, 0x20, 0xff, 0x32, 0x1f, 0x28, 0xff, 0x2f, 0x1c, 0x26, 0xff, 0x28, 0x19, 0x1d, 0xff, 0x26, 0x15, 0x1c, 0xff, 0x29, 0x13, 0x1b, 0xff, 0x1d, 0x07, 0x05, 0xff, 0x39, 0x2e, 0x43, 0xff, 0x77, 0x84, 0xb3, 0xff, 0x95, 0xaa, 0xc9, 0xff, 0xaa, 0xbe, 0xd1, 0xff, 0xb0, 0xc4, 0xd4, 0xff, 0xb4, 0xc9, 0xda, 0xff, 0xb7, 0xcf, 0xde, 0xff, 0xb9, 0xd0, 0xe0, 0xff, 0xba, 0xd1, 0xe1, 0xff, 0xb7, 0xce, 0xde, 0xff, 0xb9, 0xd0, 0xdf, 0xff, 0xb9, 0xd0, 0xe0, 0xff, 0xba, 0xd0, 0xe1, 0xff, 0xb7, 0xcd, 0xe0, 0xff, 0xb8, 0xce, 0xe0, 0xff, 0xb8, 0xce, 0xe0, 0xff, 0xb7, 0xcd, 0xdf, 0xff, 0xb8, 0xce, 0xdf, 0xff, 0xb9, 0xcf, 0xe1, 0xff, 0xbc, 0xd3, 0xe4, 0xff, 0xbc, 0xd3, 0xe3, 0xff, 0xbb, 0xd3, 0xe3, 0xff, 0xbe, 0xd4, 0xe4, 0xff, 0xc1, 0xd5, 0xe4, 0xff, 0xc0, 0xd5, 0xe4, 0xff, 0xc2, 0xd6, 0xe4, 0xff, 0xc6, 0xd9, 0xe7, 0xff, 0xc4, 0xd7, 0xe4, 0xff, 0xc4, 0xd7, 0xe4, 0xff, 0xc5, 0xd8, 0xe5, 0xff, 0xc4, 0xd8, 0xe4, 0xff, 0xc2, 0xd6, 0xe3, 0xff, 0xc1, 0xd3, 0xe1, 0xff, 0xc2, 0xd3, 0xdf, 0xff, 0xc0, 0xd3, 0xdd, 0xff, 0xbe, 0xcf, 0xdb, 0xff, 0xbc, 0xcc, 0xda, 0xff, 0xbb, 0xcc, 0xd9, 0xff, 0xba, 0xca, 0xd7, 0xff, 0xb8, 0xc9, 0xd7, 0xff, 0xb7, 0xc7, 0xd7, 0xff, 0xb2, 0xc2, 0xd1, 0xff, 0xb0, 0xc1, 0xcf, 0xff, 0xad, 0xbe, 0xcb, 0xff, 0xa7, 0xb5, 0xc2, 0xff, 0xa5, 0xb1, 0xbf, 0xff, 0xa2, 0xad, 0xbb, 0xff, 0x9c, 0xa8, 0xb3, 0xff, 0x9c, 0xa8, 0xb5, 0xff, 0x9a, 0xa5, 0xb3, 0xff, 0x9c, 0xa5, 0xb5, 0xff, 0x8b, 0x91, 0xa8, 0xff, 0x6c, 0x6e, 0x89, 0xff, 0x58, 0x56, 0x73, 0xff, 0x3e, 0x37, 0x52, 0xff, 0x35, 0x2b, 0x3e, 0xff, 0x2c, 0x20, 0x2d, 0xff, 0x2e, 0x1f, 0x28, 0xff, 0x38, 0x2a, 0x30, 0xff, 0x34, 0x23, 0x27, 0xff, 0x4e, 0x3c, 0x3b, 0xff, 0x68, 0x57, 0x4e, 0xff, 0x54, 0x46, 0x40, 0xff, 0x33, 0x24, 0x25, 0xff, 0x1b, 0x09, 0x0c, 0xff, 0x21, 0x0f, 0x12, 0xff, 0x21, 0x11, 0x14, 0xff, 0x1c, 0x0c, 0x10, 0xff, 0x2f, 0x1a, 0x1d, 0xff, 0x2c, 0x19, 0x1b, 0xff, 0x25, 0x15, 0x16, 0xff, 0x24, 0x11, 0x10, 0xff, 0x27, 0x14, 0x15, 0xff, 0x2a, 0x15, 0x19, 0xff, 0x2e, 0x18, 0x1a, 0xff, 0x2d, 0x1b, 0x1b, 0xff, 0x2a, 0x17, 0x17, 0xff, 0x2c, 0x16, 0x18, 0xff, 0x2b, 0x16, 0x14, 0xff, 0x39, 0x24, 0x1c, 0xff, 0x34, 0x1b, 0x18, 0xff, 0x2a, 0x12, 0x0e, 0xff, 0x50, 0x36, 0x28, 0xff, 0x74, 0x59, 0x45, 0xff, 0x69, 0x4f, 0x3e, 0xff, 0x56, 0x3a, 0x2e, 0xff, 0x4e, 0x35, 0x24, 0xff, 0x4e, 0x35, 0x28, 0xff, 0x53, 0x37, 0x32, 0xff, 0x46, 0x2d, 0x2a, 0xff, 0x59, 0x4a, 0x48, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xc7, 0xad, 0xff, 0xaf, 0xa9, 0x7f, 0xff, 0xa9, 0xa3, 0x76, 0xff, 0xa9, 0xa4, 0x77, 0xff, 0xaa, 0xa5, 0x78, 0xff, 0xaa, 0xa6, 0x79, 0xff, 0xaa, 0xa7, 0x7a, 0xff, 0xad, 0xaa, 0x7d, 0xff, 0xb3, 0xb0, 0x83, 0xff, 0xba, 0xb7, 0x8b, 0xff, 0xbb, 0xbb, 0x8f, 0xff, 0xbc, 0xbc, 0x90, 0xff, 0xba, 0xb9, 0x8d, 0xff, 0xb9, 0xb8, 0x8c, 0xff, 0xb4, 0xb0, 0x83, 0xff, 0xae, 0xa4, 0x75, 0xff, 0xad, 0x9d, 0x6e, 0xff, 0xa4, 0x92, 0x62, 0xff, 0xa0, 0x92, 0x62, 0xff, 0xc0, 0xb4, 0x84, 0xff, 0xbe, 0xab, 0x7d, 0xff, 0x8b, 0x6b, 0x3f, 0xff, 0x92, 0x78, 0x51, 0xff, 0xdb, 0xe9, 0xda, 0xff, 0xdf, 0xf5, 0xf7, 0xff, 0xde, 0xe4, 0xd8, 0xff, 0xc3, 0xc0, 0xa7, 0xff, 0xb1, 0xab, 0x92, 0xff, 0x99, 0x8c, 0x81, 0xff, 0x87, 0x74, 0x6d, 0xff, 0x75, 0x64, 0x66, 0xff, 0x6e, 0x5c, 0x60, 0xff, 0x5e, 0x4c, 0x4f, 0xff, 0x5d, 0x4a, 0x4d, 0xff, 0x52, 0x41, 0x43, 0xff, 0x3c, 0x2b, 0x31, 0xff, 0x3c, 0x27, 0x30, 0xff, 0x38, 0x22, 0x28, 0xff, 0x2e, 0x1a, 0x1d, 0xff, 0x35, 0x24, 0x2a, 0xff, 0x34, 0x23, 0x2a, 0xff, 0x2d, 0x1a, 0x21, 0xff, 0x26, 0x16, 0x1c, 0xff, 0x28, 0x17, 0x1e, 0xff, 0x2e, 0x18, 0x1a, 0xff, 0x1d, 0x05, 0x02, 0xff, 0x47, 0x44, 0x62, 0xff, 0x94, 0xac, 0xd2, 0xff, 0xab, 0xc2, 0xd2, 0xff, 0xb5, 0xc4, 0xd3, 0xff, 0xb3, 0xc8, 0xd7, 0xff, 0xb4, 0xcd, 0xdd, 0xff, 0xb8, 0xcf, 0xdf, 0xff, 0xba, 0xd1, 0xe0, 0xff, 0xb9, 0xd0, 0xe0, 0xff, 0xba, 0xd1, 0xe0, 0xff, 0xb9, 0xcf, 0xdf, 0xff, 0xb9, 0xcf, 0xe0, 0xff, 0xb8, 0xcd, 0xdf, 0xff, 0xb7, 0xcc, 0xde, 0xff, 0xb6, 0xcc, 0xde, 0xff, 0xb6, 0xcc, 0xde, 0xff, 0xb6, 0xcc, 0xde, 0xff, 0xb6, 0xcc, 0xde, 0xff, 0xb8, 0xcf, 0xe1, 0xff, 0xbb, 0xd1, 0xe2, 0xff, 0xbc, 0xd3, 0xe3, 0xff, 0xbb, 0xd3, 0xe3, 0xff, 0xbd, 0xd3, 0xe3, 0xff, 0xc0, 0xd5, 0xe4, 0xff, 0xc1, 0xd7, 0xe3, 0xff, 0xc4, 0xd8, 0xe4, 0xff, 0xc5, 0xd7, 0xe4, 0xff, 0xc5, 0xd8, 0xe4, 0xff, 0xc5, 0xd8, 0xe5, 0xff, 0xc4, 0xd7, 0xe4, 0xff, 0xc1, 0xd4, 0xe1, 0xff, 0xc1, 0xd3, 0xe0, 0xff, 0xc3, 0xd4, 0xe0, 0xff, 0xc3, 0xd5, 0xe0, 0xff, 0xc1, 0xd2, 0xdd, 0xff, 0xc0, 0xd2, 0xde, 0xff, 0xbd, 0xd0, 0xdb, 0xff, 0xb9, 0xcb, 0xd8, 0xff, 0xb8, 0xca, 0xda, 0xff, 0xb6, 0xc7, 0xd8, 0xff, 0xb5, 0xc5, 0xd4, 0xff, 0xb1, 0xc1, 0xd1, 0xff, 0xad, 0xbd, 0xce, 0xff, 0xa9, 0xb9, 0xc9, 0xff, 0xa7, 0xb4, 0xc3, 0xff, 0xa6, 0xb2, 0xc0, 0xff, 0xa4, 0xaf, 0xbd, 0xff, 0xa0, 0xab, 0xb8, 0xff, 0x9b, 0xa5, 0xb5, 0xff, 0x98, 0xa1, 0xb3, 0xff, 0x8d, 0x95, 0xa5, 0xff, 0x86, 0x8c, 0x9f, 0xff, 0x89, 0x8f, 0xa7, 0xff, 0x82, 0x88, 0xa6, 0xff, 0x6a, 0x6c, 0x8b, 0xff, 0x4a, 0x4b, 0x6c, 0xff, 0x38, 0x34, 0x51, 0xff, 0x33, 0x26, 0x38, 0xff, 0x39, 0x2b, 0x37, 0xff, 0x2a, 0x19, 0x1f, 0xff, 0x48, 0x38, 0x39, 0xff, 0x58, 0x47, 0x47, 0xff, 0x34, 0x23, 0x25, 0xff, 0x2d, 0x1c, 0x20, 0xff, 0x27, 0x15, 0x17, 0xff, 0x23, 0x11, 0x13, 0xff, 0x23, 0x12, 0x16, 0xff, 0x19, 0x09, 0x0b, 0xff, 0x37, 0x25, 0x22, 0xff, 0x34, 0x22, 0x1f, 0xff, 0x2c, 0x19, 0x1a, 0xff, 0x31, 0x1c, 0x1a, 0xff, 0x31, 0x1c, 0x1c, 0xff, 0x37, 0x22, 0x21, 0xff, 0x3a, 0x23, 0x20, 0xff, 0x2f, 0x1b, 0x18, 0xff, 0x27, 0x15, 0x11, 0xff, 0x32, 0x20, 0x1b, 0xff, 0x40, 0x28, 0x22, 0xff, 0x48, 0x2e, 0x22, 0xff, 0x4f, 0x32, 0x29, 0xff, 0x5d, 0x3e, 0x36, 0xff, 0x66, 0x48, 0x3b, 0xff, 0x56, 0x3b, 0x2a, 0xff, 0x65, 0x49, 0x3c, 0xff, 0x5f, 0x45, 0x3b, 0xff, 0x4a, 0x30, 0x24, 0xff, 0x50, 0x35, 0x26, 0xff, 0x61, 0x48, 0x38, 0xff, 0x35, 0x1f, 0x17, 0xff, 0x48, 0x36, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf0, 0xf0, 0xe1, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xe7, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc4, 0xa6, 0xff, 0xad, 0xa8, 0x7c, 0xff, 0xaa, 0xa4, 0x77, 0xff, 0xad, 0xa7, 0x79, 0xff, 0xae, 0xaa, 0x7d, 0xff, 0xad, 0xaa, 0x7f, 0xff, 0xac, 0xa9, 0x7d, 0xff, 0xab, 0xa9, 0x7d, 0xff, 0xb0, 0xae, 0x82, 0xff, 0xb7, 0xb5, 0x89, 0xff, 0xb9, 0xba, 0x8e, 0xff, 0xba, 0xbb, 0x8f, 0xff, 0xba, 0xb9, 0x8d, 0xff, 0xba, 0xb9, 0x8d, 0xff, 0xb4, 0xb2, 0x86, 0xff, 0xb6, 0xae, 0x83, 0xff, 0xb4, 0xab, 0x80, 0xff, 0xad, 0x9f, 0x6f, 0xff, 0xc7, 0xc0, 0x95, 0xff, 0xbb, 0xb6, 0x8a, 0xff, 0x9c, 0x8b, 0x59, 0xff, 0x85, 0x6d, 0x44, 0xff, 0x8c, 0x74, 0x4a, 0xff, 0xd5, 0xd4, 0xbb, 0xff, 0xe4, 0xf7, 0xfd, 0xff, 0xd4, 0xde, 0xd8, 0xff, 0xaf, 0xa5, 0x8b, 0xff, 0xac, 0xa2, 0x87, 0xff, 0xbd, 0xbd, 0xae, 0xff, 0x81, 0x76, 0x6f, 0xff, 0x75, 0x68, 0x6b, 0xff, 0x71, 0x64, 0x69, 0xff, 0x59, 0x49, 0x50, 0xff, 0x58, 0x44, 0x48, 0xff, 0x41, 0x2d, 0x31, 0xff, 0x45, 0x31, 0x38, 0xff, 0x3e, 0x2c, 0x33, 0xff, 0x40, 0x2d, 0x33, 0xff, 0x4a, 0x38, 0x40, 0xff, 0x44, 0x34, 0x3e, 0xff, 0x39, 0x29, 0x30, 0xff, 0x2d, 0x1c, 0x1f, 0xff, 0x27, 0x16, 0x1c, 0xff, 0x25, 0x15, 0x19, 0xff, 0x27, 0x14, 0x18, 0xff, 0x1d, 0x09, 0x0a, 0xff, 0x63, 0x5e, 0x78, 0xff, 0xab, 0xbb, 0xde, 0xff, 0xc3, 0xd4, 0xe5, 0xff, 0xc5, 0xd8, 0xe5, 0xff, 0xb8, 0xce, 0xdb, 0xff, 0xb0, 0xc8, 0xd7, 0xff, 0xb2, 0xc8, 0xd9, 0xff, 0xb6, 0xcd, 0xdd, 0xff, 0xb6, 0xcd, 0xdd, 0xff, 0xb7, 0xce, 0xdf, 0xff, 0xb3, 0xcb, 0xdd, 0xff, 0xb4, 0xcb, 0xdd, 0xff, 0xb3, 0xcb, 0xdd, 0xff, 0xb4, 0xcc, 0xe0, 0xff, 0xb5, 0xcb, 0xde, 0xff, 0xb6, 0xcc, 0xdd, 0xff, 0xb6, 0xcc, 0xde, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xba, 0xcf, 0xe2, 0xff, 0xbb, 0xd2, 0xe2, 0xff, 0xbb, 0xd2, 0xe2, 0xff, 0xbb, 0xd2, 0xe2, 0xff, 0xbd, 0xd3, 0xe3, 0xff, 0xc0, 0xd4, 0xe3, 0xff, 0xc0, 0xd5, 0xe4, 0xff, 0xc3, 0xd6, 0xe5, 0xff, 0xc2, 0xd4, 0xe3, 0xff, 0xc2, 0xd6, 0xe3, 0xff, 0xc2, 0xd6, 0xe3, 0xff, 0xc1, 0xd5, 0xe2, 0xff, 0xc1, 0xd5, 0xe1, 0xff, 0xc4, 0xd6, 0xe1, 0xff, 0xc3, 0xd4, 0xe0, 0xff, 0xc4, 0xd5, 0xe1, 0xff, 0xc5, 0xd6, 0xe0, 0xff, 0xc4, 0xd7, 0xde, 0xff, 0xc3, 0xd8, 0xdd, 0xff, 0xbf, 0xd3, 0xdc, 0xff, 0xbd, 0xd1, 0xda, 0xff, 0xba, 0xcd, 0xd6, 0xff, 0xb8, 0xca, 0xd3, 0xff, 0xb4, 0xc5, 0xd0, 0xff, 0xaf, 0xc0, 0xce, 0xff, 0xac, 0xbb, 0xc9, 0xff, 0xa6, 0xb5, 0xc3, 0xff, 0xa2, 0xae, 0xbc, 0xff, 0xa1, 0xab, 0xb9, 0xff, 0x9e, 0xa9, 0xb7, 0xff, 0x9a, 0xa4, 0xb4, 0xff, 0x97, 0xa1, 0xb2, 0xff, 0x8b, 0x95, 0xa6, 0xff, 0x86, 0x8f, 0xa2, 0xff, 0x7e, 0x86, 0x9e, 0xff, 0x7a, 0x81, 0x9b, 0xff, 0x7c, 0x81, 0x9b, 0xff, 0x70, 0x74, 0x93, 0xff, 0x5f, 0x61, 0x83, 0xff, 0x4b, 0x47, 0x64, 0xff, 0x3d, 0x32, 0x46, 0xff, 0x3f, 0x33, 0x3e, 0xff, 0x46, 0x3b, 0x43, 0xff, 0x29, 0x1a, 0x20, 0xff, 0x2b, 0x1a, 0x1e, 0xff, 0x38, 0x26, 0x29, 0xff, 0x2a, 0x18, 0x1b, 0xff, 0x27, 0x14, 0x17, 0xff, 0x25, 0x13, 0x16, 0xff, 0x26, 0x14, 0x17, 0xff, 0x33, 0x1f, 0x1a, 0xff, 0x28, 0x16, 0x14, 0xff, 0x26, 0x13, 0x16, 0xff, 0x36, 0x20, 0x20, 0xff, 0x33, 0x1f, 0x1e, 0xff, 0x33, 0x1f, 0x1c, 0xff, 0x4f, 0x37, 0x30, 0xff, 0x50, 0x36, 0x30, 0xff, 0x3f, 0x27, 0x20, 0xff, 0x4f, 0x3a, 0x31, 0xff, 0x6c, 0x55, 0x49, 0xff, 0x60, 0x49, 0x37, 0xff, 0x57, 0x3d, 0x30, 0xff, 0x51, 0x38, 0x2c, 0xff, 0x3d, 0x22, 0x1a, 0xff, 0x4a, 0x30, 0x29, 0xff, 0x3c, 0x22, 0x1f, 0xff, 0x2a, 0x10, 0x14, 0xff, 0x45, 0x2b, 0x26, 0xff, 0x59, 0x3d, 0x2d, 0xff, 0x54, 0x39, 0x27, 0xff, 0x51, 0x37, 0x2c, 0xff, 0x43, 0x2d, 0x2d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfa, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xbf, 0x9f, 0xff, 0xab, 0xa7, 0x7b, 0xff, 0xab, 0xa7, 0x7a, 0xff, 0xae, 0xa8, 0x7b, 0xff, 0xad, 0xa9, 0x7c, 0xff, 0xab, 0xa9, 0x7d, 0xff, 0xab, 0xa9, 0x7d, 0xff, 0xaa, 0xaa, 0x7e, 0xff, 0xaf, 0xae, 0x82, 0xff, 0xb4, 0xb4, 0x88, 0xff, 0xb8, 0xba, 0x8e, 0xff, 0xbb, 0xbc, 0x90, 0xff, 0xbb, 0xba, 0x8f, 0xff, 0xb9, 0xb8, 0x8c, 0xff, 0xb4, 0xb4, 0x87, 0xff, 0xb3, 0xb1, 0x85, 0xff, 0xbe, 0xbc, 0x91, 0xff, 0xc4, 0xc2, 0x96, 0xff, 0xd0, 0xd2, 0xaf, 0xff, 0xa9, 0x9f, 0x7a, 0xff, 0xad, 0x9c, 0x6b, 0xff, 0xba, 0xb5, 0x8a, 0xff, 0xaf, 0xa5, 0x74, 0xff, 0xbf, 0xbb, 0x9c, 0xff, 0xe0, 0xf0, 0xf1, 0xff, 0xd8, 0xe5, 0xe0, 0xff, 0xc3, 0xc4, 0xb2, 0xff, 0xb5, 0xae, 0x8e, 0xff, 0xce, 0xd1, 0xb9, 0xff, 0xb5, 0xb7, 0xaf, 0xff, 0x8b, 0x87, 0x88, 0xff, 0x69, 0x5c, 0x61, 0xff, 0x52, 0x3d, 0x47, 0xff, 0x4a, 0x34, 0x38, 0xff, 0x3b, 0x26, 0x28, 0xff, 0x41, 0x2a, 0x30, 0xff, 0x3b, 0x28, 0x2f, 0xff, 0x4a, 0x3a, 0x43, 0xff, 0x53, 0x43, 0x4e, 0xff, 0x43, 0x33, 0x3d, 0xff, 0x2e, 0x1f, 0x24, 0xff, 0x26, 0x16, 0x19, 0xff, 0x29, 0x15, 0x1c, 0xff, 0x26, 0x13, 0x16, 0xff, 0x23, 0x13, 0x1d, 0xff, 0x24, 0x15, 0x19, 0xff, 0x41, 0x37, 0x47, 0xff, 0x4e, 0x49, 0x68, 0xff, 0x6e, 0x70, 0x8e, 0xff, 0x98, 0xac, 0xc6, 0xff, 0xbc, 0xd3, 0xe7, 0xff, 0xbe, 0xd5, 0xe2, 0xff, 0xb1, 0xc8, 0xd7, 0xff, 0xb2, 0xc7, 0xd9, 0xff, 0xb3, 0xc7, 0xda, 0xff, 0xb2, 0xc7, 0xdb, 0xff, 0xb1, 0xca, 0xdb, 0xff, 0xb2, 0xca, 0xde, 0xff, 0xaf, 0xc7, 0xdc, 0xff, 0xb1, 0xca, 0xdd, 0xff, 0xb5, 0xcc, 0xdd, 0xff, 0xb5, 0xca, 0xdc, 0xff, 0xb4, 0xca, 0xdc, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb8, 0xce, 0xe0, 0xff, 0xb8, 0xcf, 0xdf, 0xff, 0xb9, 0xd0, 0xe0, 0xff, 0xb9, 0xd0, 0xe0, 0xff, 0xba, 0xd0, 0xe1, 0xff, 0xba, 0xcf, 0xe2, 0xff, 0xbb, 0xd1, 0xe2, 0xff, 0xbd, 0xd1, 0xe0, 0xff, 0xbf, 0xd3, 0xe1, 0xff, 0xbf, 0xd0, 0xe1, 0xff, 0xbf, 0xd0, 0xe0, 0xff, 0xc0, 0xd4, 0xdf, 0xff, 0xc3, 0xd8, 0xde, 0xff, 0xc6, 0xdb, 0xe1, 0xff, 0xcd, 0xe2, 0xe8, 0xff, 0xcf, 0xe3, 0xe9, 0xff, 0xcb, 0xde, 0xe5, 0xff, 0xca, 0xdd, 0xe7, 0xff, 0xc6, 0xdb, 0xe4, 0xff, 0xc0, 0xd3, 0xde, 0xff, 0xb8, 0xcb, 0xd7, 0xff, 0xb1, 0xc4, 0xd0, 0xff, 0xbb, 0xcd, 0xd4, 0xff, 0xc0, 0xd2, 0xda, 0xff, 0xbc, 0xce, 0xd9, 0xff, 0xb6, 0xc8, 0xd3, 0xff, 0xab, 0xbb, 0xc6, 0xff, 0xa3, 0xb0, 0xbd, 0xff, 0xa0, 0xac, 0xbb, 0xff, 0x9d, 0xa8, 0xb7, 0xff, 0x98, 0xa4, 0xb2, 0xff, 0x93, 0x9f, 0xac, 0xff, 0x8f, 0x99, 0xaa, 0xff, 0x87, 0x8f, 0xa5, 0xff, 0x81, 0x89, 0xa3, 0xff, 0x7d, 0x86, 0xa1, 0xff, 0x7d, 0x84, 0x9d, 0xff, 0x79, 0x7c, 0x98, 0xff, 0x6f, 0x73, 0x92, 0xff, 0x63, 0x67, 0x85, 0xff, 0x4d, 0x49, 0x5f, 0xff, 0x56, 0x4d, 0x5d, 0xff, 0x49, 0x3f, 0x4e, 0xff, 0x24, 0x16, 0x1d, 0xff, 0x30, 0x21, 0x23, 0xff, 0x35, 0x23, 0x24, 0xff, 0x29, 0x17, 0x1a, 0xff, 0x26, 0x14, 0x17, 0xff, 0x21, 0x0d, 0x11, 0xff, 0x2d, 0x1a, 0x1d, 0xff, 0x39, 0x24, 0x23, 0xff, 0x25, 0x12, 0x13, 0xff, 0x20, 0x0e, 0x14, 0xff, 0x2b, 0x17, 0x19, 0xff, 0x2c, 0x19, 0x19, 0xff, 0x1d, 0x0b, 0x0b, 0xff, 0x22, 0x0f, 0x0e, 0xff, 0x3b, 0x24, 0x23, 0xff, 0x38, 0x21, 0x20, 0xff, 0x3c, 0x27, 0x26, 0xff, 0x42, 0x2e, 0x29, 0xff, 0x48, 0x33, 0x2a, 0xff, 0x30, 0x19, 0x17, 0xff, 0x29, 0x14, 0x16, 0xff, 0x27, 0x15, 0x10, 0xff, 0x43, 0x30, 0x25, 0xff, 0x41, 0x2c, 0x26, 0xff, 0x32, 0x1b, 0x1b, 0xff, 0x33, 0x1d, 0x1a, 0xff, 0x3d, 0x26, 0x22, 0xff, 0x3b, 0x23, 0x20, 0xff, 0x3b, 0x23, 0x22, 0xff, 0x42, 0x2e, 0x2d, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xbc, 0x9a, 0xff, 0xab, 0xa9, 0x7c, 0xff, 0xaa, 0xa7, 0x7b, 0xff, 0xaa, 0xa7, 0x7b, 0xff, 0xaa, 0xa7, 0x7b, 0xff, 0xa9, 0xa8, 0x7c, 0xff, 0xaa, 0xa8, 0x7c, 0xff, 0xac, 0xaa, 0x7e, 0xff, 0xaf, 0xad, 0x81, 0xff, 0xb2, 0xb2, 0x89, 0xff, 0xb9, 0xba, 0x90, 0xff, 0xba, 0xba, 0x91, 0xff, 0xb9, 0xba, 0x91, 0xff, 0xb8, 0xb8, 0x8d, 0xff, 0xb4, 0xb5, 0x87, 0xff, 0xb2, 0xb0, 0x83, 0xff, 0xb4, 0xaf, 0x84, 0xff, 0xb0, 0xae, 0x82, 0xff, 0xb4, 0xac, 0x81, 0xff, 0xaf, 0x9b, 0x6c, 0xff, 0xc1, 0xa8, 0x77, 0xff, 0xb9, 0xa6, 0x75, 0xff, 0x9f, 0x89, 0x5c, 0xff, 0x9d, 0x8a, 0x68, 0xff, 0xd7, 0xe3, 0xd5, 0xff, 0xd6, 0xe3, 0xe0, 0xff, 0xc8, 0xcc, 0xbf, 0xff, 0xb7, 0xb3, 0x9e, 0xff, 0xac, 0xa4, 0x87, 0xff, 0xcb, 0xcc, 0xb5, 0xff, 0xa6, 0xa5, 0x9e, 0xff, 0x53, 0x42, 0x49, 0xff, 0x4a, 0x30, 0x38, 0xff, 0x5d, 0x48, 0x4c, 0xff, 0x56, 0x44, 0x4a, 0xff, 0x45, 0x31, 0x39, 0xff, 0x47, 0x36, 0x41, 0xff, 0x44, 0x32, 0x3d, 0xff, 0x47, 0x33, 0x3b, 0xff, 0x36, 0x22, 0x27, 0xff, 0x29, 0x15, 0x19, 0xff, 0x2d, 0x19, 0x1e, 0xff, 0x2c, 0x15, 0x19, 0xff, 0x29, 0x15, 0x1c, 0xff, 0x21, 0x11, 0x19, 0xff, 0x2f, 0x1d, 0x1f, 0xff, 0x36, 0x25, 0x31, 0xff, 0x2a, 0x1b, 0x29, 0xff, 0x25, 0x18, 0x2c, 0xff, 0x2e, 0x26, 0x42, 0xff, 0x60, 0x66, 0x8c, 0xff, 0xa3, 0xb5, 0xd7, 0xff, 0xbd, 0xd4, 0xe5, 0xff, 0xba, 0xd4, 0xe1, 0xff, 0xb2, 0xca, 0xd7, 0xff, 0xb0, 0xc5, 0xd5, 0xff, 0xb2, 0xc5, 0xd9, 0xff, 0xaf, 0xc6, 0xd8, 0xff, 0xae, 0xc6, 0xd8, 0xff, 0xb1, 0xc7, 0xdb, 0xff, 0xb3, 0xc8, 0xdd, 0xff, 0xb3, 0xc9, 0xdb, 0xff, 0xb5, 0xcb, 0xdc, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb4, 0xcb, 0xdd, 0xff, 0xb4, 0xca, 0xdd, 0xff, 0xb6, 0xca, 0xdc, 0xff, 0xb8, 0xcc, 0xdd, 0xff, 0xb7, 0xcc, 0xdb, 0xff, 0xb7, 0xcc, 0xd9, 0xff, 0xb5, 0xca, 0xd8, 0xff, 0xb7, 0xc9, 0xdb, 0xff, 0xba, 0xca, 0xda, 0xff, 0xbd, 0xd2, 0xd8, 0xff, 0xc9, 0xdf, 0xe3, 0xff, 0xcc, 0xde, 0xe7, 0xff, 0xbc, 0xcc, 0xde, 0xff, 0x9f, 0xad, 0xc8, 0xff, 0x8f, 0x99, 0xb8, 0xff, 0x7d, 0x81, 0xa2, 0xff, 0x72, 0x71, 0x8f, 0xff, 0x70, 0x6f, 0x85, 0xff, 0x63, 0x5a, 0x6f, 0xff, 0x6f, 0x64, 0x7e, 0xff, 0x77, 0x72, 0x8f, 0xff, 0x73, 0x71, 0x8d, 0xff, 0x8d, 0x8f, 0xa4, 0xff, 0xa0, 0xa7, 0xb8, 0xff, 0xa6, 0xb2, 0xc1, 0xff, 0xa7, 0xb3, 0xbe, 0xff, 0xa7, 0xb1, 0xbf, 0xff, 0xa5, 0xae, 0xbe, 0xff, 0xa0, 0xab, 0xb8, 0xff, 0x9a, 0xa7, 0xb4, 0xff, 0x93, 0x9f, 0xae, 0xff, 0x8f, 0x99, 0xaa, 0xff, 0x86, 0x91, 0xa2, 0xff, 0x85, 0x8d, 0xa2, 0xff, 0x81, 0x89, 0xa2, 0xff, 0x7e, 0x83, 0x9d, 0xff, 0x79, 0x7d, 0x96, 0xff, 0x77, 0x7b, 0x94, 0xff, 0x7a, 0x7e, 0x97, 0xff, 0x69, 0x6b, 0x82, 0xff, 0x52, 0x4b, 0x5e, 0xff, 0x43, 0x35, 0x45, 0xff, 0x34, 0x26, 0x30, 0xff, 0x2c, 0x1c, 0x21, 0xff, 0x30, 0x1e, 0x1e, 0xff, 0x2c, 0x19, 0x1b, 0xff, 0x24, 0x13, 0x16, 0xff, 0x1c, 0x0c, 0x0f, 0xff, 0x29, 0x18, 0x1a, 0xff, 0x34, 0x22, 0x22, 0xff, 0x2c, 0x1a, 0x1c, 0xff, 0x24, 0x11, 0x16, 0xff, 0x1e, 0x0c, 0x0e, 0xff, 0x27, 0x15, 0x15, 0xff, 0x30, 0x1c, 0x1c, 0xff, 0x26, 0x11, 0x14, 0xff, 0x21, 0x0d, 0x0f, 0xff, 0x26, 0x14, 0x15, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x1b, 0x06, 0x0a, 0xff, 0x32, 0x1e, 0x1d, 0xff, 0x31, 0x1d, 0x1c, 0xff, 0x26, 0x12, 0x15, 0xff, 0x2e, 0x1a, 0x1b, 0xff, 0x3f, 0x28, 0x22, 0xff, 0x43, 0x28, 0x22, 0xff, 0x37, 0x1f, 0x1d, 0xff, 0x2a, 0x15, 0x17, 0xff, 0x27, 0x12, 0x15, 0xff, 0x24, 0x11, 0x11, 0xff, 0x21, 0x0c, 0x0c, 0xff, 0x41, 0x29, 0x2a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe3, 0xe3, 0x09, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbd, 0xba, 0x98, 0xff, 0xa9, 0xa8, 0x7d, 0xff, 0xa8, 0xa6, 0x7a, 0xff, 0xa9, 0xa7, 0x7b, 0xff, 0xaa, 0xa6, 0x7b, 0xff, 0xab, 0xa8, 0x7c, 0xff, 0xac, 0xa9, 0x7d, 0xff, 0xac, 0xa9, 0x7d, 0xff, 0xae, 0xaa, 0x7f, 0xff, 0xb0, 0xb0, 0x86, 0xff, 0xb6, 0xb5, 0x8b, 0xff, 0xb8, 0xb7, 0x8d, 0xff, 0xb7, 0xb6, 0x8c, 0xff, 0xb8, 0xb9, 0x8f, 0xff, 0xb4, 0xb4, 0x8a, 0xff, 0xbc, 0xba, 0x91, 0xff, 0xae, 0xac, 0x83, 0xff, 0xc0, 0xbc, 0x94, 0xff, 0xbe, 0xb3, 0x8a, 0xff, 0xb0, 0x9b, 0x70, 0xff, 0x97, 0x81, 0x56, 0xff, 0x8b, 0x74, 0x4c, 0xff, 0x8d, 0x74, 0x50, 0xff, 0xa1, 0x8c, 0x63, 0xff, 0xc9, 0xc7, 0xad, 0xff, 0xcf, 0xe3, 0xdc, 0xff, 0xc0, 0xcf, 0xc3, 0xff, 0xc9, 0xcd, 0xbc, 0xff, 0xae, 0xa9, 0x8d, 0xff, 0xb5, 0xb4, 0x9a, 0xff, 0xc9, 0xc9, 0xba, 0xff, 0x97, 0x91, 0x8e, 0xff, 0x64, 0x59, 0x5a, 0xff, 0x64, 0x51, 0x52, 0xff, 0x50, 0x3c, 0x46, 0xff, 0x4b, 0x3b, 0x49, 0xff, 0x50, 0x40, 0x4c, 0xff, 0x46, 0x34, 0x3b, 0xff, 0x36, 0x22, 0x26, 0xff, 0x2e, 0x1b, 0x1f, 0xff, 0x2f, 0x1c, 0x21, 0xff, 0x29, 0x16, 0x1b, 0xff, 0x2e, 0x1b, 0x1e, 0xff, 0x29, 0x18, 0x1f, 0xff, 0x21, 0x0f, 0x15, 0xff, 0x33, 0x20, 0x24, 0xff, 0x30, 0x22, 0x2f, 0xff, 0x33, 0x23, 0x36, 0xff, 0x2a, 0x17, 0x23, 0xff, 0x29, 0x13, 0x16, 0xff, 0x2b, 0x1a, 0x2c, 0xff, 0x40, 0x3b, 0x61, 0xff, 0x65, 0x6a, 0x96, 0xff, 0x93, 0xa3, 0xcd, 0xff, 0xae, 0xc9, 0xe4, 0xff, 0xb9, 0xd3, 0xde, 0xff, 0xb3, 0xc8, 0xd4, 0xff, 0xac, 0xc3, 0xd3, 0xff, 0xac, 0xc3, 0xd6, 0xff, 0xaf, 0xc3, 0xda, 0xff, 0xb0, 0xc4, 0xda, 0xff, 0xb1, 0xc8, 0xda, 0xff, 0xb4, 0xca, 0xdb, 0xff, 0xb4, 0xca, 0xdc, 0xff, 0xb3, 0xc8, 0xdc, 0xff, 0xb2, 0xc9, 0xdb, 0xff, 0xb3, 0xc8, 0xd9, 0xff, 0xb7, 0xca, 0xdb, 0xff, 0xb4, 0xc8, 0xd9, 0xff, 0xb5, 0xc5, 0xd6, 0xff, 0xb6, 0xc7, 0xd6, 0xff, 0xbd, 0xcf, 0xde, 0xff, 0xc8, 0xda, 0xe6, 0xff, 0xc0, 0xd5, 0xe0, 0xff, 0xaa, 0xbb, 0xd2, 0xff, 0x96, 0xa0, 0xc3, 0xff, 0x70, 0x73, 0x9b, 0xff, 0x54, 0x4f, 0x7a, 0xff, 0x3e, 0x36, 0x5e, 0xff, 0x35, 0x2b, 0x4b, 0xff, 0x32, 0x24, 0x3d, 0xff, 0x33, 0x21, 0x34, 0xff, 0x32, 0x1e, 0x2c, 0xff, 0x30, 0x1a, 0x26, 0xff, 0x3e, 0x29, 0x35, 0xff, 0x53, 0x42, 0x56, 0xff, 0x4f, 0x3c, 0x5a, 0xff, 0x4a, 0x38, 0x50, 0xff, 0x60, 0x52, 0x65, 0xff, 0x63, 0x58, 0x69, 0xff, 0x67, 0x60, 0x6f, 0xff, 0x77, 0x73, 0x84, 0xff, 0x8d, 0x8e, 0xa0, 0xff, 0x8b, 0x8f, 0x9f, 0xff, 0x93, 0x99, 0xa9, 0xff, 0x9e, 0xa4, 0xb6, 0xff, 0x91, 0x98, 0xab, 0xff, 0x87, 0x91, 0xa3, 0xff, 0x83, 0x8e, 0xa0, 0xff, 0x82, 0x8a, 0xa0, 0xff, 0x84, 0x8a, 0xa1, 0xff, 0x81, 0x87, 0x9c, 0xff, 0x7b, 0x80, 0x95, 0xff, 0x7d, 0x82, 0x97, 0xff, 0x7f, 0x83, 0x99, 0xff, 0x50, 0x4b, 0x5e, 0xff, 0x32, 0x25, 0x34, 0xff, 0x31, 0x22, 0x2e, 0xff, 0x2e, 0x1e, 0x23, 0xff, 0x2e, 0x1c, 0x1c, 0xff, 0x2c, 0x19, 0x1b, 0xff, 0x25, 0x14, 0x17, 0xff, 0x26, 0x16, 0x18, 0xff, 0x27, 0x16, 0x16, 0xff, 0x29, 0x17, 0x18, 0xff, 0x2b, 0x1b, 0x1a, 0xff, 0x1f, 0x0e, 0x0c, 0xff, 0x2c, 0x1b, 0x19, 0xff, 0x2c, 0x1b, 0x19, 0xff, 0x2d, 0x18, 0x19, 0xff, 0x2f, 0x19, 0x1c, 0xff, 0x22, 0x0f, 0x11, 0xff, 0x2b, 0x1a, 0x1b, 0xff, 0x26, 0x14, 0x16, 0xff, 0x27, 0x12, 0x14, 0xff, 0x38, 0x23, 0x20, 0xff, 0x3e, 0x29, 0x23, 0xff, 0x2d, 0x19, 0x18, 0xff, 0x3a, 0x24, 0x22, 0xff, 0x3c, 0x25, 0x1e, 0xff, 0x43, 0x28, 0x22, 0xff, 0x36, 0x1e, 0x1a, 0xff, 0x28, 0x10, 0x13, 0xff, 0x27, 0x0f, 0x13, 0xff, 0x28, 0x13, 0x16, 0xff, 0x31, 0x1d, 0x18, 0xff, 0x50, 0x3c, 0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xbb, 0x9a, 0xff, 0xa9, 0xa8, 0x80, 0xff, 0xa7, 0xa5, 0x7c, 0xff, 0xa9, 0xa7, 0x7a, 0xff, 0xaa, 0xa7, 0x7b, 0xff, 0xa8, 0xa5, 0x79, 0xff, 0xa9, 0xa7, 0x7a, 0xff, 0xab, 0xa9, 0x7d, 0xff, 0xac, 0xa9, 0x7d, 0xff, 0xae, 0xac, 0x7f, 0xff, 0xb4, 0xb2, 0x85, 0xff, 0xb7, 0xb5, 0x88, 0xff, 0xb6, 0xb4, 0x87, 0xff, 0xb4, 0xb2, 0x88, 0xff, 0xb6, 0xb4, 0x8c, 0xff, 0xc2, 0xbf, 0x97, 0xff, 0xc3, 0xc1, 0x99, 0xff, 0xc5, 0xbf, 0x96, 0xff, 0xa4, 0x99, 0x6d, 0xff, 0x9c, 0x8e, 0x61, 0xff, 0x9c, 0x8c, 0x61, 0xff, 0x9f, 0x93, 0x6b, 0xff, 0xc2, 0xbe, 0x99, 0xff, 0xc3, 0xc0, 0x9d, 0xff, 0xb6, 0xac, 0x8c, 0xff, 0xb7, 0xb3, 0x95, 0xff, 0xd5, 0xd9, 0xc8, 0xff, 0xc8, 0xcf, 0xc2, 0xff, 0xb1, 0xb1, 0xa0, 0xff, 0xa6, 0xa4, 0x92, 0xff, 0xa9, 0xa9, 0x9b, 0xff, 0xaa, 0xac, 0xa8, 0xff, 0x7f, 0x7f, 0x81, 0xff, 0x62, 0x54, 0x59, 0xff, 0x4e, 0x3d, 0x46, 0xff, 0x4a, 0x3d, 0x45, 0xff, 0x4a, 0x3a, 0x3f, 0xff, 0x41, 0x2c, 0x30, 0xff, 0x2e, 0x1b, 0x1f, 0xff, 0x2d, 0x1d, 0x21, 0xff, 0x34, 0x23, 0x27, 0xff, 0x34, 0x24, 0x28, 0xff, 0x2f, 0x1d, 0x21, 0xff, 0x29, 0x17, 0x1a, 0xff, 0x17, 0x03, 0x04, 0xff, 0x44, 0x3c, 0x4d, 0xff, 0x84, 0x92, 0xb3, 0xff, 0x79, 0x84, 0x9c, 0xff, 0x67, 0x67, 0x7d, 0xff, 0x47, 0x37, 0x49, 0xff, 0x2c, 0x18, 0x1f, 0xff, 0x23, 0x0f, 0x12, 0xff, 0x25, 0x14, 0x23, 0xff, 0x3d, 0x34, 0x54, 0xff, 0x56, 0x5a, 0x86, 0xff, 0x7b, 0x89, 0xb2, 0xff, 0xac, 0xbf, 0xd9, 0xff, 0xb7, 0xcd, 0xe3, 0xff, 0xa9, 0xc0, 0xd2, 0xff, 0xa9, 0xc0, 0xd1, 0xff, 0xaf, 0xc4, 0xd8, 0xff, 0xb0, 0xc5, 0xd8, 0xff, 0xb1, 0xc8, 0xd8, 0xff, 0xb0, 0xc6, 0xd8, 0xff, 0xb1, 0xc5, 0xd9, 0xff, 0xaf, 0xc3, 0xd5, 0xff, 0xaf, 0xc4, 0xd3, 0xff, 0xb1, 0xc4, 0xd5, 0xff, 0xb0, 0xc3, 0xd5, 0xff, 0xb3, 0xc5, 0xd9, 0xff, 0xb2, 0xc0, 0xd8, 0xff, 0x9f, 0xa9, 0xc3, 0xff, 0x7b, 0x82, 0x9c, 0xff, 0x65, 0x65, 0x85, 0xff, 0x51, 0x4b, 0x72, 0xff, 0x32, 0x27, 0x4a, 0xff, 0x2b, 0x1b, 0x34, 0xff, 0x2e, 0x1f, 0x2d, 0xff, 0x38, 0x28, 0x30, 0xff, 0x38, 0x26, 0x2e, 0xff, 0x36, 0x22, 0x2e, 0xff, 0x36, 0x21, 0x2f, 0xff, 0x2e, 0x1d, 0x2d, 0xff, 0x2f, 0x1d, 0x30, 0xff, 0x27, 0x15, 0x2b, 0xff, 0x41, 0x35, 0x50, 0xff, 0x5d, 0x52, 0x70, 0xff, 0x4a, 0x3d, 0x55, 0xff, 0x48, 0x3a, 0x50, 0xff, 0x50, 0x43, 0x59, 0xff, 0x54, 0x48, 0x5d, 0xff, 0x5d, 0x54, 0x6c, 0xff, 0x72, 0x6f, 0x87, 0xff, 0x74, 0x75, 0x8b, 0xff, 0x81, 0x86, 0x98, 0xff, 0x91, 0x97, 0xa8, 0xff, 0x99, 0x9f, 0xb1, 0xff, 0x91, 0x99, 0xac, 0xff, 0x8a, 0x94, 0xa5, 0xff, 0x8a, 0x91, 0xa3, 0xff, 0x88, 0x8f, 0xa3, 0xff, 0x86, 0x8d, 0xa0, 0xff, 0x80, 0x88, 0x98, 0xff, 0x80, 0x87, 0x98, 0xff, 0x80, 0x86, 0x9a, 0xff, 0x62, 0x5f, 0x70, 0xff, 0x44, 0x39, 0x49, 0xff, 0x39, 0x2b, 0x36, 0xff, 0x3e, 0x2f, 0x33, 0xff, 0x33, 0x22, 0x22, 0xff, 0x2c, 0x19, 0x1c, 0xff, 0x2f, 0x1e, 0x1e, 0xff, 0x36, 0x24, 0x23, 0xff, 0x29, 0x17, 0x18, 0xff, 0x29, 0x14, 0x17, 0xff, 0x2a, 0x19, 0x17, 0xff, 0x37, 0x28, 0x22, 0xff, 0x33, 0x22, 0x20, 0xff, 0x27, 0x14, 0x16, 0xff, 0x1f, 0x0b, 0x0d, 0xff, 0x1e, 0x0c, 0x0d, 0xff, 0x2a, 0x18, 0x19, 0xff, 0x2d, 0x1b, 0x1d, 0xff, 0x29, 0x16, 0x17, 0xff, 0x39, 0x25, 0x23, 0xff, 0x38, 0x22, 0x1f, 0xff, 0x3c, 0x26, 0x23, 0xff, 0x3a, 0x24, 0x24, 0xff, 0x43, 0x2c, 0x28, 0xff, 0x4c, 0x35, 0x2e, 0xff, 0x42, 0x2b, 0x24, 0xff, 0x41, 0x2a, 0x25, 0xff, 0x48, 0x30, 0x27, 0xff, 0x59, 0x40, 0x34, 0xff, 0x3e, 0x24, 0x20, 0xff, 0x38, 0x20, 0x1c, 0xff, 0x44, 0x30, 0x2e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbd, 0xbe, 0x9d, 0xff, 0xab, 0xac, 0x84, 0xff, 0xa9, 0xa8, 0x7f, 0xff, 0xa9, 0xa7, 0x7c, 0xff, 0xa9, 0xa5, 0x79, 0xff, 0xa5, 0xa1, 0x75, 0xff, 0xa6, 0xa2, 0x77, 0xff, 0xa7, 0xa4, 0x78, 0xff, 0xa8, 0xa4, 0x79, 0xff, 0xab, 0xa7, 0x7c, 0xff, 0xb0, 0xae, 0x81, 0xff, 0xb4, 0xb1, 0x85, 0xff, 0xb4, 0xb1, 0x84, 0xff, 0xae, 0xa9, 0x7f, 0xff, 0xc1, 0xbc, 0x93, 0xff, 0xb9, 0xb5, 0x8b, 0xff, 0xb7, 0xb5, 0x8c, 0xff, 0xac, 0xa1, 0x78, 0xff, 0xa1, 0x91, 0x65, 0xff, 0xad, 0xa2, 0x76, 0xff, 0xa4, 0x96, 0x6c, 0xff, 0xc8, 0xc2, 0x9d, 0xff, 0xd4, 0xd3, 0xb6, 0xff, 0x92, 0x81, 0x5b, 0xff, 0x9a, 0x8b, 0x61, 0xff, 0x8b, 0x70, 0x47, 0xff, 0x95, 0x80, 0x58, 0xff, 0xc5, 0xc5, 0xb2, 0xff, 0xcb, 0xd1, 0xc2, 0xff, 0xbc, 0xbf, 0xb1, 0xff, 0xb6, 0xb7, 0xae, 0xff, 0x9e, 0x9d, 0x9a, 0xff, 0x8e, 0x8c, 0x8e, 0xff, 0x90, 0x8b, 0x8e, 0xff, 0x7d, 0x72, 0x74, 0xff, 0x60, 0x51, 0x4e, 0xff, 0x46, 0x33, 0x30, 0xff, 0x3d, 0x27, 0x2b, 0xff, 0x44, 0x2f, 0x37, 0xff, 0x4c, 0x3b, 0x3f, 0xff, 0x44, 0x33, 0x37, 0xff, 0x37, 0x25, 0x29, 0xff, 0x2c, 0x1c, 0x20, 0xff, 0x28, 0x17, 0x1b, 0xff, 0x12, 0x00, 0x00, 0xff, 0x46, 0x43, 0x53, 0xff, 0xae, 0xc8, 0xe6, 0xff, 0xab, 0xc5, 0xd8, 0xff, 0xa9, 0xc3, 0xdd, 0xff, 0x97, 0xaa, 0xcb, 0xff, 0x73, 0x7b, 0x9a, 0xff, 0x4a, 0x43, 0x5b, 0xff, 0x2d, 0x1b, 0x24, 0xff, 0x23, 0x0d, 0x0b, 0xff, 0x20, 0x0e, 0x17, 0xff, 0x2f, 0x2a, 0x5a, 0xff, 0x55, 0x5e, 0xa2, 0xff, 0x81, 0x90, 0xc2, 0xff, 0xa5, 0xba, 0xd7, 0xff, 0xaf, 0xc3, 0xd8, 0xff, 0xad, 0xc0, 0xd4, 0xff, 0xaf, 0xc3, 0xd5, 0xff, 0xb0, 0xc5, 0xd6, 0xff, 0xb0, 0xc5, 0xd7, 0xff, 0xaf, 0xc3, 0xd5, 0xff, 0xac, 0xc0, 0xd0, 0xff, 0xaa, 0xbe, 0xce, 0xff, 0xaf, 0xc1, 0xd7, 0xff, 0xa1, 0xae, 0xcd, 0xff, 0x79, 0x7f, 0xa5, 0xff, 0x4d, 0x4c, 0x73, 0xff, 0x31, 0x28, 0x4d, 0xff, 0x1c, 0x0f, 0x30, 0xff, 0x1f, 0x13, 0x2c, 0xff, 0x2d, 0x21, 0x2e, 0xff, 0x31, 0x20, 0x24, 0xff, 0x34, 0x1d, 0x1c, 0xff, 0x32, 0x1d, 0x1f, 0xff, 0x2b, 0x18, 0x1f, 0xff, 0x34, 0x22, 0x32, 0xff, 0x3b, 0x2e, 0x49, 0xff, 0x4c, 0x42, 0x66, 0xff, 0x6b, 0x69, 0x8c, 0xff, 0x7e, 0x7f, 0x9d, 0xff, 0x81, 0x81, 0x9c, 0xff, 0x91, 0x98, 0xb0, 0xff, 0xa4, 0xac, 0xc2, 0xff, 0xa4, 0xa9, 0xbb, 0xff, 0x9c, 0xa1, 0xb2, 0xff, 0xa3, 0xaa, 0xbb, 0xff, 0xa6, 0xad, 0xbb, 0xff, 0xa1, 0xa7, 0xb7, 0xff, 0x98, 0x9f, 0xaf, 0xff, 0x9a, 0xa1, 0xb1, 0xff, 0x9e, 0xa8, 0xb7, 0xff, 0x9a, 0xa4, 0xb2, 0xff, 0x9c, 0xa5, 0xb3, 0xff, 0x98, 0xa0, 0xaf, 0xff, 0x92, 0x9b, 0xa9, 0xff, 0x92, 0x9a, 0xa9, 0xff, 0x8d, 0x95, 0xa7, 0xff, 0x8b, 0x94, 0xa4, 0xff, 0x90, 0x99, 0xa6, 0xff, 0x8e, 0x96, 0xa4, 0xff, 0x83, 0x8b, 0x9b, 0xff, 0x68, 0x68, 0x7a, 0xff, 0x49, 0x40, 0x50, 0xff, 0x34, 0x27, 0x32, 0xff, 0x45, 0x37, 0x3c, 0xff, 0x39, 0x2a, 0x2a, 0xff, 0x25, 0x13, 0x16, 0xff, 0x31, 0x1f, 0x20, 0xff, 0x37, 0x25, 0x23, 0xff, 0x28, 0x15, 0x15, 0xff, 0x2c, 0x16, 0x18, 0xff, 0x39, 0x27, 0x27, 0xff, 0x33, 0x23, 0x21, 0xff, 0x1f, 0x0d, 0x0e, 0xff, 0x26, 0x13, 0x16, 0xff, 0x27, 0x13, 0x16, 0xff, 0x1e, 0x0d, 0x0e, 0xff, 0x28, 0x16, 0x17, 0xff, 0x29, 0x14, 0x17, 0xff, 0x30, 0x1b, 0x1c, 0xff, 0x34, 0x21, 0x1e, 0xff, 0x34, 0x21, 0x1e, 0xff, 0x37, 0x22, 0x22, 0xff, 0x30, 0x1b, 0x1e, 0xff, 0x34, 0x1c, 0x1c, 0xff, 0x4a, 0x30, 0x2e, 0xff, 0x4a, 0x32, 0x2d, 0xff, 0x4d, 0x34, 0x2c, 0xff, 0x55, 0x3a, 0x31, 0xff, 0x6a, 0x4e, 0x3f, 0xff, 0x77, 0x5d, 0x49, 0xff, 0x48, 0x30, 0x25, 0xff, 0x2f, 0x1a, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc2, 0xa3, 0xff, 0xae, 0xb0, 0x88, 0xff, 0xab, 0xaa, 0x81, 0xff, 0xaa, 0xa7, 0x7c, 0xff, 0xa6, 0xa2, 0x76, 0xff, 0xa3, 0x9d, 0x72, 0xff, 0xa4, 0x9e, 0x73, 0xff, 0xa3, 0x9d, 0x72, 0xff, 0xa5, 0x9f, 0x72, 0xff, 0xa9, 0xa3, 0x78, 0xff, 0xae, 0xa9, 0x7e, 0xff, 0xb4, 0xaf, 0x84, 0xff, 0xb0, 0xab, 0x80, 0xff, 0xaf, 0xab, 0x80, 0xff, 0xc1, 0xbc, 0x91, 0xff, 0xa2, 0x9b, 0x70, 0xff, 0xa6, 0x9d, 0x73, 0xff, 0xa7, 0x9e, 0x74, 0xff, 0xa4, 0x96, 0x6b, 0xff, 0x9c, 0x87, 0x5f, 0xff, 0xb0, 0xa4, 0x7a, 0xff, 0xc7, 0xc5, 0x9e, 0xff, 0x91, 0x80, 0x5d, 0xff, 0x74, 0x5b, 0x33, 0xff, 0x97, 0x81, 0x57, 0xff, 0x84, 0x67, 0x42, 0xff, 0x8b, 0x75, 0x4e, 0xff, 0x9f, 0x8f, 0x67, 0xff, 0xa2, 0x95, 0x75, 0xff, 0xae, 0xa6, 0x8d, 0xff, 0x93, 0x8d, 0x6c, 0xff, 0xae, 0xa6, 0x8b, 0xff, 0xb5, 0xb3, 0x98, 0xff, 0x9e, 0x9d, 0x8c, 0xff, 0x8f, 0x88, 0x82, 0xff, 0x7a, 0x6e, 0x63, 0xff, 0x57, 0x46, 0x44, 0xff, 0x4a, 0x36, 0x3b, 0xff, 0x47, 0x37, 0x3d, 0xff, 0x42, 0x32, 0x35, 0xff, 0x36, 0x21, 0x24, 0xff, 0x34, 0x20, 0x26, 0xff, 0x2e, 0x1d, 0x1f, 0xff, 0x2a, 0x18, 0x1c, 0xff, 0x1a, 0x05, 0x09, 0xff, 0x3e, 0x34, 0x3b, 0xff, 0x8e, 0x9f, 0xb6, 0xff, 0x8d, 0xa2, 0xbd, 0xff, 0x8e, 0xa0, 0xb7, 0xff, 0x92, 0xa7, 0xbe, 0xff, 0xa4, 0xb7, 0xdc, 0xff, 0xa5, 0xb2, 0xde, 0xff, 0x77, 0x78, 0xa2, 0xff, 0x4a, 0x3f, 0x64, 0xff, 0x35, 0x25, 0x38, 0xff, 0x26, 0x1a, 0x29, 0xff, 0x29, 0x23, 0x4b, 0xff, 0x2a, 0x29, 0x69, 0xff, 0x57, 0x60, 0xa6, 0xff, 0x9b, 0xb0, 0xd5, 0xff, 0xab, 0xc1, 0xd4, 0xff, 0xab, 0xc0, 0xd4, 0xff, 0xae, 0xc2, 0xd5, 0xff, 0xad, 0xc1, 0xd3, 0xff, 0xab, 0xbd, 0xd1, 0xff, 0xaa, 0xbc, 0xd1, 0xff, 0xaa, 0xbc, 0xd5, 0xff, 0x77, 0x84, 0xad, 0xff, 0x43, 0x45, 0x7c, 0xff, 0x23, 0x18, 0x43, 0xff, 0x1d, 0x10, 0x2c, 0xff, 0x24, 0x14, 0x2a, 0xff, 0x2d, 0x1e, 0x32, 0xff, 0x2a, 0x1b, 0x2b, 0xff, 0x24, 0x10, 0x1b, 0xff, 0x28, 0x12, 0x1d, 0xff, 0x27, 0x17, 0x22, 0xff, 0x33, 0x25, 0x38, 0xff, 0x59, 0x4f, 0x70, 0xff, 0x77, 0x75, 0x9f, 0xff, 0x8c, 0x90, 0xb9, 0xff, 0xa7, 0xb1, 0xd3, 0xff, 0xb8, 0xc5, 0xe0, 0xff, 0xbe, 0xcb, 0xe1, 0xff, 0xc7, 0xd5, 0xe0, 0xff, 0xc8, 0xd8, 0xde, 0xff, 0xc4, 0xcf, 0xd7, 0xff, 0xc5, 0xcf, 0xd8, 0xff, 0xc6, 0xd0, 0xda, 0xff, 0xc8, 0xd5, 0xdc, 0xff, 0xc1, 0xcc, 0xd5, 0xff, 0xb0, 0xba, 0xc7, 0xff, 0xa6, 0xb1, 0xbf, 0xff, 0xaa, 0xb6, 0xc2, 0xff, 0xab, 0xb6, 0xc2, 0xff, 0xa5, 0xae, 0xbb, 0xff, 0x9a, 0xa3, 0xb0, 0xff, 0x96, 0x9f, 0xad, 0xff, 0x94, 0x9d, 0xaa, 0xff, 0x94, 0x9d, 0xaa, 0xff, 0x96, 0x9f, 0xb0, 0xff, 0x93, 0x9b, 0xac, 0xff, 0x93, 0x9d, 0xab, 0xff, 0x98, 0xa1, 0xad, 0xff, 0x91, 0x98, 0xa5, 0xff, 0x6d, 0x6f, 0x84, 0xff, 0x42, 0x39, 0x4d, 0xff, 0x33, 0x26, 0x31, 0xff, 0x4e, 0x41, 0x45, 0xff, 0x43, 0x32, 0x35, 0xff, 0x32, 0x1f, 0x21, 0xff, 0x30, 0x1d, 0x1e, 0xff, 0x22, 0x10, 0x12, 0xff, 0x22, 0x11, 0x0e, 0xff, 0x35, 0x25, 0x21, 0xff, 0x34, 0x22, 0x20, 0xff, 0x1f, 0x0b, 0x0b, 0xff, 0x21, 0x0d, 0x11, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x21, 0x10, 0x13, 0xff, 0x25, 0x12, 0x16, 0xff, 0x20, 0x0e, 0x11, 0xff, 0x25, 0x11, 0x13, 0xff, 0x2e, 0x18, 0x1a, 0xff, 0x2c, 0x17, 0x1a, 0xff, 0x2f, 0x1d, 0x1c, 0xff, 0x28, 0x17, 0x15, 0xff, 0x28, 0x14, 0x16, 0xff, 0x31, 0x1b, 0x1f, 0xff, 0x2a, 0x14, 0x16, 0xff, 0x30, 0x17, 0x19, 0xff, 0x42, 0x29, 0x29, 0xff, 0x45, 0x2b, 0x2c, 0xff, 0x4d, 0x33, 0x2f, 0xff, 0x66, 0x52, 0x40, 0xff, 0x8c, 0x79, 0x61, 0xff, 0x6a, 0x53, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe3, 0xe3, 0x09, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc5, 0xc8, 0xab, 0xff, 0xae, 0xaf, 0x88, 0xff, 0xa9, 0xa8, 0x80, 0xff, 0xa8, 0xa3, 0x7a, 0xff, 0xa5, 0xa0, 0x75, 0xff, 0xa0, 0x99, 0x6f, 0xff, 0x9f, 0x98, 0x6d, 0xff, 0x9f, 0x98, 0x6c, 0xff, 0xa1, 0x9a, 0x6d, 0xff, 0xa3, 0x9a, 0x6e, 0xff, 0xa8, 0xa1, 0x75, 0xff, 0xab, 0xa6, 0x7a, 0xff, 0xad, 0xa7, 0x7b, 0xff, 0xb1, 0xac, 0x80, 0xff, 0xb6, 0xb0, 0x84, 0xff, 0xb5, 0xac, 0x81, 0xff, 0xb6, 0xab, 0x7f, 0xff, 0xaa, 0x9d, 0x71, 0xff, 0xa4, 0x95, 0x6a, 0xff, 0xb9, 0xad, 0x84, 0xff, 0xb4, 0xa8, 0x7e, 0xff, 0x9c, 0x8e, 0x64, 0xff, 0x92, 0x7d, 0x59, 0xff, 0x87, 0x6c, 0x4a, 0xff, 0x8e, 0x78, 0x54, 0xff, 0x8a, 0x76, 0x50, 0xff, 0x93, 0x7d, 0x5a, 0xff, 0x88, 0x6d, 0x4e, 0xff, 0x90, 0x7c, 0x55, 0xff, 0x7e, 0x66, 0x3d, 0xff, 0x7d, 0x5f, 0x33, 0xff, 0xa0, 0x99, 0x79, 0xff, 0xc2, 0xc4, 0xa7, 0xff, 0xa6, 0xa1, 0x7f, 0xff, 0x92, 0x8f, 0x7b, 0xff, 0x95, 0x8f, 0x87, 0xff, 0x76, 0x6b, 0x68, 0xff, 0x4f, 0x41, 0x42, 0xff, 0x3f, 0x32, 0x33, 0xff, 0x3c, 0x2d, 0x32, 0xff, 0x3e, 0x30, 0x3c, 0xff, 0x3a, 0x2f, 0x3f, 0xff, 0x3d, 0x2d, 0x33, 0xff, 0x2a, 0x17, 0x18, 0xff, 0x25, 0x13, 0x17, 0xff, 0x2c, 0x1c, 0x21, 0xff, 0x7e, 0x85, 0x98, 0xff, 0x85, 0x8d, 0xae, 0xff, 0x6f, 0x6c, 0x86, 0xff, 0x53, 0x4d, 0x5d, 0xff, 0x57, 0x4c, 0x58, 0xff, 0x61, 0x5a, 0x67, 0xff, 0x65, 0x5f, 0x77, 0xff, 0x69, 0x61, 0x80, 0xff, 0x61, 0x5a, 0x79, 0xff, 0x4d, 0x46, 0x62, 0xff, 0x3d, 0x30, 0x4e, 0xff, 0x2b, 0x20, 0x42, 0xff, 0x25, 0x20, 0x5d, 0xff, 0x69, 0x79, 0xb3, 0xff, 0xb0, 0xc9, 0xdd, 0xff, 0xa9, 0xbe, 0xd1, 0xff, 0xab, 0xbf, 0xd1, 0xff, 0xac, 0xbf, 0xd2, 0xff, 0xac, 0xbe, 0xd1, 0xff, 0xab, 0xbc, 0xd3, 0xff, 0xae, 0xbf, 0xd9, 0xff, 0x54, 0x5b, 0x8c, 0xff, 0x12, 0x0f, 0x47, 0xff, 0x1e, 0x14, 0x2f, 0xff, 0x26, 0x18, 0x28, 0xff, 0x21, 0x12, 0x1f, 0xff, 0x24, 0x14, 0x25, 0xff, 0x2a, 0x1e, 0x37, 0xff, 0x3b, 0x31, 0x50, 0xff, 0x47, 0x41, 0x67, 0xff, 0x60, 0x5e, 0x8b, 0xff, 0x7b, 0x7b, 0xa8, 0xff, 0x8f, 0x94, 0xbf, 0xff, 0x9a, 0xa3, 0xcf, 0xff, 0xa1, 0xaa, 0xd5, 0xff, 0x9b, 0xa6, 0xcd, 0xff, 0x9c, 0xa8, 0xcb, 0xff, 0x9f, 0xa9, 0xcb, 0xff, 0xa7, 0xb2, 0xd2, 0xff, 0xb1, 0xb9, 0xd4, 0xff, 0xb9, 0xbf, 0xd1, 0xff, 0xbe, 0xc8, 0xd3, 0xff, 0xb6, 0xc3, 0xcc, 0xff, 0xb3, 0xc0, 0xca, 0xff, 0xb1, 0xbd, 0xc6, 0xff, 0xa7, 0xb3, 0xbe, 0xff, 0xa5, 0xaf, 0xbd, 0xff, 0xa5, 0xb0, 0xbc, 0xff, 0xa5, 0xaf, 0xbb, 0xff, 0xa2, 0xaa, 0xb7, 0xff, 0x9c, 0xa5, 0xb2, 0xff, 0x99, 0xa2, 0xb1, 0xff, 0x9a, 0xa3, 0xb0, 0xff, 0x98, 0xa1, 0xae, 0xff, 0x9a, 0xa2, 0xb3, 0xff, 0x97, 0x9e, 0xaf, 0xff, 0x91, 0x9b, 0xa8, 0xff, 0x99, 0xa4, 0xad, 0xff, 0x96, 0x9b, 0xa8, 0xff, 0x70, 0x70, 0x88, 0xff, 0x3b, 0x32, 0x49, 0xff, 0x3d, 0x2f, 0x3b, 0xff, 0x4f, 0x41, 0x43, 0xff, 0x43, 0x2f, 0x33, 0xff, 0x3c, 0x29, 0x2a, 0xff, 0x33, 0x21, 0x20, 0xff, 0x29, 0x16, 0x18, 0xff, 0x2e, 0x1c, 0x1c, 0xff, 0x31, 0x1f, 0x1f, 0xff, 0x2b, 0x18, 0x17, 0xff, 0x37, 0x25, 0x23, 0xff, 0x20, 0x0e, 0x10, 0xff, 0x20, 0x0f, 0x11, 0xff, 0x2a, 0x19, 0x1a, 0xff, 0x29, 0x18, 0x19, 0xff, 0x27, 0x15, 0x17, 0xff, 0x2e, 0x19, 0x1c, 0xff, 0x2e, 0x19, 0x1b, 0xff, 0x34, 0x1e, 0x1f, 0xff, 0x30, 0x1c, 0x1c, 0xff, 0x2b, 0x18, 0x19, 0xff, 0x2d, 0x18, 0x18, 0xff, 0x42, 0x2c, 0x27, 0xff, 0x46, 0x30, 0x29, 0xff, 0x3b, 0x26, 0x20, 0xff, 0x39, 0x23, 0x1f, 0xff, 0x30, 0x1a, 0x16, 0xff, 0x33, 0x1e, 0x1d, 0xff, 0x33, 0x1e, 0x1c, 0xff, 0x5d, 0x46, 0x3c, 0xff, 0x92, 0x7b, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xcb, 0xb0, 0xff, 0xae, 0xaf, 0x88, 0xff, 0xa7, 0xa6, 0x7f, 0xff, 0xa6, 0xa1, 0x78, 0xff, 0xa4, 0x9e, 0x73, 0xff, 0x9d, 0x95, 0x6a, 0xff, 0x9a, 0x90, 0x66, 0xff, 0x9c, 0x90, 0x66, 0xff, 0xa0, 0x92, 0x67, 0xff, 0xa3, 0x96, 0x69, 0xff, 0xa5, 0x9b, 0x6e, 0xff, 0xa5, 0x9c, 0x6f, 0xff, 0xac, 0xa2, 0x75, 0xff, 0xad, 0xa3, 0x76, 0xff, 0xa5, 0x99, 0x6d, 0xff, 0xac, 0x9e, 0x72, 0xff, 0xaf, 0xa0, 0x74, 0xff, 0xa5, 0x95, 0x67, 0xff, 0xad, 0x9e, 0x73, 0xff, 0xae, 0xa4, 0x7d, 0xff, 0x8e, 0x7c, 0x56, 0xff, 0x9e, 0x8a, 0x65, 0xff, 0x92, 0x7d, 0x5d, 0xff, 0x73, 0x57, 0x38, 0xff, 0x91, 0x7f, 0x5d, 0xff, 0xaa, 0x9e, 0x79, 0xff, 0x76, 0x61, 0x3c, 0xff, 0x93, 0x7f, 0x5f, 0xff, 0xa2, 0x95, 0x6f, 0xff, 0xaf, 0xa5, 0x80, 0xff, 0xbd, 0xb0, 0x90, 0xff, 0xb5, 0xb6, 0x91, 0xff, 0xb3, 0xac, 0x84, 0xff, 0xa9, 0x9c, 0x76, 0xff, 0xa5, 0xa6, 0x8e, 0xff, 0xa1, 0xa0, 0x96, 0xff, 0x8a, 0x86, 0x7a, 0xff, 0x7f, 0x79, 0x71, 0xff, 0x81, 0x78, 0x73, 0xff, 0x6b, 0x61, 0x62, 0xff, 0x61, 0x63, 0x6d, 0xff, 0x58, 0x59, 0x6b, 0xff, 0x40, 0x30, 0x39, 0xff, 0x28, 0x14, 0x17, 0xff, 0x34, 0x22, 0x27, 0xff, 0x20, 0x0f, 0x11, 0xff, 0x62, 0x5b, 0x64, 0xff, 0x75, 0x6f, 0x7e, 0xff, 0x35, 0x26, 0x31, 0xff, 0x2c, 0x16, 0x19, 0xff, 0x3a, 0x1c, 0x1b, 0xff, 0x36, 0x16, 0x14, 0xff, 0x3c, 0x1c, 0x1f, 0xff, 0x41, 0x24, 0x24, 0xff, 0x3b, 0x23, 0x25, 0xff, 0x4f, 0x3d, 0x50, 0xff, 0x59, 0x4b, 0x6a, 0xff, 0x56, 0x4d, 0x6c, 0xff, 0x31, 0x2e, 0x5d, 0xff, 0x5c, 0x62, 0x94, 0xff, 0xaf, 0xc4, 0xdc, 0xff, 0xaa, 0xc1, 0xd3, 0xff, 0xad, 0xc2, 0xd3, 0xff, 0xb1, 0xc4, 0xd6, 0xff, 0xae, 0xc2, 0xd3, 0xff, 0xa9, 0xbd, 0xcf, 0xff, 0xab, 0xbc, 0xd1, 0xff, 0x78, 0x80, 0xa8, 0xff, 0x25, 0x21, 0x52, 0xff, 0x24, 0x1a, 0x3e, 0xff, 0x33, 0x2b, 0x50, 0xff, 0x41, 0x3c, 0x64, 0xff, 0x57, 0x57, 0x82, 0xff, 0x68, 0x69, 0xa0, 0xff, 0x73, 0x7a, 0xb3, 0xff, 0x78, 0x7e, 0xaf, 0xff, 0x71, 0x71, 0x98, 0xff, 0x6a, 0x64, 0x89, 0xff, 0x57, 0x52, 0x6c, 0xff, 0x54, 0x4e, 0x5f, 0xff, 0x4f, 0x44, 0x58, 0xff, 0x4c, 0x3f, 0x54, 0xff, 0x55, 0x4c, 0x65, 0xff, 0x54, 0x51, 0x72, 0xff, 0x6c, 0x6e, 0x98, 0xff, 0x79, 0x7e, 0xad, 0xff, 0x87, 0x91, 0xbb, 0xff, 0x97, 0xa4, 0xbf, 0xff, 0xa3, 0xb0, 0xc1, 0xff, 0xa6, 0xb0, 0xbf, 0xff, 0xa4, 0xaf, 0xbe, 0xff, 0xa1, 0xac, 0xb9, 0xff, 0xa3, 0xac, 0xb8, 0xff, 0xa3, 0xac, 0xb9, 0xff, 0xa0, 0xa8, 0xb6, 0xff, 0xa0, 0xa9, 0xb6, 0xff, 0x9c, 0xa5, 0xb2, 0xff, 0x9b, 0xa4, 0xb1, 0xff, 0x9d, 0xa6, 0xb3, 0xff, 0x9b, 0xa5, 0xb3, 0xff, 0x98, 0xa1, 0xb3, 0xff, 0x99, 0xa2, 0xb2, 0xff, 0x97, 0xa2, 0xae, 0xff, 0x98, 0xa2, 0xab, 0xff, 0x8c, 0x91, 0xa1, 0xff, 0x6e, 0x6e, 0x87, 0xff, 0x41, 0x38, 0x4f, 0xff, 0x48, 0x3b, 0x47, 0xff, 0x57, 0x49, 0x4a, 0xff, 0x43, 0x2e, 0x30, 0xff, 0x35, 0x23, 0x23, 0xff, 0x36, 0x22, 0x22, 0xff, 0x32, 0x1e, 0x1b, 0xff, 0x2f, 0x1b, 0x1c, 0xff, 0x30, 0x1a, 0x1e, 0xff, 0x26, 0x14, 0x12, 0xff, 0x38, 0x28, 0x24, 0xff, 0x29, 0x17, 0x17, 0xff, 0x31, 0x1b, 0x1e, 0xff, 0x34, 0x20, 0x20, 0xff, 0x2c, 0x1a, 0x19, 0xff, 0x2f, 0x1b, 0x1c, 0xff, 0x2c, 0x16, 0x1a, 0xff, 0x29, 0x14, 0x16, 0xff, 0x40, 0x2c, 0x28, 0xff, 0x36, 0x21, 0x1f, 0xff, 0x2d, 0x17, 0x19, 0xff, 0x2f, 0x1b, 0x17, 0xff, 0x45, 0x2c, 0x23, 0xff, 0x4f, 0x34, 0x2a, 0xff, 0x3e, 0x27, 0x20, 0xff, 0x2f, 0x19, 0x16, 0xff, 0x3d, 0x2a, 0x23, 0xff, 0x3e, 0x2b, 0x23, 0xff, 0x3a, 0x23, 0x24, 0xff, 0x3a, 0x22, 0x21, 0xff, 0x5e, 0x49, 0x41, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xfa, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xcd, 0xb4, 0xff, 0xae, 0xaf, 0x8a, 0xff, 0xa8, 0xa6, 0x7f, 0xff, 0xa3, 0x9f, 0x77, 0xff, 0xa0, 0x99, 0x70, 0xff, 0x9c, 0x91, 0x67, 0xff, 0x95, 0x87, 0x5d, 0xff, 0x99, 0x88, 0x5e, 0xff, 0x9e, 0x8c, 0x62, 0xff, 0x9e, 0x8e, 0x63, 0xff, 0xa1, 0x91, 0x66, 0xff, 0xa5, 0x95, 0x6a, 0xff, 0xa6, 0x98, 0x6d, 0xff, 0x9f, 0x90, 0x63, 0xff, 0xa2, 0x92, 0x64, 0xff, 0xa9, 0x99, 0x6b, 0xff, 0x9e, 0x8d, 0x60, 0xff, 0xa4, 0x95, 0x65, 0xff, 0xb1, 0xa2, 0x74, 0xff, 0x94, 0x7f, 0x5b, 0xff, 0x8e, 0x7a, 0x57, 0xff, 0x9b, 0x88, 0x66, 0xff, 0x7f, 0x68, 0x49, 0xff, 0x8f, 0x7d, 0x5d, 0xff, 0xb3, 0xaa, 0x85, 0xff, 0x91, 0x86, 0x60, 0xff, 0xab, 0xa0, 0x7f, 0xff, 0xac, 0x9f, 0x81, 0xff, 0xa1, 0x9a, 0x76, 0xff, 0xb2, 0xaa, 0x88, 0xff, 0xb2, 0xab, 0x90, 0xff, 0x8f, 0x83, 0x5f, 0xff, 0x96, 0x8c, 0x66, 0xff, 0xca, 0xc8, 0xac, 0xff, 0xb1, 0xad, 0x96, 0xff, 0x93, 0x90, 0x7a, 0xff, 0x88, 0x87, 0x70, 0xff, 0xb4, 0xb8, 0xa6, 0xff, 0xb3, 0xb8, 0xa9, 0xff, 0x97, 0x98, 0x86, 0xff, 0xa6, 0xae, 0x9f, 0xff, 0x84, 0x86, 0x87, 0xff, 0x30, 0x1d, 0x22, 0xff, 0x33, 0x1f, 0x25, 0xff, 0x33, 0x24, 0x2f, 0xff, 0x35, 0x23, 0x25, 0xff, 0x3f, 0x28, 0x26, 0xff, 0x48, 0x35, 0x33, 0xff, 0x20, 0x0e, 0x0d, 0xff, 0x2d, 0x15, 0x17, 0xff, 0x31, 0x15, 0x15, 0xff, 0x3b, 0x1d, 0x17, 0xff, 0x4c, 0x2c, 0x29, 0xff, 0x40, 0x21, 0x23, 0xff, 0x24, 0x08, 0x06, 0xff, 0x25, 0x0a, 0x0a, 0xff, 0x36, 0x1a, 0x18, 0xff, 0x39, 0x26, 0x2e, 0xff, 0x4e, 0x46, 0x79, 0xff, 0x66, 0x71, 0xa9, 0xff, 0xa7, 0xbf, 0xd4, 0xff, 0xb2, 0xc8, 0xda, 0xff, 0xb3, 0xc7, 0xd8, 0xff, 0xb4, 0xc7, 0xd8, 0xff, 0xb1, 0xc3, 0xd4, 0xff, 0xac, 0xc0, 0xd0, 0xff, 0xa0, 0xb5, 0xc6, 0xff, 0x92, 0xa0, 0xbe, 0xff, 0x64, 0x66, 0x96, 0xff, 0x57, 0x56, 0x8c, 0xff, 0x72, 0x75, 0xb0, 0xff, 0x7f, 0x85, 0xc4, 0xff, 0x7a, 0x82, 0xc2, 0xff, 0x83, 0x8a, 0xc0, 0xff, 0x7c, 0x7c, 0xa6, 0xff, 0x58, 0x4d, 0x67, 0xff, 0x37, 0x23, 0x2f, 0xff, 0x28, 0x11, 0x16, 0xff, 0x29, 0x0f, 0x11, 0xff, 0x27, 0x0b, 0x0b, 0xff, 0x28, 0x0d, 0x0b, 0xff, 0x2e, 0x13, 0x11, 0xff, 0x26, 0x0d, 0x0b, 0xff, 0x23, 0x0f, 0x13, 0xff, 0x24, 0x12, 0x1d, 0xff, 0x2f, 0x21, 0x36, 0xff, 0x47, 0x40, 0x67, 0xff, 0x55, 0x55, 0x88, 0xff, 0x6b, 0x6e, 0x9f, 0xff, 0x7d, 0x80, 0xa8, 0xff, 0x88, 0x8f, 0xae, 0xff, 0x8e, 0x97, 0xae, 0xff, 0x94, 0x9c, 0xac, 0xff, 0x98, 0xa0, 0xae, 0xff, 0x98, 0xa0, 0xae, 0xff, 0x98, 0xa0, 0xae, 0xff, 0x9b, 0xa3, 0xb1, 0xff, 0x9f, 0xa7, 0xb3, 0xff, 0x9c, 0xa6, 0xb3, 0xff, 0x9c, 0xa7, 0xb7, 0xff, 0x9a, 0xa4, 0xb6, 0xff, 0x9c, 0xa8, 0xb5, 0xff, 0x9c, 0xa9, 0xb5, 0xff, 0x93, 0x9c, 0xa9, 0xff, 0x8e, 0x91, 0xa5, 0xff, 0x73, 0x73, 0x8c, 0xff, 0x55, 0x4e, 0x60, 0xff, 0x4b, 0x40, 0x4b, 0xff, 0x5d, 0x4e, 0x4e, 0xff, 0x4c, 0x37, 0x36, 0xff, 0x2e, 0x1c, 0x1e, 0xff, 0x2b, 0x17, 0x1a, 0xff, 0x49, 0x37, 0x2e, 0xff, 0x35, 0x22, 0x20, 0xff, 0x35, 0x22, 0x20, 0xff, 0x42, 0x30, 0x28, 0xff, 0x2a, 0x15, 0x15, 0xff, 0x33, 0x1e, 0x20, 0xff, 0x31, 0x1c, 0x20, 0xff, 0x25, 0x11, 0x15, 0xff, 0x2c, 0x17, 0x19, 0xff, 0x31, 0x1b, 0x1d, 0xff, 0x26, 0x10, 0x13, 0xff, 0x34, 0x1e, 0x1e, 0xff, 0x37, 0x23, 0x20, 0xff, 0x35, 0x1f, 0x18, 0xff, 0x49, 0x32, 0x27, 0xff, 0x34, 0x1f, 0x1b, 0xff, 0x37, 0x20, 0x1d, 0xff, 0x3d, 0x26, 0x20, 0xff, 0x39, 0x22, 0x1f, 0xff, 0x32, 0x1c, 0x1b, 0xff, 0x34, 0x20, 0x21, 0xff, 0x3e, 0x2a, 0x27, 0xff, 0x3f, 0x29, 0x22, 0xff, 0x24, 0x0d, 0x08, 0xff, 0x3e, 0x2a, 0x2a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xff, 0xf6, 0xed, 0xe6, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xed, 0xe7, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf5, 0xed, 0xe7, 0xd6, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xd1, 0xbc, 0xff, 0xb1, 0xb2, 0x90, 0xff, 0xa5, 0xa3, 0x7c, 0xff, 0xa2, 0x9c, 0x74, 0xff, 0x9f, 0x94, 0x6c, 0xff, 0x9a, 0x8d, 0x64, 0xff, 0x94, 0x85, 0x5b, 0xff, 0x93, 0x81, 0x57, 0xff, 0x98, 0x85, 0x5a, 0xff, 0x98, 0x86, 0x5b, 0xff, 0x9a, 0x89, 0x5d, 0xff, 0x9a, 0x89, 0x5c, 0xff, 0xa0, 0x8f, 0x63, 0xff, 0xab, 0x9a, 0x6e, 0xff, 0xae, 0x9e, 0x70, 0xff, 0x9f, 0x8f, 0x60, 0xff, 0x9e, 0x8e, 0x5f, 0xff, 0xb3, 0xa4, 0x75, 0xff, 0xac, 0x9c, 0x70, 0xff, 0x8e, 0x79, 0x51, 0xff, 0x93, 0x7d, 0x59, 0xff, 0x91, 0x7d, 0x5c, 0xff, 0x82, 0x6f, 0x4e, 0xff, 0xa7, 0x99, 0x74, 0xff, 0x98, 0x8e, 0x6a, 0xff, 0x7d, 0x6f, 0x51, 0xff, 0xb1, 0xa5, 0x8a, 0xff, 0x9c, 0x8f, 0x75, 0xff, 0x75, 0x61, 0x42, 0xff, 0x87, 0x72, 0x4e, 0xff, 0x91, 0x7e, 0x58, 0xff, 0x88, 0x73, 0x56, 0xff, 0xa1, 0x9a, 0x81, 0xff, 0x9c, 0x97, 0x76, 0xff, 0xa9, 0xa1, 0x81, 0xff, 0xaa, 0xa6, 0x8c, 0xff, 0x96, 0x94, 0x7c, 0xff, 0x8d, 0x8f, 0x7e, 0xff, 0xb4, 0xb7, 0xaa, 0xff, 0xae, 0xb3, 0xa2, 0xff, 0xc0, 0xc4, 0xaf, 0xff, 0x82, 0x7b, 0x75, 0xff, 0x2c, 0x1a, 0x1e, 0xff, 0x36, 0x23, 0x2b, 0xff, 0x30, 0x23, 0x2d, 0xff, 0x32, 0x1e, 0x21, 0xff, 0x3d, 0x2d, 0x2d, 0xff, 0x4b, 0x41, 0x46, 0xff, 0x31, 0x13, 0x19, 0xff, 0x2c, 0x12, 0x0e, 0xff, 0x56, 0x48, 0x41, 0xff, 0xa9, 0xa5, 0x8e, 0xff, 0xb8, 0xac, 0x96, 0xff, 0x6c, 0x52, 0x4c, 0xff, 0x56, 0x3e, 0x3e, 0xff, 0x54, 0x36, 0x34, 0xff, 0x2f, 0x12, 0x13, 0xff, 0x2b, 0x0b, 0x07, 0xff, 0x40, 0x26, 0x31, 0xff, 0x60, 0x63, 0x95, 0xff, 0x98, 0xb0, 0xd2, 0xff, 0xc0, 0xda, 0xe7, 0xff, 0xb7, 0xcf, 0xde, 0xff, 0xb3, 0xcc, 0xdd, 0xff, 0xb4, 0xca, 0xdb, 0xff, 0xb2, 0xc6, 0xd6, 0xff, 0xad, 0xbf, 0xcc, 0xff, 0xa1, 0xaf, 0xc6, 0xff, 0x85, 0x8f, 0xbb, 0xff, 0x74, 0x7a, 0xb6, 0xff, 0x79, 0x7c, 0xbb, 0xff, 0x79, 0x7e, 0xba, 0xff, 0x85, 0x8a, 0xbe, 0xff, 0x6c, 0x68, 0x8c, 0xff, 0x4d, 0x3b, 0x49, 0xff, 0x2c, 0x16, 0x16, 0xff, 0x2d, 0x13, 0x0c, 0xff, 0x36, 0x1a, 0x13, 0xff, 0x24, 0x07, 0x09, 0xff, 0x27, 0x07, 0x0d, 0xff, 0x42, 0x29, 0x25, 0xff, 0x3d, 0x28, 0x22, 0xff, 0x41, 0x2a, 0x26, 0xff, 0x39, 0x21, 0x1b, 0xff, 0x29, 0x10, 0x09, 0xff, 0x31, 0x17, 0x14, 0xff, 0x2b, 0x13, 0x14, 0xff, 0x33, 0x22, 0x34, 0xff, 0x43, 0x3d, 0x68, 0xff, 0x4c, 0x51, 0x83, 0xff, 0x5e, 0x62, 0x96, 0xff, 0x84, 0x88, 0xae, 0xff, 0x8a, 0x90, 0xa6, 0xff, 0x8a, 0x91, 0xa2, 0xff, 0x93, 0x9c, 0xab, 0xff, 0x97, 0xa1, 0xb0, 0xff, 0x9b, 0xa6, 0xb2, 0xff, 0xa0, 0xaa, 0xb7, 0xff, 0xa0, 0xaa, 0xb8, 0xff, 0x9c, 0xa6, 0xb8, 0xff, 0x95, 0x9f, 0xb2, 0xff, 0x94, 0x9f, 0xad, 0xff, 0x98, 0xa2, 0xae, 0xff, 0x94, 0x9c, 0xad, 0xff, 0x90, 0x96, 0xab, 0xff, 0x65, 0x66, 0x7c, 0xff, 0x3a, 0x33, 0x43, 0xff, 0x58, 0x4b, 0x55, 0xff, 0x4f, 0x40, 0x41, 0xff, 0x45, 0x31, 0x31, 0xff, 0x36, 0x24, 0x25, 0xff, 0x2b, 0x17, 0x1a, 0xff, 0x3e, 0x2a, 0x24, 0xff, 0x63, 0x50, 0x47, 0xff, 0x3a, 0x27, 0x21, 0xff, 0x4e, 0x3d, 0x33, 0xff, 0x34, 0x1f, 0x1f, 0xff, 0x24, 0x0f, 0x15, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x2d, 0x1b, 0x1a, 0xff, 0x3b, 0x26, 0x26, 0xff, 0x20, 0x0c, 0x0f, 0xff, 0x3c, 0x2b, 0x28, 0xff, 0x3b, 0x26, 0x28, 0xff, 0x37, 0x1f, 0x1e, 0xff, 0x51, 0x3b, 0x2c, 0xff, 0x3d, 0x27, 0x1f, 0xff, 0x3d, 0x27, 0x23, 0xff, 0x44, 0x2f, 0x29, 0xff, 0x37, 0x21, 0x1c, 0xff, 0x38, 0x22, 0x20, 0xff, 0x30, 0x1a, 0x1a, 0xff, 0x25, 0x0c, 0x10, 0xff, 0x20, 0x06, 0x07, 0xff, 0x39, 0x25, 0x1b, 0xff, 0x4a, 0x37, 0x2b, 0xff, 0x65, 0x55, 0x50, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xeb, 0xeb, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xc6, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0xd8, 0xc7, 0xff, 0xb4, 0xb5, 0x95, 0xff, 0xa5, 0xa2, 0x7b, 0xff, 0xa2, 0x98, 0x71, 0xff, 0xa0, 0x92, 0x6b, 0xff, 0x98, 0x89, 0x61, 0xff, 0x94, 0x84, 0x5a, 0xff, 0x93, 0x80, 0x56, 0xff, 0x91, 0x7d, 0x52, 0xff, 0x8d, 0x79, 0x4f, 0xff, 0x91, 0x80, 0x54, 0xff, 0xa3, 0x93, 0x64, 0xff, 0xae, 0x9e, 0x6f, 0xff, 0xac, 0x9c, 0x6f, 0xff, 0x9d, 0x8d, 0x5f, 0xff, 0x90, 0x80, 0x53, 0xff, 0x9e, 0x8c, 0x5f, 0xff, 0xa3, 0x90, 0x65, 0xff, 0xa1, 0x90, 0x66, 0xff, 0xa6, 0x96, 0x6b, 0xff, 0x94, 0x7f, 0x59, 0xff, 0x7b, 0x65, 0x43, 0xff, 0x92, 0x81, 0x5e, 0xff, 0x99, 0x8c, 0x66, 0xff, 0x9b, 0x8e, 0x6e, 0xff, 0x91, 0x82, 0x67, 0xff, 0x84, 0x71, 0x52, 0xff, 0x6c, 0x55, 0x39, 0xff, 0x76, 0x5f, 0x43, 0xff, 0x8f, 0x7a, 0x57, 0xff, 0x92, 0x80, 0x5c, 0xff, 0x9e, 0x90, 0x6f, 0xff, 0x90, 0x7d, 0x57, 0xff, 0x8c, 0x81, 0x61, 0xff, 0xb4, 0xb9, 0xa1, 0xff, 0xb8, 0xb6, 0xa3, 0xff, 0xa9, 0xa6, 0x94, 0xff, 0x97, 0x97, 0x8a, 0xff, 0x94, 0x94, 0x8a, 0xff, 0x98, 0x9b, 0x8f, 0xff, 0xa2, 0xa1, 0x93, 0xff, 0x52, 0x44, 0x45, 0xff, 0x34, 0x22, 0x29, 0xff, 0x35, 0x23, 0x2b, 0xff, 0x35, 0x27, 0x2f, 0xff, 0x1b, 0x07, 0x0a, 0xff, 0x51, 0x48, 0x50, 0xff, 0xaf, 0xc2, 0xcf, 0xff, 0x7d, 0x79, 0x83, 0xff, 0x44, 0x2c, 0x25, 0xff, 0xa4, 0xa0, 0x96, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0xd3, 0xc3, 0xff, 0x5b, 0x3b, 0x39, 0xff, 0x68, 0x49, 0x47, 0xff, 0x53, 0x2f, 0x25, 0xff, 0x36, 0x16, 0x17, 0xff, 0x34, 0x1b, 0x22, 0xff, 0x23, 0x06, 0x03, 0xff, 0x2e, 0x1a, 0x2a, 0xff, 0x88, 0x95, 0xb8, 0xff, 0xc4, 0xe0, 0xf0, 0xff, 0xbf, 0xd7, 0xe5, 0xff, 0xb6, 0xd2, 0xe1, 0xff, 0xb7, 0xd0, 0xdf, 0xff, 0xb9, 0xcc, 0xdc, 0xff, 0xb9, 0xc8, 0xd3, 0xff, 0xa8, 0xb5, 0xc9, 0xff, 0x8c, 0x97, 0xc2, 0xff, 0x6f, 0x70, 0xaa, 0xff, 0x6f, 0x6b, 0xa7, 0xff, 0x75, 0x75, 0xb2, 0xff, 0x8d, 0x96, 0xce, 0xff, 0x76, 0x76, 0x9c, 0xff, 0x31, 0x15, 0x19, 0xff, 0x30, 0x12, 0x0e, 0xff, 0x24, 0x07, 0x09, 0xff, 0x3c, 0x1d, 0x1e, 0xff, 0x6c, 0x57, 0x4c, 0xff, 0x67, 0x52, 0x44, 0xff, 0x42, 0x2d, 0x2a, 0xff, 0x44, 0x2e, 0x2d, 0xff, 0x3f, 0x25, 0x21, 0xff, 0x38, 0x20, 0x1f, 0xff, 0x3c, 0x25, 0x22, 0xff, 0x35, 0x1c, 0x1a, 0xff, 0x2d, 0x10, 0x0e, 0xff, 0x2f, 0x16, 0x14, 0xff, 0x28, 0x18, 0x25, 0xff, 0x37, 0x2f, 0x4a, 0xff, 0x32, 0x2e, 0x4c, 0xff, 0x4f, 0x4e, 0x67, 0xff, 0x7f, 0x82, 0x9c, 0xff, 0x84, 0x8b, 0xa0, 0xff, 0x8f, 0x99, 0xab, 0xff, 0x9a, 0xa5, 0xb4, 0xff, 0xa0, 0xab, 0xb7, 0xff, 0x9f, 0xaa, 0xb8, 0xff, 0x9e, 0xa8, 0xb9, 0xff, 0x99, 0xa4, 0xb6, 0xff, 0x99, 0xa4, 0xb6, 0xff, 0x91, 0x9a, 0xab, 0xff, 0x9f, 0xa7, 0xb4, 0xff, 0x98, 0xa1, 0xb1, 0xff, 0x86, 0x8e, 0xa3, 0xff, 0x5c, 0x5c, 0x72, 0xff, 0x3e, 0x34, 0x48, 0xff, 0x53, 0x43, 0x4e, 0xff, 0x48, 0x37, 0x3c, 0xff, 0x35, 0x24, 0x25, 0xff, 0x35, 0x23, 0x24, 0xff, 0x32, 0x1e, 0x22, 0xff, 0x31, 0x1c, 0x1c, 0xff, 0x44, 0x30, 0x27, 0xff, 0x33, 0x20, 0x1c, 0xff, 0x55, 0x45, 0x3c, 0xff, 0x44, 0x32, 0x2b, 0xff, 0x21, 0x0c, 0x12, 0xff, 0x33, 0x21, 0x21, 0xff, 0x46, 0x33, 0x30, 0xff, 0x2e, 0x19, 0x1a, 0xff, 0x28, 0x16, 0x19, 0xff, 0x4a, 0x3b, 0x37, 0xff, 0x34, 0x21, 0x24, 0xff, 0x43, 0x2f, 0x31, 0xff, 0x5d, 0x4a, 0x37, 0xff, 0x2d, 0x18, 0x16, 0xff, 0x4e, 0x37, 0x32, 0xff, 0x63, 0x51, 0x3c, 0xff, 0x31, 0x1d, 0x13, 0xff, 0x36, 0x1e, 0x1f, 0xff, 0x2d, 0x18, 0x17, 0xff, 0x34, 0x26, 0x1e, 0xff, 0x54, 0x48, 0x3d, 0xff, 0x84, 0x7c, 0x71, 0xff, 0x83, 0x7d, 0x69, 0xff, 0xac, 0xa8, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xfa, 0xf5, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe0, 0xd1, 0xff, 0xb9, 0xb9, 0x9b, 0xff, 0xa6, 0xa2, 0x7b, 0xff, 0xa0, 0x97, 0x70, 0xff, 0x9c, 0x8f, 0x67, 0xff, 0x92, 0x83, 0x5b, 0xff, 0x8f, 0x7f, 0x55, 0xff, 0x91, 0x7f, 0x55, 0xff, 0x96, 0x82, 0x57, 0xff, 0x9a, 0x87, 0x5c, 0xff, 0xa3, 0x92, 0x67, 0xff, 0xa8, 0x98, 0x6b, 0xff, 0x9e, 0x8e, 0x5f, 0xff, 0x96, 0x86, 0x56, 0xff, 0x97, 0x87, 0x59, 0xff, 0x9a, 0x87, 0x5c, 0xff, 0x91, 0x7c, 0x54, 0xff, 0x8e, 0x7a, 0x52, 0xff, 0x8c, 0x7b, 0x51, 0xff, 0x9f, 0x8d, 0x65, 0xff, 0x9f, 0x8d, 0x67, 0xff, 0x76, 0x61, 0x3d, 0xff, 0x85, 0x71, 0x51, 0xff, 0xb3, 0xad, 0x8b, 0xff, 0x9d, 0x93, 0x73, 0xff, 0x6e, 0x57, 0x3c, 0xff, 0x7a, 0x63, 0x44, 0xff, 0x83, 0x6d, 0x4e, 0xff, 0x83, 0x6e, 0x4f, 0xff, 0x8d, 0x7b, 0x59, 0xff, 0x90, 0x7e, 0x57, 0xff, 0x89, 0x78, 0x54, 0xff, 0x93, 0x8d, 0x6c, 0xff, 0xbf, 0xc5, 0xac, 0xff, 0xc4, 0xc9, 0xb5, 0xff, 0x93, 0x8d, 0x79, 0xff, 0x94, 0x8c, 0x79, 0xff, 0xa1, 0x9d, 0x8d, 0xff, 0x8a, 0x89, 0x79, 0xff, 0xa2, 0xa2, 0x93, 0xff, 0x89, 0x82, 0x7b, 0xff, 0x36, 0x27, 0x2b, 0xff, 0x39, 0x27, 0x30, 0xff, 0x3c, 0x2b, 0x33, 0xff, 0x33, 0x26, 0x2d, 0xff, 0x14, 0x00, 0x05, 0xff, 0x63, 0x5b, 0x68, 0xff, 0xd5, 0xed, 0xff, 0xff, 0xc8, 0xe2, 0xee, 0xff, 0x4f, 0x36, 0x38, 0xff, 0x97, 0x90, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0xda, 0xd0, 0xff, 0x31, 0x1c, 0x29, 0xff, 0x37, 0x1a, 0x2b, 0xff, 0x43, 0x22, 0x27, 0xff, 0x52, 0x35, 0x3c, 0xff, 0x24, 0x11, 0x1e, 0xff, 0x50, 0x3b, 0x40, 0xff, 0x4d, 0x35, 0x3d, 0xff, 0x59, 0x5b, 0x71, 0xff, 0xcc, 0xe6, 0xf8, 0xff, 0xc4, 0xda, 0xe8, 0xff, 0xc0, 0xda, 0xe4, 0xff, 0xc0, 0xd8, 0xe2, 0xff, 0xbd, 0xd1, 0xdd, 0xff, 0xb7, 0xc7, 0xd3, 0xff, 0xa9, 0xb6, 0xc9, 0xff, 0x89, 0x92, 0xbb, 0xff, 0x74, 0x71, 0xa6, 0xff, 0x71, 0x65, 0x95, 0xff, 0x6b, 0x69, 0x9f, 0xff, 0x90, 0x9b, 0xd4, 0xff, 0x79, 0x77, 0x99, 0xff, 0x35, 0x15, 0x15, 0xff, 0x2a, 0x09, 0x0c, 0xff, 0x50, 0x3f, 0x4e, 0xff, 0xa4, 0x97, 0x9b, 0xff, 0xf8, 0xf2, 0xe0, 0xff, 0xa5, 0x99, 0x85, 0xff, 0x1e, 0x03, 0x06, 0xff, 0x3f, 0x22, 0x26, 0xff, 0x30, 0x17, 0x13, 0xff, 0x3e, 0x24, 0x27, 0xff, 0x2a, 0x12, 0x14, 0xff, 0x4d, 0x37, 0x2e, 0xff, 0x5f, 0x4c, 0x45, 0xff, 0x28, 0x13, 0x17, 0xff, 0x32, 0x1f, 0x19, 0xff, 0x40, 0x28, 0x29, 0xff, 0x32, 0x1f, 0x1f, 0xff, 0x40, 0x35, 0x43, 0xff, 0x6d, 0x6a, 0x8b, 0xff, 0x85, 0x8d, 0xa7, 0xff, 0x96, 0xa2, 0xb2, 0xff, 0x9f, 0xaa, 0xb9, 0xff, 0xa5, 0xb0, 0xbe, 0xff, 0xa7, 0xb2, 0xc1, 0xff, 0xa3, 0xb0, 0xc1, 0xff, 0x9f, 0xab, 0xbe, 0xff, 0x9d, 0xa7, 0xba, 0xff, 0x90, 0x99, 0xab, 0xff, 0x9a, 0xa3, 0xb4, 0xff, 0x99, 0xa4, 0xb4, 0xff, 0x8c, 0x93, 0xa8, 0xff, 0x54, 0x52, 0x6c, 0xff, 0x4c, 0x43, 0x5c, 0xff, 0x48, 0x3a, 0x48, 0xff, 0x45, 0x35, 0x3b, 0xff, 0x42, 0x31, 0x32, 0xff, 0x29, 0x16, 0x17, 0xff, 0x25, 0x12, 0x17, 0xff, 0x38, 0x26, 0x27, 0xff, 0x3c, 0x2b, 0x28, 0xff, 0x3b, 0x2a, 0x28, 0xff, 0x58, 0x47, 0x3f, 0xff, 0x54, 0x41, 0x37, 0xff, 0x2b, 0x17, 0x15, 0xff, 0x3e, 0x29, 0x2a, 0xff, 0x36, 0x21, 0x24, 0xff, 0x2c, 0x18, 0x1d, 0xff, 0x51, 0x3f, 0x3e, 0xff, 0x52, 0x41, 0x3e, 0xff, 0x37, 0x25, 0x2b, 0xff, 0x59, 0x4c, 0x58, 0xff, 0x60, 0x50, 0x44, 0xff, 0x57, 0x41, 0x34, 0xff, 0x4a, 0x37, 0x2d, 0xff, 0x5c, 0x46, 0x35, 0xff, 0x4b, 0x37, 0x28, 0xff, 0x46, 0x35, 0x26, 0xff, 0x60, 0x51, 0x48, 0xff, 0x75, 0x68, 0x5d, 0xff, 0x88, 0x7d, 0x69, 0xff, 0xb0, 0xac, 0x97, 0xff, 0xb5, 0xb7, 0xa9, 0xff, 0xc4, 0xc6, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0x8c, 0xf6, 0xed, 0xe7, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0xe6, 0xdc, 0xff, 0xbd, 0xbc, 0xa1, 0xff, 0xa2, 0x9e, 0x77, 0xff, 0xa0, 0x96, 0x6f, 0xff, 0x96, 0x89, 0x61, 0xff, 0x9c, 0x8e, 0x65, 0xff, 0xa4, 0x94, 0x6a, 0xff, 0x9b, 0x89, 0x5f, 0xff, 0x98, 0x85, 0x59, 0xff, 0x96, 0x83, 0x59, 0xff, 0x93, 0x82, 0x57, 0xff, 0x90, 0x80, 0x55, 0xff, 0x96, 0x85, 0x5a, 0xff, 0x9a, 0x8a, 0x5d, 0xff, 0x98, 0x86, 0x5a, 0xff, 0x98, 0x84, 0x59, 0xff, 0x90, 0x7b, 0x52, 0xff, 0x8c, 0x79, 0x50, 0xff, 0xa0, 0x8e, 0x66, 0xff, 0x87, 0x74, 0x50, 0xff, 0x97, 0x85, 0x5f, 0xff, 0x89, 0x76, 0x50, 0xff, 0x8f, 0x81, 0x5e, 0xff, 0xb4, 0xae, 0x8a, 0xff, 0x9b, 0x90, 0x6e, 0xff, 0x77, 0x61, 0x43, 0xff, 0x8c, 0x7a, 0x59, 0xff, 0x95, 0x85, 0x63, 0xff, 0x7e, 0x6c, 0x4b, 0xff, 0x87, 0x73, 0x51, 0xff, 0x92, 0x7e, 0x5a, 0xff, 0xb0, 0xb2, 0x96, 0xff, 0xb7, 0xbf, 0xa6, 0xff, 0xb2, 0xaf, 0x95, 0xff, 0xa5, 0xa2, 0x87, 0xff, 0x95, 0x8e, 0x70, 0xff, 0x97, 0x8a, 0x73, 0xff, 0x89, 0x83, 0x71, 0xff, 0xa0, 0xa0, 0x8c, 0xff, 0x9d, 0x9c, 0x90, 0xff, 0x56, 0x49, 0x4f, 0xff, 0x43, 0x33, 0x3c, 0xff, 0x3f, 0x2f, 0x3a, 0xff, 0x38, 0x28, 0x33, 0xff, 0x2c, 0x1c, 0x24, 0xff, 0x16, 0x02, 0x08, 0xff, 0x72, 0x74, 0x85, 0xff, 0xca, 0xe3, 0xf6, 0xff, 0xbc, 0xce, 0xd5, 0xff, 0x49, 0x32, 0x2b, 0xff, 0x49, 0x32, 0x30, 0xff, 0xdf, 0xec, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xcf, 0xc7, 0xff, 0x53, 0x41, 0x51, 0xff, 0x41, 0x29, 0x3e, 0xff, 0x2b, 0x12, 0x1e, 0xff, 0x49, 0x32, 0x39, 0xff, 0x80, 0x6e, 0x7c, 0xff, 0x3e, 0x2d, 0x41, 0xff, 0x45, 0x3f, 0x47, 0xff, 0xcb, 0xe5, 0xf2, 0xff, 0xc8, 0xe2, 0xef, 0xff, 0xc5, 0xe1, 0xe7, 0xff, 0xc4, 0xdd, 0xe5, 0xff, 0xc1, 0xd3, 0xe0, 0xff, 0xb9, 0xca, 0xd6, 0xff, 0xa9, 0xb6, 0xc4, 0xff, 0x9a, 0xa2, 0xbc, 0xff, 0x89, 0x89, 0xaa, 0xff, 0x70, 0x68, 0x8b, 0xff, 0x79, 0x79, 0xa3, 0xff, 0x95, 0x98, 0xbd, 0xff, 0x45, 0x30, 0x38, 0xff, 0x07, 0x00, 0x00, 0xff, 0x4d, 0x41, 0x5e, 0xff, 0xda, 0xdf, 0xf2, 0xff, 0xe9, 0xf6, 0xf1, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xa4, 0x9e, 0x9a, 0xff, 0x32, 0x13, 0x18, 0xff, 0x46, 0x28, 0x2b, 0xff, 0x2c, 0x16, 0x18, 0xff, 0x40, 0x29, 0x2f, 0xff, 0x27, 0x11, 0x12, 0xff, 0xb7, 0xa9, 0xa0, 0xff, 0x8e, 0x80, 0x74, 0xff, 0x2a, 0x10, 0x0b, 0xff, 0x3d, 0x22, 0x1e, 0xff, 0x3b, 0x27, 0x25, 0xff, 0x49, 0x3b, 0x40, 0xff, 0x67, 0x62, 0x75, 0xff, 0x7b, 0x7d, 0x9e, 0xff, 0x93, 0x9d, 0xb3, 0xff, 0xa3, 0xb1, 0xbf, 0xff, 0xa9, 0xb7, 0xc4, 0xff, 0xad, 0xba, 0xc7, 0xff, 0xa9, 0xb6, 0xc5, 0xff, 0xa7, 0xb4, 0xc7, 0xff, 0xa4, 0xb2, 0xc4, 0xff, 0x9f, 0xab, 0xbc, 0xff, 0x8f, 0x96, 0xa9, 0xff, 0x8c, 0x95, 0xa9, 0xff, 0x9e, 0xaa, 0xba, 0xff, 0x89, 0x8f, 0xa2, 0xff, 0x48, 0x46, 0x5c, 0xff, 0x5d, 0x55, 0x6c, 0xff, 0x50, 0x45, 0x55, 0xff, 0x43, 0x35, 0x3d, 0xff, 0x45, 0x35, 0x37, 0xff, 0x3b, 0x27, 0x2a, 0xff, 0x34, 0x21, 0x21, 0xff, 0x3a, 0x29, 0x29, 0xff, 0x58, 0x47, 0x47, 0xff, 0x49, 0x35, 0x30, 0xff, 0x38, 0x24, 0x1e, 0xff, 0x73, 0x63, 0x59, 0xff, 0x4f, 0x3e, 0x36, 0xff, 0x24, 0x0f, 0x15, 0xff, 0x30, 0x1c, 0x26, 0xff, 0x43, 0x37, 0x36, 0xff, 0x82, 0x78, 0x6a, 0xff, 0x6b, 0x5d, 0x5b, 0xff, 0x33, 0x23, 0x2f, 0xff, 0x50, 0x49, 0x60, 0xff, 0x62, 0x52, 0x53, 0xff, 0x6a, 0x52, 0x37, 0xff, 0x72, 0x65, 0x54, 0xff, 0x57, 0x40, 0x30, 0xff, 0x54, 0x3d, 0x30, 0xff, 0x5c, 0x47, 0x38, 0xff, 0x46, 0x34, 0x2d, 0xff, 0x39, 0x25, 0x26, 0xff, 0x3d, 0x2d, 0x22, 0xff, 0x4c, 0x39, 0x30, 0xff, 0x47, 0x32, 0x2f, 0xff, 0xaa, 0xa2, 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf5, 0xec, 0xe7, 0x6b, 0xf6, 0xed, 0xe7, 0xff, 0xf8, 0xf1, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf0, 0xe9, 0xff, 0xc2, 0xc0, 0xa7, 0xff, 0xa1, 0x9c, 0x76, 0xff, 0x99, 0x8e, 0x67, 0xff, 0xa6, 0x99, 0x72, 0xff, 0xae, 0x9f, 0x77, 0xff, 0x98, 0x89, 0x5e, 0xff, 0x91, 0x7f, 0x55, 0xff, 0x8c, 0x79, 0x4f, 0xff, 0x8b, 0x77, 0x4f, 0xff, 0x8b, 0x79, 0x4f, 0xff, 0x92, 0x81, 0x56, 0xff, 0x9a, 0x89, 0x5e, 0xff, 0x9b, 0x89, 0x60, 0xff, 0x99, 0x85, 0x5c, 0xff, 0x96, 0x83, 0x58, 0xff, 0x91, 0x7d, 0x52, 0xff, 0x8e, 0x7b, 0x51, 0xff, 0xa7, 0x95, 0x6f, 0xff, 0x8b, 0x78, 0x54, 0xff, 0x92, 0x82, 0x5c, 0xff, 0xb9, 0xab, 0x82, 0xff, 0xb4, 0xab, 0x85, 0xff, 0xae, 0xa8, 0x85, 0xff, 0x8f, 0x80, 0x5f, 0xff, 0xa2, 0x93, 0x6c, 0xff, 0xb5, 0xb1, 0x8c, 0xff, 0x94, 0x87, 0x69, 0xff, 0x84, 0x6d, 0x4c, 0xff, 0x8a, 0x7a, 0x5a, 0xff, 0xb2, 0xb1, 0x9c, 0xff, 0xba, 0xba, 0xa0, 0xff, 0xb2, 0xac, 0x8e, 0xff, 0x98, 0x92, 0x75, 0xff, 0xa6, 0xa5, 0x87, 0xff, 0xb9, 0xb5, 0x99, 0xff, 0x98, 0x8d, 0x78, 0xff, 0x96, 0x99, 0x89, 0xff, 0xbb, 0xc3, 0xb5, 0xff, 0x74, 0x73, 0x70, 0xff, 0x3a, 0x2d, 0x39, 0xff, 0x47, 0x39, 0x45, 0xff, 0x43, 0x38, 0x44, 0xff, 0x32, 0x21, 0x2e, 0xff, 0x2f, 0x1f, 0x2a, 0xff, 0x25, 0x12, 0x1d, 0xff, 0x86, 0x8d, 0x9d, 0xff, 0xc5, 0xe5, 0xf1, 0xff, 0xbb, 0xcc, 0xce, 0xff, 0x78, 0x72, 0x65, 0xff, 0x44, 0x28, 0x1d, 0xff, 0x49, 0x32, 0x2c, 0xff, 0x7b, 0x71, 0x65, 0xff, 0xb4, 0xb1, 0x9d, 0xff, 0x96, 0x87, 0x78, 0xff, 0x5c, 0x41, 0x3b, 0xff, 0x5a, 0x41, 0x39, 0xff, 0x66, 0x51, 0x47, 0xff, 0x61, 0x4f, 0x4a, 0xff, 0x23, 0x0c, 0x09, 0xff, 0x62, 0x5d, 0x60, 0xff, 0xd4, 0xf1, 0xfc, 0xff, 0xcb, 0xe6, 0xf0, 0xff, 0xc9, 0xe4, 0xeb, 0xff, 0xc3, 0xde, 0xe7, 0xff, 0xc1, 0xd6, 0xe2, 0xff, 0xba, 0xcc, 0xd8, 0xff, 0xac, 0xb9, 0xc6, 0xff, 0xa6, 0xb1, 0xbd, 0xff, 0x9e, 0xa1, 0xaf, 0xff, 0x8b, 0x88, 0xa3, 0xff, 0x9a, 0x9d, 0xbc, 0xff, 0x53, 0x4c, 0x61, 0xff, 0x01, 0x00, 0x00, 0xff, 0x3e, 0x2d, 0x34, 0xff, 0x99, 0x95, 0xcf, 0xff, 0xca, 0xd5, 0xf8, 0xff, 0xec, 0xf9, 0xfa, 0xff, 0xeb, 0xff, 0xf7, 0xff, 0xfb, 0xff, 0xfb, 0xff, 0x88, 0x7f, 0x79, 0xff, 0x32, 0x1e, 0x22, 0xff, 0x36, 0x1f, 0x27, 0xff, 0x2a, 0x12, 0x18, 0xff, 0x50, 0x3b, 0x39, 0xff, 0x67, 0x51, 0x4b, 0xff, 0x46, 0x27, 0x1a, 0xff, 0x52, 0x34, 0x24, 0xff, 0x73, 0x5f, 0x5b, 0xff, 0x92, 0x90, 0x8a, 0xff, 0xa1, 0xa9, 0xaa, 0xff, 0xaa, 0xb7, 0xbf, 0xff, 0xa7, 0xb6, 0xc4, 0xff, 0xaa, 0xb9, 0xc4, 0xff, 0xae, 0xbd, 0xc9, 0xff, 0xb1, 0xc0, 0xce, 0xff, 0xaf, 0xbe, 0xcb, 0xff, 0xa9, 0xb7, 0xc7, 0xff, 0xaa, 0xb8, 0xca, 0xff, 0xa3, 0xb2, 0xc3, 0xff, 0xa4, 0xb1, 0xc2, 0xff, 0x93, 0x9c, 0xb0, 0xff, 0x87, 0x8f, 0xa5, 0xff, 0x9c, 0xa6, 0xb9, 0xff, 0x80, 0x87, 0x99, 0xff, 0x4f, 0x4c, 0x5f, 0xff, 0x6b, 0x66, 0x7a, 0xff, 0x59, 0x52, 0x60, 0xff, 0x52, 0x47, 0x4f, 0xff, 0x4c, 0x3d, 0x41, 0xff, 0x48, 0x35, 0x39, 0xff, 0x39, 0x26, 0x28, 0xff, 0x3d, 0x29, 0x2b, 0xff, 0x47, 0x35, 0x36, 0xff, 0x68, 0x56, 0x51, 0xff, 0x34, 0x21, 0x1f, 0xff, 0x4f, 0x41, 0x3f, 0xff, 0x59, 0x4c, 0x46, 0xff, 0x35, 0x24, 0x2a, 0xff, 0x39, 0x28, 0x35, 0xff, 0x6f, 0x67, 0x64, 0xff, 0xa3, 0x9e, 0x89, 0xff, 0x7c, 0x73, 0x6a, 0xff, 0x50, 0x45, 0x51, 0xff, 0x42, 0x38, 0x57, 0xff, 0x61, 0x52, 0x55, 0xff, 0x75, 0x67, 0x4a, 0xff, 0x9c, 0x94, 0x7c, 0xff, 0x7e, 0x6d, 0x50, 0xff, 0x4b, 0x37, 0x29, 0xff, 0x3e, 0x23, 0x28, 0xff, 0x26, 0x0d, 0x0a, 0xff, 0x4a, 0x32, 0x28, 0xff, 0x4a, 0x32, 0x24, 0xff, 0x5c, 0x44, 0x33, 0xff, 0x4e, 0x36, 0x2b, 0xff, 0x9e, 0x94, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf5, 0xee, 0xe7, 0x4b, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf9, 0xf6, 0xff, 0xcd, 0xcc, 0xb9, 0xff, 0x9b, 0x93, 0x6e, 0xff, 0xa3, 0x99, 0x73, 0xff, 0xb5, 0xa9, 0x82, 0xff, 0x95, 0x86, 0x5e, 0xff, 0x8c, 0x7c, 0x52, 0xff, 0x8f, 0x7d, 0x54, 0xff, 0x8e, 0x7c, 0x54, 0xff, 0x8d, 0x7b, 0x52, 0xff, 0x8d, 0x7b, 0x51, 0xff, 0x8f, 0x7e, 0x53, 0xff, 0x97, 0x86, 0x5b, 0xff, 0x9a, 0x89, 0x5e, 0xff, 0x97, 0x86, 0x5b, 0xff, 0x95, 0x83, 0x58, 0xff, 0x96, 0x82, 0x57, 0xff, 0x96, 0x84, 0x58, 0xff, 0xa2, 0x93, 0x69, 0xff, 0x90, 0x7d, 0x58, 0xff, 0x8b, 0x7a, 0x53, 0xff, 0xaf, 0xa4, 0x7c, 0xff, 0xb5, 0xae, 0x88, 0xff, 0xb2, 0xb0, 0x8b, 0xff, 0x83, 0x74, 0x55, 0xff, 0x8f, 0x81, 0x5d, 0xff, 0xb4, 0xab, 0x84, 0xff, 0xa4, 0x9d, 0x77, 0xff, 0x93, 0x84, 0x66, 0xff, 0xb0, 0xab, 0x8d, 0xff, 0xb6, 0xb8, 0x96, 0xff, 0xa7, 0xa2, 0x84, 0xff, 0xb6, 0xb8, 0xa6, 0xff, 0xac, 0xb0, 0x93, 0xff, 0xb2, 0xb1, 0x8d, 0xff, 0x9e, 0x99, 0x80, 0xff, 0x9e, 0x9c, 0x87, 0xff, 0xc1, 0xcd, 0xbf, 0xff, 0xb0, 0xbb, 0xb7, 0xff, 0x5d, 0x59, 0x61, 0xff, 0x46, 0x3b, 0x48, 0xff, 0x4a, 0x3e, 0x4e, 0xff, 0x3d, 0x33, 0x44, 0xff, 0x31, 0x22, 0x2c, 0xff, 0x37, 0x2b, 0x38, 0xff, 0x2f, 0x20, 0x30, 0xff, 0x92, 0x9d, 0xae, 0xff, 0xc5, 0xe0, 0xed, 0xff, 0xb4, 0xc5, 0xd0, 0xff, 0xa5, 0xaf, 0xb2, 0xff, 0xa8, 0xa8, 0xa4, 0xff, 0x76, 0x68, 0x66, 0xff, 0x45, 0x32, 0x32, 0xff, 0x51, 0x44, 0x42, 0xff, 0x82, 0x75, 0x74, 0xff, 0x87, 0x79, 0x7d, 0xff, 0x8f, 0x84, 0x88, 0xff, 0x72, 0x6a, 0x6b, 0xff, 0x56, 0x4e, 0x54, 0xff, 0x5d, 0x52, 0x61, 0xff, 0xad, 0xb4, 0xc4, 0xff, 0xd6, 0xf5, 0xfb, 0xff, 0xc9, 0xe3, 0xe9, 0xff, 0xcc, 0xe2, 0xec, 0xff, 0xc5, 0xe3, 0xea, 0xff, 0xc2, 0xdb, 0xe1, 0xff, 0xbc, 0xcf, 0xda, 0xff, 0xb4, 0xc3, 0xcf, 0xff, 0xa9, 0xb8, 0xc4, 0xff, 0xa9, 0xad, 0xb8, 0xff, 0xa5, 0xa7, 0xb0, 0xff, 0x96, 0x9f, 0xaf, 0xff, 0x4f, 0x45, 0x62, 0xff, 0x3a, 0x25, 0x2e, 0xff, 0x5e, 0x4a, 0x42, 0xff, 0x66, 0x54, 0x65, 0xff, 0x53, 0x44, 0x5c, 0xff, 0x7f, 0x76, 0x7e, 0xff, 0xa7, 0xaa, 0x9f, 0xff, 0xc4, 0xc6, 0xb7, 0xff, 0xac, 0xa3, 0x94, 0xff, 0x51, 0x3d, 0x35, 0xff, 0x4a, 0x32, 0x2f, 0xff, 0x50, 0x37, 0x2f, 0xff, 0x51, 0x35, 0x2a, 0xff, 0x3d, 0x1f, 0x11, 0xff, 0x64, 0x48, 0x38, 0xff, 0x92, 0x87, 0x7e, 0xff, 0xc1, 0xc7, 0xc5, 0xff, 0xc7, 0xd6, 0xd7, 0xff, 0xc0, 0xd1, 0xd6, 0xff, 0xba, 0xcb, 0xd1, 0xff, 0xb7, 0xc9, 0xce, 0xff, 0xb5, 0xc7, 0xce, 0xff, 0xb3, 0xc3, 0xce, 0xff, 0xb3, 0xc3, 0xce, 0xff, 0xac, 0xbc, 0xca, 0xff, 0xab, 0xba, 0xcb, 0xff, 0xaa, 0xb7, 0xca, 0xff, 0xa6, 0xb5, 0xc6, 0xff, 0xa1, 0xb1, 0xc2, 0xff, 0x97, 0xa3, 0xb8, 0xff, 0x82, 0x88, 0xa1, 0xff, 0x8e, 0x96, 0xac, 0xff, 0x8e, 0x97, 0xaa, 0xff, 0x5e, 0x5a, 0x70, 0xff, 0x65, 0x63, 0x78, 0xff, 0x6d, 0x6a, 0x77, 0xff, 0x5d, 0x55, 0x5d, 0xff, 0x56, 0x4a, 0x4d, 0xff, 0x3b, 0x2d, 0x2f, 0xff, 0x4d, 0x3e, 0x43, 0xff, 0x46, 0x31, 0x39, 0xff, 0x2d, 0x19, 0x21, 0xff, 0x62, 0x56, 0x55, 0xff, 0x62, 0x52, 0x52, 0xff, 0x38, 0x27, 0x2e, 0xff, 0x43, 0x35, 0x37, 0xff, 0x43, 0x35, 0x37, 0xff, 0x52, 0x44, 0x4c, 0xff, 0x83, 0x7c, 0x76, 0xff, 0xa2, 0xa0, 0x8a, 0xff, 0x99, 0x96, 0x82, 0xff, 0x65, 0x5c, 0x65, 0xff, 0x41, 0x33, 0x4b, 0xff, 0x7a, 0x6e, 0x6c, 0xff, 0x94, 0x93, 0x7e, 0xff, 0xa1, 0x9c, 0x84, 0xff, 0x8d, 0x7e, 0x64, 0xff, 0x7f, 0x72, 0x5e, 0xff, 0x42, 0x31, 0x2a, 0xff, 0x43, 0x2c, 0x20, 0xff, 0x6a, 0x52, 0x39, 0xff, 0x73, 0x5b, 0x43, 0xff, 0x82, 0x6f, 0x54, 0xff, 0x83, 0x75, 0x5e, 0xff, 0xba, 0xb2, 0xa9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf5, 0xf2, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0xe8, 0x42, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfb, 0xff, 0xd9, 0xd7, 0xc8, 0xff, 0xa0, 0x97, 0x74, 0xff, 0xb5, 0xab, 0x84, 0xff, 0x9c, 0x90, 0x69, 0xff, 0x90, 0x82, 0x5a, 0xff, 0x94, 0x84, 0x5b, 0xff, 0x91, 0x7f, 0x55, 0xff, 0x8d, 0x7c, 0x52, 0xff, 0x8d, 0x7b, 0x52, 0xff, 0x8c, 0x7a, 0x52, 0xff, 0x8d, 0x7c, 0x52, 0xff, 0x92, 0x82, 0x56, 0xff, 0x97, 0x86, 0x5b, 0xff, 0x9a, 0x89, 0x5e, 0xff, 0x9b, 0x88, 0x5d, 0xff, 0x95, 0x82, 0x57, 0xff, 0x94, 0x83, 0x57, 0xff, 0x9c, 0x8d, 0x63, 0xff, 0x97, 0x83, 0x5c, 0xff, 0x84, 0x6c, 0x45, 0xff, 0x9c, 0x91, 0x6e, 0xff, 0xc3, 0xc4, 0x9f, 0xff, 0x97, 0x93, 0x6a, 0xff, 0x95, 0x89, 0x68, 0xff, 0x9a, 0x8e, 0x73, 0xff, 0x94, 0x83, 0x62, 0xff, 0xa7, 0xa3, 0x73, 0xff, 0xaf, 0xac, 0x85, 0xff, 0xba, 0xb5, 0x97, 0xff, 0xae, 0xa8, 0x84, 0xff, 0xc7, 0xd0, 0xbb, 0xff, 0xc1, 0xcf, 0xba, 0xff, 0xad, 0xaf, 0x8f, 0xff, 0xb0, 0xab, 0x90, 0xff, 0x95, 0x90, 0x7a, 0xff, 0xac, 0xb9, 0xaa, 0xff, 0xcd, 0xe3, 0xe0, 0xff, 0x94, 0x9a, 0xa2, 0xff, 0x4c, 0x45, 0x53, 0xff, 0x51, 0x46, 0x57, 0xff, 0x46, 0x3c, 0x51, 0xff, 0x44, 0x3d, 0x4d, 0xff, 0x3f, 0x32, 0x44, 0xff, 0x3a, 0x2d, 0x3e, 0xff, 0x28, 0x1d, 0x28, 0xff, 0x9a, 0xab, 0xbe, 0xff, 0xc4, 0xde, 0xea, 0xff, 0xb5, 0xc7, 0xd7, 0xff, 0xa9, 0xbc, 0xca, 0xff, 0xa1, 0xb2, 0xc3, 0xff, 0xb6, 0xc8, 0xd7, 0xff, 0xb6, 0xc8, 0xd9, 0xff, 0xa6, 0xb4, 0xcb, 0xff, 0x96, 0xa2, 0xc0, 0xff, 0x82, 0x8c, 0xb1, 0xff, 0x77, 0x80, 0xa8, 0xff, 0x87, 0x8f, 0xb8, 0xff, 0x8a, 0x95, 0xb5, 0xff, 0xab, 0xb9, 0xcb, 0xff, 0xcd, 0xe1, 0xed, 0xff, 0xc7, 0xe6, 0xee, 0xff, 0xc8, 0xe3, 0xe9, 0xff, 0xcb, 0xe2, 0xec, 0xff, 0xc8, 0xe2, 0xeb, 0xff, 0xc5, 0xdc, 0xe3, 0xff, 0xc0, 0xd3, 0xde, 0xff, 0xb9, 0xc9, 0xd4, 0xff, 0xb1, 0xc1, 0xcf, 0xff, 0xaf, 0xb9, 0xc6, 0xff, 0xa7, 0xb2, 0xb7, 0xff, 0x9a, 0xa7, 0xb1, 0xff, 0x8f, 0x93, 0xab, 0xff, 0x80, 0x7a, 0xa1, 0xff, 0x5f, 0x52, 0x77, 0xff, 0x6e, 0x66, 0x6b, 0xff, 0x7e, 0x6f, 0x6d, 0xff, 0x56, 0x3f, 0x40, 0xff, 0x43, 0x2c, 0x22, 0xff, 0x39, 0x21, 0x18, 0xff, 0x50, 0x3a, 0x34, 0xff, 0x78, 0x68, 0x60, 0xff, 0x7f, 0x74, 0x6a, 0xff, 0x77, 0x6b, 0x62, 0xff, 0x7a, 0x6c, 0x63, 0xff, 0x95, 0x8b, 0x86, 0xff, 0xa4, 0xa8, 0xa7, 0xff, 0xbf, 0xc9, 0xcb, 0xff, 0xaf, 0xc0, 0xc5, 0xff, 0xad, 0xc1, 0xc8, 0xff, 0xb3, 0xc4, 0xcb, 0xff, 0xba, 0xcb, 0xd2, 0xff, 0xbb, 0xcc, 0xd5, 0xff, 0xb8, 0xca, 0xd3, 0xff, 0xb8, 0xca, 0xd2, 0xff, 0xb6, 0xc7, 0xd0, 0xff, 0xab, 0xbc, 0xc9, 0xff, 0xad, 0xbc, 0xcb, 0xff, 0xa8, 0xb6, 0xc7, 0xff, 0xa5, 0xb3, 0xc6, 0xff, 0xa3, 0xb2, 0xc5, 0xff, 0xa4, 0xb0, 0xc4, 0xff, 0x8c, 0x93, 0xaa, 0xff, 0x83, 0x8a, 0xa2, 0xff, 0x95, 0x9c, 0xaf, 0xff, 0x70, 0x70, 0x82, 0xff, 0x64, 0x63, 0x75, 0xff, 0x7b, 0x7a, 0x8a, 0xff, 0x61, 0x5b, 0x63, 0xff, 0x81, 0x77, 0x78, 0xff, 0x6f, 0x65, 0x60, 0xff, 0x78, 0x70, 0x6a, 0xff, 0x52, 0x43, 0x49, 0xff, 0x3b, 0x28, 0x36, 0xff, 0x6c, 0x60, 0x63, 0xff, 0x72, 0x64, 0x62, 0xff, 0x55, 0x44, 0x47, 0xff, 0x45, 0x37, 0x3b, 0xff, 0x2e, 0x1e, 0x24, 0xff, 0x85, 0x79, 0x77, 0xff, 0x95, 0x93, 0x81, 0xff, 0x9c, 0x9d, 0x84, 0xff, 0xae, 0xad, 0x97, 0xff, 0x7c, 0x73, 0x72, 0xff, 0x6f, 0x65, 0x6d, 0xff, 0x8f, 0x89, 0x7d, 0xff, 0x91, 0x90, 0x7b, 0xff, 0xab, 0xab, 0x97, 0xff, 0xa0, 0x99, 0x81, 0xff, 0x9c, 0x94, 0x7a, 0xff, 0x89, 0x82, 0x68, 0xff, 0x93, 0x86, 0x6c, 0xff, 0x8f, 0x83, 0x66, 0xff, 0x88, 0x7d, 0x5f, 0xff, 0x95, 0x89, 0x6a, 0xff, 0x9f, 0x95, 0x7a, 0xff, 0xd8, 0xd3, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xe5, 0xe3, 0xd9, 0xff, 0xb6, 0xad, 0x89, 0xff, 0xab, 0x9f, 0x79, 0xff, 0x8e, 0x80, 0x59, 0xff, 0x95, 0x86, 0x5f, 0xff, 0x94, 0x83, 0x5c, 0xff, 0x90, 0x7e, 0x57, 0xff, 0x8d, 0x7a, 0x52, 0xff, 0x8d, 0x7a, 0x53, 0xff, 0x8e, 0x7d, 0x54, 0xff, 0x8c, 0x7a, 0x51, 0xff, 0x91, 0x7f, 0x56, 0xff, 0x98, 0x88, 0x5c, 0xff, 0xa0, 0x90, 0x63, 0xff, 0x9e, 0x8c, 0x60, 0xff, 0x93, 0x7f, 0x53, 0xff, 0x94, 0x82, 0x57, 0xff, 0x9b, 0x8b, 0x61, 0xff, 0xaa, 0x97, 0x6e, 0xff, 0x89, 0x75, 0x4e, 0xff, 0xa4, 0x9b, 0x75, 0xff, 0xc2, 0xc0, 0x9c, 0xff, 0xa5, 0xa1, 0x7d, 0xff, 0xb2, 0xae, 0x92, 0xff, 0x9e, 0x97, 0x76, 0xff, 0x98, 0x93, 0x6f, 0xff, 0x90, 0x7b, 0x5a, 0xff, 0x96, 0x88, 0x62, 0xff, 0xbd, 0xbc, 0xa5, 0xff, 0xbb, 0xbe, 0xb0, 0xff, 0xb1, 0xb3, 0x9a, 0xff, 0x9f, 0x96, 0x6f, 0xff, 0xa9, 0xa3, 0x85, 0xff, 0xa4, 0xa8, 0x99, 0xff, 0xb8, 0xc4, 0xb9, 0xff, 0xcc, 0xe4, 0xe4, 0xff, 0xa9, 0xbe, 0xc9, 0xff, 0x71, 0x73, 0x84, 0xff, 0x62, 0x5d, 0x71, 0xff, 0x52, 0x4c, 0x61, 0xff, 0x50, 0x49, 0x5f, 0xff, 0x47, 0x3e, 0x4e, 0xff, 0x38, 0x34, 0x4d, 0xff, 0x37, 0x2b, 0x40, 0xff, 0x41, 0x36, 0x42, 0xff, 0xae, 0xc5, 0xd7, 0xff, 0xbf, 0xda, 0xe4, 0xff, 0xbb, 0xd2, 0xdd, 0xff, 0xb4, 0xc8, 0xd6, 0xff, 0xa7, 0xb9, 0xcb, 0xff, 0xa5, 0xb8, 0xd1, 0xff, 0xa4, 0xba, 0xd4, 0xff, 0x9f, 0xb2, 0xcd, 0xff, 0x9a, 0xab, 0xc7, 0xff, 0x9c, 0xad, 0xc6, 0xff, 0xa4, 0xb6, 0xcb, 0xff, 0xae, 0xbf, 0xd6, 0xff, 0xb8, 0xc9, 0xd8, 0xff, 0xbc, 0xd2, 0xd6, 0xff, 0xc4, 0xe0, 0xea, 0xff, 0xca, 0xe5, 0xef, 0xff, 0xcd, 0xe5, 0xec, 0xff, 0xcb, 0xe2, 0xec, 0xff, 0xc8, 0xdf, 0xe8, 0xff, 0xc4, 0xdb, 0xe3, 0xff, 0xc1, 0xd6, 0xe1, 0xff, 0xbe, 0xd1, 0xdd, 0xff, 0xbb, 0xce, 0xd7, 0xff, 0xb5, 0xc8, 0xd1, 0xff, 0xaf, 0xbe, 0xce, 0xff, 0xb0, 0xbe, 0xcb, 0xff, 0xad, 0xbd, 0xc2, 0xff, 0x98, 0xa7, 0xb7, 0xff, 0x86, 0x8d, 0xb4, 0xff, 0x79, 0x86, 0xb3, 0xff, 0x89, 0x96, 0xbd, 0xff, 0xa1, 0xa8, 0xbe, 0xff, 0x99, 0xa0, 0xa8, 0xff, 0x92, 0x99, 0xa5, 0xff, 0x9d, 0xa9, 0xb4, 0xff, 0xa0, 0xad, 0xb8, 0xff, 0xa5, 0xb0, 0xbe, 0xff, 0xa9, 0xb4, 0xc3, 0xff, 0xaa, 0xb5, 0xc8, 0xff, 0xac, 0xb8, 0xc9, 0xff, 0xa7, 0xb6, 0xc1, 0xff, 0xad, 0xbe, 0xc7, 0xff, 0xb4, 0xc5, 0xce, 0xff, 0xb8, 0xca, 0xd3, 0xff, 0xba, 0xcc, 0xd5, 0xff, 0xbb, 0xcd, 0xd6, 0xff, 0xbc, 0xcf, 0xd7, 0xff, 0xbb, 0xce, 0xd6, 0xff, 0xba, 0xcd, 0xd6, 0xff, 0xb6, 0xc9, 0xd3, 0xff, 0xae, 0xbe, 0xcd, 0xff, 0xab, 0xbb, 0xcb, 0xff, 0xa9, 0xb8, 0xc9, 0xff, 0xa8, 0xb6, 0xc9, 0xff, 0xa3, 0xb1, 0xc6, 0xff, 0x9f, 0xaa, 0xbd, 0xff, 0x94, 0x9e, 0xb1, 0xff, 0x87, 0x8e, 0xa4, 0xff, 0x8d, 0x93, 0xa3, 0xff, 0x87, 0x8e, 0x98, 0xff, 0x7d, 0x81, 0x89, 0xff, 0x92, 0x91, 0x97, 0xff, 0x84, 0x80, 0x80, 0xff, 0x89, 0x84, 0x7e, 0xff, 0xa3, 0xa5, 0x9a, 0xff, 0xa0, 0xa6, 0x98, 0xff, 0x7d, 0x76, 0x70, 0xff, 0x5e, 0x52, 0x54, 0xff, 0x87, 0x83, 0x80, 0xff, 0x39, 0x2e, 0x2e, 0xff, 0x66, 0x58, 0x52, 0xff, 0x70, 0x66, 0x5a, 0xff, 0x6a, 0x5d, 0x56, 0xff, 0x98, 0x91, 0x81, 0xff, 0xa6, 0xa8, 0x95, 0xff, 0xa8, 0xae, 0x98, 0xff, 0xb2, 0xb5, 0x9f, 0xff, 0x8e, 0x8a, 0x7c, 0xff, 0x8b, 0x88, 0x7c, 0xff, 0x9d, 0x9f, 0x89, 0xff, 0x95, 0x90, 0x7c, 0xff, 0xa3, 0xa2, 0x91, 0xff, 0x99, 0x99, 0x86, 0xff, 0x91, 0x8f, 0x7c, 0xff, 0xb1, 0xaf, 0x98, 0xff, 0xaa, 0xa6, 0x8d, 0xff, 0xa1, 0x9e, 0x85, 0xff, 0x9a, 0x97, 0x79, 0xff, 0x9a, 0x97, 0x7a, 0xff, 0xad, 0xaa, 0x94, 0xff, 0xea, 0xe8, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xbe, 0xb4, 0x93, 0xff, 0x93, 0x86, 0x60, 0xff, 0x92, 0x84, 0x5e, 0xff, 0x92, 0x82, 0x5c, 0xff, 0x93, 0x83, 0x5b, 0xff, 0x90, 0x7d, 0x57, 0xff, 0x8c, 0x79, 0x53, 0xff, 0x8c, 0x7a, 0x54, 0xff, 0x8a, 0x78, 0x51, 0xff, 0x88, 0x75, 0x4d, 0xff, 0x8f, 0x7d, 0x55, 0xff, 0x9a, 0x8a, 0x5e, 0xff, 0x9d, 0x8e, 0x5f, 0xff, 0x9d, 0x8b, 0x5d, 0xff, 0x99, 0x86, 0x59, 0xff, 0x97, 0x83, 0x57, 0xff, 0x98, 0x87, 0x5b, 0xff, 0xa1, 0x92, 0x64, 0xff, 0xa3, 0x95, 0x6d, 0xff, 0xb2, 0xaa, 0x82, 0xff, 0xab, 0x9f, 0x78, 0xff, 0xb9, 0xb6, 0x9c, 0xff, 0xa2, 0xa2, 0x8a, 0xff, 0x96, 0x88, 0x66, 0xff, 0x84, 0x71, 0x4a, 0xff, 0x77, 0x66, 0x44, 0xff, 0xb4, 0xb5, 0xa2, 0xff, 0xcd, 0xd9, 0xc7, 0xff, 0xb6, 0xb9, 0x9e, 0xff, 0x8a, 0x7d, 0x52, 0xff, 0x91, 0x86, 0x62, 0xff, 0xb2, 0xb5, 0xa3, 0xff, 0xb6, 0xc7, 0xbe, 0xff, 0xc4, 0xdd, 0xe0, 0xff, 0xba, 0xcf, 0xda, 0xff, 0x7f, 0x89, 0x9b, 0xff, 0x63, 0x60, 0x75, 0xff, 0x6e, 0x70, 0x84, 0xff, 0x60, 0x60, 0x75, 0xff, 0x48, 0x41, 0x57, 0xff, 0x3c, 0x31, 0x47, 0xff, 0x4f, 0x55, 0x71, 0xff, 0x2d, 0x25, 0x38, 0xff, 0x64, 0x5c, 0x6a, 0xff, 0xc4, 0xde, 0xed, 0xff, 0xbf, 0xd5, 0xe4, 0xff, 0xc0, 0xd4, 0xe0, 0xff, 0xc0, 0xd4, 0xe0, 0xff, 0xbd, 0xd1, 0xe0, 0xff, 0xb7, 0xcc, 0xdc, 0xff, 0xb1, 0xc6, 0xd7, 0xff, 0xb0, 0xc4, 0xd5, 0xff, 0xb2, 0xc4, 0xd5, 0xff, 0xb9, 0xcb, 0xd8, 0xff, 0xbf, 0xcf, 0xd9, 0xff, 0xbf, 0xd2, 0xda, 0xff, 0xbf, 0xd1, 0xdc, 0xff, 0xc0, 0xd4, 0xe2, 0xff, 0xc7, 0xe2, 0xee, 0xff, 0xd0, 0xe6, 0xef, 0xff, 0xcd, 0xe4, 0xeb, 0xff, 0xca, 0xe1, 0xeb, 0xff, 0xc8, 0xde, 0xe8, 0xff, 0xc5, 0xdc, 0xe4, 0xff, 0xc5, 0xda, 0xe5, 0xff, 0xc4, 0xd9, 0xe3, 0xff, 0xc0, 0xd3, 0xe0, 0xff, 0xbc, 0xd0, 0xdd, 0xff, 0xbb, 0xd0, 0xd9, 0xff, 0xbb, 0xcc, 0xd4, 0xff, 0xb7, 0xc9, 0xd3, 0xff, 0xb5, 0xc4, 0xcd, 0xff, 0xae, 0xbf, 0xc7, 0xff, 0x9c, 0xaf, 0xc6, 0xff, 0x89, 0x93, 0xba, 0xff, 0x88, 0x93, 0xbb, 0xff, 0x9c, 0xb1, 0xcf, 0xff, 0xaa, 0xbf, 0xdd, 0xff, 0xab, 0xbe, 0xd7, 0xff, 0xa7, 0xb8, 0xd0, 0xff, 0xa5, 0xb6, 0xcd, 0xff, 0xa2, 0xb3, 0xc8, 0xff, 0xa4, 0xb5, 0xc9, 0xff, 0xaa, 0xbd, 0xcd, 0xff, 0xb7, 0xc9, 0xd6, 0xff, 0xbf, 0xd1, 0xdb, 0xff, 0xbf, 0xd1, 0xda, 0xff, 0xc0, 0xd2, 0xdb, 0xff, 0xbf, 0xd2, 0xdd, 0xff, 0xbe, 0xd1, 0xdd, 0xff, 0xbd, 0xd0, 0xda, 0xff, 0xbd, 0xd0, 0xd7, 0xff, 0xbc, 0xce, 0xd9, 0xff, 0xb6, 0xc6, 0xd5, 0xff, 0xae, 0xbd, 0xce, 0xff, 0xab, 0xbb, 0xcd, 0xff, 0xa7, 0xb5, 0xc7, 0xff, 0xa4, 0xb1, 0xc5, 0xff, 0xa0, 0xae, 0xc5, 0xff, 0x9f, 0xab, 0xbf, 0xff, 0x94, 0xa0, 0xb2, 0xff, 0x87, 0x8f, 0xa3, 0xff, 0x84, 0x89, 0x96, 0xff, 0x96, 0xa4, 0xa7, 0xff, 0x9b, 0xa3, 0xa3, 0xff, 0x8a, 0x8b, 0x85, 0xff, 0x91, 0x8d, 0x84, 0xff, 0x90, 0x90, 0x85, 0xff, 0xa5, 0xb0, 0xa5, 0xff, 0xb7, 0xc2, 0xb7, 0xff, 0xa4, 0xa4, 0x95, 0xff, 0x84, 0x80, 0x70, 0xff, 0xaa, 0xad, 0x9f, 0xff, 0x34, 0x29, 0x2c, 0xff, 0x62, 0x55, 0x4b, 0xff, 0xa7, 0xa5, 0x87, 0xff, 0xa4, 0x9d, 0x86, 0xff, 0xa2, 0xa0, 0x89, 0xff, 0xad, 0xb3, 0xa3, 0xff, 0xb0, 0xb8, 0xaa, 0xff, 0xaf, 0xb6, 0xa5, 0xff, 0xa9, 0xab, 0x95, 0xff, 0xa7, 0xa8, 0x92, 0xff, 0x9b, 0x9e, 0x84, 0xff, 0x98, 0x90, 0x7b, 0xff, 0xa9, 0xa6, 0x94, 0xff, 0x9f, 0xa3, 0x8f, 0xff, 0x99, 0x9a, 0x8a, 0xff, 0x9f, 0x9e, 0x8f, 0xff, 0xa5, 0xa5, 0x90, 0xff, 0xa8, 0xa8, 0x94, 0xff, 0xa5, 0xa4, 0x8f, 0xff, 0x9b, 0x9d, 0x87, 0xff, 0xbf, 0xbf, 0xb2, 0xff, 0xf7, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe6, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xb9, 0xb1, 0x95, 0xff, 0x8f, 0x82, 0x5c, 0xff, 0x94, 0x84, 0x5e, 0xff, 0x96, 0x85, 0x5d, 0xff, 0x95, 0x84, 0x5d, 0xff, 0x90, 0x7e, 0x57, 0xff, 0x8d, 0x7a, 0x54, 0xff, 0x8b, 0x7a, 0x54, 0xff, 0x89, 0x75, 0x4e, 0xff, 0x8a, 0x76, 0x4d, 0xff, 0x8f, 0x7b, 0x53, 0xff, 0x9a, 0x8a, 0x5e, 0xff, 0x9e, 0x8d, 0x5f, 0xff, 0x9f, 0x8b, 0x5e, 0xff, 0x9e, 0x8b, 0x5c, 0xff, 0x99, 0x84, 0x53, 0xff, 0xa0, 0x8d, 0x5e, 0xff, 0x9c, 0x8b, 0x5e, 0xff, 0xa0, 0x90, 0x65, 0xff, 0xab, 0x9f, 0x74, 0xff, 0xb7, 0xb1, 0x8a, 0xff, 0xc4, 0xc4, 0xad, 0xff, 0x87, 0x79, 0x58, 0xff, 0x7a, 0x64, 0x3d, 0xff, 0x91, 0x81, 0x5c, 0xff, 0xc9, 0xc8, 0xac, 0xff, 0xce, 0xd9, 0xc8, 0xff, 0xb7, 0xbb, 0xa3, 0xff, 0xb1, 0xa9, 0x84, 0xff, 0xa7, 0x9c, 0x71, 0xff, 0xac, 0xab, 0x8d, 0xff, 0xbd, 0xc8, 0xbe, 0xff, 0xbd, 0xd5, 0xdc, 0xff, 0xb2, 0xca, 0xd8, 0xff, 0x9d, 0xa9, 0xbb, 0xff, 0x6d, 0x70, 0x84, 0xff, 0x6a, 0x68, 0x7c, 0xff, 0x6c, 0x6f, 0x81, 0xff, 0x6d, 0x6e, 0x82, 0xff, 0x46, 0x3d, 0x58, 0xff, 0x47, 0x4a, 0x68, 0xff, 0x6e, 0x77, 0x8f, 0xff, 0x34, 0x28, 0x36, 0xff, 0x69, 0x6b, 0x77, 0xff, 0xcb, 0xe3, 0xf2, 0xff, 0xc1, 0xd7, 0xe5, 0xff, 0xc0, 0xd7, 0xe2, 0xff, 0xbf, 0xd6, 0xe2, 0xff, 0xbf, 0xd6, 0xe2, 0xff, 0xbd, 0xd6, 0xe1, 0xff, 0xbd, 0xd5, 0xe2, 0xff, 0xbd, 0xd3, 0xe0, 0xff, 0xbe, 0xd1, 0xdf, 0xff, 0xbf, 0xd0, 0xe0, 0xff, 0xbf, 0xd0, 0xdf, 0xff, 0xc2, 0xd5, 0xe0, 0xff, 0xc3, 0xd8, 0xe3, 0xff, 0xc3, 0xda, 0xe7, 0xff, 0xcc, 0xe5, 0xed, 0xff, 0xcd, 0xe8, 0xed, 0xff, 0xcc, 0xe5, 0xeb, 0xff, 0xcc, 0xe2, 0xed, 0xff, 0xc9, 0xe0, 0xea, 0xff, 0xc8, 0xde, 0xe5, 0xff, 0xc8, 0xdd, 0xe5, 0xff, 0xc6, 0xdc, 0xe3, 0xff, 0xc3, 0xd7, 0xe1, 0xff, 0xbf, 0xd3, 0xdf, 0xff, 0xc0, 0xd4, 0xdf, 0xff, 0xbe, 0xd2, 0xdd, 0xff, 0xbe, 0xd1, 0xde, 0xff, 0xbf, 0xd1, 0xdc, 0xff, 0xb9, 0xca, 0xd5, 0xff, 0xb3, 0xc2, 0xd0, 0xff, 0xb7, 0xc4, 0xd3, 0xff, 0xaa, 0xba, 0xd0, 0xff, 0x9d, 0xaf, 0xcb, 0xff, 0xa0, 0xb2, 0xcc, 0xff, 0xa7, 0xb9, 0xd2, 0xff, 0xac, 0xbc, 0xd4, 0xff, 0xaf, 0xbe, 0xd3, 0xff, 0xb1, 0xc2, 0xd3, 0xff, 0xb8, 0xca, 0xd7, 0xff, 0xc0, 0xd4, 0xde, 0xff, 0xc3, 0xd8, 0xe1, 0xff, 0xc3, 0xd8, 0xe2, 0xff, 0xc1, 0xd5, 0xe0, 0xff, 0xc0, 0xd4, 0xdf, 0xff, 0xbf, 0xd2, 0xdf, 0xff, 0xbe, 0xd1, 0xde, 0xff, 0xbd, 0xd0, 0xdc, 0xff, 0xbc, 0xce, 0xd9, 0xff, 0xb9, 0xca, 0xd6, 0xff, 0xb7, 0xc6, 0xd5, 0xff, 0xaf, 0xbd, 0xcf, 0xff, 0xa6, 0xb5, 0xc6, 0xff, 0xa6, 0xb3, 0xc5, 0xff, 0xa1, 0xad, 0xc0, 0xff, 0x9b, 0xa8, 0xbf, 0xff, 0x97, 0xa4, 0xbb, 0xff, 0x9c, 0xa7, 0xb7, 0xff, 0x80, 0x85, 0x96, 0xff, 0x89, 0x91, 0x9c, 0xff, 0xaa, 0xba, 0xbd, 0xff, 0xaa, 0xb6, 0xb5, 0xff, 0x84, 0x85, 0x82, 0xff, 0x8c, 0x8b, 0x81, 0xff, 0xb1, 0xb8, 0xaa, 0xff, 0xac, 0xb5, 0xad, 0xff, 0xb5, 0xc1, 0xb8, 0xff, 0xa2, 0xac, 0x9d, 0xff, 0x98, 0x94, 0x84, 0xff, 0xb9, 0xbf, 0xae, 0xff, 0x6e, 0x62, 0x5a, 0xff, 0x74, 0x68, 0x5b, 0xff, 0xbf, 0xc4, 0xa9, 0xff, 0xba, 0xbb, 0xa2, 0xff, 0xb0, 0xb4, 0xa2, 0xff, 0xaf, 0xb8, 0xa7, 0xff, 0xb6, 0xbd, 0xb1, 0xff, 0xb1, 0xbb, 0xb0, 0xff, 0xac, 0xb2, 0x9b, 0xff, 0x98, 0x96, 0x7f, 0xff, 0x96, 0x92, 0x7a, 0xff, 0x8e, 0x89, 0x70, 0xff, 0xad, 0xaf, 0x9d, 0xff, 0xa2, 0xa5, 0x96, 0xff, 0x9d, 0x9c, 0x88, 0xff, 0x96, 0x97, 0x85, 0xff, 0xa4, 0xa7, 0x97, 0xff, 0xa5, 0xa7, 0x92, 0xff, 0x9f, 0x9f, 0x8c, 0xff, 0xa6, 0xa8, 0x97, 0xff, 0xd4, 0xd5, 0xcd, 0xff, 0xfd, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xc6, 0xbf, 0xaa, 0xff, 0x94, 0x83, 0x5f, 0xff, 0x96, 0x84, 0x5f, 0xff, 0x96, 0x85, 0x5e, 0xff, 0x95, 0x84, 0x5d, 0xff, 0x90, 0x7e, 0x57, 0xff, 0x8c, 0x7a, 0x54, 0xff, 0x8c, 0x79, 0x53, 0xff, 0x8a, 0x76, 0x4e, 0xff, 0x89, 0x75, 0x4b, 0xff, 0x8f, 0x7b, 0x52, 0xff, 0x9e, 0x8e, 0x62, 0xff, 0xa4, 0x93, 0x65, 0xff, 0xa4, 0x8f, 0x62, 0xff, 0xa0, 0x89, 0x5a, 0xff, 0x9c, 0x84, 0x57, 0xff, 0xa2, 0x8c, 0x63, 0xff, 0xad, 0x99, 0x6d, 0xff, 0x8e, 0x7d, 0x4f, 0xff, 0xa7, 0xa0, 0x7a, 0xff, 0xd0, 0xd4, 0xb3, 0xff, 0xaa, 0xa4, 0x7e, 0xff, 0x92, 0x84, 0x58, 0xff, 0xa7, 0x9e, 0x78, 0xff, 0xd1, 0xd3, 0xb6, 0xff, 0xd5, 0xdb, 0xc1, 0xff, 0xbe, 0xc3, 0xac, 0xff, 0xaf, 0xab, 0x8e, 0xff, 0x85, 0x73, 0x49, 0xff, 0x9d, 0x92, 0x68, 0xff, 0xc2, 0xc5, 0xac, 0xff, 0xc6, 0xda, 0xdd, 0xff, 0xa2, 0xbc, 0xd0, 0xff, 0x97, 0xaa, 0xc0, 0xff, 0x83, 0x8a, 0x9e, 0xff, 0x77, 0x78, 0x88, 0xff, 0x74, 0x72, 0x83, 0xff, 0x7a, 0x79, 0x8c, 0xff, 0x56, 0x55, 0x6b, 0xff, 0x3e, 0x3a, 0x56, 0xff, 0x73, 0x7d, 0x97, 0xff, 0x8a, 0x90, 0x9e, 0xff, 0x35, 0x28, 0x30, 0xff, 0x6f, 0x72, 0x7e, 0xff, 0xcf, 0xe5, 0xf6, 0xff, 0xbe, 0xd8, 0xe5, 0xff, 0xc0, 0xda, 0xe6, 0xff, 0xc0, 0xda, 0xe6, 0xff, 0xbf, 0xd9, 0xe5, 0xff, 0xbf, 0xd8, 0xe3, 0xff, 0xbe, 0xd6, 0xe1, 0xff, 0xc0, 0xd6, 0xe1, 0xff, 0xc1, 0xd4, 0xe0, 0xff, 0xc3, 0xd6, 0xe2, 0xff, 0xc5, 0xda, 0xe6, 0xff, 0xc7, 0xdb, 0xe8, 0xff, 0xc4, 0xd9, 0xe6, 0xff, 0xc5, 0xdc, 0xe8, 0xff, 0xcc, 0xe6, 0xee, 0xff, 0xcd, 0xe8, 0xed, 0xff, 0xd0, 0xe7, 0xee, 0xff, 0xcd, 0xe4, 0xee, 0xff, 0xca, 0xdf, 0xe9, 0xff, 0xc8, 0xdd, 0xe4, 0xff, 0xc6, 0xdb, 0xe3, 0xff, 0xc7, 0xdb, 0xe3, 0xff, 0xc7, 0xdb, 0xe2, 0xff, 0xc2, 0xd8, 0xe0, 0xff, 0xc1, 0xd6, 0xde, 0xff, 0xc2, 0xd8, 0xe0, 0xff, 0xc3, 0xd8, 0xe0, 0xff, 0xc4, 0xd8, 0xe1, 0xff, 0xc3, 0xd6, 0xe0, 0xff, 0xbc, 0xcd, 0xd9, 0xff, 0xb5, 0xc6, 0xd2, 0xff, 0xb8, 0xca, 0xd5, 0xff, 0xba, 0xcd, 0xd8, 0xff, 0xb9, 0xcd, 0xda, 0xff, 0xba, 0xcf, 0xdb, 0xff, 0xbb, 0xd0, 0xdb, 0xff, 0xbf, 0xd4, 0xdd, 0xff, 0xc3, 0xd9, 0xe0, 0xff, 0xc5, 0xdb, 0xe1, 0xff, 0xc6, 0xdc, 0xe2, 0xff, 0xc3, 0xd9, 0xe0, 0xff, 0xc3, 0xd8, 0xe0, 0xff, 0xc2, 0xd7, 0xe0, 0xff, 0xc1, 0xd5, 0xdf, 0xff, 0xc0, 0xd3, 0xdf, 0xff, 0xbd, 0xd1, 0xdd, 0xff, 0xbf, 0xd1, 0xde, 0xff, 0xbe, 0xce, 0xdb, 0xff, 0xba, 0xcb, 0xd8, 0xff, 0xb2, 0xc2, 0xd1, 0xff, 0xa9, 0xb8, 0xca, 0xff, 0xa3, 0xb2, 0xc4, 0xff, 0xa7, 0xb5, 0xc7, 0xff, 0x9f, 0xad, 0xbe, 0xff, 0x96, 0xa3, 0xb9, 0xff, 0x90, 0x9a, 0xb4, 0xff, 0x95, 0x9f, 0xaf, 0xff, 0x85, 0x8b, 0x97, 0xff, 0x94, 0x9d, 0xa7, 0xff, 0xb1, 0xc1, 0xc2, 0xff, 0xb0, 0xbc, 0xba, 0xff, 0x9c, 0x9f, 0x9c, 0xff, 0x92, 0x94, 0x8c, 0xff, 0xb2, 0xba, 0xaf, 0xff, 0xaf, 0xb7, 0xad, 0xff, 0xae, 0xba, 0xb2, 0xff, 0xa8, 0xb4, 0xab, 0xff, 0x9a, 0x99, 0x8d, 0xff, 0xb9, 0xbf, 0xb0, 0xff, 0x90, 0x88, 0x79, 0xff, 0x85, 0x7d, 0x6a, 0xff, 0xc5, 0xca, 0xb1, 0xff, 0xb1, 0xb3, 0x9d, 0xff, 0xa6, 0xa8, 0x9a, 0xff, 0xaf, 0xb6, 0xa7, 0xff, 0xb7, 0xc5, 0xb6, 0xff, 0xbe, 0xce, 0xc0, 0xff, 0xa1, 0xa2, 0x8c, 0xff, 0x8b, 0x84, 0x6a, 0xff, 0xa1, 0x9b, 0x82, 0xff, 0x96, 0x97, 0x81, 0xff, 0xaa, 0xb1, 0xa3, 0xff, 0xa0, 0xa3, 0x97, 0xff, 0x9c, 0x9c, 0x89, 0xff, 0xa1, 0xa2, 0x91, 0xff, 0x9e, 0xa2, 0x93, 0xff, 0x96, 0x97, 0x82, 0xff, 0x90, 0x91, 0x7c, 0xff, 0x9c, 0x9f, 0x90, 0xff, 0xde, 0xe0, 0xda, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xd4, 0xc7, 0xff, 0x93, 0x82, 0x5e, 0xff, 0x97, 0x85, 0x60, 0xff, 0x96, 0x85, 0x5d, 0xff, 0x94, 0x84, 0x5d, 0xff, 0x91, 0x7e, 0x58, 0xff, 0x8d, 0x7a, 0x54, 0xff, 0x8b, 0x77, 0x52, 0xff, 0x8b, 0x75, 0x4d, 0xff, 0x8a, 0x73, 0x4a, 0xff, 0x8d, 0x79, 0x50, 0xff, 0x9b, 0x8b, 0x5e, 0xff, 0xa6, 0x97, 0x69, 0xff, 0x9d, 0x8c, 0x5e, 0xff, 0x9c, 0x88, 0x58, 0xff, 0x9b, 0x81, 0x55, 0xff, 0x9d, 0x86, 0x5c, 0xff, 0xab, 0x97, 0x69, 0xff, 0xa6, 0x9a, 0x6f, 0xff, 0xcf, 0xd0, 0xb5, 0xff, 0xc5, 0xc6, 0xa9, 0xff, 0x94, 0x86, 0x5a, 0xff, 0xb6, 0xab, 0x84, 0xff, 0xd0, 0xcd, 0xaa, 0xff, 0xb4, 0xb0, 0x86, 0xff, 0x94, 0x91, 0x67, 0xff, 0xc2, 0xc1, 0xa5, 0xff, 0xa9, 0xa4, 0x87, 0xff, 0x85, 0x75, 0x4a, 0xff, 0xa7, 0x9b, 0x74, 0xff, 0xc8, 0xd0, 0xc6, 0xff, 0xa7, 0xc2, 0xd9, 0xff, 0x88, 0x9e, 0xbd, 0xff, 0x83, 0x8e, 0xa9, 0xff, 0x7d, 0x7e, 0x90, 0xff, 0x75, 0x72, 0x81, 0xff, 0x75, 0x75, 0x85, 0xff, 0x6d, 0x6c, 0x7f, 0xff, 0x3b, 0x37, 0x50, 0xff, 0x5e, 0x63, 0x7a, 0xff, 0xa4, 0xb0, 0xbe, 0xff, 0x88, 0x88, 0x8d, 0xff, 0x22, 0x10, 0x15, 0xff, 0x75, 0x77, 0x83, 0xff, 0xd0, 0xe8, 0xf9, 0xff, 0xbd, 0xd8, 0xe7, 0xff, 0xbf, 0xda, 0xe8, 0xff, 0xc0, 0xda, 0xe9, 0xff, 0xc0, 0xdb, 0xe9, 0xff, 0xc0, 0xd9, 0xe5, 0xff, 0xc0, 0xda, 0xe2, 0xff, 0xc1, 0xda, 0xe4, 0xff, 0xc3, 0xda, 0xe4, 0xff, 0xc6, 0xdc, 0xe7, 0xff, 0xc7, 0xdd, 0xe9, 0xff, 0xc8, 0xde, 0xea, 0xff, 0xc6, 0xdc, 0xe8, 0xff, 0xc8, 0xde, 0xea, 0xff, 0xcd, 0xe6, 0xed, 0xff, 0xd1, 0xea, 0xef, 0xff, 0xd1, 0xe9, 0xef, 0xff, 0xcd, 0xe4, 0xec, 0xff, 0xca, 0xdf, 0xe8, 0xff, 0xc8, 0xdd, 0xe4, 0xff, 0xc7, 0xdb, 0xe3, 0xff, 0xc8, 0xda, 0xe3, 0xff, 0xc7, 0xda, 0xe2, 0xff, 0xc4, 0xda, 0xe1, 0xff, 0xc4, 0xd9, 0xe0, 0xff, 0xc5, 0xda, 0xe2, 0xff, 0xc7, 0xdc, 0xe3, 0xff, 0xc5, 0xdb, 0xe2, 0xff, 0xc6, 0xdc, 0xe3, 0xff, 0xc5, 0xd9, 0xe1, 0xff, 0xc1, 0xd5, 0xdd, 0xff, 0xc0, 0xd2, 0xda, 0xff, 0xc0, 0xd2, 0xdd, 0xff, 0xc1, 0xd5, 0xe0, 0xff, 0xc3, 0xd7, 0xe1, 0xff, 0xc3, 0xd8, 0xdf, 0xff, 0xc4, 0xd9, 0xe0, 0xff, 0xc6, 0xdb, 0xe3, 0xff, 0xc7, 0xdc, 0xe3, 0xff, 0xc6, 0xda, 0xe2, 0xff, 0xc5, 0xdb, 0xe0, 0xff, 0xc6, 0xdb, 0xe2, 0xff, 0xc5, 0xda, 0xe2, 0xff, 0xc3, 0xd8, 0xe0, 0xff, 0xc1, 0xd6, 0xe0, 0xff, 0xbf, 0xd3, 0xe0, 0xff, 0xbd, 0xce, 0xdc, 0xff, 0xbd, 0xcc, 0xdb, 0xff, 0xb9, 0xc9, 0xd8, 0xff, 0xb1, 0xc1, 0xd1, 0xff, 0xaa, 0xb9, 0xcd, 0xff, 0xaa, 0xba, 0xcd, 0xff, 0xa3, 0xb1, 0xc6, 0xff, 0x99, 0xa7, 0xba, 0xff, 0x97, 0xa3, 0xb7, 0xff, 0x90, 0x96, 0xad, 0xff, 0x8c, 0x94, 0xa2, 0xff, 0x96, 0xa0, 0xa7, 0xff, 0xa7, 0xb3, 0xb8, 0xff, 0xb3, 0xc4, 0xc2, 0xff, 0xa9, 0xb5, 0xb1, 0xff, 0xa4, 0xac, 0xa6, 0xff, 0x9b, 0x9e, 0x97, 0xff, 0xa0, 0xa9, 0x9f, 0xff, 0xa6, 0xaf, 0xa4, 0xff, 0xa3, 0xae, 0xa7, 0xff, 0xa6, 0xb2, 0xa9, 0xff, 0x99, 0x9b, 0x90, 0xff, 0xa1, 0xa8, 0x9c, 0xff, 0xa5, 0xa5, 0x92, 0xff, 0x95, 0x92, 0x79, 0xff, 0xa8, 0xaa, 0x95, 0xff, 0xb0, 0xb4, 0xa2, 0xff, 0xa3, 0xa5, 0x98, 0xff, 0xae, 0xb4, 0xa8, 0xff, 0xb5, 0xc4, 0xb3, 0xff, 0xc2, 0xd2, 0xc4, 0xff, 0xa2, 0xa1, 0x8c, 0xff, 0x80, 0x74, 0x59, 0xff, 0x9a, 0x96, 0x7e, 0xff, 0xa9, 0xb0, 0xa1, 0xff, 0xa3, 0xae, 0xa4, 0xff, 0x9a, 0xa0, 0x93, 0xff, 0x9f, 0xa0, 0x90, 0xff, 0x98, 0x99, 0x8c, 0xff, 0xa2, 0xa6, 0x97, 0xff, 0x97, 0x96, 0x81, 0xff, 0x88, 0x85, 0x72, 0xff, 0xb0, 0xb4, 0xa7, 0xff, 0xee, 0xef, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0xeb, 0xe5, 0xff, 0x9d, 0x90, 0x6f, 0xff, 0x93, 0x83, 0x5e, 0xff, 0x96, 0x85, 0x5d, 0xff, 0x93, 0x83, 0x5c, 0xff, 0x90, 0x7d, 0x57, 0xff, 0x8d, 0x7a, 0x55, 0xff, 0x8a, 0x76, 0x51, 0xff, 0x89, 0x73, 0x4b, 0xff, 0x8b, 0x74, 0x4c, 0xff, 0x8d, 0x78, 0x50, 0xff, 0x94, 0x80, 0x55, 0xff, 0xad, 0xa0, 0x71, 0xff, 0xbd, 0xb4, 0x86, 0xff, 0xb6, 0xa9, 0x79, 0xff, 0x9f, 0x8a, 0x5b, 0xff, 0x93, 0x7d, 0x4d, 0xff, 0x8e, 0x7c, 0x4c, 0xff, 0xc6, 0xc3, 0xa3, 0xff, 0xda, 0xe1, 0xcb, 0xff, 0xa5, 0x9b, 0x6f, 0xff, 0xae, 0xa1, 0x75, 0xff, 0xb5, 0xae, 0x8a, 0xff, 0xb2, 0xa8, 0x7f, 0xff, 0xa7, 0x99, 0x70, 0xff, 0xac, 0xa2, 0x7d, 0xff, 0xbe, 0xbe, 0x9b, 0xff, 0xb2, 0xae, 0x88, 0xff, 0xbc, 0xb6, 0x94, 0xff, 0xb0, 0xab, 0x8a, 0xff, 0xb1, 0xbf, 0xc1, 0xff, 0x8b, 0xa4, 0xc8, 0xff, 0x7e, 0x8d, 0xae, 0xff, 0x7f, 0x86, 0x9c, 0xff, 0x79, 0x75, 0x83, 0xff, 0x75, 0x72, 0x82, 0xff, 0x77, 0x7c, 0x90, 0xff, 0x47, 0x47, 0x5c, 0xff, 0x56, 0x52, 0x6d, 0xff, 0x8e, 0x9a, 0xa9, 0xff, 0xb3, 0xc2, 0xc1, 0xff, 0x66, 0x61, 0x63, 0xff, 0x24, 0x0e, 0x15, 0xff, 0x7e, 0x81, 0x8e, 0xff, 0xc8, 0xe2, 0xf3, 0xff, 0xbd, 0xd6, 0xe5, 0xff, 0xbf, 0xd6, 0xe5, 0xff, 0xc1, 0xd8, 0xe7, 0xff, 0xc2, 0xd9, 0xe8, 0xff, 0xc2, 0xd9, 0xe7, 0xff, 0xc4, 0xdb, 0xe9, 0xff, 0xc5, 0xdd, 0xea, 0xff, 0xc5, 0xdf, 0xec, 0xff, 0xc5, 0xde, 0xeb, 0xff, 0xc6, 0xde, 0xea, 0xff, 0xc7, 0xde, 0xea, 0xff, 0xc7, 0xdd, 0xe9, 0xff, 0xcb, 0xe1, 0xec, 0xff, 0xd1, 0xe8, 0xef, 0xff, 0xd2, 0xeb, 0xef, 0xff, 0xd1, 0xe9, 0xee, 0xff, 0xd0, 0xe5, 0xed, 0xff, 0xcd, 0xe2, 0xea, 0xff, 0xcb, 0xdf, 0xe7, 0xff, 0xc9, 0xdb, 0xe3, 0xff, 0xc8, 0xda, 0xe2, 0xff, 0xc6, 0xda, 0xe2, 0xff, 0xc3, 0xd8, 0xe1, 0xff, 0xc5, 0xda, 0xe3, 0xff, 0xc6, 0xdb, 0xe3, 0xff, 0xc6, 0xda, 0xe4, 0xff, 0xc7, 0xde, 0xe7, 0xff, 0xc7, 0xde, 0xe6, 0xff, 0xc7, 0xdd, 0xe4, 0xff, 0xc8, 0xdd, 0xe4, 0xff, 0xc6, 0xdc, 0xe4, 0xff, 0xc4, 0xd9, 0xe5, 0xff, 0xc2, 0xd7, 0xe3, 0xff, 0xc1, 0xd8, 0xe2, 0xff, 0xc2, 0xd8, 0xe2, 0xff, 0xc5, 0xda, 0xe5, 0xff, 0xc7, 0xdd, 0xe5, 0xff, 0xc5, 0xdb, 0xe3, 0xff, 0xc4, 0xd9, 0xe1, 0xff, 0xc6, 0xda, 0xe3, 0xff, 0xc3, 0xd8, 0xe1, 0xff, 0xc3, 0xd7, 0xe1, 0xff, 0xc1, 0xd5, 0xe1, 0xff, 0xc1, 0xd5, 0xe0, 0xff, 0xbd, 0xd1, 0xde, 0xff, 0xba, 0xcb, 0xdb, 0xff, 0xb8, 0xc7, 0xd8, 0xff, 0xb4, 0xc4, 0xd4, 0xff, 0xac, 0xbc, 0xce, 0xff, 0xa8, 0xb6, 0xcc, 0xff, 0xa3, 0xb1, 0xc7, 0xff, 0x96, 0xa3, 0xbb, 0xff, 0x95, 0xa3, 0xb8, 0xff, 0x99, 0xa7, 0xb5, 0xff, 0x94, 0x9a, 0xa7, 0xff, 0x93, 0x9c, 0xa1, 0xff, 0xa5, 0xb4, 0xb5, 0xff, 0xab, 0xb9, 0xb8, 0xff, 0xb0, 0xbf, 0xbd, 0xff, 0xa7, 0xb2, 0xaf, 0xff, 0xaa, 0xb5, 0xad, 0xff, 0xa7, 0xad, 0xa6, 0xff, 0xa2, 0xab, 0xa1, 0xff, 0xab, 0xb5, 0xa9, 0xff, 0x94, 0x99, 0x8f, 0xff, 0x9a, 0xa3, 0x96, 0xff, 0x9c, 0x9f, 0x92, 0xff, 0x9d, 0xa4, 0x9b, 0xff, 0xad, 0xb3, 0xa0, 0xff, 0xa1, 0x9f, 0x88, 0xff, 0x9f, 0x9f, 0x8f, 0xff, 0xa6, 0xae, 0x9f, 0xff, 0xa1, 0xa7, 0x9b, 0xff, 0xb6, 0xc2, 0xb7, 0xff, 0xab, 0xb4, 0xa5, 0xff, 0xb1, 0xbb, 0xad, 0xff, 0xab, 0xaf, 0x9a, 0xff, 0x86, 0x7a, 0x5f, 0xff, 0xa4, 0xa2, 0x8f, 0xff, 0xac, 0xb8, 0xac, 0xff, 0x9f, 0xac, 0xa1, 0xff, 0xa1, 0xa8, 0x9b, 0xff, 0x98, 0x9b, 0x8c, 0xff, 0x91, 0x94, 0x8a, 0xff, 0xa9, 0xaf, 0xa1, 0xff, 0x8b, 0x88, 0x74, 0xff, 0x87, 0x82, 0x70, 0xff, 0xc2, 0xc7, 0xbc, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe7, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xb4, 0xa8, 0x8f, 0xff, 0x95, 0x83, 0x5e, 0xff, 0x95, 0x84, 0x5d, 0xff, 0x96, 0x84, 0x5d, 0xff, 0x96, 0x84, 0x5e, 0xff, 0x8c, 0x79, 0x52, 0xff, 0x85, 0x71, 0x48, 0xff, 0x89, 0x71, 0x49, 0xff, 0x8d, 0x74, 0x4b, 0xff, 0x93, 0x7a, 0x50, 0xff, 0x95, 0x7e, 0x55, 0xff, 0x9e, 0x8c, 0x5f, 0xff, 0xb9, 0xae, 0x7e, 0xff, 0xd0, 0xca, 0x9d, 0xff, 0xce, 0xc9, 0x9b, 0xff, 0xc0, 0xb6, 0x85, 0xff, 0xb8, 0xaf, 0x80, 0xff, 0xda, 0xe4, 0xcd, 0xff, 0xc5, 0xc7, 0xac, 0xff, 0xae, 0xa4, 0x72, 0xff, 0xbf, 0xbc, 0x8e, 0xff, 0xaf, 0xa4, 0x7a, 0xff, 0xab, 0xa0, 0x77, 0xff, 0xb7, 0xac, 0x85, 0xff, 0xbf, 0xbc, 0x99, 0xff, 0xbe, 0xbc, 0x9d, 0xff, 0x9f, 0x93, 0x66, 0xff, 0xa7, 0xa2, 0x7e, 0xff, 0xa7, 0xa4, 0x85, 0xff, 0xa5, 0xb2, 0xb6, 0xff, 0x93, 0xaa, 0xcd, 0xff, 0x85, 0x90, 0xaa, 0xff, 0x73, 0x74, 0x87, 0xff, 0x6f, 0x6c, 0x7c, 0xff, 0x89, 0x8d, 0xa1, 0xff, 0x64, 0x68, 0x7d, 0xff, 0x45, 0x44, 0x5b, 0xff, 0x7b, 0x7d, 0x96, 0xff, 0xa5, 0xb4, 0xb5, 0xff, 0x9c, 0xa8, 0xa4, 0xff, 0x40, 0x33, 0x3b, 0xff, 0x38, 0x27, 0x2b, 0xff, 0x7b, 0x7d, 0x8a, 0xff, 0xc3, 0xdb, 0xed, 0xff, 0xbe, 0xd3, 0xe4, 0xff, 0xbe, 0xd5, 0xe4, 0xff, 0xc1, 0xd7, 0xe6, 0xff, 0xc1, 0xda, 0xe9, 0xff, 0xc3, 0xdc, 0xeb, 0xff, 0xc5, 0xde, 0xed, 0xff, 0xc4, 0xde, 0xeb, 0xff, 0xc4, 0xde, 0xea, 0xff, 0xc6, 0xde, 0xea, 0xff, 0xc7, 0xe0, 0xeb, 0xff, 0xc7, 0xdd, 0xe9, 0xff, 0xc7, 0xdd, 0xe9, 0xff, 0xcc, 0xe2, 0xed, 0xff, 0xd1, 0xe9, 0xee, 0xff, 0xd2, 0xea, 0xef, 0xff, 0xd0, 0xe9, 0xee, 0xff, 0xcf, 0xe6, 0xec, 0xff, 0xcf, 0xe2, 0xec, 0xff, 0xce, 0xe0, 0xea, 0xff, 0xcb, 0xde, 0xe5, 0xff, 0xc6, 0xda, 0xe1, 0xff, 0xc3, 0xd7, 0xe1, 0xff, 0xc1, 0xd5, 0xe1, 0xff, 0xc3, 0xd7, 0xe2, 0xff, 0xc4, 0xd8, 0xe4, 0xff, 0xc2, 0xd8, 0xe4, 0xff, 0xc5, 0xdb, 0xe6, 0xff, 0xc7, 0xde, 0xe8, 0xff, 0xc8, 0xde, 0xe8, 0xff, 0xc8, 0xdd, 0xe7, 0xff, 0xc5, 0xdd, 0xe6, 0xff, 0xc7, 0xde, 0xe9, 0xff, 0xc7, 0xdc, 0xe8, 0xff, 0xc5, 0xdc, 0xe6, 0xff, 0xc4, 0xdb, 0xe5, 0xff, 0xc6, 0xdd, 0xe6, 0xff, 0xc3, 0xda, 0xe4, 0xff, 0xc2, 0xd9, 0xe2, 0xff, 0xc2, 0xd9, 0xe3, 0xff, 0xc0, 0xd5, 0xe4, 0xff, 0xbe, 0xd3, 0xe1, 0xff, 0xc0, 0xd3, 0xe1, 0xff, 0xbf, 0xd2, 0xe0, 0xff, 0xbd, 0xcf, 0xdf, 0xff, 0xb8, 0xcb, 0xda, 0xff, 0xb7, 0xc9, 0xd8, 0xff, 0xb3, 0xc3, 0xd3, 0xff, 0xa9, 0xb6, 0xcb, 0xff, 0xa4, 0xb0, 0xcb, 0xff, 0x9f, 0xae, 0xc5, 0xff, 0x94, 0xa1, 0xba, 0xff, 0x8f, 0x98, 0xb7, 0xff, 0x8d, 0x9a, 0xb2, 0xff, 0x9e, 0xaa, 0xb4, 0xff, 0xa3, 0xa9, 0xad, 0xff, 0x9f, 0xaa, 0xac, 0xff, 0xad, 0xbb, 0xbc, 0xff, 0xab, 0xb7, 0xb5, 0xff, 0xa8, 0xb3, 0xae, 0xff, 0xa9, 0xb5, 0xaf, 0xff, 0xa9, 0xb4, 0xad, 0xff, 0xb1, 0xb8, 0xb3, 0xff, 0xa8, 0xb4, 0xac, 0xff, 0xaf, 0xb6, 0xab, 0xff, 0x89, 0x89, 0x7c, 0xff, 0x94, 0x97, 0x85, 0xff, 0x93, 0x93, 0x86, 0xff, 0xa3, 0xac, 0xa5, 0xff, 0xa7, 0xac, 0x9c, 0xff, 0x8c, 0x89, 0x74, 0xff, 0x9e, 0xa2, 0x92, 0xff, 0x9a, 0xa1, 0x95, 0xff, 0x9c, 0xa0, 0x97, 0xff, 0xac, 0xb7, 0xad, 0xff, 0xa3, 0xac, 0xa0, 0xff, 0xb2, 0xbc, 0xad, 0xff, 0xb0, 0xb7, 0xa1, 0xff, 0x8a, 0x81, 0x68, 0xff, 0xa7, 0xaa, 0x9c, 0xff, 0xac, 0xb6, 0xac, 0xff, 0x9e, 0xa8, 0x9b, 0xff, 0xa3, 0xac, 0x9f, 0xff, 0x97, 0x9a, 0x90, 0xff, 0x94, 0x97, 0x8e, 0xff, 0xa1, 0xa5, 0x93, 0xff, 0x90, 0x8a, 0x72, 0xff, 0x95, 0x90, 0x81, 0xff, 0xd0, 0xd3, 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xcc, 0xc3, 0xb1, 0xff, 0x98, 0x85, 0x60, 0xff, 0x94, 0x82, 0x5c, 0xff, 0x99, 0x86, 0x60, 0xff, 0xa2, 0x90, 0x6a, 0xff, 0xa4, 0x92, 0x6a, 0xff, 0x96, 0x84, 0x5a, 0xff, 0x86, 0x6f, 0x46, 0xff, 0x86, 0x6d, 0x43, 0xff, 0x8f, 0x78, 0x4e, 0xff, 0x96, 0x83, 0x58, 0xff, 0x9d, 0x88, 0x5a, 0xff, 0x9d, 0x88, 0x58, 0xff, 0x9c, 0x91, 0x62, 0xff, 0xae, 0xa7, 0x79, 0xff, 0xc1, 0xb2, 0x7d, 0xff, 0xc9, 0xc0, 0x94, 0xff, 0xe2, 0xf3, 0xe9, 0xff, 0xcd, 0xc8, 0xa5, 0xff, 0xb0, 0x9f, 0x6d, 0xff, 0xbc, 0xb4, 0x89, 0xff, 0xb5, 0xb1, 0x8b, 0xff, 0xb9, 0xb2, 0x91, 0xff, 0xa9, 0x9f, 0x77, 0xff, 0xab, 0xa4, 0x7d, 0xff, 0xbd, 0xb5, 0x8c, 0xff, 0xb1, 0xa2, 0x73, 0xff, 0xa6, 0xa1, 0x7e, 0xff, 0x9d, 0x99, 0x7d, 0xff, 0xa3, 0xae, 0xae, 0xff, 0x98, 0xab, 0xc3, 0xff, 0x7f, 0x85, 0x99, 0xff, 0x74, 0x73, 0x86, 0xff, 0x7c, 0x7f, 0x92, 0xff, 0x85, 0x8a, 0x9f, 0xff, 0x56, 0x52, 0x68, 0xff, 0x5f, 0x5e, 0x77, 0xff, 0x87, 0x94, 0xa4, 0xff, 0xb4, 0xc4, 0xbd, 0xff, 0x72, 0x70, 0x72, 0xff, 0x32, 0x21, 0x2b, 0xff, 0x50, 0x41, 0x44, 0xff, 0x63, 0x61, 0x6f, 0xff, 0xb9, 0xd0, 0xe4, 0xff, 0xbd, 0xd3, 0xe5, 0xff, 0xbc, 0xd3, 0xe2, 0xff, 0xbf, 0xd6, 0xe4, 0xff, 0xc0, 0xda, 0xe9, 0xff, 0xc1, 0xdc, 0xeb, 0xff, 0xc2, 0xdd, 0xeb, 0xff, 0xc2, 0xdd, 0xeb, 0xff, 0xc6, 0xde, 0xea, 0xff, 0xc6, 0xde, 0xeb, 0xff, 0xc4, 0xdb, 0xe9, 0xff, 0xc6, 0xdc, 0xe8, 0xff, 0xcb, 0xe1, 0xed, 0xff, 0xcf, 0xe5, 0xee, 0xff, 0xd1, 0xe9, 0xed, 0xff, 0xd1, 0xea, 0xef, 0xff, 0xd0, 0xe7, 0xee, 0xff, 0xd2, 0xe8, 0xed, 0xff, 0xd0, 0xe5, 0xed, 0xff, 0xcc, 0xe1, 0xea, 0xff, 0xc9, 0xdd, 0xe4, 0xff, 0xc6, 0xd6, 0xe1, 0xff, 0xc1, 0xd1, 0xdf, 0xff, 0xbf, 0xd1, 0xde, 0xff, 0xc0, 0xd4, 0xe0, 0xff, 0xc2, 0xd6, 0xe3, 0xff, 0xc2, 0xd7, 0xe4, 0xff, 0xc4, 0xda, 0xe5, 0xff, 0xc7, 0xdd, 0xe8, 0xff, 0xc7, 0xdd, 0xe9, 0xff, 0xc7, 0xdd, 0xe9, 0xff, 0xc5, 0xde, 0xe8, 0xff, 0xc7, 0xde, 0xe9, 0xff, 0xc8, 0xde, 0xe9, 0xff, 0xc5, 0xdb, 0xe6, 0xff, 0xc5, 0xdb, 0xe5, 0xff, 0xc2, 0xd9, 0xe3, 0xff, 0xc0, 0xd5, 0xe3, 0xff, 0xbe, 0xd3, 0xe2, 0xff, 0xbd, 0xd2, 0xe2, 0xff, 0xbc, 0xd1, 0xe2, 0xff, 0xbd, 0xd0, 0xe1, 0xff, 0xbc, 0xce, 0xde, 0xff, 0xb8, 0xcb, 0xdb, 0xff, 0xb8, 0xca, 0xda, 0xff, 0xb5, 0xc8, 0xd8, 0xff, 0xb5, 0xc6, 0xd7, 0xff, 0xad, 0xbd, 0xce, 0xff, 0xa5, 0xb3, 0xc4, 0xff, 0xa3, 0xb0, 0xc8, 0xff, 0x97, 0xa4, 0xc0, 0xff, 0x8e, 0x9b, 0xb5, 0xff, 0x90, 0x9a, 0xb1, 0xff, 0x9d, 0xaa, 0xb8, 0xff, 0xa9, 0xb4, 0xb6, 0xff, 0xa2, 0xa6, 0xa7, 0xff, 0x9f, 0xaa, 0xad, 0xff, 0xaf, 0xbd, 0xbe, 0xff, 0xaf, 0xba, 0xb9, 0xff, 0xa5, 0xaf, 0xaa, 0xff, 0xa8, 0xb5, 0xae, 0xff, 0xae, 0xb8, 0xb2, 0xff, 0xb1, 0xbb, 0xb6, 0xff, 0xab, 0xb8, 0xb1, 0xff, 0xa6, 0xab, 0xa1, 0xff, 0x8c, 0x8c, 0x7c, 0xff, 0x9d, 0xa2, 0x8d, 0xff, 0x8f, 0x90, 0x85, 0xff, 0xa1, 0xad, 0xa7, 0xff, 0x95, 0x98, 0x8a, 0xff, 0x88, 0x87, 0x75, 0xff, 0x9c, 0xa5, 0x95, 0xff, 0x9a, 0xa5, 0x9a, 0xff, 0x9a, 0x9f, 0x98, 0xff, 0xa0, 0xad, 0xa3, 0xff, 0xa6, 0xb0, 0xa4, 0xff, 0xbb, 0xc3, 0xb2, 0xff, 0xa4, 0xa8, 0x93, 0xff, 0x89, 0x86, 0x71, 0xff, 0xa4, 0xab, 0xa0, 0xff, 0xa7, 0xaf, 0xa5, 0xff, 0x9d, 0xa3, 0x96, 0xff, 0x9a, 0xa4, 0x98, 0xff, 0x96, 0x9c, 0x93, 0xff, 0x9b, 0x9f, 0x95, 0xff, 0x93, 0x92, 0x7e, 0xff, 0x93, 0x8e, 0x74, 0xff, 0x96, 0x95, 0x85, 0xff, 0xe4, 0xe6, 0xe3, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xde, 0xd4, 0xff, 0x9d, 0x8b, 0x67, 0xff, 0x94, 0x81, 0x5c, 0xff, 0x8d, 0x7a, 0x54, 0xff, 0x91, 0x7d, 0x57, 0xff, 0x9f, 0x8b, 0x63, 0xff, 0xa7, 0x96, 0x6b, 0xff, 0xa6, 0x92, 0x67, 0xff, 0xa0, 0x8a, 0x60, 0xff, 0x99, 0x85, 0x5b, 0xff, 0x95, 0x7d, 0x50, 0xff, 0x91, 0x7b, 0x4c, 0xff, 0x97, 0x83, 0x53, 0xff, 0x9f, 0x83, 0x53, 0xff, 0x9c, 0x80, 0x51, 0xff, 0x90, 0x76, 0x3e, 0xff, 0xb2, 0xa8, 0x85, 0xff, 0xe5, 0xf5, 0xf0, 0xff, 0xa3, 0x97, 0x6b, 0xff, 0x8c, 0x71, 0x40, 0xff, 0xa4, 0x94, 0x6d, 0xff, 0xc4, 0xc9, 0xa6, 0xff, 0xbb, 0xb6, 0x97, 0xff, 0xa2, 0x95, 0x70, 0xff, 0xb0, 0xa2, 0x75, 0xff, 0xa5, 0x96, 0x67, 0xff, 0x9b, 0x8b, 0x5e, 0xff, 0xb8, 0xb3, 0x93, 0xff, 0xbc, 0xbb, 0xa1, 0xff, 0xb3, 0xba, 0xb4, 0xff, 0x99, 0xa3, 0xae, 0xff, 0x87, 0x89, 0x97, 0xff, 0x77, 0x75, 0x88, 0xff, 0x7c, 0x81, 0x95, 0xff, 0x73, 0x76, 0x8a, 0xff, 0x63, 0x5d, 0x73, 0xff, 0x77, 0x7c, 0x92, 0xff, 0x9b, 0xb1, 0xb8, 0xff, 0xa5, 0xb3, 0xaf, 0xff, 0x4e, 0x3e, 0x45, 0xff, 0x45, 0x35, 0x3e, 0xff, 0x5b, 0x4d, 0x55, 0xff, 0x4e, 0x46, 0x52, 0xff, 0xaa, 0xbf, 0xd2, 0xff, 0xbf, 0xd7, 0xea, 0xff, 0xbc, 0xd3, 0xe2, 0xff, 0xc0, 0xd7, 0xe6, 0xff, 0xbf, 0xd8, 0xe7, 0xff, 0xc0, 0xda, 0xe9, 0xff, 0xc1, 0xdb, 0xe9, 0xff, 0xc2, 0xdc, 0xea, 0xff, 0xc6, 0xde, 0xec, 0xff, 0xc4, 0xdb, 0xec, 0xff, 0xc1, 0xd8, 0xe7, 0xff, 0xc9, 0xdf, 0xea, 0xff, 0xd0, 0xe7, 0xef, 0xff, 0xce, 0xe6, 0xed, 0xff, 0xd0, 0xe9, 0xed, 0xff, 0xd2, 0xea, 0xf0, 0xff, 0xd3, 0xe9, 0xf0, 0xff, 0xd4, 0xe9, 0xef, 0xff, 0xd1, 0xe9, 0xee, 0xff, 0xcb, 0xe3, 0xe9, 0xff, 0xc6, 0xd9, 0xe3, 0xff, 0xc0, 0xcf, 0xdc, 0xff, 0xba, 0xc8, 0xd8, 0xff, 0xba, 0xc9, 0xda, 0xff, 0xbc, 0xcd, 0xde, 0xff, 0xc0, 0xd3, 0xe3, 0xff, 0xc3, 0xd6, 0xe4, 0xff, 0xc2, 0xd8, 0xe5, 0xff, 0xc5, 0xdb, 0xe7, 0xff, 0xc8, 0xde, 0xe9, 0xff, 0xc7, 0xdc, 0xe7, 0xff, 0xc6, 0xde, 0xe9, 0xff, 0xc5, 0xdc, 0xe7, 0xff, 0xc6, 0xdc, 0xe8, 0xff, 0xc4, 0xda, 0xe6, 0xff, 0xc1, 0xd7, 0xe3, 0xff, 0xbf, 0xd4, 0xe3, 0xff, 0xbd, 0xd1, 0xe1, 0xff, 0xbb, 0xcf, 0xe1, 0xff, 0xba, 0xcd, 0xdf, 0xff, 0xba, 0xcc, 0xde, 0xff, 0xbc, 0xcd, 0xe0, 0xff, 0xb7, 0xc8, 0xda, 0xff, 0xb3, 0xc4, 0xd6, 0xff, 0xb3, 0xc6, 0xd5, 0xff, 0xb0, 0xc3, 0xd5, 0xff, 0xaa, 0xb9, 0xcf, 0xff, 0xa5, 0xb3, 0xc8, 0xff, 0xac, 0xbd, 0xcb, 0xff, 0xaa, 0xba, 0xc8, 0xff, 0x8f, 0x9a, 0xb5, 0xff, 0x8c, 0x97, 0xad, 0xff, 0xa4, 0xb1, 0xba, 0xff, 0xb8, 0xc8, 0xc9, 0xff, 0xac, 0xb8, 0xb5, 0xff, 0x97, 0x9c, 0x9b, 0xff, 0x9e, 0xaa, 0xaa, 0xff, 0xad, 0xbb, 0xbb, 0xff, 0xae, 0xbc, 0xba, 0xff, 0xaa, 0xb9, 0xb5, 0xff, 0xa8, 0xb6, 0xb0, 0xff, 0xae, 0xb9, 0xb3, 0xff, 0xa9, 0xb4, 0xaf, 0xff, 0xaa, 0xb8, 0xb0, 0xff, 0xa2, 0xa8, 0x9d, 0xff, 0x9f, 0xa0, 0x91, 0xff, 0xab, 0xb3, 0xa0, 0xff, 0x95, 0x9b, 0x90, 0xff, 0xa7, 0xb3, 0xaa, 0xff, 0x81, 0x83, 0x76, 0xff, 0x8a, 0x89, 0x7a, 0xff, 0x9a, 0xa2, 0x95, 0xff, 0xa0, 0xab, 0x9f, 0xff, 0x93, 0x9b, 0x92, 0xff, 0xa5, 0xb1, 0xa7, 0xff, 0xb6, 0xba, 0xaf, 0xff, 0x94, 0x94, 0x86, 0xff, 0x88, 0x8a, 0x78, 0xff, 0x99, 0x9c, 0x8d, 0xff, 0x9e, 0xa7, 0x9e, 0xff, 0x88, 0x8e, 0x82, 0xff, 0x93, 0x98, 0x8b, 0xff, 0x9b, 0xa6, 0x9c, 0xff, 0x9e, 0xa6, 0x9d, 0xff, 0xa2, 0xa7, 0x9b, 0xff, 0x89, 0x86, 0x72, 0xff, 0x92, 0x90, 0x7a, 0xff, 0xa2, 0xa4, 0x96, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf1, 0xed, 0xff, 0xaa, 0x9d, 0x80, 0xff, 0x94, 0x81, 0x5b, 0xff, 0x9d, 0x89, 0x63, 0xff, 0xa2, 0x8c, 0x66, 0xff, 0x9d, 0x89, 0x61, 0xff, 0x9a, 0x85, 0x5b, 0xff, 0xa3, 0x8c, 0x62, 0xff, 0xaf, 0x98, 0x6c, 0xff, 0xb3, 0x9e, 0x71, 0xff, 0xbc, 0xaf, 0x80, 0xff, 0xbc, 0xb0, 0x82, 0xff, 0xaf, 0x9c, 0x6f, 0xff, 0xa1, 0x8a, 0x59, 0xff, 0x99, 0x7c, 0x4b, 0xff, 0x90, 0x77, 0x47, 0xff, 0xd6, 0xdb, 0xc3, 0xff, 0xdc, 0xe3, 0xc9, 0xff, 0xa6, 0x93, 0x60, 0xff, 0x96, 0x81, 0x53, 0xff, 0xab, 0x9f, 0x77, 0xff, 0xb6, 0xa8, 0x81, 0xff, 0xa4, 0x97, 0x6c, 0xff, 0xa0, 0x8d, 0x5f, 0xff, 0x9d, 0x8e, 0x62, 0xff, 0xa8, 0x9b, 0x72, 0xff, 0x98, 0x87, 0x5d, 0xff, 0xa3, 0x9b, 0x78, 0xff, 0xc2, 0xc3, 0xa8, 0xff, 0xbf, 0xc2, 0xb6, 0xff, 0xae, 0xb2, 0xb1, 0xff, 0x82, 0x85, 0x8e, 0xff, 0x5d, 0x57, 0x6a, 0xff, 0x57, 0x50, 0x69, 0xff, 0x5b, 0x5d, 0x72, 0xff, 0x81, 0x87, 0x95, 0xff, 0xa1, 0xb2, 0xba, 0xff, 0xb0, 0xc3, 0xc8, 0xff, 0x76, 0x7a, 0x81, 0xff, 0x33, 0x20, 0x27, 0xff, 0x54, 0x49, 0x51, 0xff, 0x55, 0x4a, 0x57, 0xff, 0x3a, 0x2d, 0x37, 0xff, 0x9c, 0xaf, 0xbc, 0xff, 0xc7, 0xdf, 0xf3, 0xff, 0xbb, 0xd3, 0xe2, 0xff, 0xbf, 0xd6, 0xe5, 0xff, 0xbe, 0xd7, 0xe6, 0xff, 0xc0, 0xda, 0xe9, 0xff, 0xc1, 0xdb, 0xe9, 0xff, 0xc0, 0xdb, 0xe9, 0xff, 0xc4, 0xdb, 0xeb, 0xff, 0xc1, 0xd8, 0xe9, 0xff, 0xc2, 0xd9, 0xe9, 0xff, 0xcf, 0xe5, 0xec, 0xff, 0xd1, 0xe8, 0xee, 0xff, 0xcf, 0xe7, 0xee, 0xff, 0xd2, 0xe9, 0xee, 0xff, 0xd6, 0xeb, 0xf0, 0xff, 0xd4, 0xe9, 0xef, 0xff, 0xd3, 0xe8, 0xee, 0xff, 0xd1, 0xe6, 0xed, 0xff, 0xc9, 0xde, 0xe8, 0xff, 0xc4, 0xd6, 0xe2, 0xff, 0xbe, 0xcf, 0xda, 0xff, 0xc1, 0xd3, 0xdb, 0xff, 0xc2, 0xd4, 0xdd, 0xff, 0xb7, 0xc7, 0xd6, 0xff, 0xb2, 0xc2, 0xd4, 0xff, 0xbc, 0xcf, 0xe0, 0xff, 0xc0, 0xd4, 0xe3, 0xff, 0xc3, 0xd9, 0xe5, 0xff, 0xc6, 0xdd, 0xe8, 0xff, 0xc5, 0xdb, 0xe6, 0xff, 0xc3, 0xda, 0xe7, 0xff, 0xc4, 0xdb, 0xe7, 0xff, 0xc4, 0xdb, 0xe6, 0xff, 0xc3, 0xda, 0xe5, 0xff, 0xc1, 0xd7, 0xe3, 0xff, 0xbe, 0xd3, 0xe1, 0xff, 0xbc, 0xd0, 0xe0, 0xff, 0xb9, 0xcd, 0xdf, 0xff, 0xb8, 0xcc, 0xde, 0xff, 0xb7, 0xc9, 0xda, 0xff, 0xb4, 0xc6, 0xd7, 0xff, 0xb5, 0xc5, 0xd5, 0xff, 0xb5, 0xc4, 0xd5, 0xff, 0xad, 0xbf, 0xd2, 0xff, 0xa4, 0xb4, 0xcd, 0xff, 0xa2, 0xb1, 0xca, 0xff, 0xa2, 0xb0, 0xc4, 0xff, 0xa4, 0xb4, 0xc7, 0xff, 0xb4, 0xc7, 0xce, 0xff, 0x94, 0x9f, 0xad, 0xff, 0x94, 0x9c, 0xac, 0xff, 0xa4, 0xb2, 0xb4, 0xff, 0xb8, 0xcc, 0xcc, 0xff, 0xb3, 0xc3, 0xc2, 0xff, 0x9e, 0xa9, 0xa8, 0xff, 0xa4, 0xb1, 0xb1, 0xff, 0xae, 0xbb, 0xb9, 0xff, 0xae, 0xc0, 0xbd, 0xff, 0xac, 0xbd, 0xba, 0xff, 0xab, 0xbb, 0xb5, 0xff, 0xa4, 0xb3, 0xac, 0xff, 0xab, 0xb7, 0xb1, 0xff, 0xaa, 0xb9, 0xb1, 0xff, 0x9f, 0xa5, 0x9a, 0xff, 0xa5, 0xa8, 0x9c, 0xff, 0xb1, 0xc0, 0xb1, 0xff, 0xab, 0xb6, 0xac, 0xff, 0xad, 0xb6, 0xa8, 0xff, 0x7b, 0x76, 0x66, 0xff, 0x75, 0x6f, 0x63, 0xff, 0x8e, 0x91, 0x84, 0xff, 0x9e, 0xa3, 0x95, 0xff, 0xa1, 0xa4, 0x96, 0xff, 0xa3, 0xa9, 0x99, 0xff, 0x80, 0x7d, 0x6f, 0xff, 0x74, 0x6f, 0x63, 0xff, 0x8f, 0x94, 0x88, 0xff, 0x92, 0x96, 0x8d, 0xff, 0x97, 0x9d, 0x94, 0xff, 0x88, 0x89, 0x79, 0xff, 0x9b, 0xa1, 0x90, 0xff, 0x96, 0xa0, 0x94, 0xff, 0xa2, 0xa8, 0x9d, 0xff, 0x9f, 0xa5, 0x98, 0xff, 0x8c, 0x8b, 0x78, 0xff, 0x96, 0x94, 0x84, 0xff, 0xbd, 0xbc, 0xb2, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xc5, 0xbc, 0xa9, 0xff, 0xa2, 0x8f, 0x69, 0xff, 0x9e, 0x8a, 0x64, 0xff, 0x8e, 0x7a, 0x55, 0xff, 0x89, 0x76, 0x4e, 0xff, 0x8d, 0x76, 0x4b, 0xff, 0x8a, 0x70, 0x47, 0xff, 0x8a, 0x6f, 0x45, 0xff, 0x91, 0x78, 0x4c, 0xff, 0x9c, 0x89, 0x5b, 0xff, 0xb4, 0xa3, 0x75, 0xff, 0xc6, 0xb1, 0x83, 0xff, 0xb3, 0xa2, 0x6f, 0xff, 0xae, 0x9b, 0x67, 0xff, 0xb3, 0xa1, 0x79, 0xff, 0xe1, 0xed, 0xd7, 0xff, 0xbf, 0xba, 0x95, 0xff, 0xa0, 0x8e, 0x5a, 0xff, 0xa4, 0x98, 0x68, 0xff, 0xb8, 0xae, 0x82, 0xff, 0xa2, 0x8d, 0x5f, 0xff, 0x9f, 0x8c, 0x5b, 0xff, 0xa1, 0x8e, 0x5c, 0xff, 0xa1, 0x8e, 0x62, 0xff, 0xac, 0xa1, 0x7a, 0xff, 0xa5, 0x9a, 0x71, 0xff, 0xab, 0xa2, 0x7f, 0xff, 0xac, 0xa8, 0x8f, 0xff, 0xa7, 0xa6, 0x9b, 0xff, 0x89, 0x87, 0x88, 0xff, 0x75, 0x71, 0x7e, 0xff, 0x5e, 0x59, 0x6d, 0xff, 0x54, 0x50, 0x66, 0xff, 0x5a, 0x5e, 0x74, 0xff, 0x95, 0xa2, 0xb1, 0xff, 0xc0, 0xd8, 0xda, 0xff, 0xa1, 0xaf, 0xb7, 0xff, 0x49, 0x3c, 0x4b, 0xff, 0x45, 0x3b, 0x43, 0xff, 0x67, 0x63, 0x6d, 0xff, 0x4c, 0x41, 0x51, 0xff, 0x27, 0x14, 0x1b, 0xff, 0x8d, 0x94, 0xa0, 0xff, 0xce, 0xe8, 0xfb, 0xff, 0xba, 0xd0, 0xdf, 0xff, 0xbd, 0xd3, 0xe1, 0xff, 0xbc, 0xd6, 0xe4, 0xff, 0xbe, 0xd7, 0xe5, 0xff, 0xc0, 0xdb, 0xe8, 0xff, 0xbe, 0xd8, 0xe7, 0xff, 0xbf, 0xd8, 0xe9, 0xff, 0xbe, 0xd5, 0xe4, 0xff, 0xc3, 0xda, 0xe7, 0xff, 0xcc, 0xe4, 0xef, 0xff, 0xcf, 0xea, 0xed, 0xff, 0xd0, 0xe7, 0xee, 0xff, 0xd2, 0xe9, 0xef, 0xff, 0xd1, 0xe8, 0xee, 0xff, 0xcf, 0xe5, 0xed, 0xff, 0xce, 0xe3, 0xeb, 0xff, 0xce, 0xe0, 0xe7, 0xff, 0xc9, 0xdb, 0xe1, 0xff, 0xc3, 0xd5, 0xdf, 0xff, 0xc8, 0xdb, 0xe4, 0xff, 0xd0, 0xe4, 0xeb, 0xff, 0xcf, 0xe2, 0xeb, 0xff, 0xbf, 0xd0, 0xda, 0xff, 0xaf, 0xbe, 0xcf, 0xff, 0xb2, 0xc2, 0xd8, 0xff, 0xbc, 0xd2, 0xe2, 0xff, 0xc0, 0xd6, 0xe4, 0xff, 0xc2, 0xd7, 0xe4, 0xff, 0xc6, 0xdb, 0xe6, 0xff, 0xc5, 0xdb, 0xe5, 0xff, 0xc0, 0xd7, 0xe3, 0xff, 0xc1, 0xd6, 0xe4, 0xff, 0xbf, 0xd3, 0xe2, 0xff, 0xc0, 0xd6, 0xe2, 0xff, 0xbd, 0xd4, 0xe0, 0xff, 0xba, 0xcf, 0xde, 0xff, 0xbb, 0xcf, 0xe0, 0xff, 0xba, 0xcc, 0xde, 0xff, 0xb4, 0xc5, 0xd7, 0xff, 0xb1, 0xc1, 0xd2, 0xff, 0xb2, 0xc2, 0xd3, 0xff, 0xae, 0xbe, 0xd2, 0xff, 0xa1, 0xb2, 0xce, 0xff, 0x9e, 0xad, 0xc8, 0xff, 0xa5, 0xb2, 0xc8, 0xff, 0x98, 0xa7, 0xbb, 0xff, 0x9e, 0xb1, 0xc1, 0xff, 0xbb, 0xcf, 0xd4, 0xff, 0x99, 0xa4, 0xa8, 0xff, 0x93, 0x9b, 0xa3, 0xff, 0xa4, 0xb2, 0xb7, 0xff, 0xae, 0xc2, 0xc3, 0xff, 0xae, 0xba, 0xb9, 0xff, 0xa2, 0xaf, 0xab, 0xff, 0xa0, 0xaf, 0xae, 0xff, 0xac, 0xbc, 0xbc, 0xff, 0xb0, 0xc1, 0xbe, 0xff, 0xa3, 0xb2, 0xb0, 0xff, 0xa7, 0xb7, 0xb4, 0xff, 0xab, 0xb9, 0xb5, 0xff, 0xb0, 0xbe, 0xba, 0xff, 0xa2, 0xb1, 0xa9, 0xff, 0x9a, 0xa0, 0x94, 0xff, 0xa6, 0xad, 0xa2, 0xff, 0xb3, 0xc3, 0xb9, 0xff, 0xad, 0xbb, 0xb2, 0xff, 0xb2, 0xba, 0xa8, 0xff, 0x7d, 0x75, 0x60, 0xff, 0x68, 0x5e, 0x4f, 0xff, 0x8b, 0x8b, 0x7b, 0xff, 0x9e, 0xa2, 0x94, 0xff, 0xa8, 0xac, 0x9b, 0xff, 0x94, 0x93, 0x83, 0xff, 0x50, 0x43, 0x39, 0xff, 0x89, 0x8a, 0x80, 0xff, 0xa8, 0xb0, 0xa4, 0xff, 0x93, 0x92, 0x88, 0xff, 0x9b, 0x9f, 0x90, 0xff, 0xa1, 0xa4, 0x8f, 0xff, 0x9b, 0x9e, 0x8c, 0xff, 0x9b, 0xa0, 0x94, 0xff, 0x9e, 0xa4, 0x97, 0xff, 0x99, 0xa2, 0x92, 0xff, 0x9d, 0xa0, 0x8f, 0xff, 0x9a, 0x9b, 0x8c, 0xff, 0xdb, 0xd9, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xe1, 0xd7, 0xff, 0x9e, 0x8d, 0x6a, 0xff, 0x87, 0x74, 0x4e, 0xff, 0x8b, 0x76, 0x51, 0xff, 0x91, 0x7b, 0x53, 0xff, 0x8c, 0x72, 0x49, 0xff, 0x7c, 0x64, 0x3c, 0xff, 0x8c, 0x76, 0x4d, 0xff, 0x91, 0x7b, 0x4e, 0xff, 0x8c, 0x74, 0x46, 0xff, 0x85, 0x6a, 0x3c, 0xff, 0x87, 0x6a, 0x3c, 0xff, 0xa0, 0x87, 0x57, 0xff, 0xaa, 0x94, 0x62, 0xff, 0xbb, 0xb8, 0x98, 0xff, 0xdf, 0xe9, 0xd9, 0xff, 0xc4, 0xc0, 0x9d, 0xff, 0xbf, 0xb7, 0x90, 0xff, 0xbc, 0xb2, 0x8b, 0xff, 0xaa, 0x97, 0x6a, 0xff, 0xa3, 0x91, 0x65, 0xff, 0xa5, 0x99, 0x76, 0xff, 0xa2, 0x96, 0x6c, 0xff, 0xae, 0x9c, 0x72, 0xff, 0xb4, 0xaa, 0x85, 0xff, 0xaf, 0xa8, 0x83, 0xff, 0xad, 0xa5, 0x84, 0xff, 0xa0, 0x98, 0x82, 0xff, 0x81, 0x7e, 0x79, 0xff, 0x79, 0x76, 0x7f, 0xff, 0x74, 0x67, 0x76, 0xff, 0x4f, 0x42, 0x53, 0xff, 0x6e, 0x69, 0x7b, 0xff, 0x70, 0x79, 0x90, 0xff, 0x9b, 0xb3, 0xcc, 0xff, 0xbc, 0xd1, 0xde, 0xff, 0x6c, 0x6f, 0x7b, 0xff, 0x43, 0x36, 0x43, 0xff, 0x77, 0x74, 0x80, 0xff, 0x6a, 0x66, 0x71, 0xff, 0x4d, 0x3f, 0x50, 0xff, 0x20, 0x0c, 0x12, 0xff, 0x66, 0x62, 0x6d, 0xff, 0xcb, 0xe5, 0xf7, 0xff, 0xb4, 0xcb, 0xdb, 0xff, 0xbb, 0xd0, 0xe1, 0xff, 0xba, 0xd3, 0xe3, 0xff, 0xbc, 0xd4, 0xe3, 0xff, 0xbf, 0xd9, 0xe7, 0xff, 0xbe, 0xd7, 0xe6, 0xff, 0xb9, 0xd3, 0xe5, 0xff, 0xc4, 0xdc, 0xe6, 0xff, 0xcd, 0xe1, 0xe9, 0xff, 0xc4, 0xdb, 0xee, 0xff, 0xc6, 0xe2, 0xed, 0xff, 0xcd, 0xe4, 0xed, 0xff, 0xcb, 0xe3, 0xeb, 0xff, 0xc8, 0xe1, 0xe8, 0xff, 0xc9, 0xdf, 0xe7, 0xff, 0xcb, 0xe0, 0xe8, 0xff, 0xca, 0xdd, 0xe5, 0xff, 0xc7, 0xdb, 0xe3, 0xff, 0xc6, 0xda, 0xe3, 0xff, 0xcd, 0xe1, 0xec, 0xff, 0xd0, 0xe6, 0xf0, 0xff, 0xcd, 0xe1, 0xeb, 0xff, 0xc5, 0xd7, 0xdd, 0xff, 0xaa, 0xb6, 0xcc, 0xff, 0xa6, 0xb4, 0xcf, 0xff, 0xb7, 0xce, 0xdf, 0xff, 0xbd, 0xd4, 0xe4, 0xff, 0xc0, 0xd4, 0xe2, 0xff, 0xc6, 0xda, 0xe4, 0xff, 0xc6, 0xdb, 0xe5, 0xff, 0xbe, 0xd4, 0xe2, 0xff, 0xbc, 0xcf, 0xe2, 0xff, 0xbd, 0xd0, 0xe2, 0xff, 0xbe, 0xd2, 0xe2, 0xff, 0xbd, 0xd1, 0xe1, 0xff, 0xb9, 0xce, 0xde, 0xff, 0xb8, 0xcc, 0xdc, 0xff, 0xb7, 0xc9, 0xd8, 0xff, 0xb7, 0xc7, 0xd5, 0xff, 0xb5, 0xc4, 0xd3, 0xff, 0xaf, 0xc0, 0xd0, 0xff, 0xa8, 0xb7, 0xcd, 0xff, 0x9f, 0xab, 0xc8, 0xff, 0xa2, 0xaf, 0xc3, 0xff, 0xa2, 0xae, 0xbb, 0xff, 0xa2, 0xb6, 0xc3, 0xff, 0xb7, 0xcd, 0xd6, 0xff, 0xb5, 0xca, 0xcf, 0xff, 0x95, 0xa4, 0xa6, 0xff, 0x8e, 0x97, 0x9a, 0xff, 0x98, 0xa2, 0xa6, 0xff, 0xa5, 0xb6, 0xb4, 0xff, 0x8e, 0x95, 0x91, 0xff, 0x92, 0x9a, 0x97, 0xff, 0x99, 0xa6, 0xa5, 0xff, 0xa9, 0xba, 0xbb, 0xff, 0xa9, 0xb6, 0xb4, 0xff, 0x99, 0xa3, 0xa1, 0xff, 0x98, 0xa4, 0xa3, 0xff, 0xa8, 0xb8, 0xb8, 0xff, 0xaa, 0xb9, 0xb7, 0xff, 0xa0, 0xac, 0xa4, 0xff, 0x9c, 0xa6, 0x99, 0xff, 0xa6, 0xb2, 0xa8, 0xff, 0xa9, 0xb3, 0xab, 0xff, 0xaf, 0xbc, 0xb3, 0xff, 0xaa, 0xaf, 0x9b, 0xff, 0x7d, 0x77, 0x5f, 0xff, 0x74, 0x6d, 0x59, 0xff, 0x9a, 0x9b, 0x86, 0xff, 0xa9, 0xb0, 0xa3, 0xff, 0xa9, 0xb1, 0xa0, 0xff, 0x5d, 0x55, 0x46, 0xff, 0x46, 0x37, 0x32, 0xff, 0xaf, 0xbb, 0xb2, 0xff, 0xa1, 0xac, 0x9e, 0xff, 0x9a, 0x9a, 0x8c, 0xff, 0x9a, 0x9b, 0x89, 0xff, 0x8e, 0x8f, 0x7a, 0xff, 0x99, 0x9a, 0x89, 0xff, 0xa2, 0xa8, 0x9b, 0xff, 0xa1, 0xa8, 0x9a, 0xff, 0x9f, 0xa2, 0x91, 0xff, 0x9d, 0xa1, 0x92, 0xff, 0xab, 0xae, 0xa1, 0xff, 0xf2, 0xf1, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf4, 0xf0, 0xff, 0xa9, 0x9b, 0x80, 0xff, 0x8c, 0x77, 0x51, 0xff, 0x8a, 0x72, 0x4d, 0xff, 0x7d, 0x65, 0x3e, 0xff, 0x8b, 0x7a, 0x53, 0xff, 0xb4, 0xa9, 0x80, 0xff, 0xb4, 0xa4, 0x79, 0xff, 0xac, 0x94, 0x67, 0xff, 0xa8, 0x8c, 0x60, 0xff, 0xb1, 0x9f, 0x73, 0xff, 0xb7, 0xb0, 0x82, 0xff, 0xc4, 0xb9, 0x8b, 0xff, 0xc1, 0xaf, 0x83, 0xff, 0xc8, 0xc9, 0xab, 0xff, 0xd7, 0xdc, 0xc9, 0xff, 0xd8, 0xdf, 0xc6, 0xff, 0xc9, 0xc0, 0x97, 0xff, 0xb3, 0xa7, 0x79, 0xff, 0xbb, 0xb3, 0x8c, 0xff, 0xbd, 0xb7, 0x93, 0xff, 0xbd, 0xba, 0x94, 0xff, 0xbe, 0xb2, 0x88, 0xff, 0xa8, 0x98, 0x71, 0xff, 0xb6, 0xaf, 0x89, 0xff, 0xb2, 0xa7, 0x7e, 0xff, 0xa0, 0x97, 0x75, 0xff, 0x94, 0x8c, 0x7a, 0xff, 0x7e, 0x78, 0x7a, 0xff, 0x83, 0x7f, 0x8b, 0xff, 0x51, 0x46, 0x53, 0xff, 0x3f, 0x2f, 0x3c, 0xff, 0x67, 0x58, 0x63, 0xff, 0x89, 0x8f, 0x94, 0xff, 0xb2, 0xd1, 0xdc, 0xff, 0x80, 0x8f, 0xa5, 0xff, 0x46, 0x3d, 0x4c, 0xff, 0x6d, 0x6f, 0x7b, 0xff, 0x7e, 0x7d, 0x8c, 0xff, 0x59, 0x4d, 0x5b, 0xff, 0x43, 0x32, 0x41, 0xff, 0x2a, 0x16, 0x1e, 0xff, 0x36, 0x29, 0x32, 0xff, 0xb4, 0xc5, 0xd5, 0xff, 0xb9, 0xd5, 0xe7, 0xff, 0xb5, 0xce, 0xe1, 0xff, 0xba, 0xd0, 0xe3, 0xff, 0xb9, 0xd1, 0xe2, 0xff, 0xba, 0xd3, 0xe2, 0xff, 0xbb, 0xd4, 0xe4, 0xff, 0xb6, 0xd0, 0xe2, 0xff, 0xc2, 0xd9, 0xe4, 0xff, 0xc1, 0xd4, 0xda, 0xff, 0xb8, 0xcd, 0xe1, 0xff, 0xc0, 0xd8, 0xed, 0xff, 0xc4, 0xdd, 0xea, 0xff, 0xc8, 0xdc, 0xea, 0xff, 0xc9, 0xdc, 0xe9, 0xff, 0xc8, 0xdb, 0xe8, 0xff, 0xc9, 0xda, 0xe8, 0xff, 0xcb, 0xdc, 0xe7, 0xff, 0xca, 0xdc, 0xe2, 0xff, 0xcb, 0xde, 0xe2, 0xff, 0xd2, 0xe5, 0xe8, 0xff, 0xd2, 0xe7, 0xed, 0xff, 0xcb, 0xe0, 0xe7, 0xff, 0xc7, 0xd8, 0xe1, 0xff, 0x99, 0xa2, 0xc1, 0xff, 0x9a, 0xa8, 0xc5, 0xff, 0xba, 0xd1, 0xe1, 0xff, 0xbe, 0xd4, 0xe4, 0xff, 0xc0, 0xd3, 0xe1, 0xff, 0xc3, 0xd6, 0xe2, 0xff, 0xbf, 0xd4, 0xe2, 0xff, 0xba, 0xcf, 0xe1, 0xff, 0xbc, 0xd0, 0xe2, 0xff, 0xbc, 0xd1, 0xe0, 0xff, 0xb9, 0xcd, 0xde, 0xff, 0xbb, 0xce, 0xe1, 0xff, 0xbc, 0xcf, 0xdf, 0xff, 0xb5, 0xc9, 0xd7, 0xff, 0xb2, 0xc6, 0xd3, 0xff, 0xb7, 0xc7, 0xd6, 0xff, 0xae, 0xbd, 0xd0, 0xff, 0xa6, 0xb4, 0xcb, 0xff, 0x9f, 0xad, 0xc5, 0xff, 0xa4, 0xb4, 0xc6, 0xff, 0xb0, 0xbd, 0xc6, 0xff, 0x8e, 0x94, 0x97, 0xff, 0xa3, 0xae, 0xb1, 0xff, 0xad, 0xba, 0xc0, 0xff, 0xa3, 0xae, 0xb3, 0xff, 0xa7, 0xb5, 0xb5, 0xff, 0x83, 0x8a, 0x88, 0xff, 0x8e, 0x91, 0x8f, 0xff, 0xad, 0xbc, 0xb8, 0xff, 0x85, 0x8c, 0x8a, 0xff, 0x88, 0x8d, 0x8e, 0xff, 0x96, 0x9e, 0x9f, 0xff, 0xad, 0xbb, 0xb9, 0xff, 0xa1, 0xab, 0xa7, 0xff, 0x89, 0x8f, 0x88, 0xff, 0x91, 0x9b, 0x97, 0xff, 0xa9, 0xbe, 0xbe, 0xff, 0xa7, 0xb4, 0xb3, 0xff, 0xa8, 0xaf, 0xaa, 0xff, 0xa6, 0xb5, 0xa9, 0xff, 0xad, 0xbb, 0xb2, 0xff, 0x91, 0x9a, 0x8f, 0xff, 0xad, 0xb6, 0xa9, 0xff, 0x9c, 0x9f, 0x8a, 0xff, 0x82, 0x7f, 0x67, 0xff, 0x93, 0x92, 0x7d, 0xff, 0xa0, 0xa3, 0x8e, 0xff, 0xa9, 0xb2, 0xa4, 0xff, 0xb0, 0xb5, 0xa3, 0xff, 0x4c, 0x3f, 0x2b, 0xff, 0x72, 0x6f, 0x65, 0xff, 0xc9, 0xda, 0xd2, 0xff, 0x9c, 0xa5, 0x9a, 0xff, 0x9d, 0xa2, 0x91, 0xff, 0x81, 0x7c, 0x6b, 0xff, 0x7b, 0x74, 0x62, 0xff, 0x9c, 0x9f, 0x8e, 0xff, 0xa2, 0xae, 0x9e, 0xff, 0xa9, 0xad, 0x9f, 0xff, 0x97, 0x94, 0x85, 0xff, 0x90, 0x93, 0x83, 0xff, 0xd0, 0xd1, 0xc6, 0xff, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcd, 0xc4, 0xb3, 0xff, 0x85, 0x71, 0x4c, 0xff, 0x96, 0x84, 0x5e, 0xff, 0xa1, 0x93, 0x6b, 0xff, 0xc1, 0xb7, 0x8f, 0xff, 0xa6, 0x93, 0x6b, 0xff, 0x7f, 0x65, 0x3c, 0xff, 0xa0, 0x8a, 0x63, 0xff, 0xae, 0x9c, 0x75, 0xff, 0xac, 0x9a, 0x71, 0xff, 0xc6, 0xb7, 0x8d, 0xff, 0xc0, 0xb0, 0x82, 0xff, 0x9b, 0x86, 0x56, 0xff, 0xd1, 0xd3, 0xba, 0xff, 0xdf, 0xde, 0xbd, 0xff, 0xba, 0xad, 0x81, 0xff, 0x91, 0x7c, 0x4e, 0xff, 0xa4, 0x8e, 0x5e, 0xff, 0xa5, 0x90, 0x60, 0xff, 0xa8, 0x99, 0x6b, 0xff, 0xc6, 0xbc, 0x94, 0xff, 0xaf, 0x9f, 0x70, 0xff, 0xa7, 0x98, 0x6d, 0xff, 0xbb, 0xb6, 0x8e, 0xff, 0xab, 0x9f, 0x73, 0xff, 0xaf, 0xa8, 0x8b, 0xff, 0x95, 0x8d, 0x88, 0xff, 0x74, 0x6b, 0x77, 0xff, 0x59, 0x51, 0x5d, 0xff, 0x39, 0x2c, 0x39, 0xff, 0x53, 0x48, 0x54, 0xff, 0x70, 0x69, 0x6a, 0xff, 0xa5, 0xa4, 0x92, 0xff, 0xac, 0xb7, 0xab, 0xff, 0x5c, 0x5c, 0x68, 0xff, 0x69, 0x63, 0x75, 0xff, 0x83, 0x87, 0x9a, 0xff, 0x56, 0x4e, 0x5f, 0xff, 0x40, 0x2e, 0x39, 0xff, 0x37, 0x26, 0x2f, 0xff, 0x38, 0x29, 0x34, 0xff, 0x2d, 0x19, 0x23, 0xff, 0x82, 0x82, 0x8e, 0xff, 0xc2, 0xdc, 0xec, 0xff, 0xae, 0xc8, 0xdc, 0xff, 0xb9, 0xce, 0xe1, 0xff, 0xb8, 0xd1, 0xe1, 0xff, 0xb8, 0xd1, 0xe1, 0xff, 0xbb, 0xd4, 0xe4, 0xff, 0xb8, 0xd0, 0xe4, 0xff, 0xbe, 0xd5, 0xe5, 0xff, 0xc4, 0xda, 0xe1, 0xff, 0xb0, 0xc5, 0xd3, 0xff, 0xb0, 0xc3, 0xdf, 0xff, 0xb9, 0xcd, 0xe6, 0xff, 0xb8, 0xca, 0xe1, 0xff, 0xa6, 0xb6, 0xd2, 0xff, 0x9d, 0xad, 0xcc, 0xff, 0xa8, 0xb8, 0xd9, 0xff, 0xb3, 0xc3, 0xe4, 0xff, 0xbd, 0xd0, 0xe8, 0xff, 0xc7, 0xdc, 0xee, 0xff, 0xca, 0xdd, 0xee, 0xff, 0xc2, 0xd4, 0xe3, 0xff, 0xc1, 0xd3, 0xdf, 0xff, 0xc4, 0xd2, 0xe0, 0xff, 0x82, 0x87, 0xb0, 0xff, 0x9b, 0xa9, 0xc4, 0xff, 0xc2, 0xd9, 0xe6, 0xff, 0xc0, 0xd4, 0xe5, 0xff, 0xbf, 0xd2, 0xe1, 0xff, 0xc0, 0xd2, 0xde, 0xff, 0xbe, 0xd1, 0xdf, 0xff, 0xba, 0xce, 0xdd, 0xff, 0xbd, 0xcf, 0xe0, 0xff, 0xbb, 0xce, 0xdf, 0xff, 0xb6, 0xcb, 0xdb, 0xff, 0xb4, 0xc7, 0xd9, 0xff, 0xb7, 0xc9, 0xda, 0xff, 0xb4, 0xc6, 0xd6, 0xff, 0xaa, 0xbb, 0xcc, 0xff, 0xa8, 0xb6, 0xcc, 0xff, 0xab, 0xb9, 0xcf, 0xff, 0xa3, 0xb1, 0xc5, 0xff, 0xaa, 0xb8, 0xc6, 0xff, 0xa0, 0xb0, 0xb6, 0xff, 0x9c, 0xa2, 0xa2, 0xff, 0x64, 0x5a, 0x5a, 0xff, 0x59, 0x51, 0x54, 0xff, 0x81, 0x83, 0x85, 0xff, 0x79, 0x74, 0x72, 0xff, 0x8f, 0x90, 0x8c, 0xff, 0x9f, 0xa6, 0xa3, 0xff, 0x8f, 0x90, 0x8e, 0xff, 0xa1, 0xae, 0xab, 0xff, 0x8a, 0x94, 0x93, 0xff, 0x86, 0x8d, 0x8d, 0xff, 0x91, 0x98, 0x97, 0xff, 0xa4, 0xb4, 0xaf, 0xff, 0x9d, 0xa9, 0xa0, 0xff, 0x8f, 0x95, 0x8b, 0xff, 0x95, 0x9e, 0x96, 0xff, 0xaa, 0xba, 0xb4, 0xff, 0xa0, 0xae, 0xa6, 0xff, 0xae, 0xbb, 0xb3, 0xff, 0xb0, 0xc0, 0xb8, 0xff, 0xaf, 0xbc, 0xb2, 0xff, 0x8c, 0x91, 0x81, 0xff, 0xa2, 0xa5, 0x94, 0xff, 0x9e, 0x9f, 0x8d, 0xff, 0x91, 0x91, 0x7e, 0xff, 0x95, 0x94, 0x82, 0xff, 0x9d, 0x9f, 0x8b, 0xff, 0xac, 0xb3, 0xa2, 0xff, 0x99, 0x97, 0x80, 0xff, 0x73, 0x67, 0x54, 0xff, 0xaf, 0xb6, 0xad, 0xff, 0xc1, 0xd5, 0xcf, 0xff, 0xa9, 0xb3, 0xa7, 0xff, 0x83, 0x7f, 0x6d, 0xff, 0x65, 0x5e, 0x4d, 0xff, 0x84, 0x80, 0x6f, 0xff, 0x9b, 0x9d, 0x8b, 0xff, 0xa8, 0xad, 0x9d, 0xff, 0xa4, 0xaa, 0x9c, 0xff, 0x93, 0x96, 0x87, 0xff, 0xa8, 0xab, 0x9b, 0xff, 0xe9, 0xea, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xe9, 0xe3, 0xff, 0xb4, 0xa6, 0x84, 0xff, 0xa3, 0x95, 0x6d, 0xff, 0xc4, 0xb6, 0x8d, 0xff, 0x93, 0x7e, 0x56, 0xff, 0x73, 0x56, 0x2e, 0xff, 0xae, 0x95, 0x6e, 0xff, 0xac, 0x9c, 0x77, 0xff, 0x9c, 0x89, 0x65, 0xff, 0x98, 0x7b, 0x52, 0xff, 0x8c, 0x6d, 0x3f, 0xff, 0x88, 0x69, 0x36, 0xff, 0xb0, 0xa0, 0x74, 0xff, 0xf0, 0xf8, 0xe5, 0xff, 0xc3, 0xbf, 0x95, 0xff, 0x94, 0x7d, 0x4d, 0xff, 0x91, 0x74, 0x44, 0xff, 0x97, 0x7f, 0x4f, 0xff, 0xa0, 0x8c, 0x5d, 0xff, 0xb2, 0xa5, 0x78, 0xff, 0xa9, 0x9a, 0x6c, 0xff, 0xa6, 0x93, 0x63, 0xff, 0xae, 0xa0, 0x77, 0xff, 0xc2, 0xbd, 0x9b, 0xff, 0xbc, 0xb2, 0x8b, 0xff, 0xae, 0xa7, 0x90, 0xff, 0x6f, 0x68, 0x6a, 0xff, 0x66, 0x5b, 0x69, 0xff, 0x4a, 0x3b, 0x4a, 0xff, 0x38, 0x26, 0x32, 0xff, 0x5f, 0x50, 0x59, 0xff, 0x6c, 0x67, 0x62, 0xff, 0xa7, 0xa4, 0x98, 0xff, 0x94, 0x8f, 0x87, 0xff, 0x78, 0x74, 0x75, 0xff, 0x90, 0x90, 0xa0, 0xff, 0x5a, 0x56, 0x6b, 0xff, 0x3b, 0x2b, 0x39, 0xff, 0x42, 0x2c, 0x37, 0xff, 0x3c, 0x28, 0x30, 0xff, 0x38, 0x30, 0x39, 0xff, 0x23, 0x0d, 0x19, 0xff, 0x51, 0x49, 0x55, 0xff, 0xc0, 0xd5, 0xe5, 0xff, 0xae, 0xc7, 0xd9, 0xff, 0xb5, 0xcb, 0xdb, 0xff, 0xb6, 0xd2, 0xe1, 0xff, 0xba, 0xd3, 0xe2, 0xff, 0xb7, 0xd0, 0xe1, 0xff, 0xb9, 0xd1, 0xe4, 0xff, 0xba, 0xd1, 0xe4, 0xff, 0xc3, 0xda, 0xe7, 0xff, 0xc2, 0xd7, 0xe2, 0xff, 0xa4, 0xb5, 0xce, 0xff, 0x96, 0xa5, 0xcb, 0xff, 0x93, 0xa1, 0xcd, 0xff, 0x93, 0xa1, 0xc8, 0xff, 0x93, 0x9e, 0xc6, 0xff, 0x60, 0x68, 0x98, 0xff, 0x4c, 0x52, 0x81, 0xff, 0x58, 0x5d, 0x88, 0xff, 0x61, 0x69, 0x8e, 0xff, 0x6e, 0x7a, 0xa6, 0xff, 0x86, 0x95, 0xcd, 0xff, 0xa9, 0xb8, 0xe0, 0xff, 0xa8, 0xb3, 0xd0, 0xff, 0x75, 0x7c, 0xa1, 0xff, 0xb1, 0xbf, 0xd8, 0xff, 0xbf, 0xd5, 0xe3, 0xff, 0xbc, 0xd2, 0xe0, 0xff, 0xc2, 0xd3, 0xe1, 0xff, 0xb6, 0xc6, 0xd4, 0xff, 0xb5, 0xc8, 0xd0, 0xff, 0xbd, 0xd1, 0xdc, 0xff, 0xb9, 0xcb, 0xdd, 0xff, 0xb9, 0xca, 0xdb, 0xff, 0xb9, 0xca, 0xd9, 0xff, 0xb5, 0xc6, 0xd6, 0xff, 0xa9, 0xbb, 0xcc, 0xff, 0xab, 0xba, 0xcf, 0xff, 0xa4, 0xb4, 0xce, 0xff, 0x99, 0xac, 0xc2, 0xff, 0xac, 0xbb, 0xca, 0xff, 0xb5, 0xcb, 0xcf, 0xff, 0xb4, 0xc4, 0xc4, 0xff, 0x89, 0x88, 0x8c, 0xff, 0x74, 0x6b, 0x6d, 0xff, 0x58, 0x48, 0x4b, 0xff, 0x84, 0x85, 0x89, 0xff, 0xa0, 0xaa, 0xa8, 0xff, 0x46, 0x37, 0x35, 0xff, 0x73, 0x6d, 0x69, 0xff, 0xa9, 0xba, 0xb4, 0xff, 0x92, 0x9c, 0x99, 0xff, 0x97, 0xa3, 0xa1, 0xff, 0x9a, 0xaa, 0xab, 0xff, 0x8d, 0x99, 0x98, 0xff, 0x94, 0x9e, 0x98, 0xff, 0xa2, 0xb0, 0xa6, 0xff, 0x94, 0xa1, 0x96, 0xff, 0xa0, 0xa9, 0xa0, 0xff, 0x97, 0x9b, 0x8f, 0xff, 0x90, 0x96, 0x88, 0xff, 0x96, 0xa1, 0x97, 0xff, 0xbc, 0xcc, 0xc5, 0xff, 0xb9, 0xc8, 0xc2, 0xff, 0xb2, 0xbb, 0xb1, 0xff, 0x95, 0x97, 0x84, 0xff, 0x9e, 0x9f, 0x88, 0xff, 0x9f, 0xa3, 0x91, 0xff, 0x98, 0x9a, 0x8b, 0xff, 0x8f, 0x8e, 0x7c, 0xff, 0x93, 0x91, 0x80, 0xff, 0xb6, 0xbb, 0xa6, 0xff, 0x66, 0x5d, 0x49, 0xff, 0x7f, 0x7c, 0x6a, 0xff, 0xc1, 0xcf, 0xc6, 0xff, 0xc0, 0xd4, 0xcf, 0xff, 0x9e, 0xa3, 0x91, 0xff, 0x5e, 0x49, 0x38, 0xff, 0x78, 0x74, 0x62, 0xff, 0xa3, 0xa4, 0x92, 0xff, 0x95, 0x93, 0x82, 0xff, 0xa2, 0xa1, 0x8e, 0xff, 0xa0, 0xa3, 0x92, 0xff, 0x9e, 0xa5, 0x97, 0xff, 0xc6, 0xc8, 0xbc, 0xff, 0xfa, 0xfa, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0x74, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xc6, 0xbd, 0xa5, 0xff, 0x9f, 0x8c, 0x64, 0xff, 0x9f, 0x88, 0x5f, 0xff, 0x7e, 0x65, 0x3d, 0xff, 0xb4, 0xa5, 0x7a, 0xff, 0xa4, 0x93, 0x6a, 0xff, 0x81, 0x63, 0x3b, 0xff, 0x81, 0x5f, 0x39, 0xff, 0x95, 0x78, 0x50, 0xff, 0x9f, 0x83, 0x4f, 0xff, 0x99, 0x80, 0x47, 0xff, 0xdc, 0xe0, 0xca, 0xff, 0xe0, 0xe2, 0xcf, 0xff, 0xc5, 0xc1, 0x9b, 0xff, 0xbb, 0xb4, 0x8d, 0xff, 0xb9, 0xad, 0x82, 0xff, 0x9e, 0x90, 0x65, 0xff, 0xb8, 0xab, 0x82, 0xff, 0xac, 0x9b, 0x69, 0xff, 0x9d, 0x88, 0x5b, 0xff, 0xaf, 0x9d, 0x71, 0xff, 0xa6, 0x97, 0x6e, 0xff, 0xaa, 0xa2, 0x80, 0xff, 0xaa, 0x9f, 0x74, 0xff, 0x94, 0x89, 0x74, 0xff, 0x60, 0x59, 0x5e, 0xff, 0x64, 0x5b, 0x65, 0xff, 0x42, 0x2e, 0x41, 0xff, 0x46, 0x36, 0x40, 0xff, 0x60, 0x55, 0x56, 0xff, 0x75, 0x6e, 0x68, 0xff, 0x9c, 0x97, 0x96, 0xff, 0x7c, 0x78, 0x7b, 0xff, 0x8d, 0x8e, 0x91, 0xff, 0x70, 0x6d, 0x77, 0xff, 0x43, 0x37, 0x43, 0xff, 0x46, 0x35, 0x40, 0xff, 0x3b, 0x2b, 0x37, 0xff, 0x3e, 0x2f, 0x37, 0xff, 0x29, 0x19, 0x20, 0xff, 0x4f, 0x49, 0x56, 0xff, 0x75, 0x78, 0x87, 0xff, 0x95, 0xa0, 0xb3, 0xff, 0xbe, 0xd8, 0xe8, 0xff, 0xb1, 0xc8, 0xd8, 0xff, 0xb5, 0xce, 0xdf, 0xff, 0xb9, 0xd0, 0xe0, 0xff, 0xb5, 0xce, 0xde, 0xff, 0xb8, 0xd1, 0xe3, 0xff, 0xbc, 0xd5, 0xe5, 0xff, 0xbc, 0xd3, 0xe5, 0xff, 0xc6, 0xdb, 0xea, 0xff, 0xc3, 0xd8, 0xe6, 0xff, 0x94, 0xa7, 0xc9, 0xff, 0x79, 0x89, 0xbb, 0xff, 0x95, 0xa7, 0xcc, 0xff, 0x94, 0xa0, 0xc7, 0xff, 0x4b, 0x4d, 0x85, 0xff, 0x33, 0x35, 0x6c, 0xff, 0x28, 0x24, 0x5c, 0xff, 0x22, 0x18, 0x49, 0xff, 0x16, 0x10, 0x3b, 0xff, 0x31, 0x34, 0x77, 0xff, 0x49, 0x55, 0x98, 0xff, 0x6a, 0x74, 0x9b, 0xff, 0xb0, 0xbe, 0xd0, 0xff, 0xc2, 0xd4, 0xe0, 0xff, 0xbc, 0xd1, 0xe2, 0xff, 0xba, 0xd0, 0xdf, 0xff, 0xbb, 0xc9, 0xd6, 0xff, 0xb1, 0xc0, 0xcf, 0xff, 0xa5, 0xb5, 0xbe, 0xff, 0xb7, 0xc8, 0xd1, 0xff, 0xb5, 0xc6, 0xda, 0xff, 0xb5, 0xc6, 0xd6, 0xff, 0xb2, 0xc3, 0xd0, 0xff, 0xa9, 0xbb, 0xcc, 0xff, 0xa9, 0xba, 0xcc, 0xff, 0xa4, 0xb4, 0xcb, 0xff, 0xa0, 0xae, 0xc6, 0xff, 0xa8, 0xb3, 0xc4, 0xff, 0xb0, 0xbc, 0xc0, 0xff, 0xb0, 0xc1, 0xbd, 0xff, 0xbd, 0xd2, 0xce, 0xff, 0x95, 0x95, 0x97, 0xff, 0x53, 0x3f, 0x43, 0xff, 0x66, 0x5f, 0x5f, 0xff, 0xd1, 0xe2, 0xdf, 0xff, 0xaa, 0xb5, 0xb0, 0xff, 0x2e, 0x1c, 0x1e, 0xff, 0x6d, 0x66, 0x63, 0xff, 0xab, 0xba, 0xb1, 0xff, 0xa1, 0xb0, 0xab, 0xff, 0xb0, 0xbc, 0xbb, 0xff, 0xa1, 0xb1, 0xb4, 0xff, 0x96, 0xa0, 0x9f, 0xff, 0x9e, 0xac, 0xa3, 0xff, 0xa7, 0xae, 0xa2, 0xff, 0x94, 0x9c, 0x95, 0xff, 0xb4, 0xc1, 0xba, 0xff, 0x89, 0x87, 0x77, 0xff, 0x70, 0x6f, 0x5f, 0xff, 0x98, 0xa1, 0x9e, 0xff, 0xbd, 0xcc, 0xc8, 0xff, 0xbb, 0xcc, 0xc3, 0xff, 0xaf, 0xb9, 0xab, 0xff, 0x97, 0x95, 0x82, 0xff, 0x94, 0x93, 0x7e, 0xff, 0xa2, 0xab, 0x97, 0xff, 0xa3, 0xa5, 0x93, 0xff, 0x74, 0x71, 0x5b, 0xff, 0x8a, 0x8a, 0x7b, 0xff, 0xb1, 0xb5, 0x9e, 0xff, 0x61, 0x56, 0x46, 0xff, 0x92, 0x91, 0x80, 0xff, 0xb4, 0xc3, 0xb7, 0xff, 0xc5, 0xd3, 0xcd, 0xff, 0x8a, 0x82, 0x6c, 0xff, 0x67, 0x5a, 0x46, 0xff, 0x8a, 0x8a, 0x77, 0xff, 0x93, 0x90, 0x7b, 0xff, 0x94, 0x8f, 0x7b, 0xff, 0x98, 0x9b, 0x87, 0xff, 0xa3, 0xa4, 0x92, 0xff, 0x9d, 0x9f, 0x8d, 0xff, 0xdf, 0xde, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0xdf, 0xd6, 0xff, 0xb3, 0x9f, 0x79, 0xff, 0x84, 0x6c, 0x44, 0xff, 0xa2, 0x8c, 0x63, 0xff, 0x98, 0x86, 0x5d, 0xff, 0x79, 0x62, 0x3a, 0xff, 0x90, 0x70, 0x4a, 0xff, 0x96, 0x72, 0x4d, 0xff, 0x89, 0x6b, 0x43, 0xff, 0x87, 0x6b, 0x37, 0xff, 0xcb, 0xbc, 0x91, 0xff, 0xe8, 0xf6, 0xe5, 0xff, 0xcc, 0xcc, 0xad, 0xff, 0xcb, 0xbf, 0x90, 0xff, 0xa6, 0x94, 0x67, 0xff, 0xa3, 0x91, 0x64, 0xff, 0xc6, 0xc0, 0x98, 0xff, 0xc1, 0xb9, 0x95, 0xff, 0x9e, 0x8a, 0x55, 0xff, 0xab, 0x96, 0x69, 0xff, 0xc0, 0xb2, 0x89, 0xff, 0xba, 0xb3, 0x8d, 0xff, 0xa1, 0x9b, 0x76, 0xff, 0xa1, 0x97, 0x6c, 0xff, 0x97, 0x8f, 0x81, 0xff, 0x74, 0x6f, 0x78, 0xff, 0x55, 0x4e, 0x58, 0xff, 0x35, 0x24, 0x33, 0xff, 0x63, 0x54, 0x57, 0xff, 0x74, 0x6a, 0x67, 0xff, 0x86, 0x85, 0x86, 0xff, 0x90, 0x8d, 0x90, 0xff, 0x89, 0x81, 0x85, 0xff, 0x83, 0x7d, 0x81, 0xff, 0x49, 0x40, 0x49, 0xff, 0x44, 0x35, 0x41, 0xff, 0x3d, 0x2b, 0x33, 0xff, 0x37, 0x2b, 0x31, 0xff, 0x2e, 0x1f, 0x28, 0xff, 0x45, 0x35, 0x43, 0xff, 0xae, 0xbd, 0xce, 0xff, 0x57, 0x5c, 0x6e, 0xff, 0x55, 0x51, 0x63, 0xff, 0xc6, 0xde, 0xef, 0xff, 0xb1, 0xc8, 0xdb, 0xff, 0xb4, 0xc8, 0xdb, 0xff, 0xb5, 0xcb, 0xde, 0xff, 0xb7, 0xcf, 0xe0, 0xff, 0xb7, 0xd0, 0xe0, 0xff, 0xb9, 0xd3, 0xe3, 0xff, 0xbf, 0xd9, 0xe9, 0xff, 0xc3, 0xdc, 0xe8, 0xff, 0xca, 0xe1, 0xe8, 0xff, 0xbe, 0xd0, 0xe0, 0xff, 0x92, 0xa5, 0xc4, 0xff, 0x81, 0x93, 0xbd, 0xff, 0x68, 0x75, 0xb5, 0xff, 0x5c, 0x66, 0xb1, 0xff, 0x5a, 0x62, 0xa6, 0xff, 0x65, 0x6b, 0xaa, 0xff, 0x75, 0x79, 0xb2, 0xff, 0x69, 0x6a, 0x9c, 0xff, 0x45, 0x47, 0x78, 0xff, 0x58, 0x60, 0x88, 0xff, 0x9c, 0xa8, 0xc0, 0xff, 0xc6, 0xda, 0xe2, 0xff, 0xc2, 0xda, 0xde, 0xff, 0xbf, 0xd5, 0xe1, 0xff, 0xba, 0xd0, 0xdf, 0xff, 0xae, 0xbd, 0xcd, 0xff, 0xab, 0xbb, 0xc9, 0xff, 0xa4, 0xb2, 0xba, 0xff, 0xb0, 0xbe, 0xc3, 0xff, 0xb1, 0xc1, 0xd1, 0xff, 0xb1, 0xc3, 0xd4, 0xff, 0xb0, 0xc4, 0xd0, 0xff, 0xaa, 0xbb, 0xce, 0xff, 0xab, 0xb7, 0xd0, 0xff, 0xa7, 0xb5, 0xc5, 0xff, 0xab, 0xb8, 0xbf, 0xff, 0xba, 0xc8, 0xc9, 0xff, 0xad, 0xba, 0xb5, 0xff, 0x8e, 0x8e, 0x88, 0xff, 0xa1, 0xa4, 0x9f, 0xff, 0x90, 0x8e, 0x8a, 0xff, 0x50, 0x3c, 0x38, 0xff, 0x8f, 0x90, 0x88, 0xff, 0xbf, 0xcf, 0xc6, 0xff, 0x8e, 0x92, 0x8b, 0xff, 0x18, 0x00, 0x02, 0xff, 0x6e, 0x68, 0x62, 0xff, 0xaa, 0xb5, 0xac, 0xff, 0xa5, 0xb4, 0xaf, 0xff, 0xa2, 0xb1, 0xad, 0xff, 0x9b, 0xa1, 0xa0, 0xff, 0x9c, 0xa2, 0x9b, 0xff, 0x9e, 0xaf, 0xa4, 0xff, 0x9f, 0xa8, 0x9f, 0xff, 0x93, 0x9a, 0x92, 0xff, 0xc1, 0xd0, 0xc7, 0xff, 0x79, 0x76, 0x68, 0xff, 0x5d, 0x54, 0x48, 0xff, 0xa6, 0xb3, 0xad, 0xff, 0xb3, 0xca, 0xc1, 0xff, 0xbd, 0xcd, 0xc1, 0xff, 0xa2, 0xa6, 0x92, 0xff, 0x76, 0x6b, 0x59, 0xff, 0x81, 0x79, 0x6c, 0xff, 0xb7, 0xbc, 0xa8, 0xff, 0x8a, 0x87, 0x70, 0xff, 0x6c, 0x5d, 0x49, 0xff, 0xad, 0xb4, 0x9e, 0xff, 0x92, 0x92, 0x7a, 0xff, 0x61, 0x4e, 0x3d, 0xff, 0x98, 0x94, 0x81, 0xff, 0xac, 0xbd, 0xad, 0xff, 0xb8, 0xc4, 0xb6, 0xff, 0x81, 0x7b, 0x67, 0xff, 0x75, 0x72, 0x5c, 0xff, 0x7a, 0x6d, 0x5b, 0xff, 0xa1, 0x9d, 0x86, 0xff, 0x97, 0x97, 0x7e, 0xff, 0xa3, 0xa6, 0x98, 0xff, 0xac, 0xb0, 0xa0, 0xff, 0xab, 0xae, 0x98, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xbc, 0xae, 0x94, 0xff, 0x94, 0x7d, 0x54, 0xff, 0x9d, 0x87, 0x5d, 0xff, 0x83, 0x6b, 0x42, 0xff, 0x8d, 0x74, 0x4c, 0xff, 0x8f, 0x71, 0x4a, 0xff, 0x85, 0x67, 0x42, 0xff, 0x8b, 0x6e, 0x3d, 0xff, 0xb6, 0xa2, 0x6e, 0xff, 0xd3, 0xd2, 0xb2, 0xff, 0xd0, 0xd4, 0xba, 0xff, 0xbd, 0xb4, 0x87, 0xff, 0xaf, 0x99, 0x63, 0xff, 0x93, 0x7b, 0x46, 0xff, 0xb2, 0x9f, 0x71, 0xff, 0xc2, 0xb2, 0x8e, 0xff, 0xa4, 0x93, 0x66, 0xff, 0xad, 0xa4, 0x76, 0xff, 0xb9, 0xaa, 0x7b, 0xff, 0xb0, 0xa0, 0x75, 0xff, 0xc3, 0xbe, 0x9a, 0xff, 0xb9, 0xb5, 0x8f, 0xff, 0xb7, 0xb0, 0x8b, 0xff, 0x98, 0x97, 0x8f, 0xff, 0x7e, 0x7d, 0x89, 0xff, 0x5f, 0x57, 0x61, 0xff, 0x39, 0x2a, 0x37, 0xff, 0x65, 0x59, 0x61, 0xff, 0x84, 0x7e, 0x82, 0xff, 0x91, 0x8f, 0x94, 0xff, 0x8c, 0x88, 0x89, 0xff, 0x86, 0x7c, 0x81, 0xff, 0x52, 0x44, 0x4f, 0xff, 0x45, 0x35, 0x40, 0xff, 0x4a, 0x39, 0x43, 0xff, 0x38, 0x27, 0x2e, 0xff, 0x32, 0x1c, 0x23, 0xff, 0x30, 0x1f, 0x2c, 0xff, 0x77, 0x7e, 0x92, 0xff, 0x72, 0x7b, 0x95, 0xff, 0x2f, 0x27, 0x43, 0xff, 0x38, 0x2d, 0x3d, 0xff, 0xa0, 0xb0, 0xbe, 0xff, 0xbf, 0xd6, 0xec, 0xff, 0xb2, 0xc8, 0xda, 0xff, 0xb5, 0xcb, 0xdd, 0xff, 0xb6, 0xcf, 0xe0, 0xff, 0xb7, 0xd0, 0xe0, 0xff, 0xbb, 0xd4, 0xe3, 0xff, 0xc1, 0xdd, 0xea, 0xff, 0xc4, 0xde, 0xeb, 0xff, 0xc2, 0xda, 0xe2, 0xff, 0xc3, 0xd6, 0xde, 0xff, 0xb9, 0xc9, 0xda, 0xff, 0x8a, 0x9a, 0xc0, 0xff, 0x5f, 0x69, 0xaa, 0xff, 0x56, 0x61, 0xa5, 0xff, 0x73, 0x79, 0xb1, 0xff, 0x96, 0x9e, 0xc0, 0xff, 0x9d, 0xad, 0xc0, 0xff, 0xa3, 0xb1, 0xc9, 0xff, 0xb4, 0xc1, 0xd6, 0xff, 0xc8, 0xd8, 0xe2, 0xff, 0xc4, 0xd5, 0xdf, 0xff, 0xbc, 0xd0, 0xdb, 0xff, 0xc0, 0xd4, 0xe1, 0xff, 0xc1, 0xd6, 0xe1, 0xff, 0xbb, 0xd1, 0xdf, 0xff, 0xb0, 0xbf, 0xca, 0xff, 0xac, 0xb8, 0xbd, 0xff, 0xab, 0xb4, 0xb1, 0xff, 0xac, 0xb6, 0xb0, 0xff, 0xb3, 0xc0, 0xc5, 0xff, 0xb1, 0xc3, 0xcf, 0xff, 0xaf, 0xc2, 0xcd, 0xff, 0xaf, 0xbf, 0xce, 0xff, 0xb3, 0xc0, 0xd0, 0xff, 0xaf, 0xbc, 0xc2, 0xff, 0xb4, 0xc6, 0xc4, 0xff, 0xb0, 0xc0, 0xbd, 0xff, 0xb3, 0xbf, 0xbc, 0xff, 0xb0, 0xbd, 0xb8, 0xff, 0x8e, 0x89, 0x86, 0xff, 0x4d, 0x3e, 0x3a, 0xff, 0x58, 0x4a, 0x42, 0xff, 0x84, 0x79, 0x70, 0xff, 0x8c, 0x95, 0x8b, 0xff, 0x96, 0x98, 0x8f, 0xff, 0x1d, 0x02, 0x04, 0xff, 0x77, 0x78, 0x74, 0xff, 0xa5, 0xb2, 0xad, 0xff, 0xb6, 0xc5, 0xc3, 0xff, 0x9c, 0xab, 0xa8, 0xff, 0x97, 0x9e, 0x9b, 0xff, 0xa2, 0xa8, 0x9e, 0xff, 0xab, 0xba, 0xae, 0xff, 0x92, 0x9a, 0x91, 0xff, 0x8b, 0x91, 0x85, 0xff, 0xc5, 0xd2, 0xc5, 0xff, 0x73, 0x6e, 0x5f, 0xff, 0x54, 0x46, 0x3a, 0xff, 0xb0, 0xbc, 0xb2, 0xff, 0xac, 0xb8, 0xa8, 0xff, 0xb1, 0xb7, 0xa4, 0xff, 0x93, 0x91, 0x79, 0xff, 0x5b, 0x4b, 0x36, 0xff, 0x88, 0x7c, 0x6a, 0xff, 0x95, 0x91, 0x7a, 0xff, 0x77, 0x71, 0x55, 0xff, 0xa3, 0x9a, 0x84, 0xff, 0xb1, 0xb4, 0x99, 0xff, 0x5e, 0x51, 0x3d, 0xff, 0x47, 0x31, 0x24, 0xff, 0x99, 0x9b, 0x84, 0xff, 0xaa, 0xb9, 0xa4, 0xff, 0x93, 0x98, 0x83, 0xff, 0x87, 0x82, 0x6f, 0xff, 0x7f, 0x70, 0x5e, 0xff, 0x8e, 0x7d, 0x6a, 0xff, 0xae, 0xae, 0x9a, 0xff, 0xa1, 0xa9, 0x93, 0xff, 0xb1, 0xb3, 0xa3, 0xff, 0x8a, 0x85, 0x72, 0xff, 0xd0, 0xcd, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xdb, 0xd0, 0xff, 0xa4, 0x91, 0x6a, 0xff, 0x8d, 0x70, 0x48, 0xff, 0x87, 0x69, 0x41, 0xff, 0x84, 0x69, 0x40, 0xff, 0x84, 0x6a, 0x40, 0xff, 0x95, 0x7d, 0x4e, 0xff, 0xb4, 0xa6, 0x76, 0xff, 0xdb, 0xd7, 0xa8, 0xff, 0xc3, 0xc0, 0x94, 0xff, 0xcb, 0xcc, 0xaa, 0xff, 0xad, 0x9b, 0x6e, 0xff, 0xab, 0x9a, 0x68, 0xff, 0xb8, 0xa9, 0x79, 0xff, 0xbf, 0xb1, 0x80, 0xff, 0xb1, 0xa1, 0x77, 0xff, 0xae, 0x9e, 0x72, 0xff, 0xaf, 0x9b, 0x69, 0xff, 0xab, 0x93, 0x65, 0xff, 0xa3, 0x90, 0x62, 0xff, 0xb6, 0xb3, 0x90, 0xff, 0xb8, 0xaf, 0x86, 0xff, 0xbb, 0xb5, 0x94, 0xff, 0x99, 0xa0, 0x9c, 0xff, 0x87, 0x8a, 0x92, 0xff, 0x69, 0x64, 0x6e, 0xff, 0x4e, 0x40, 0x4f, 0xff, 0x68, 0x62, 0x74, 0xff, 0x7c, 0x79, 0x86, 0xff, 0x8d, 0x89, 0x88, 0xff, 0x8e, 0x88, 0x85, 0xff, 0x59, 0x52, 0x58, 0xff, 0x34, 0x27, 0x35, 0xff, 0x48, 0x38, 0x44, 0xff, 0x3a, 0x2b, 0x34, 0xff, 0x2c, 0x1d, 0x24, 0xff, 0x33, 0x22, 0x2d, 0xff, 0x5f, 0x5a, 0x6f, 0xff, 0x64, 0x68, 0x85, 0xff, 0x44, 0x49, 0x6c, 0xff, 0x4c, 0x4c, 0x6e, 0xff, 0x1b, 0x0d, 0x1d, 0xff, 0x70, 0x71, 0x7f, 0xff, 0xc9, 0xde, 0xf3, 0xff, 0xac, 0xc5, 0xd7, 0xff, 0xb2, 0xc7, 0xdb, 0xff, 0xb6, 0xce, 0xdf, 0xff, 0xb9, 0xd2, 0xe2, 0xff, 0xbf, 0xd8, 0xe5, 0xff, 0xc5, 0xdd, 0xe9, 0xff, 0xc2, 0xdd, 0xec, 0xff, 0xbf, 0xd9, 0xe4, 0xff, 0xbf, 0xd4, 0xe0, 0xff, 0xbb, 0xcd, 0xdd, 0xff, 0x9d, 0xb0, 0xcc, 0xff, 0x8b, 0x9d, 0xc7, 0xff, 0x95, 0xa6, 0xca, 0xff, 0xa8, 0xbb, 0xd0, 0xff, 0xb7, 0xcc, 0xd4, 0xff, 0xbf, 0xcd, 0xd9, 0xff, 0xc2, 0xd2, 0xe0, 0xff, 0xc1, 0xd8, 0xde, 0xff, 0xbc, 0xd3, 0xda, 0xff, 0xc1, 0xd8, 0xdf, 0xff, 0xc5, 0xda, 0xe3, 0xff, 0xc1, 0xd4, 0xdd, 0xff, 0xb9, 0xcb, 0xd9, 0xff, 0xb7, 0xca, 0xda, 0xff, 0xac, 0xb9, 0xc0, 0xff, 0xb1, 0xbb, 0xb0, 0xff, 0xa7, 0xad, 0x9c, 0xff, 0xa6, 0xab, 0x99, 0xff, 0xb1, 0xbb, 0xb2, 0xff, 0xb2, 0xc2, 0xc4, 0xff, 0xab, 0xbb, 0xc4, 0xff, 0xaf, 0xbc, 0xc3, 0xff, 0xb7, 0xc6, 0xc4, 0xff, 0xb1, 0xc1, 0xbb, 0xff, 0xb5, 0xbe, 0xb8, 0xff, 0xaa, 0xb1, 0xac, 0xff, 0xac, 0xba, 0xb4, 0xff, 0xc0, 0xcb, 0xc4, 0xff, 0x9d, 0xa0, 0x97, 0xff, 0x58, 0x48, 0x41, 0xff, 0x60, 0x4d, 0x47, 0xff, 0x7a, 0x78, 0x70, 0xff, 0xb0, 0xb2, 0xac, 0xff, 0x69, 0x5d, 0x59, 0xff, 0x18, 0x00, 0x02, 0xff, 0x84, 0x8e, 0x8c, 0xff, 0xb4, 0xca, 0xc7, 0xff, 0xb8, 0xc4, 0xc6, 0xff, 0x9e, 0xaf, 0xae, 0xff, 0xa9, 0xbc, 0xb7, 0xff, 0xa3, 0xa9, 0x9f, 0xff, 0xa5, 0xaf, 0xa3, 0xff, 0x92, 0x98, 0x8c, 0xff, 0x8b, 0x8d, 0x7c, 0xff, 0xab, 0xb5, 0xa4, 0xff, 0x7c, 0x75, 0x63, 0xff, 0x67, 0x5c, 0x4e, 0xff, 0xa9, 0xb0, 0xa2, 0xff, 0x7c, 0x75, 0x61, 0xff, 0x8d, 0x85, 0x6e, 0xff, 0x93, 0x8a, 0x73, 0xff, 0x66, 0x55, 0x3c, 0xff, 0x89, 0x7b, 0x5d, 0xff, 0x76, 0x67, 0x4c, 0xff, 0x8d, 0x84, 0x68, 0xff, 0xba, 0xc3, 0xaa, 0xff, 0x9a, 0x90, 0x74, 0xff, 0x39, 0x1e, 0x15, 0xff, 0x41, 0x2e, 0x24, 0xff, 0xac, 0xae, 0x92, 0xff, 0xae, 0xaa, 0x94, 0xff, 0x5f, 0x4f, 0x39, 0xff, 0x7b, 0x70, 0x5e, 0xff, 0x8f, 0x8c, 0x75, 0xff, 0xb1, 0xc0, 0xa9, 0xff, 0xb3, 0xbf, 0xaf, 0xff, 0xb2, 0xb8, 0xab, 0xff, 0x7b, 0x73, 0x5f, 0xff, 0x92, 0x84, 0x70, 0xff, 0xee, 0xec, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0xc5, 0xb8, 0x9d, 0xff, 0x9c, 0x87, 0x5d, 0xff, 0x97, 0x80, 0x58, 0xff, 0x98, 0x83, 0x5a, 0xff, 0xb5, 0xa0, 0x73, 0xff, 0xb4, 0x9f, 0x70, 0xff, 0xc6, 0xc5, 0x99, 0xff, 0xc8, 0xc5, 0x97, 0xff, 0xbd, 0xb1, 0x83, 0xff, 0xc7, 0xbe, 0x8f, 0xff, 0xa7, 0x99, 0x68, 0xff, 0xbd, 0xb4, 0x85, 0xff, 0xb3, 0xa3, 0x76, 0xff, 0xb2, 0x9e, 0x6e, 0xff, 0xab, 0x98, 0x67, 0xff, 0xad, 0x9c, 0x6e, 0xff, 0xab, 0x96, 0x66, 0xff, 0xa8, 0x92, 0x61, 0xff, 0xa7, 0x95, 0x66, 0xff, 0xb4, 0xad, 0x86, 0xff, 0xb5, 0xac, 0x82, 0xff, 0xb8, 0xb0, 0x8a, 0xff, 0x9a, 0xa5, 0xa0, 0xff, 0x94, 0x9b, 0x9e, 0xff, 0x8a, 0x8b, 0x95, 0xff, 0x5c, 0x56, 0x69, 0xff, 0x5b, 0x54, 0x67, 0xff, 0x77, 0x76, 0x7d, 0xff, 0x8d, 0x8a, 0x84, 0xff, 0x71, 0x68, 0x6f, 0xff, 0x38, 0x2b, 0x36, 0xff, 0x43, 0x34, 0x3d, 0xff, 0x3d, 0x2e, 0x39, 0xff, 0x2c, 0x1c, 0x26, 0xff, 0x31, 0x20, 0x26, 0xff, 0x41, 0x37, 0x41, 0xff, 0x58, 0x51, 0x6e, 0xff, 0x4e, 0x4d, 0x70, 0xff, 0x5f, 0x6e, 0x98, 0xff, 0x54, 0x56, 0x76, 0xff, 0x23, 0x14, 0x23, 0xff, 0x59, 0x56, 0x64, 0xff, 0xb3, 0xc5, 0xd5, 0xff, 0xb1, 0xc8, 0xdc, 0xff, 0xb0, 0xc3, 0xd9, 0xff, 0xb4, 0xca, 0xdb, 0xff, 0xba, 0xd1, 0xe1, 0xff, 0xbf, 0xd7, 0xe5, 0xff, 0xc3, 0xdc, 0xea, 0xff, 0xc1, 0xdc, 0xe8, 0xff, 0xbe, 0xd8, 0xe3, 0xff, 0xc0, 0xd5, 0xe2, 0xff, 0xbc, 0xd0, 0xe0, 0xff, 0xa8, 0xbe, 0xd1, 0xff, 0x9c, 0xac, 0xca, 0xff, 0xaa, 0xbb, 0xd4, 0xff, 0xbc, 0xd2, 0xdd, 0xff, 0xc2, 0xd6, 0xe3, 0xff, 0xc1, 0xd3, 0xe4, 0xff, 0xc0, 0xd4, 0xe2, 0xff, 0xbc, 0xd2, 0xdd, 0xff, 0xc0, 0xd7, 0xdf, 0xff, 0xc4, 0xdb, 0xe4, 0xff, 0xc5, 0xdb, 0xe4, 0xff, 0xc1, 0xd7, 0xdf, 0xff, 0xb5, 0xc6, 0xcf, 0xff, 0x9d, 0xaa, 0xbd, 0xff, 0x8c, 0x93, 0xa6, 0xff, 0xb3, 0xbf, 0xb4, 0xff, 0xb5, 0xc0, 0xa9, 0xff, 0xb4, 0xb9, 0xa4, 0xff, 0xb4, 0xbf, 0xb1, 0xff, 0xb3, 0xc2, 0xbd, 0xff, 0xb2, 0xc3, 0xc5, 0xff, 0xb7, 0xc1, 0xbe, 0xff, 0x9f, 0xa6, 0x9c, 0xff, 0x9e, 0xa3, 0x95, 0xff, 0x98, 0x94, 0x81, 0xff, 0x83, 0x84, 0x75, 0xff, 0x8f, 0x95, 0x8a, 0xff, 0xb3, 0xb8, 0xad, 0xff, 0x9b, 0x96, 0x87, 0xff, 0x6b, 0x58, 0x49, 0xff, 0x9b, 0x9a, 0x8d, 0xff, 0xc7, 0xde, 0xd1, 0xff, 0xa3, 0xa5, 0x9e, 0xff, 0x26, 0x10, 0x10, 0xff, 0x37, 0x2b, 0x2b, 0xff, 0xa6, 0xb5, 0xb1, 0xff, 0x95, 0xa3, 0xa0, 0xff, 0x9a, 0xa6, 0xa2, 0xff, 0xa8, 0xba, 0xb7, 0xff, 0xa4, 0xb9, 0xb8, 0xff, 0x8f, 0x90, 0x89, 0xff, 0x8f, 0x93, 0x85, 0xff, 0x89, 0x8d, 0x7f, 0xff, 0x75, 0x6d, 0x5d, 0xff, 0xa9, 0xb1, 0x98, 0xff, 0x74, 0x6c, 0x55, 0xff, 0x79, 0x77, 0x69, 0xff, 0xa9, 0xad, 0x9c, 0xff, 0x58, 0x4a, 0x35, 0xff, 0x83, 0x77, 0x5e, 0xff, 0x7e, 0x6b, 0x56, 0xff, 0x63, 0x51, 0x3b, 0xff, 0x5b, 0x45, 0x2c, 0xff, 0x6d, 0x5e, 0x43, 0xff, 0xb1, 0xb1, 0x97, 0xff, 0xb1, 0xb6, 0x9a, 0xff, 0x69, 0x5a, 0x44, 0xff, 0x25, 0x0c, 0x07, 0xff, 0x7f, 0x73, 0x5f, 0xff, 0xbb, 0xbe, 0xa1, 0xff, 0x60, 0x4d, 0x36, 0xff, 0x66, 0x4f, 0x3b, 0xff, 0x8c, 0x8a, 0x6e, 0xff, 0x9b, 0x9e, 0x88, 0xff, 0xb5, 0xc6, 0xb4, 0xff, 0xb4, 0xc4, 0xb4, 0xff, 0x86, 0x81, 0x75, 0xff, 0x64, 0x53, 0x41, 0xff, 0xc8, 0xc1, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe7, 0xdc, 0xff, 0xaa, 0x9d, 0x72, 0xff, 0xb2, 0xa1, 0x75, 0xff, 0xc0, 0xad, 0x83, 0xff, 0x9f, 0x8c, 0x60, 0xff, 0xbc, 0xb7, 0x8f, 0xff, 0xd7, 0xd4, 0xa9, 0xff, 0xab, 0x94, 0x5e, 0xff, 0xad, 0x96, 0x63, 0xff, 0xb2, 0xa8, 0x7a, 0xff, 0xcc, 0xca, 0xa3, 0xff, 0xc7, 0xbf, 0x94, 0xff, 0xad, 0xa0, 0x71, 0xff, 0xa0, 0x8b, 0x59, 0xff, 0x9b, 0x7f, 0x4d, 0xff, 0xb1, 0x9e, 0x6f, 0xff, 0xbb, 0xae, 0x7d, 0xff, 0xaf, 0xa1, 0x6f, 0xff, 0xa4, 0x94, 0x66, 0xff, 0xb1, 0xa6, 0x7b, 0xff, 0xb9, 0xb7, 0x96, 0xff, 0xab, 0xa4, 0x77, 0xff, 0xac, 0xb4, 0xa8, 0xff, 0xa7, 0xb5, 0xc0, 0xff, 0x8d, 0x92, 0x9e, 0xff, 0x4e, 0x48, 0x5d, 0xff, 0x57, 0x4e, 0x5e, 0xff, 0x87, 0x83, 0x82, 0xff, 0x95, 0x92, 0x8b, 0xff, 0x51, 0x48, 0x53, 0xff, 0x3b, 0x2e, 0x3c, 0xff, 0x43, 0x33, 0x3e, 0xff, 0x37, 0x22, 0x2d, 0xff, 0x34, 0x1f, 0x27, 0xff, 0x37, 0x28, 0x2e, 0xff, 0x54, 0x4c, 0x58, 0xff, 0x55, 0x57, 0x77, 0xff, 0x53, 0x62, 0x89, 0xff, 0x81, 0x8e, 0xb6, 0xff, 0x42, 0x3a, 0x52, 0xff, 0x35, 0x29, 0x37, 0xff, 0x58, 0x57, 0x65, 0xff, 0x99, 0xa9, 0xb8, 0xff, 0xb4, 0xcc, 0xe0, 0xff, 0xac, 0xc1, 0xd6, 0xff, 0xb2, 0xc9, 0xda, 0xff, 0xba, 0xd0, 0xe0, 0xff, 0xbf, 0xd8, 0xe6, 0xff, 0xbf, 0xdb, 0xe6, 0xff, 0xc1, 0xd9, 0xe5, 0xff, 0xbf, 0xd7, 0xe4, 0xff, 0xc0, 0xd9, 0xe8, 0xff, 0xc0, 0xd3, 0xe3, 0xff, 0xb2, 0xc6, 0xd7, 0xff, 0xb0, 0xc0, 0xd8, 0xff, 0xb8, 0xcc, 0xdf, 0xff, 0xbe, 0xd7, 0xe2, 0xff, 0xbc, 0xd4, 0xe1, 0xff, 0xbd, 0xd4, 0xe0, 0xff, 0xbc, 0xd1, 0xdd, 0xff, 0xbe, 0xd4, 0xdf, 0xff, 0xc1, 0xd9, 0xe1, 0xff, 0xc1, 0xd7, 0xe3, 0xff, 0xbe, 0xd5, 0xe2, 0xff, 0xbc, 0xd2, 0xdf, 0xff, 0xb8, 0xca, 0xd4, 0xff, 0x94, 0xa0, 0xb0, 0xff, 0x70, 0x74, 0x8a, 0xff, 0xae, 0xb2, 0xb1, 0xff, 0xcc, 0xd5, 0xc5, 0xff, 0xb8, 0xc3, 0xb3, 0xff, 0xbf, 0xcc, 0xbd, 0xff, 0xba, 0xcb, 0xc4, 0xff, 0xb8, 0xd1, 0xd3, 0xff, 0xae, 0xbb, 0xb7, 0xff, 0x8d, 0x89, 0x7c, 0xff, 0xa0, 0x9f, 0x8d, 0xff, 0x9c, 0x94, 0x7d, 0xff, 0x8f, 0x84, 0x71, 0xff, 0x85, 0x7d, 0x71, 0xff, 0x7b, 0x76, 0x6a, 0xff, 0x4d, 0x3f, 0x33, 0xff, 0x6c, 0x5f, 0x52, 0xff, 0xa5, 0xac, 0xa0, 0xff, 0xac, 0xb8, 0xb1, 0xff, 0x40, 0x34, 0x32, 0xff, 0x1a, 0x05, 0x03, 0xff, 0xa6, 0xb5, 0xac, 0xff, 0x95, 0x9d, 0x99, 0xff, 0x46, 0x3e, 0x40, 0xff, 0x95, 0xac, 0xa5, 0xff, 0x8d, 0x98, 0x92, 0xff, 0x89, 0x93, 0x8f, 0xff, 0x89, 0x91, 0x86, 0xff, 0x84, 0x7c, 0x6c, 0xff, 0x6f, 0x64, 0x59, 0xff, 0x40, 0x2d, 0x26, 0xff, 0x9c, 0x97, 0x78, 0xff, 0x78, 0x6e, 0x58, 0xff, 0xa3, 0xab, 0x9f, 0xff, 0x92, 0x93, 0x81, 0xff, 0x6e, 0x58, 0x40, 0xff, 0x8d, 0x82, 0x5f, 0xff, 0x72, 0x62, 0x45, 0xff, 0x59, 0x43, 0x32, 0xff, 0x35, 0x1b, 0x0e, 0xff, 0x8c, 0x87, 0x6d, 0xff, 0xcb, 0xd2, 0xb3, 0xff, 0x8e, 0x7f, 0x62, 0xff, 0x30, 0x19, 0x0c, 0xff, 0x56, 0x44, 0x32, 0xff, 0xc5, 0xc1, 0xa1, 0xff, 0x73, 0x67, 0x55, 0xff, 0x2e, 0x1a, 0x0f, 0xff, 0x75, 0x67, 0x4c, 0xff, 0x9a, 0x94, 0x7a, 0xff, 0xab, 0xac, 0x9c, 0xff, 0xb3, 0xb5, 0xa3, 0xff, 0x95, 0x93, 0x7d, 0xff, 0x74, 0x68, 0x4f, 0xff, 0xab, 0xa4, 0x8c, 0xff, 0xf3, 0xf3, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xfc, 0xfb, 0xff, 0xb7, 0xa5, 0x8b, 0xff, 0x9f, 0x84, 0x56, 0xff, 0x8c, 0x78, 0x4c, 0xff, 0xb1, 0xa3, 0x78, 0xff, 0xc0, 0xba, 0x8d, 0xff, 0xae, 0x9f, 0x6c, 0xff, 0xac, 0x94, 0x5f, 0xff, 0xb3, 0xa3, 0x73, 0xff, 0xb7, 0xb2, 0x88, 0xff, 0xca, 0xc9, 0xa6, 0xff, 0xc0, 0xbb, 0x8c, 0xff, 0xb4, 0x9d, 0x6a, 0xff, 0xa5, 0x92, 0x5d, 0xff, 0xa7, 0x96, 0x62, 0xff, 0xab, 0x9a, 0x68, 0xff, 0xb7, 0xa8, 0x76, 0xff, 0xa8, 0x95, 0x62, 0xff, 0xa1, 0x93, 0x67, 0xff, 0xb9, 0xb6, 0x95, 0xff, 0xba, 0xb8, 0x96, 0xff, 0xac, 0xa7, 0x7d, 0xff, 0xc0, 0xc8, 0xb9, 0xff, 0xaf, 0xc1, 0xd0, 0xff, 0x7f, 0x87, 0x97, 0xff, 0x5b, 0x52, 0x69, 0xff, 0x4a, 0x3f, 0x50, 0xff, 0x83, 0x7d, 0x7d, 0xff, 0x88, 0x87, 0x83, 0xff, 0x53, 0x4a, 0x57, 0xff, 0x38, 0x2b, 0x3b, 0xff, 0x28, 0x17, 0x23, 0xff, 0x16, 0x01, 0x0a, 0xff, 0x1d, 0x09, 0x11, 0xff, 0x3c, 0x2d, 0x35, 0xff, 0x5a, 0x56, 0x68, 0xff, 0x60, 0x70, 0x93, 0xff, 0x7e, 0x94, 0xc2, 0xff, 0x65, 0x68, 0x88, 0xff, 0x20, 0x14, 0x23, 0xff, 0x63, 0x61, 0x75, 0xff, 0x6d, 0x6c, 0x81, 0xff, 0x6e, 0x74, 0x83, 0xff, 0xb2, 0xc9, 0xdd, 0xff, 0xaa, 0xc0, 0xd4, 0xff, 0xb1, 0xc8, 0xd9, 0xff, 0xb9, 0xd1, 0xe1, 0xff, 0xc0, 0xd7, 0xe5, 0xff, 0xc1, 0xd9, 0xe4, 0xff, 0xba, 0xd2, 0xde, 0xff, 0xd0, 0xed, 0xf7, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xc4, 0xe5, 0xf3, 0xff, 0xbe, 0xd1, 0xe4, 0xff, 0xd5, 0xf4, 0xff, 0xff, 0xd9, 0xfd, 0xff, 0xff, 0xc9, 0xe3, 0xee, 0xff, 0xbc, 0xd6, 0xdf, 0xff, 0xbe, 0xd6, 0xdf, 0xff, 0xbb, 0xd1, 0xdc, 0xff, 0xbd, 0xd4, 0xdf, 0xff, 0xbd, 0xd2, 0xdd, 0xff, 0xb8, 0xcc, 0xd8, 0xff, 0xbc, 0xcf, 0xdc, 0xff, 0xbb, 0xcf, 0xde, 0xff, 0xb8, 0xca, 0xd2, 0xff, 0xa5, 0xb1, 0xb6, 0xff, 0x73, 0x74, 0x80, 0xff, 0x89, 0x88, 0x86, 0xff, 0xc1, 0xca, 0xbd, 0xff, 0xb9, 0xc9, 0xbe, 0xff, 0xb8, 0xc2, 0xb0, 0xff, 0xbc, 0xc8, 0xbc, 0xff, 0xce, 0xe8, 0xeb, 0xff, 0xa0, 0xaa, 0xa8, 0xff, 0x8f, 0x86, 0x78, 0xff, 0xad, 0xa8, 0x99, 0xff, 0x74, 0x6b, 0x5b, 0xff, 0x73, 0x65, 0x59, 0xff, 0x8d, 0x87, 0x7e, 0xff, 0x89, 0x8d, 0x86, 0xff, 0xaa, 0xad, 0xa4, 0xff, 0xb7, 0xbf, 0xb4, 0xff, 0xa3, 0xac, 0xa6, 0xff, 0x6f, 0x68, 0x68, 0xff, 0x00, 0x00, 0x00, 0xff, 0x65, 0x52, 0x4e, 0xff, 0xa5, 0xad, 0xa4, 0xff, 0x6f, 0x78, 0x73, 0xff, 0x75, 0x78, 0x78, 0xff, 0xad, 0xc2, 0xbd, 0xff, 0x61, 0x60, 0x5b, 0xff, 0x70, 0x6b, 0x64, 0xff, 0x7c, 0x7f, 0x74, 0xff, 0x50, 0x3b, 0x30, 0xff, 0x44, 0x32, 0x2a, 0xff, 0x30, 0x1c, 0x16, 0xff, 0x60, 0x4e, 0x37, 0xff, 0x84, 0x7d, 0x68, 0xff, 0xc6, 0xd4, 0xc4, 0xff, 0x68, 0x60, 0x52, 0xff, 0x54, 0x39, 0x24, 0xff, 0x8f, 0x80, 0x5b, 0xff, 0x7b, 0x6a, 0x4a, 0xff, 0x44, 0x2c, 0x1d, 0xff, 0x4b, 0x39, 0x2f, 0xff, 0xbe, 0xc1, 0xa8, 0xff, 0x9d, 0x9a, 0x78, 0xff, 0x58, 0x42, 0x2d, 0xff, 0x43, 0x27, 0x13, 0xff, 0xa1, 0x94, 0x77, 0xff, 0x92, 0x8e, 0x70, 0xff, 0x2a, 0x11, 0x07, 0xff, 0x5b, 0x48, 0x3d, 0xff, 0x7e, 0x74, 0x54, 0xff, 0x61, 0x54, 0x40, 0xff, 0x9b, 0x9a, 0x87, 0xff, 0x9e, 0x97, 0x80, 0xff, 0x5c, 0x4a, 0x30, 0xff, 0x8c, 0x7e, 0x61, 0xff, 0xda, 0xd9, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xee, 0xe6, 0xff, 0xc1, 0xb2, 0x8b, 0xff, 0xc2, 0xb3, 0x83, 0xff, 0xb6, 0xad, 0x7f, 0xff, 0x8c, 0x74, 0x41, 0xff, 0xb7, 0xa2, 0x77, 0xff, 0xca, 0xc5, 0x96, 0xff, 0xbc, 0xb6, 0x84, 0xff, 0xb4, 0xa3, 0x74, 0xff, 0xc0, 0xba, 0x8e, 0xff, 0xa5, 0x98, 0x62, 0xff, 0xa2, 0x80, 0x4d, 0xff, 0xaa, 0x95, 0x61, 0xff, 0xae, 0x99, 0x66, 0xff, 0xae, 0x9c, 0x67, 0xff, 0xb1, 0xa3, 0x70, 0xff, 0xb1, 0xa5, 0x74, 0xff, 0xbc, 0xba, 0x9a, 0xff, 0xba, 0xb6, 0x91, 0xff, 0xba, 0xbb, 0x9e, 0xff, 0xbe, 0xbe, 0xa3, 0xff, 0xbb, 0xbe, 0xa9, 0xff, 0xa6, 0xb8, 0xc5, 0xff, 0x81, 0x8a, 0x9d, 0xff, 0x46, 0x3b, 0x52, 0xff, 0x40, 0x35, 0x4d, 0xff, 0x70, 0x6e, 0x75, 0xff, 0x7e, 0x80, 0x82, 0xff, 0x59, 0x4f, 0x5d, 0xff, 0x2a, 0x1a, 0x27, 0xff, 0x57, 0x4d, 0x54, 0xff, 0x83, 0x7e, 0x81, 0xff, 0x69, 0x66, 0x6a, 0xff, 0x40, 0x35, 0x42, 0xff, 0x6e, 0x74, 0x88, 0xff, 0x77, 0x8c, 0xb0, 0xff, 0x6f, 0x7c, 0xb1, 0xff, 0x3f, 0x35, 0x4c, 0xff, 0x3a, 0x2e, 0x3a, 0xff, 0x7e, 0x87, 0xa8, 0xff, 0x3f, 0x3c, 0x5a, 0xff, 0x24, 0x1a, 0x2b, 0xff, 0x9b, 0xac, 0xc1, 0xff, 0xb1, 0xc9, 0xdf, 0xff, 0xab, 0xc1, 0xd3, 0xff, 0xb5, 0xcc, 0xda, 0xff, 0xb9, 0xcd, 0xdb, 0xff, 0xaf, 0xc2, 0xd2, 0xff, 0xd6, 0xf0, 0xf9, 0xff, 0xaf, 0xbf, 0xc3, 0xff, 0x64, 0x65, 0x67, 0xff, 0xba, 0xc7, 0xcb, 0xff, 0xdb, 0xf3, 0xf9, 0xff, 0x9b, 0x9e, 0x9c, 0xff, 0x9a, 0x9e, 0x9d, 0xff, 0xbe, 0xd5, 0xdd, 0xff, 0xce, 0xe7, 0xf0, 0xff, 0xc1, 0xdb, 0xe3, 0xff, 0xbd, 0xd5, 0xdb, 0xff, 0xc4, 0xd9, 0xe1, 0xff, 0xbf, 0xd1, 0xdf, 0xff, 0xb4, 0xc6, 0xd1, 0xff, 0xb0, 0xc2, 0xcb, 0xff, 0xa7, 0xb5, 0xc1, 0xff, 0xa3, 0xaf, 0xb1, 0xff, 0xb2, 0xba, 0xaf, 0xff, 0xaa, 0xac, 0xa6, 0xff, 0xa3, 0xa8, 0x98, 0xff, 0xb8, 0xc6, 0xb6, 0xff, 0xc9, 0xdb, 0xd3, 0xff, 0xaf, 0xb1, 0x9c, 0xff, 0x91, 0x90, 0x80, 0xff, 0xb0, 0xbe, 0xbc, 0xff, 0xa2, 0xa9, 0x9b, 0xff, 0xaa, 0xa9, 0x90, 0xff, 0x8c, 0x87, 0x77, 0xff, 0x6e, 0x69, 0x5d, 0xff, 0x99, 0x9f, 0x93, 0xff, 0xa2, 0xa6, 0x9a, 0xff, 0xbd, 0xc8, 0xbc, 0xff, 0xa1, 0xa6, 0x9c, 0xff, 0x6b, 0x6a, 0x63, 0xff, 0x88, 0x88, 0x85, 0xff, 0x59, 0x51, 0x52, 0xff, 0x6b, 0x69, 0x68, 0xff, 0x83, 0x86, 0x80, 0xff, 0x87, 0x8c, 0x84, 0xff, 0x9d, 0xa7, 0xa1, 0xff, 0x8c, 0x91, 0x8d, 0xff, 0x81, 0x89, 0x84, 0xff, 0x6e, 0x68, 0x64, 0xff, 0x43, 0x31, 0x2a, 0xff, 0x49, 0x35, 0x2f, 0xff, 0x1e, 0x02, 0x04, 0xff, 0x51, 0x49, 0x44, 0xff, 0x81, 0x7b, 0x6c, 0xff, 0x27, 0x0d, 0x09, 0xff, 0x86, 0x84, 0x71, 0xff, 0xc4, 0xcd, 0xb9, 0xff, 0x4e, 0x3f, 0x34, 0xff, 0x39, 0x23, 0x14, 0xff, 0x64, 0x4e, 0x37, 0xff, 0x5a, 0x40, 0x2e, 0xff, 0x36, 0x1d, 0x11, 0xff, 0xa0, 0x9e, 0x8e, 0xff, 0xb4, 0xb5, 0x9b, 0xff, 0x4a, 0x35, 0x1c, 0xff, 0x50, 0x3b, 0x2b, 0xff, 0x8f, 0x80, 0x61, 0xff, 0x94, 0x84, 0x64, 0xff, 0x4f, 0x3f, 0x2e, 0xff, 0x71, 0x60, 0x4e, 0xff, 0x85, 0x74, 0x56, 0xff, 0x64, 0x53, 0x3a, 0xff, 0x65, 0x59, 0x45, 0xff, 0x98, 0x92, 0x75, 0xff, 0x71, 0x65, 0x47, 0xff, 0x79, 0x6d, 0x53, 0xff, 0xa3, 0x99, 0x8c, 0xff, 0xf5, 0xf4, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe2, 0xdf, 0xc9, 0xff, 0xac, 0x9c, 0x6d, 0xff, 0x9c, 0x88, 0x5a, 0xff, 0xb4, 0xa1, 0x71, 0xff, 0xc3, 0xb1, 0x81, 0xff, 0xb4, 0xab, 0x7b, 0xff, 0xaf, 0xa3, 0x6f, 0xff, 0xbb, 0xa5, 0x6e, 0xff, 0xbd, 0xad, 0x79, 0xff, 0xad, 0x9c, 0x6c, 0xff, 0x9c, 0x87, 0x54, 0xff, 0xac, 0x92, 0x60, 0xff, 0xaa, 0x92, 0x61, 0xff, 0xaa, 0x95, 0x60, 0xff, 0x9e, 0x8e, 0x5a, 0xff, 0xae, 0xa6, 0x79, 0xff, 0xc2, 0xc2, 0x9f, 0xff, 0xb4, 0xad, 0x80, 0xff, 0xb1, 0xb2, 0x91, 0xff, 0xb7, 0xba, 0xa3, 0xff, 0xb1, 0xb1, 0x9f, 0xff, 0xa2, 0xb2, 0xbd, 0xff, 0x74, 0x80, 0x9c, 0xff, 0x4f, 0x4d, 0x6a, 0xff, 0x3e, 0x37, 0x52, 0xff, 0x5f, 0x5c, 0x63, 0xff, 0x87, 0x83, 0x81, 0xff, 0x38, 0x30, 0x3a, 0xff, 0x8f, 0x94, 0x9a, 0xff, 0xe9, 0xfb, 0xfb, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xff, 0xb6, 0xcd, 0xd2, 0xff, 0x9e, 0xb0, 0xbb, 0xff, 0x6e, 0x78, 0x97, 0xff, 0x5a, 0x67, 0x93, 0xff, 0x2d, 0x1f, 0x33, 0xff, 0x42, 0x38, 0x4e, 0xff, 0x6f, 0x7a, 0x98, 0xff, 0x71, 0x7c, 0x8d, 0xff, 0x7e, 0x83, 0x8d, 0xff, 0x8b, 0x9b, 0xac, 0xff, 0xa5, 0xbc, 0xd3, 0xff, 0xa6, 0xba, 0xcc, 0xff, 0xa9, 0xbc, 0xce, 0xff, 0xaf, 0xc4, 0xd0, 0xff, 0xc0, 0xd9, 0xe7, 0xff, 0xca, 0xe2, 0xf1, 0xff, 0x3f, 0x30, 0x30, 0xff, 0x0c, 0x00, 0x00, 0xff, 0x43, 0x2b, 0x2a, 0xff, 0x58, 0x4b, 0x48, 0xff, 0x1c, 0x09, 0x05, 0xff, 0x1c, 0x07, 0x06, 0xff, 0x6c, 0x6c, 0x6b, 0xff, 0xbe, 0xcc, 0xcb, 0xff, 0xca, 0xe5, 0xe7, 0xff, 0xd2, 0xeb, 0xf2, 0xff, 0xba, 0xcc, 0xd8, 0xff, 0xa1, 0xad, 0xc0, 0xff, 0xa7, 0xb3, 0xbe, 0xff, 0xa3, 0xb1, 0xb3, 0xff, 0xa8, 0xb2, 0xb4, 0xff, 0xb3, 0xbf, 0xb7, 0xff, 0xb9, 0xca, 0xb7, 0xff, 0xc8, 0xd0, 0xbb, 0xff, 0xbe, 0xc6, 0xb3, 0xff, 0xb8, 0xcd, 0xc3, 0xff, 0xb7, 0xbb, 0xac, 0xff, 0xa2, 0x98, 0x7b, 0xff, 0x87, 0x83, 0x72, 0xff, 0x9b, 0x9d, 0x92, 0xff, 0xa4, 0xa2, 0x8a, 0xff, 0xa0, 0x9c, 0x83, 0xff, 0x82, 0x7f, 0x6f, 0xff, 0xa9, 0xa9, 0x9e, 0xff, 0xbb, 0xc4, 0xb3, 0xff, 0xae, 0xb0, 0x9e, 0xff, 0x7b, 0x73, 0x67, 0xff, 0x0d, 0x00, 0x00, 0xff, 0x64, 0x5c, 0x5b, 0xff, 0x80, 0x84, 0x81, 0xff, 0x95, 0x9f, 0x9c, 0xff, 0xaa, 0xb8, 0xb3, 0xff, 0x96, 0xa5, 0x9f, 0xff, 0xa6, 0xb2, 0xb1, 0xff, 0xa1, 0xad, 0xab, 0xff, 0x42, 0x3c, 0x39, 0xff, 0x79, 0x78, 0x6f, 0xff, 0x98, 0x99, 0x89, 0xff, 0x46, 0x2f, 0x26, 0xff, 0x3a, 0x1f, 0x1e, 0xff, 0x0b, 0x00, 0x00, 0xff, 0x7a, 0x83, 0x81, 0xff, 0xa0, 0xa1, 0x94, 0xff, 0x17, 0x00, 0x00, 0xff, 0xa4, 0xa3, 0x8d, 0xff, 0x91, 0x8f, 0x76, 0xff, 0x2a, 0x16, 0x0c, 0xff, 0x41, 0x28, 0x23, 0xff, 0x64, 0x4f, 0x3e, 0xff, 0x43, 0x2a, 0x1d, 0xff, 0x65, 0x5b, 0x4a, 0xff, 0xd7, 0xe0, 0xc4, 0xff, 0x79, 0x6e, 0x53, 0xff, 0x42, 0x27, 0x16, 0xff, 0x6e, 0x59, 0x45, 0xff, 0x82, 0x73, 0x52, 0xff, 0x54, 0x3f, 0x25, 0xff, 0x6b, 0x58, 0x3d, 0xff, 0x8b, 0x7a, 0x5c, 0xff, 0x74, 0x5e, 0x40, 0xff, 0x89, 0x7a, 0x5b, 0xff, 0xad, 0xa6, 0x86, 0xff, 0x97, 0x8f, 0x6c, 0xff, 0x96, 0x8b, 0x6a, 0xff, 0x86, 0x79, 0x66, 0xff, 0xcd, 0xc9, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xf3, 0xee, 0xff, 0xab, 0x9a, 0x78, 0xff, 0xa1, 0x8a, 0x5b, 0xff, 0x94, 0x82, 0x52, 0xff, 0xa1, 0x92, 0x5f, 0xff, 0xae, 0x9b, 0x68, 0xff, 0xc1, 0xaf, 0x7d, 0xff, 0xab, 0x97, 0x62, 0xff, 0x9e, 0x89, 0x54, 0xff, 0xbc, 0xae, 0x7f, 0xff, 0xa6, 0x95, 0x64, 0xff, 0xa4, 0x8e, 0x5b, 0xff, 0xa8, 0x95, 0x62, 0xff, 0xa6, 0x95, 0x62, 0xff, 0xad, 0xa0, 0x6f, 0xff, 0xad, 0xa4, 0x75, 0xff, 0xab, 0xa6, 0x78, 0xff, 0xaa, 0xa1, 0x77, 0xff, 0xac, 0xa1, 0x6f, 0xff, 0xb6, 0xbb, 0x9d, 0xff, 0xa6, 0xa7, 0x9b, 0xff, 0x9c, 0xae, 0xaf, 0xff, 0x81, 0x8f, 0xa9, 0xff, 0x6e, 0x76, 0x9a, 0xff, 0x49, 0x52, 0x74, 0xff, 0x7c, 0x79, 0x85, 0xff, 0x76, 0x71, 0x6f, 0xff, 0x91, 0x9a, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xf8, 0xfa, 0xff, 0xcc, 0xea, 0xec, 0xff, 0xd0, 0xec, 0xf1, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xe2, 0xfe, 0xff, 0xff, 0xb0, 0xc3, 0xd1, 0xff, 0x6e, 0x75, 0x91, 0xff, 0x26, 0x1d, 0x2f, 0xff, 0x84, 0x91, 0x9b, 0xff, 0xd3, 0xea, 0xed, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xcc, 0xe7, 0xec, 0xff, 0xb1, 0xc9, 0xd5, 0xff, 0x9f, 0xb2, 0xc5, 0xff, 0x94, 0xa4, 0xc4, 0xff, 0x7c, 0x86, 0xac, 0xff, 0xb7, 0xcb, 0xe2, 0xff, 0x72, 0x77, 0x81, 0xff, 0x18, 0x00, 0x00, 0xff, 0x35, 0x20, 0x1b, 0xff, 0x28, 0x1c, 0x17, 0xff, 0x49, 0x3c, 0x3a, 0xff, 0x72, 0x6e, 0x6e, 0xff, 0x77, 0x73, 0x6e, 0xff, 0x5d, 0x57, 0x4f, 0xff, 0x44, 0x3a, 0x30, 0xff, 0x5a, 0x4b, 0x44, 0xff, 0x7b, 0x73, 0x83, 0xff, 0x7e, 0x83, 0x95, 0xff, 0x9a, 0xa1, 0xa2, 0xff, 0xad, 0xb2, 0xb7, 0xff, 0xbc, 0xcc, 0xc7, 0xff, 0xc2, 0xd7, 0xce, 0xff, 0xbe, 0xd1, 0xc4, 0xff, 0xb4, 0xbd, 0xaa, 0xff, 0x99, 0x96, 0x80, 0xff, 0xb1, 0xb4, 0xa3, 0xff, 0xc9, 0xd7, 0xc7, 0xff, 0x8e, 0x89, 0x72, 0xff, 0x89, 0x7e, 0x65, 0xff, 0x91, 0x8b, 0x79, 0xff, 0x85, 0x7d, 0x69, 0xff, 0x7e, 0x73, 0x59, 0xff, 0x8a, 0x83, 0x6e, 0xff, 0x95, 0x97, 0x88, 0xff, 0x96, 0x91, 0x87, 0xff, 0x97, 0x9a, 0x8a, 0xff, 0x8e, 0x95, 0x85, 0xff, 0x21, 0x0a, 0x05, 0xff, 0x38, 0x19, 0x1b, 0xff, 0x92, 0x94, 0x8c, 0xff, 0xb3, 0xc3, 0xb9, 0xff, 0xa8, 0xba, 0xb5, 0xff, 0x9b, 0xa9, 0xa6, 0xff, 0xac, 0xba, 0xb8, 0xff, 0xa3, 0xbb, 0xbc, 0xff, 0xbd, 0xdb, 0xd7, 0xff, 0x56, 0x50, 0x4d, 0xff, 0x4b, 0x3d, 0x32, 0xff, 0x9d, 0x98, 0x7f, 0xff, 0x5c, 0x48, 0x38, 0xff, 0x41, 0x2d, 0x29, 0xff, 0x11, 0x05, 0x04, 0xff, 0xb1, 0xb8, 0xa4, 0xff, 0x6b, 0x61, 0x55, 0xff, 0x3b, 0x29, 0x1d, 0xff, 0x9c, 0x92, 0x76, 0xff, 0x4f, 0x3e, 0x27, 0xff, 0x32, 0x1d, 0x17, 0xff, 0x3e, 0x27, 0x25, 0xff, 0x4d, 0x37, 0x2a, 0xff, 0x46, 0x31, 0x1e, 0xff, 0xa1, 0x9a, 0x7f, 0xff, 0xb7, 0xb5, 0x94, 0xff, 0x5a, 0x46, 0x30, 0xff, 0x40, 0x26, 0x1a, 0xff, 0x56, 0x3e, 0x2a, 0xff, 0x54, 0x3a, 0x25, 0xff, 0x51, 0x39, 0x27, 0xff, 0x6b, 0x57, 0x41, 0xff, 0x46, 0x2c, 0x1c, 0xff, 0x62, 0x48, 0x32, 0xff, 0x94, 0x84, 0x60, 0xff, 0x79, 0x6e, 0x4d, 0xff, 0x6d, 0x5c, 0x42, 0xff, 0x5e, 0x49, 0x34, 0xff, 0x97, 0x8b, 0x82, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe7, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0xde, 0xd2, 0xff, 0x9c, 0x88, 0x5d, 0xff, 0xa3, 0x8f, 0x5e, 0xff, 0xb7, 0xa4, 0x71, 0xff, 0xa7, 0x94, 0x60, 0xff, 0xaa, 0x98, 0x64, 0xff, 0xb0, 0x9a, 0x66, 0xff, 0x9b, 0x83, 0x50, 0xff, 0xb2, 0xa2, 0x71, 0xff, 0xb8, 0xab, 0x78, 0xff, 0x9d, 0x8e, 0x58, 0xff, 0xa3, 0x92, 0x5e, 0xff, 0xa8, 0x9c, 0x6d, 0xff, 0xba, 0xb2, 0x86, 0xff, 0xaf, 0xa9, 0x7d, 0xff, 0xb3, 0xad, 0x84, 0xff, 0x94, 0x80, 0x56, 0xff, 0x9c, 0x8b, 0x62, 0xff, 0xaa, 0xa5, 0x88, 0xff, 0x90, 0x90, 0x80, 0xff, 0x91, 0x95, 0x91, 0xff, 0x8b, 0x95, 0xa7, 0xff, 0x76, 0x84, 0xa8, 0xff, 0x6a, 0x78, 0xa5, 0xff, 0x41, 0x44, 0x63, 0xff, 0x77, 0x78, 0x7e, 0xff, 0xe7, 0xfc, 0xfc, 0xff, 0xd5, 0xf9, 0xf8, 0xff, 0xcf, 0xeb, 0xec, 0xff, 0xce, 0xea, 0xed, 0xff, 0xcb, 0xe9, 0xee, 0xff, 0xca, 0xe6, 0xeb, 0xff, 0xcd, 0xed, 0xf1, 0xff, 0xd7, 0xf7, 0xfe, 0xff, 0x70, 0x6f, 0x7c, 0xff, 0x88, 0x8f, 0x97, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xda, 0xfb, 0xf7, 0xff, 0xc8, 0xeb, 0xe9, 0xff, 0xc8, 0xea, 0xe9, 0xff, 0xcf, 0xee, 0xf1, 0xff, 0xd3, 0xf0, 0xf3, 0xff, 0xc5, 0xdd, 0xe8, 0xff, 0x9c, 0xae, 0xc9, 0xff, 0x67, 0x76, 0xa3, 0xff, 0x3c, 0x34, 0x52, 0xff, 0x13, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x30, 0x24, 0x1d, 0xff, 0x70, 0x6a, 0x5d, 0xff, 0x65, 0x62, 0x5f, 0xff, 0x40, 0x3d, 0x3b, 0xff, 0x51, 0x48, 0x40, 0xff, 0x3c, 0x29, 0x22, 0xff, 0x30, 0x15, 0x12, 0xff, 0x24, 0x08, 0x05, 0xff, 0x03, 0x00, 0x00, 0xff, 0x54, 0x41, 0x37, 0xff, 0xc6, 0xce, 0xc0, 0xff, 0xc7, 0xe5, 0xe1, 0xff, 0xd3, 0xf0, 0xea, 0xff, 0xc6, 0xdd, 0xd5, 0xff, 0xa9, 0xb2, 0xa2, 0xff, 0x97, 0x8c, 0x73, 0xff, 0x7e, 0x70, 0x54, 0xff, 0x8b, 0x87, 0x6e, 0xff, 0x99, 0x94, 0x81, 0xff, 0x6a, 0x5e, 0x52, 0xff, 0x74, 0x6a, 0x61, 0xff, 0x69, 0x5b, 0x54, 0xff, 0x6b, 0x53, 0x40, 0xff, 0x7b, 0x66, 0x4b, 0xff, 0x6d, 0x63, 0x51, 0xff, 0x94, 0x93, 0x85, 0xff, 0x7f, 0x7f, 0x73, 0xff, 0xa1, 0xa9, 0x9c, 0xff, 0x93, 0x95, 0x88, 0xff, 0x48, 0x43, 0x3b, 0xff, 0x5c, 0x4a, 0x47, 0xff, 0x85, 0x80, 0x77, 0xff, 0x89, 0x96, 0x8e, 0xff, 0xaa, 0xc2, 0xc2, 0xff, 0xa1, 0xbc, 0xc1, 0xff, 0xa0, 0xb9, 0xb9, 0xff, 0x87, 0x97, 0x97, 0xff, 0x95, 0x9c, 0x97, 0xff, 0x7a, 0x72, 0x69, 0xff, 0x28, 0x13, 0x0b, 0xff, 0x4e, 0x37, 0x2a, 0xff, 0x59, 0x47, 0x38, 0xff, 0x45, 0x36, 0x31, 0xff, 0x32, 0x21, 0x20, 0xff, 0x86, 0x7c, 0x61, 0xff, 0x3b, 0x25, 0x1c, 0xff, 0x74, 0x69, 0x5d, 0xff, 0x5c, 0x44, 0x2e, 0xff, 0x4d, 0x2f, 0x23, 0xff, 0x39, 0x23, 0x21, 0xff, 0x30, 0x1e, 0x1c, 0xff, 0x37, 0x1e, 0x16, 0xff, 0x6a, 0x57, 0x3e, 0xff, 0xa4, 0x98, 0x78, 0xff, 0x6e, 0x5a, 0x3f, 0xff, 0x52, 0x39, 0x29, 0xff, 0x4a, 0x32, 0x27, 0xff, 0x5a, 0x43, 0x2d, 0xff, 0x55, 0x3c, 0x2e, 0xff, 0x58, 0x42, 0x32, 0xff, 0x4e, 0x39, 0x2a, 0xff, 0x46, 0x2d, 0x21, 0xff, 0x58, 0x40, 0x2d, 0xff, 0x51, 0x3b, 0x24, 0xff, 0x34, 0x1d, 0x14, 0xff, 0x34, 0x1e, 0x18, 0xff, 0x5e, 0x4d, 0x4b, 0xff, 0xe9, 0xe6, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xca, 0xc1, 0xa7, 0xff, 0xae, 0x9a, 0x67, 0xff, 0xa3, 0x8e, 0x5a, 0xff, 0x9f, 0x8a, 0x57, 0xff, 0x9f, 0x8a, 0x56, 0xff, 0xaf, 0x9b, 0x67, 0xff, 0xa9, 0x94, 0x62, 0xff, 0x9c, 0x89, 0x55, 0xff, 0xb5, 0xa7, 0x74, 0xff, 0xa9, 0x9d, 0x69, 0xff, 0xa0, 0x91, 0x5f, 0xff, 0xb1, 0xa8, 0x7f, 0xff, 0xb1, 0xab, 0x82, 0xff, 0xa6, 0x99, 0x69, 0xff, 0xa7, 0x9c, 0x73, 0xff, 0xbb, 0xbc, 0xa6, 0xff, 0xcf, 0xe2, 0xdf, 0xff, 0xd5, 0xe5, 0xe6, 0xff, 0xc6, 0xd7, 0xd6, 0xff, 0xbb, 0xc5, 0xc3, 0xff, 0x92, 0x9f, 0xab, 0xff, 0x79, 0x8a, 0xa9, 0xff, 0x72, 0x84, 0xb1, 0xff, 0x55, 0x63, 0x82, 0xff, 0xcb, 0xdb, 0xdc, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xce, 0xeb, 0xec, 0xff, 0xd2, 0xec, 0xf0, 0xff, 0xcf, 0xea, 0xee, 0xff, 0xcb, 0xe8, 0xed, 0xff, 0xcd, 0xe6, 0xeb, 0xff, 0xc9, 0xe6, 0xee, 0xff, 0xc2, 0xda, 0xeb, 0xff, 0x77, 0x7b, 0x87, 0xff, 0xc4, 0xdf, 0xdf, 0xff, 0xdc, 0xfc, 0xfc, 0xff, 0xcc, 0xea, 0xeb, 0xff, 0xcc, 0xe8, 0xed, 0xff, 0xca, 0xe5, 0xec, 0xff, 0xc7, 0xe3, 0xeb, 0xff, 0xc5, 0xe3, 0xe8, 0xff, 0xd4, 0xf2, 0xf5, 0xff, 0xba, 0xd3, 0xe0, 0xff, 0x9b, 0xad, 0xc7, 0xff, 0x79, 0x79, 0x89, 0xff, 0x7d, 0x7a, 0x78, 0xff, 0x9a, 0xa8, 0xaa, 0xff, 0x9f, 0xa7, 0xa8, 0xff, 0x7a, 0x6f, 0x64, 0xff, 0x46, 0x33, 0x24, 0xff, 0x44, 0x30, 0x23, 0xff, 0x3c, 0x27, 0x21, 0xff, 0x45, 0x30, 0x29, 0xff, 0x46, 0x32, 0x2b, 0xff, 0x40, 0x2d, 0x28, 0xff, 0x58, 0x4c, 0x40, 0xff, 0x9e, 0x9e, 0x8d, 0xff, 0xca, 0xe1, 0xd5, 0xff, 0xbb, 0xd2, 0xc8, 0xff, 0xb0, 0xb9, 0xae, 0xff, 0xb8, 0xc9, 0xc4, 0xff, 0xbf, 0xd2, 0xca, 0xff, 0xa4, 0xa3, 0x8f, 0xff, 0x97, 0x8f, 0x75, 0xff, 0x84, 0x74, 0x62, 0xff, 0x50, 0x3b, 0x35, 0xff, 0x36, 0x21, 0x21, 0xff, 0x4f, 0x3f, 0x3c, 0xff, 0x4f, 0x3b, 0x36, 0xff, 0x71, 0x56, 0x47, 0xff, 0x6d, 0x59, 0x46, 0xff, 0x70, 0x68, 0x5c, 0xff, 0xa6, 0xa8, 0x9c, 0xff, 0x94, 0x9c, 0x8f, 0xff, 0x65, 0x60, 0x56, 0xff, 0xac, 0xae, 0xa4, 0xff, 0x8b, 0x95, 0x88, 0xff, 0x5d, 0x54, 0x4c, 0xff, 0x63, 0x58, 0x52, 0xff, 0x40, 0x3a, 0x37, 0xff, 0x80, 0x86, 0x87, 0xff, 0x58, 0x5f, 0x64, 0xff, 0x67, 0x6d, 0x6b, 0xff, 0x4c, 0x43, 0x42, 0xff, 0x51, 0x3e, 0x37, 0xff, 0x4c, 0x3b, 0x2f, 0xff, 0x1e, 0x06, 0x05, 0xff, 0x25, 0x0d, 0x0b, 0xff, 0x7a, 0x6f, 0x62, 0xff, 0x4c, 0x43, 0x3d, 0xff, 0x38, 0x22, 0x20, 0xff, 0x30, 0x17, 0x0e, 0xff, 0x49, 0x3c, 0x33, 0xff, 0x87, 0x7f, 0x6b, 0xff, 0x43, 0x28, 0x19, 0xff, 0x40, 0x28, 0x21, 0xff, 0x34, 0x1f, 0x1c, 0xff, 0x2c, 0x19, 0x16, 0xff, 0x3b, 0x24, 0x1c, 0xff, 0x59, 0x44, 0x37, 0xff, 0x54, 0x43, 0x34, 0xff, 0x40, 0x2a, 0x1c, 0xff, 0x45, 0x2b, 0x1f, 0xff, 0x4a, 0x34, 0x23, 0xff, 0x4f, 0x38, 0x2d, 0xff, 0x3d, 0x29, 0x21, 0xff, 0x39, 0x28, 0x1e, 0xff, 0x45, 0x31, 0x25, 0xff, 0x46, 0x31, 0x24, 0xff, 0x31, 0x1c, 0x17, 0xff, 0x32, 0x1e, 0x1a, 0xff, 0x28, 0x14, 0x15, 0xff, 0x2e, 0x1d, 0x1f, 0xff, 0xbb, 0xb6, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xec, 0xe7, 0x6b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf4, 0xef, 0xff, 0xb3, 0xa3, 0x81, 0xff, 0xa1, 0x8b, 0x58, 0xff, 0xa7, 0x92, 0x5e, 0xff, 0x9c, 0x86, 0x52, 0xff, 0xa0, 0x8b, 0x57, 0xff, 0xb1, 0x9e, 0x6b, 0xff, 0x9d, 0x8a, 0x59, 0xff, 0x9d, 0x8d, 0x5b, 0xff, 0xb4, 0xa4, 0x70, 0xff, 0xa9, 0xa1, 0x73, 0xff, 0xb2, 0xac, 0x85, 0xff, 0xa2, 0x91, 0x60, 0xff, 0x86, 0x75, 0x47, 0xff, 0xc8, 0xce, 0xc0, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xdf, 0xfc, 0xff, 0xff, 0xda, 0xf4, 0xf8, 0xff, 0xe0, 0xf8, 0xff, 0xff, 0xeb, 0xff, 0xff, 0xff, 0xb9, 0xcb, 0xd3, 0xff, 0x7e, 0x8a, 0xa4, 0xff, 0x6d, 0x83, 0x9e, 0xff, 0xc4, 0xda, 0xe7, 0xff, 0xee, 0xff, 0xff, 0xff, 0xcd, 0xec, 0xe8, 0xff, 0xd5, 0xeb, 0xec, 0xff, 0xd1, 0xe9, 0xed, 0xff, 0xce, 0xe7, 0xee, 0xff, 0xcc, 0xe6, 0xec, 0xff, 0xcb, 0xe3, 0xec, 0xff, 0xcc, 0xe5, 0xf2, 0xff, 0xa3, 0xb4, 0xd0, 0xff, 0xad, 0xc2, 0xd3, 0xff, 0xdd, 0xff, 0xfc, 0xff, 0xcb, 0xea, 0xec, 0xff, 0xcb, 0xea, 0xeb, 0xff, 0xca, 0xe7, 0xed, 0xff, 0xc7, 0xe2, 0xec, 0xff, 0xc4, 0xdd, 0xeb, 0xff, 0xc3, 0xdf, 0xe8, 0xff, 0xc4, 0xe2, 0xe7, 0xff, 0xb9, 0xcd, 0xe2, 0xff, 0xaa, 0xbb, 0xcd, 0xff, 0xd5, 0xf4, 0xf7, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xff, 0xdd, 0xfd, 0xff, 0xff, 0xd2, 0xe8, 0xed, 0xff, 0x93, 0x99, 0x97, 0xff, 0x5d, 0x4b, 0x3b, 0xff, 0x67, 0x51, 0x3c, 0xff, 0x5c, 0x52, 0x4a, 0xff, 0x6f, 0x6e, 0x6a, 0xff, 0x9d, 0xa3, 0x97, 0xff, 0xc9, 0xd8, 0xca, 0xff, 0xcc, 0xe1, 0xd5, 0xff, 0xba, 0xcb, 0xbb, 0xff, 0xa4, 0xa3, 0x8f, 0xff, 0x99, 0x91, 0x7b, 0xff, 0xae, 0xb7, 0xa5, 0xff, 0xb1, 0xba, 0xac, 0xff, 0x7c, 0x76, 0x68, 0xff, 0x6a, 0x61, 0x53, 0xff, 0x51, 0x3e, 0x34, 0xff, 0x36, 0x20, 0x1e, 0xff, 0x34, 0x1f, 0x1b, 0xff, 0x43, 0x2d, 0x21, 0xff, 0x4e, 0x38, 0x31, 0xff, 0x50, 0x3d, 0x31, 0xff, 0x7e, 0x79, 0x70, 0xff, 0xb7, 0xc5, 0xc1, 0xff, 0xbe, 0xc7, 0xc3, 0xff, 0x7a, 0x7a, 0x71, 0xff, 0x37, 0x29, 0x1f, 0xff, 0x2f, 0x1d, 0x1b, 0xff, 0x3f, 0x2d, 0x2c, 0xff, 0xb1, 0xc0, 0xb2, 0xff, 0x74, 0x79, 0x70, 0xff, 0x68, 0x65, 0x60, 0xff, 0x4f, 0x3f, 0x3e, 0xff, 0x23, 0x0a, 0x0b, 0xff, 0x3a, 0x28, 0x20, 0xff, 0x27, 0x18, 0x10, 0xff, 0x59, 0x4c, 0x45, 0xff, 0x90, 0x88, 0x7c, 0xff, 0x33, 0x1d, 0x1b, 0xff, 0x1a, 0x06, 0x04, 0xff, 0x7c, 0x75, 0x68, 0xff, 0x78, 0x6d, 0x5c, 0xff, 0x4d, 0x37, 0x28, 0xff, 0x40, 0x2b, 0x23, 0xff, 0x81, 0x79, 0x68, 0xff, 0x6b, 0x60, 0x48, 0xff, 0x39, 0x23, 0x1d, 0xff, 0x28, 0x16, 0x17, 0xff, 0x2e, 0x1b, 0x16, 0xff, 0x41, 0x2b, 0x25, 0xff, 0x3e, 0x28, 0x24, 0xff, 0x36, 0x1d, 0x1b, 0xff, 0x1e, 0x09, 0x05, 0xff, 0x2a, 0x15, 0x0b, 0xff, 0x68, 0x50, 0x3e, 0xff, 0x5a, 0x48, 0x35, 0xff, 0x33, 0x1e, 0x19, 0xff, 0x2e, 0x19, 0x16, 0xff, 0x32, 0x1e, 0x1e, 0xff, 0x2b, 0x17, 0x19, 0xff, 0x23, 0x10, 0x11, 0xff, 0x1e, 0x0b, 0x10, 0xff, 0x20, 0x0f, 0x12, 0xff, 0x21, 0x13, 0x14, 0xff, 0x90, 0x88, 0x8a, 0xff, 0xfb, 0xfa, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe1, 0xd6, 0xff, 0xaf, 0x9d, 0x72, 0xff, 0xa5, 0x91, 0x5d, 0xff, 0x9b, 0x85, 0x52, 0xff, 0x9f, 0x89, 0x53, 0xff, 0xa1, 0x8f, 0x5a, 0xff, 0xb2, 0xa2, 0x72, 0xff, 0x9a, 0x89, 0x58, 0xff, 0xa7, 0x99, 0x69, 0xff, 0xb7, 0xb1, 0x87, 0xff, 0xa0, 0x90, 0x5e, 0xff, 0x91, 0x79, 0x44, 0xff, 0xc4, 0xca, 0xbc, 0xff, 0xeb, 0xff, 0xff, 0xff, 0xd9, 0xf1, 0xf1, 0xff, 0xd7, 0xef, 0xee, 0xff, 0xd6, 0xef, 0xec, 0xff, 0xd5, 0xec, 0xf0, 0xff, 0xdb, 0xf5, 0xf8, 0xff, 0xc5, 0xd6, 0xe0, 0xff, 0x6f, 0x7a, 0x8f, 0xff, 0xa1, 0xb3, 0xbe, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xd3, 0xee, 0xee, 0xff, 0xd2, 0xeb, 0xec, 0xff, 0xd0, 0xe9, 0xec, 0xff, 0xcd, 0xe7, 0xeb, 0xff, 0xca, 0xe4, 0xec, 0xff, 0xca, 0xe6, 0xeb, 0xff, 0xc9, 0xe6, 0xea, 0xff, 0xc2, 0xd7, 0xe9, 0xff, 0x8b, 0x96, 0xc3, 0xff, 0xc9, 0xe4, 0xec, 0xff, 0xd4, 0xf6, 0xec, 0xff, 0xcc, 0xea, 0xea, 0xff, 0xcb, 0xe8, 0xec, 0xff, 0xc9, 0xe6, 0xec, 0xff, 0xc5, 0xe0, 0xeb, 0xff, 0xc0, 0xd6, 0xe6, 0xff, 0xbc, 0xd4, 0xe5, 0xff, 0xbf, 0xda, 0xe3, 0xff, 0xbf, 0xd2, 0xde, 0xff, 0xb7, 0xd0, 0xdf, 0xff, 0xcb, 0xeb, 0xef, 0xff, 0xcb, 0xe8, 0xec, 0xff, 0xc8, 0xe6, 0xea, 0xff, 0xc6, 0xe3, 0xe7, 0xff, 0xd0, 0xf0, 0xf9, 0xff, 0xd4, 0xf0, 0xfc, 0xff, 0x83, 0x85, 0x86, 0xff, 0x83, 0x7d, 0x76, 0xff, 0xad, 0xb7, 0xb0, 0xff, 0xc5, 0xda, 0xd4, 0xff, 0xcb, 0xe2, 0xda, 0xff, 0xc7, 0xda, 0xcf, 0xff, 0xb1, 0xc4, 0xb6, 0xff, 0xc0, 0xce, 0xbe, 0xff, 0xbf, 0xc7, 0xb4, 0xff, 0xa1, 0xa5, 0x92, 0xff, 0x97, 0x96, 0x84, 0xff, 0x72, 0x6a, 0x56, 0xff, 0x76, 0x6c, 0x5a, 0xff, 0x7a, 0x71, 0x64, 0xff, 0x3b, 0x29, 0x22, 0xff, 0x42, 0x30, 0x2f, 0xff, 0x52, 0x47, 0x43, 0xff, 0x68, 0x59, 0x4b, 0xff, 0x50, 0x3d, 0x36, 0xff, 0x59, 0x4d, 0x48, 0xff, 0xaa, 0xbb, 0xb2, 0xff, 0xc3, 0xe0, 0xdd, 0xff, 0x80, 0x84, 0x82, 0xff, 0x5a, 0x4b, 0x48, 0xff, 0x3d, 0x2c, 0x28, 0xff, 0x00, 0x00, 0x00, 0xff, 0x53, 0x48, 0x4c, 0xff, 0xd4, 0xe9, 0xe1, 0xff, 0x95, 0xa2, 0x96, 0xff, 0x91, 0x99, 0x92, 0xff, 0x32, 0x20, 0x20, 0xff, 0x21, 0x08, 0x09, 0xff, 0x41, 0x2c, 0x28, 0xff, 0x21, 0x09, 0x05, 0xff, 0x77, 0x72, 0x6e, 0xff, 0xe3, 0xf8, 0xee, 0xff, 0x63, 0x5d, 0x57, 0xff, 0x14, 0x00, 0x00, 0xff, 0x68, 0x60, 0x55, 0xff, 0x8e, 0x88, 0x71, 0xff, 0x93, 0x89, 0x6f, 0xff, 0x82, 0x76, 0x62, 0xff, 0x86, 0x76, 0x60, 0xff, 0x58, 0x48, 0x30, 0xff, 0x39, 0x25, 0x1c, 0xff, 0x26, 0x0f, 0x13, 0xff, 0x42, 0x2f, 0x28, 0xff, 0x5e, 0x4a, 0x3d, 0xff, 0x38, 0x24, 0x1b, 0xff, 0x31, 0x1a, 0x17, 0xff, 0x41, 0x2d, 0x2b, 0xff, 0x76, 0x65, 0x50, 0xff, 0x68, 0x57, 0x40, 0xff, 0x31, 0x21, 0x1e, 0xff, 0x24, 0x14, 0x12, 0xff, 0x26, 0x12, 0x13, 0xff, 0x25, 0x11, 0x11, 0xff, 0x1a, 0x09, 0x0b, 0xff, 0x1a, 0x08, 0x0e, 0xff, 0x1d, 0x0b, 0x0c, 0xff, 0x1a, 0x0a, 0x0b, 0xff, 0x5e, 0x53, 0x55, 0xff, 0xeb, 0xe9, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0xcd, 0xb9, 0xff, 0xac, 0x99, 0x68, 0xff, 0x9f, 0x8a, 0x58, 0xff, 0x9a, 0x86, 0x53, 0xff, 0x9a, 0x87, 0x53, 0xff, 0xa0, 0x90, 0x5e, 0xff, 0xac, 0x9b, 0x6a, 0xff, 0xa1, 0x97, 0x6a, 0xff, 0xb1, 0xa7, 0x79, 0xff, 0x95, 0x82, 0x4e, 0xff, 0xc0, 0xc7, 0xb7, 0xff, 0xef, 0xff, 0xff, 0xff, 0xd9, 0xf0, 0xf3, 0xff, 0xd7, 0xee, 0xee, 0xff, 0xd6, 0xee, 0xee, 0xff, 0xd6, 0xee, 0xef, 0xff, 0xd4, 0xee, 0xef, 0xff, 0xd5, 0xf1, 0xf3, 0xff, 0xbc, 0xcd, 0xdd, 0xff, 0x80, 0x8d, 0x9a, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xdd, 0xf9, 0xf6, 0xff, 0xd2, 0xec, 0xea, 0xff, 0xd1, 0xe9, 0xef, 0xff, 0xcf, 0xe7, 0xee, 0xff, 0xcb, 0xe8, 0xec, 0xff, 0xcb, 0xe7, 0xed, 0xff, 0xc9, 0xe7, 0xeb, 0xff, 0xd8, 0xf4, 0xf8, 0xff, 0x93, 0x9d, 0xbb, 0xff, 0x90, 0x9d, 0xb5, 0xff, 0xe5, 0xff, 0xfd, 0xff, 0xcc, 0xe9, 0xee, 0xff, 0xcb, 0xe8, 0xec, 0xff, 0xc8, 0xe6, 0xea, 0xff, 0xc8, 0xe5, 0xeb, 0xff, 0xc3, 0xdf, 0xe9, 0xff, 0xbe, 0xd5, 0xe3, 0xff, 0xbb, 0xd1, 0xe4, 0xff, 0xd0, 0xea, 0xef, 0xff, 0xa5, 0xb2, 0xce, 0xff, 0x99, 0xad, 0xc6, 0xff, 0xd8, 0xf7, 0xf8, 0xff, 0xc9, 0xe6, 0xe9, 0xff, 0xc7, 0xe5, 0xe8, 0xff, 0xc7, 0xe2, 0xe7, 0xff, 0xc7, 0xe0, 0xe7, 0xff, 0xca, 0xeb, 0xf2, 0xff, 0xc7, 0xe1, 0xe6, 0xff, 0xaf, 0xc1, 0xc5, 0xff, 0xc3, 0xd8, 0xd7, 0xff, 0xc5, 0xda, 0xd7, 0xff, 0xbe, 0xd6, 0xd0, 0xff, 0xbe, 0xd5, 0xcb, 0xff, 0xc1, 0xd2, 0xc5, 0xff, 0xb4, 0xc1, 0xb2, 0xff, 0xae, 0xb7, 0xa7, 0xff, 0xa9, 0xb5, 0xa6, 0xff, 0xbe, 0xce, 0xc3, 0xff, 0xa5, 0xa7, 0x9a, 0xff, 0x70, 0x5c, 0x47, 0xff, 0x78, 0x64, 0x4d, 0xff, 0x6a, 0x5b, 0x4f, 0xff, 0x52, 0x42, 0x3a, 0xff, 0x44, 0x39, 0x38, 0xff, 0x4c, 0x3e, 0x3c, 0xff, 0x4c, 0x3c, 0x26, 0xff, 0x61, 0x57, 0x49, 0xff, 0xbb, 0xcb, 0xc3, 0xff, 0xab, 0xc0, 0xbd, 0xff, 0x7b, 0x7d, 0x76, 0xff, 0x3c, 0x30, 0x2e, 0xff, 0x30, 0x18, 0x1b, 0xff, 0x48, 0x38, 0x32, 0xff, 0x8e, 0x99, 0x93, 0xff, 0x9c, 0xac, 0xa6, 0xff, 0xad, 0xb9, 0xae, 0xff, 0xb1, 0xba, 0xae, 0xff, 0x32, 0x26, 0x1f, 0xff, 0x29, 0x17, 0x13, 0xff, 0x33, 0x20, 0x20, 0xff, 0x26, 0x10, 0x0d, 0xff, 0x8b, 0x90, 0x84, 0xff, 0xc1, 0xda, 0xce, 0xff, 0x95, 0x9a, 0x91, 0xff, 0x6b, 0x5e, 0x50, 0xff, 0x93, 0x8d, 0x7a, 0xff, 0x83, 0x7f, 0x68, 0xff, 0x6f, 0x62, 0x50, 0xff, 0x51, 0x3c, 0x31, 0xff, 0x50, 0x3b, 0x27, 0xff, 0x70, 0x58, 0x42, 0xff, 0x46, 0x2f, 0x22, 0xff, 0x46, 0x39, 0x2e, 0xff, 0x91, 0x86, 0x6f, 0xff, 0x6f, 0x5c, 0x40, 0xff, 0x5d, 0x43, 0x2a, 0xff, 0x5e, 0x4a, 0x2d, 0xff, 0x79, 0x6d, 0x51, 0xff, 0x70, 0x5e, 0x4e, 0xff, 0x2c, 0x17, 0x10, 0xff, 0x1e, 0x0b, 0x0a, 0xff, 0x24, 0x11, 0x14, 0xff, 0x2e, 0x19, 0x1a, 0xff, 0x35, 0x21, 0x1b, 0xff, 0x3c, 0x28, 0x1d, 0xff, 0x42, 0x2e, 0x24, 0xff, 0x52, 0x40, 0x33, 0xff, 0x67, 0x59, 0x50, 0xff, 0xd4, 0xd0, 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xcd, 0xc2, 0xa8, 0xff, 0xa6, 0x95, 0x66, 0xff, 0x94, 0x82, 0x52, 0xff, 0x9b, 0x86, 0x54, 0xff, 0x98, 0x86, 0x53, 0xff, 0x96, 0x88, 0x57, 0xff, 0xb7, 0xac, 0x7e, 0xff, 0x9e, 0x90, 0x5e, 0xff, 0xb1, 0xb2, 0x9c, 0xff, 0xe5, 0xff, 0xff, 0xff, 0xda, 0xf1, 0xf1, 0xff, 0xd6, 0xec, 0xed, 0xff, 0xd5, 0xec, 0xf0, 0xff, 0xd4, 0xec, 0xee, 0xff, 0xd2, 0xeb, 0xee, 0xff, 0xd3, 0xec, 0xec, 0xff, 0xd4, 0xef, 0xf3, 0xff, 0xa9, 0xb7, 0xd1, 0xff, 0xc0, 0xd2, 0xd8, 0xff, 0xe0, 0xfc, 0xf8, 0xff, 0xd0, 0xec, 0xe9, 0xff, 0xd1, 0xeb, 0xed, 0xff, 0xd0, 0xe7, 0xef, 0xff, 0xce, 0xe6, 0xed, 0xff, 0xca, 0xe6, 0xeb, 0xff, 0xcc, 0xe6, 0xed, 0xff, 0xd1, 0xea, 0xf0, 0xff, 0xbe, 0xd0, 0xe8, 0xff, 0x90, 0x9c, 0xbc, 0xff, 0xcf, 0xe9, 0xe4, 0xff, 0xd8, 0xf5, 0xf1, 0xff, 0xcb, 0xe8, 0xee, 0xff, 0xcb, 0xe7, 0xec, 0xff, 0xcb, 0xe5, 0xeb, 0xff, 0xc8, 0xe2, 0xea, 0xff, 0xc5, 0xdf, 0xea, 0xff, 0xc2, 0xda, 0xe6, 0xff, 0xc0, 0xd9, 0xe3, 0xff, 0xce, 0xe3, 0xee, 0xff, 0x65, 0x64, 0x96, 0xff, 0x98, 0xad, 0xbf, 0xff, 0xdc, 0xfd, 0xfa, 0xff, 0xca, 0xe7, 0xeb, 0xff, 0xc7, 0xe5, 0xe9, 0xff, 0xc7, 0xe1, 0xe9, 0xff, 0xc9, 0xde, 0xe5, 0xff, 0xc3, 0xdd, 0xdf, 0xff, 0xbf, 0xda, 0xe0, 0xff, 0xc4, 0xdd, 0xdf, 0xff, 0xc0, 0xd3, 0xcc, 0xff, 0xbd, 0xc7, 0xc0, 0xff, 0xb7, 0xc9, 0xc0, 0xff, 0xb8, 0xcf, 0xc1, 0xff, 0xb1, 0xb6, 0xa2, 0xff, 0x8e, 0x86, 0x6e, 0xff, 0x82, 0x76, 0x61, 0xff, 0x7e, 0x71, 0x62, 0xff, 0x82, 0x7c, 0x71, 0xff, 0x76, 0x6e, 0x5e, 0xff, 0x50, 0x3d, 0x30, 0xff, 0x44, 0x33, 0x2b, 0xff, 0x29, 0x16, 0x14, 0xff, 0x2f, 0x19, 0x18, 0xff, 0x35, 0x22, 0x1a, 0xff, 0x5d, 0x4a, 0x38, 0xff, 0x80, 0x73, 0x5b, 0xff, 0xad, 0xa5, 0x95, 0xff, 0xad, 0xb6, 0xaa, 0xff, 0x82, 0x8b, 0x88, 0xff, 0x59, 0x50, 0x50, 0xff, 0x3a, 0x27, 0x27, 0xff, 0x28, 0x16, 0x15, 0xff, 0x95, 0x99, 0x94, 0xff, 0x9f, 0xb3, 0xb1, 0xff, 0x91, 0xa1, 0x9c, 0xff, 0x70, 0x6d, 0x69, 0xff, 0x88, 0x8d, 0x82, 0xff, 0xad, 0xb3, 0xa5, 0xff, 0x4f, 0x43, 0x3e, 0xff, 0x5a, 0x54, 0x4a, 0xff, 0x51, 0x43, 0x39, 0xff, 0x7f, 0x7e, 0x73, 0xff, 0x69, 0x65, 0x5b, 0xff, 0x88, 0x8b, 0x7a, 0xff, 0xbc, 0xc3, 0xaf, 0xff, 0x8d, 0x84, 0x6d, 0xff, 0x4e, 0x3d, 0x2e, 0xff, 0x34, 0x1a, 0x10, 0xff, 0x43, 0x2e, 0x1f, 0xff, 0x68, 0x52, 0x36, 0xff, 0x5f, 0x45, 0x2d, 0xff, 0x4f, 0x39, 0x27, 0xff, 0x88, 0x7f, 0x65, 0xff, 0x9d, 0x92, 0x70, 0xff, 0x7a, 0x65, 0x45, 0xff, 0x5c, 0x3f, 0x28, 0xff, 0x5d, 0x45, 0x30, 0xff, 0x59, 0x45, 0x37, 0xff, 0x4e, 0x38, 0x28, 0xff, 0x4b, 0x33, 0x26, 0xff, 0x42, 0x2a, 0x24, 0xff, 0x3b, 0x22, 0x1a, 0xff, 0x3e, 0x27, 0x1f, 0xff, 0x47, 0x30, 0x25, 0xff, 0x4c, 0x33, 0x23, 0xff, 0x4a, 0x32, 0x20, 0xff, 0x59, 0x49, 0x39, 0xff, 0xc9, 0xc3, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf3, 0xef, 0xff, 0xc0, 0xb4, 0x95, 0xff, 0x9f, 0x8e, 0x60, 0xff, 0x91, 0x7c, 0x49, 0xff, 0xa4, 0x93, 0x60, 0xff, 0xa2, 0x91, 0x63, 0xff, 0x92, 0x82, 0x4f, 0xff, 0xa6, 0xa4, 0x8b, 0xff, 0xe3, 0xf8, 0xf8, 0xff, 0xdd, 0xf6, 0xf6, 0xff, 0xd3, 0xed, 0xeb, 0xff, 0xd4, 0xea, 0xf0, 0xff, 0xd4, 0xea, 0xef, 0xff, 0xd2, 0xeb, 0xed, 0xff, 0xd1, 0xea, 0xec, 0xff, 0xd9, 0xf1, 0xf1, 0xff, 0xbb, 0xce, 0xdd, 0xff, 0x95, 0x9f, 0xbe, 0xff, 0xdf, 0xf9, 0xf4, 0xff, 0xd3, 0xf1, 0xec, 0xff, 0xd2, 0xeb, 0xed, 0xff, 0xd0, 0xe8, 0xee, 0xff, 0xcf, 0xe6, 0xed, 0xff, 0xcf, 0xe7, 0xeb, 0xff, 0xcc, 0xe4, 0xeb, 0xff, 0xcd, 0xe4, 0xeb, 0xff, 0xcc, 0xde, 0xec, 0xff, 0xa5, 0xb3, 0xd1, 0xff, 0xc8, 0xe4, 0xe7, 0xff, 0xdc, 0xfb, 0xf7, 0xff, 0xcb, 0xe7, 0xec, 0xff, 0xca, 0xe6, 0xeb, 0xff, 0xc9, 0xe4, 0xe9, 0xff, 0xc9, 0xe2, 0xea, 0xff, 0xc7, 0xdf, 0xe9, 0xff, 0xc5, 0xdd, 0xe8, 0xff, 0xc1, 0xd9, 0xe4, 0xff, 0xda, 0xf1, 0xf9, 0xff, 0x71, 0x77, 0x97, 0xff, 0x4f, 0x57, 0x6e, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xc7, 0xe5, 0xe9, 0xff, 0xc6, 0xe3, 0xe7, 0xff, 0xca, 0xe7, 0xeb, 0xff, 0xc7, 0xe2, 0xea, 0xff, 0xc0, 0xda, 0xe2, 0xff, 0xc3, 0xdb, 0xe0, 0xff, 0xc5, 0xdc, 0xe7, 0xff, 0xb3, 0xc7, 0xcc, 0xff, 0x9f, 0xad, 0xa1, 0xff, 0xab, 0xb2, 0xa0, 0xff, 0xae, 0xb8, 0xa2, 0xff, 0xaf, 0xb5, 0x9b, 0xff, 0xaa, 0x9e, 0x84, 0xff, 0x85, 0x74, 0x5b, 0xff, 0x58, 0x49, 0x3b, 0xff, 0x54, 0x48, 0x3f, 0xff, 0x3a, 0x2c, 0x25, 0xff, 0x12, 0x00, 0x00, 0xff, 0x3f, 0x2b, 0x26, 0xff, 0x69, 0x58, 0x4c, 0xff, 0x6c, 0x5a, 0x48, 0xff, 0x4b, 0x36, 0x2a, 0xff, 0x3e, 0x2c, 0x1e, 0xff, 0x6d, 0x5a, 0x46, 0xff, 0x55, 0x46, 0x3e, 0xff, 0x60, 0x59, 0x4c, 0xff, 0x9d, 0xa0, 0x93, 0xff, 0x98, 0xa7, 0xa3, 0xff, 0x82, 0x89, 0x87, 0xff, 0x41, 0x31, 0x30, 0xff, 0x43, 0x37, 0x35, 0xff, 0x98, 0xa7, 0xa9, 0xff, 0x62, 0x6c, 0x6f, 0xff, 0x70, 0x6f, 0x6c, 0xff, 0x25, 0x0e, 0x0f, 0xff, 0x4b, 0x3f, 0x3a, 0xff, 0x75, 0x74, 0x6a, 0xff, 0x41, 0x2f, 0x2b, 0xff, 0x55, 0x44, 0x3f, 0xff, 0x41, 0x2b, 0x29, 0xff, 0x29, 0x15, 0x1a, 0xff, 0x29, 0x12, 0x12, 0xff, 0x5d, 0x53, 0x3f, 0xff, 0xac, 0xac, 0x8c, 0xff, 0x70, 0x63, 0x4a, 0xff, 0x28, 0x15, 0x0d, 0xff, 0x36, 0x1e, 0x18, 0xff, 0x57, 0x41, 0x32, 0xff, 0x6d, 0x56, 0x42, 0xff, 0x41, 0x26, 0x1d, 0xff, 0x48, 0x2e, 0x22, 0xff, 0x51, 0x37, 0x22, 0xff, 0x56, 0x3b, 0x27, 0xff, 0x4f, 0x38, 0x28, 0xff, 0x37, 0x23, 0x1c, 0xff, 0x35, 0x22, 0x1c, 0xff, 0x33, 0x20, 0x1b, 0xff, 0x39, 0x24, 0x1c, 0xff, 0x39, 0x24, 0x1b, 0xff, 0x3c, 0x28, 0x1c, 0xff, 0x41, 0x2d, 0x1f, 0xff, 0x3a, 0x27, 0x1e, 0xff, 0x36, 0x22, 0x1b, 0xff, 0x32, 0x1d, 0x18, 0xff, 0x36, 0x20, 0x1e, 0xff, 0x94, 0x8b, 0x8b, 0xff, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xee, 0xe7, 0xff, 0xb8, 0xae, 0x8a, 0xff, 0x93, 0x81, 0x4e, 0xff, 0x9c, 0x8b, 0x57, 0xff, 0x9b, 0x84, 0x52, 0xff, 0x9c, 0x93, 0x75, 0xff, 0xdf, 0xf0, 0xf0, 0xff, 0xde, 0xfd, 0xff, 0xff, 0xd7, 0xe9, 0xee, 0xff, 0xd5, 0xeb, 0xef, 0xff, 0xd2, 0xea, 0xee, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xd4, 0xea, 0xed, 0xff, 0xdc, 0xf2, 0xf8, 0xff, 0x98, 0xa4, 0xc0, 0xff, 0xac, 0xb9, 0xca, 0xff, 0xe7, 0xff, 0xfb, 0xff, 0xd1, 0xed, 0xe9, 0xff, 0xd3, 0xea, 0xeb, 0xff, 0xd1, 0xe7, 0xeb, 0xff, 0xd0, 0xe5, 0xeb, 0xff, 0xce, 0xe4, 0xea, 0xff, 0xcb, 0xe2, 0xeb, 0xff, 0xd2, 0xe7, 0xed, 0xff, 0xb6, 0xc8, 0xd9, 0xff, 0xbb, 0xcf, 0xe1, 0xff, 0xde, 0xfc, 0xf6, 0xff, 0xce, 0xea, 0xe9, 0xff, 0xcd, 0xe7, 0xeb, 0xff, 0xcb, 0xe3, 0xeb, 0xff, 0xc8, 0xe2, 0xe8, 0xff, 0xc7, 0xe1, 0xe9, 0xff, 0xc8, 0xe0, 0xeb, 0xff, 0xc5, 0xdb, 0xe5, 0xff, 0xbf, 0xd8, 0xe1, 0xff, 0xc4, 0xd6, 0xe8, 0xff, 0x49, 0x4c, 0x69, 0xff, 0xa4, 0xb7, 0xbe, 0xff, 0xdd, 0xfb, 0xfd, 0xff, 0xc3, 0xdf, 0xe7, 0xff, 0xc5, 0xe1, 0xe7, 0xff, 0xc3, 0xdd, 0xe4, 0xff, 0xbe, 0xd9, 0xe0, 0xff, 0xc2, 0xdb, 0xe0, 0xff, 0xbe, 0xd7, 0xdc, 0xff, 0xc2, 0xdc, 0xe2, 0xff, 0xa8, 0xb9, 0xb8, 0xff, 0x98, 0x9b, 0x8d, 0xff, 0xa6, 0xa8, 0x91, 0xff, 0xa8, 0xab, 0x8f, 0xff, 0x9b, 0x95, 0x7d, 0xff, 0x76, 0x6b, 0x5b, 0xff, 0x34, 0x1e, 0x1b, 0xff, 0x24, 0x0f, 0x0e, 0xff, 0x29, 0x19, 0x19, 0xff, 0x31, 0x1b, 0x12, 0xff, 0x58, 0x49, 0x3d, 0xff, 0x77, 0x62, 0x52, 0xff, 0x63, 0x4d, 0x3b, 0xff, 0x4f, 0x3f, 0x32, 0xff, 0x77, 0x6c, 0x5a, 0xff, 0xab, 0xaa, 0x8d, 0xff, 0x92, 0x86, 0x6d, 0xff, 0x3d, 0x24, 0x1a, 0xff, 0x59, 0x4c, 0x47, 0xff, 0xa0, 0xaf, 0xa6, 0xff, 0xcb, 0xde, 0xd8, 0xff, 0x72, 0x71, 0x6c, 0xff, 0x3c, 0x38, 0x2f, 0xff, 0x9a, 0x9e, 0x97, 0xff, 0x56, 0x50, 0x50, 0xff, 0x1b, 0x0b, 0x07, 0xff, 0x50, 0x3d, 0x35, 0xff, 0x43, 0x32, 0x2b, 0xff, 0x2d, 0x1a, 0x1a, 0xff, 0x31, 0x1c, 0x1b, 0xff, 0x3f, 0x2c, 0x27, 0xff, 0x1b, 0x06, 0x05, 0xff, 0x3d, 0x2a, 0x28, 0xff, 0x26, 0x11, 0x14, 0xff, 0x33, 0x1b, 0x1d, 0xff, 0x41, 0x2c, 0x22, 0xff, 0x4e, 0x3a, 0x2f, 0xff, 0x3b, 0x26, 0x24, 0xff, 0x30, 0x1c, 0x1b, 0xff, 0x39, 0x25, 0x1f, 0xff, 0x55, 0x42, 0x32, 0xff, 0x33, 0x21, 0x19, 0xff, 0x36, 0x22, 0x1c, 0xff, 0x42, 0x2c, 0x20, 0xff, 0x46, 0x2f, 0x21, 0xff, 0x3b, 0x23, 0x1d, 0xff, 0x2d, 0x17, 0x15, 0xff, 0x2e, 0x1a, 0x1a, 0xff, 0x2d, 0x1b, 0x17, 0xff, 0x2e, 0x1a, 0x15, 0xff, 0x31, 0x1d, 0x1b, 0xff, 0x30, 0x1c, 0x1a, 0xff, 0x2c, 0x1a, 0x15, 0xff, 0x2a, 0x18, 0x14, 0xff, 0x2a, 0x17, 0x15, 0xff, 0x26, 0x11, 0x13, 0xff, 0x21, 0x0e, 0x11, 0xff, 0x7a, 0x6f, 0x71, 0xff, 0xf3, 0xf2, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe9, 0xde, 0xff, 0xb6, 0xab, 0x82, 0xff, 0x89, 0x75, 0x3f, 0xff, 0x82, 0x74, 0x4b, 0xff, 0xdc, 0xed, 0xec, 0xff, 0xe2, 0xff, 0xff, 0xff, 0xd0, 0xea, 0xee, 0xff, 0xdc, 0xe8, 0xf0, 0xff, 0xd5, 0xe8, 0xed, 0xff, 0xd2, 0xeb, 0xec, 0xff, 0xd4, 0xec, 0xec, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xd6, 0xee, 0xf1, 0xff, 0xbe, 0xce, 0xe4, 0xff, 0xac, 0xb8, 0xd2, 0xff, 0xdf, 0xf4, 0xf0, 0xff, 0xd5, 0xee, 0xec, 0xff, 0xd0, 0xe9, 0xec, 0xff, 0xd1, 0xe7, 0xea, 0xff, 0xd1, 0xe7, 0xec, 0xff, 0xd0, 0xe6, 0xec, 0xff, 0xcd, 0xe4, 0xeb, 0xff, 0xcc, 0xe3, 0xeb, 0xff, 0xcd, 0xe1, 0xeb, 0xff, 0xbc, 0xcc, 0xe2, 0xff, 0xcf, 0xe9, 0xeb, 0xff, 0xd4, 0xee, 0xed, 0xff, 0xcf, 0xe8, 0xea, 0xff, 0xce, 0xe6, 0xeb, 0xff, 0xcd, 0xe3, 0xeb, 0xff, 0xca, 0xe2, 0xeb, 0xff, 0xc7, 0xe0, 0xe8, 0xff, 0xc7, 0xdd, 0xe8, 0xff, 0xc2, 0xd7, 0xe3, 0xff, 0xcb, 0xe1, 0xeb, 0xff, 0x6f, 0x7d, 0x9c, 0xff, 0x74, 0x82, 0x95, 0xff, 0xed, 0xff, 0xff, 0xff, 0xbd, 0xda, 0xe2, 0xff, 0xbd, 0xd7, 0xe2, 0xff, 0xc0, 0xd7, 0xe5, 0xff, 0xb9, 0xcf, 0xde, 0xff, 0xb6, 0xcc, 0xd9, 0xff, 0xb9, 0xd0, 0xd7, 0xff, 0xb9, 0xcf, 0xd2, 0xff, 0xbe, 0xd3, 0xd5, 0xff, 0xbc, 0xc7, 0xbf, 0xff, 0xa6, 0xa7, 0x90, 0xff, 0x9f, 0x98, 0x80, 0xff, 0x86, 0x75, 0x65, 0xff, 0x42, 0x2c, 0x26, 0xff, 0x1e, 0x0a, 0x0d, 0xff, 0x42, 0x34, 0x32, 0xff, 0x31, 0x1b, 0x14, 0xff, 0x3a, 0x20, 0x19, 0xff, 0x8e, 0x84, 0x72, 0xff, 0xb3, 0xaa, 0x8d, 0xff, 0x77, 0x65, 0x57, 0xff, 0x2b, 0x17, 0x0d, 0xff, 0x56, 0x47, 0x30, 0xff, 0xbb, 0xc3, 0xaf, 0xff, 0xd4, 0xd9, 0xc0, 0xff, 0x80, 0x76, 0x5b, 0xff, 0x55, 0x4a, 0x3e, 0xff, 0xa2, 0xa0, 0x9a, 0xff, 0xac, 0xb8, 0xb0, 0xff, 0x6d, 0x6d, 0x69, 0xff, 0x25, 0x15, 0x11, 0xff, 0x9b, 0x9b, 0x8f, 0xff, 0x72, 0x73, 0x6c, 0xff, 0x15, 0x00, 0x00, 0xff, 0x1c, 0x00, 0x04, 0xff, 0x3b, 0x2a, 0x25, 0xff, 0x4b, 0x3d, 0x32, 0xff, 0x26, 0x14, 0x13, 0xff, 0x2d, 0x1f, 0x19, 0xff, 0x5e, 0x56, 0x45, 0xff, 0x8d, 0x8f, 0x81, 0xff, 0xa1, 0xa5, 0x9d, 0xff, 0x3b, 0x32, 0x2d, 0xff, 0x32, 0x1e, 0x18, 0xff, 0x38, 0x21, 0x1f, 0xff, 0x2d, 0x17, 0x18, 0xff, 0x30, 0x1a, 0x1b, 0xff, 0x30, 0x1b, 0x1a, 0xff, 0x49, 0x36, 0x2d, 0xff, 0x45, 0x30, 0x28, 0xff, 0x35, 0x1f, 0x19, 0xff, 0x3c, 0x25, 0x1d, 0xff, 0x3d, 0x25, 0x1a, 0xff, 0x39, 0x22, 0x1d, 0xff, 0x2c, 0x18, 0x14, 0xff, 0x29, 0x14, 0x14, 0xff, 0x23, 0x10, 0x10, 0xff, 0x22, 0x0d, 0x12, 0xff, 0x23, 0x0e, 0x14, 0xff, 0x1e, 0x0b, 0x0f, 0xff, 0x1f, 0x0b, 0x0f, 0xff, 0x1e, 0x0b, 0x0f, 0xff, 0x20, 0x0e, 0x12, 0xff, 0x1e, 0x0b, 0x0f, 0xff, 0x1e, 0x0b, 0x10, 0xff, 0x69, 0x5c, 0x5e, 0xff, 0xeb, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xc6, 0xff, 0xe3, 0xe2, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0xe3, 0xd3, 0xff, 0x92, 0x83, 0x5a, 0xff, 0xce, 0xd7, 0xd4, 0xff, 0xe4, 0xff, 0xff, 0xff, 0xd1, 0xec, 0xe9, 0xff, 0xd8, 0xee, 0xec, 0xff, 0xd6, 0xed, 0xeb, 0xff, 0xd0, 0xec, 0xea, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xd3, 0xeb, 0xeb, 0xff, 0xd4, 0xeb, 0xed, 0xff, 0xcf, 0xe5, 0xeb, 0xff, 0xb5, 0xc6, 0xda, 0xff, 0xd0, 0xe4, 0xec, 0xff, 0xdd, 0xf4, 0xf0, 0xff, 0xd3, 0xe9, 0xe7, 0xff, 0xd2, 0xe7, 0xef, 0xff, 0xd0, 0xe6, 0xec, 0xff, 0xd0, 0xe6, 0xec, 0xff, 0xcf, 0xe5, 0xeb, 0xff, 0xcd, 0xe4, 0xea, 0xff, 0xd0, 0xe8, 0xef, 0xff, 0xc0, 0xd1, 0xe4, 0xff, 0xbc, 0xce, 0xe0, 0xff, 0xd8, 0xf4, 0xee, 0xff, 0xce, 0xe8, 0xea, 0xff, 0xcd, 0xe6, 0xeb, 0xff, 0xcc, 0xe4, 0xeb, 0xff, 0xca, 0xe3, 0xeb, 0xff, 0xc9, 0xdf, 0xea, 0xff, 0xc5, 0xdb, 0xe4, 0xff, 0xc1, 0xd7, 0xdf, 0xff, 0xc0, 0xd4, 0xe1, 0xff, 0xb3, 0xc1, 0xdd, 0xff, 0x5d, 0x66, 0x90, 0xff, 0xb9, 0xd1, 0xda, 0xff, 0xd8, 0xf4, 0xf5, 0xff, 0xbf, 0xd6, 0xe2, 0xff, 0xb6, 0xcb, 0xdc, 0xff, 0xb1, 0xc6, 0xd8, 0xff, 0xb2, 0xc6, 0xd8, 0xff, 0xb3, 0xc7, 0xd7, 0xff, 0xb0, 0xc2, 0xcf, 0xff, 0xb1, 0xc4, 0xcb, 0xff, 0xb4, 0xc2, 0xc9, 0xff, 0x8b, 0x8a, 0x85, 0xff, 0x81, 0x76, 0x5d, 0xff, 0x7f, 0x71, 0x56, 0xff, 0x4a, 0x36, 0x26, 0xff, 0x4c, 0x37, 0x31, 0xff, 0x66, 0x54, 0x4b, 0xff, 0x79, 0x64, 0x4d, 0xff, 0x66, 0x5b, 0x44, 0xff, 0xb8, 0xc2, 0xac, 0xff, 0xd1, 0xd2, 0xb9, 0xff, 0x55, 0x46, 0x31, 0xff, 0x5a, 0x4b, 0x41, 0xff, 0x61, 0x55, 0x45, 0xff, 0xb1, 0xa5, 0x84, 0xff, 0xa7, 0x9a, 0x7a, 0xff, 0x3d, 0x27, 0x1a, 0xff, 0x6f, 0x65, 0x5e, 0xff, 0xcb, 0xde, 0xd0, 0xff, 0xb7, 0xc7, 0xbd, 0xff, 0x53, 0x4a, 0x49, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x35, 0x27, 0x22, 0xff, 0x4d, 0x39, 0x34, 0xff, 0x23, 0x09, 0x08, 0xff, 0x24, 0x11, 0x14, 0xff, 0x1b, 0x0b, 0x10, 0xff, 0x26, 0x15, 0x13, 0xff, 0x6f, 0x5f, 0x52, 0xff, 0x42, 0x33, 0x2a, 0xff, 0x4b, 0x40, 0x35, 0xff, 0x79, 0x6f, 0x62, 0xff, 0xae, 0xb1, 0xa3, 0xff, 0xa5, 0xad, 0x9e, 0xff, 0x72, 0x6f, 0x5b, 0xff, 0x71, 0x65, 0x4a, 0xff, 0x5b, 0x4a, 0x35, 0xff, 0x33, 0x21, 0x17, 0xff, 0x30, 0x1c, 0x17, 0xff, 0x30, 0x1b, 0x19, 0xff, 0x41, 0x2e, 0x26, 0xff, 0x44, 0x2c, 0x22, 0xff, 0x51, 0x36, 0x2b, 0xff, 0x56, 0x3a, 0x28, 0xff, 0x57, 0x3c, 0x2a, 0xff, 0x45, 0x2b, 0x21, 0xff, 0x40, 0x29, 0x1e, 0xff, 0x42, 0x2b, 0x21, 0xff, 0x3b, 0x24, 0x1d, 0xff, 0x2e, 0x19, 0x16, 0xff, 0x2c, 0x1a, 0x17, 0xff, 0x2a, 0x19, 0x16, 0xff, 0x28, 0x17, 0x14, 0xff, 0x28, 0x17, 0x15, 0xff, 0x2b, 0x18, 0x18, 0xff, 0x2a, 0x17, 0x15, 0xff, 0x63, 0x57, 0x54, 0xff, 0xe6, 0xe4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0xea, 0xe4, 0xff, 0xe9, 0xff, 0xff, 0xff, 0xcc, 0xe9, 0xee, 0xff, 0xd6, 0xed, 0xeb, 0xff, 0xd6, 0xec, 0xec, 0xff, 0xd3, 0xec, 0xec, 0xff, 0xd5, 0xeb, 0xed, 0xff, 0xd3, 0xe9, 0xed, 0xff, 0xd1, 0xe8, 0xeb, 0xff, 0xd2, 0xe9, 0xeb, 0xff, 0xcd, 0xe3, 0xe8, 0xff, 0xd2, 0xe9, 0xe9, 0xff, 0xd9, 0xf3, 0xef, 0xff, 0xd3, 0xea, 0xeb, 0xff, 0xd5, 0xe8, 0xed, 0xff, 0xd3, 0xe8, 0xeb, 0xff, 0xd0, 0xe6, 0xed, 0xff, 0xd0, 0xe6, 0xec, 0xff, 0xd0, 0xe5, 0xeb, 0xff, 0xcf, 0xe7, 0xeb, 0xff, 0xcd, 0xe4, 0xec, 0xff, 0xb1, 0xc1, 0xd9, 0xff, 0xca, 0xe1, 0xe3, 0xff, 0xd4, 0xee, 0xef, 0xff, 0xce, 0xe5, 0xed, 0xff, 0xcd, 0xe5, 0xeb, 0xff, 0xcb, 0xe2, 0xea, 0xff, 0xc7, 0xde, 0xe6, 0xff, 0xc6, 0xdb, 0xe4, 0xff, 0xc6, 0xda, 0xe3, 0xff, 0xc1, 0xd6, 0xde, 0xff, 0xc7, 0xd8, 0xe8, 0xff, 0x73, 0x78, 0xa4, 0xff, 0x96, 0xa5, 0xbd, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xc0, 0xdc, 0xe2, 0xff, 0xc0, 0xd5, 0xe1, 0xff, 0xb6, 0xca, 0xd9, 0xff, 0xad, 0xc1, 0xd3, 0xff, 0xae, 0xc0, 0xd3, 0xff, 0xb2, 0xc6, 0xd4, 0xff, 0xb7, 0xcb, 0xd4, 0xff, 0xac, 0xb8, 0xc1, 0xff, 0x8f, 0x98, 0x99, 0xff, 0x74, 0x71, 0x64, 0xff, 0x88, 0x7c, 0x66, 0xff, 0x93, 0x8b, 0x73, 0xff, 0x99, 0x93, 0x7b, 0xff, 0x88, 0x7b, 0x60, 0xff, 0x7d, 0x6d, 0x55, 0xff, 0x84, 0x7d, 0x6f, 0xff, 0xcb, 0xdc, 0xcb, 0xff, 0xd7, 0xe7, 0xd6, 0xff, 0x72, 0x65, 0x50, 0xff, 0x56, 0x42, 0x2e, 0xff, 0x9d, 0x98, 0x81, 0xff, 0xbe, 0xba, 0xa2, 0xff, 0x88, 0x77, 0x5e, 0xff, 0x42, 0x2a, 0x15, 0xff, 0x6a, 0x5d, 0x4c, 0xff, 0xdf, 0xe6, 0xd6, 0xff, 0xce, 0xdc, 0xcd, 0xff, 0x47, 0x3e, 0x38, 0xff, 0x06, 0x00, 0x00, 0xff, 0x36, 0x21, 0x20, 0xff, 0x2a, 0x14, 0x16, 0xff, 0x1b, 0x07, 0x09, 0xff, 0x23, 0x0c, 0x11, 0xff, 0x1f, 0x10, 0x14, 0xff, 0x1a, 0x08, 0x0d, 0xff, 0x24, 0x10, 0x10, 0xff, 0x42, 0x31, 0x28, 0xff, 0x54, 0x44, 0x38, 0xff, 0x5d, 0x4e, 0x43, 0xff, 0x41, 0x2e, 0x2e, 0xff, 0x58, 0x4b, 0x40, 0xff, 0x79, 0x70, 0x61, 0xff, 0x5e, 0x53, 0x46, 0xff, 0x59, 0x4a, 0x3d, 0xff, 0x39, 0x27, 0x1f, 0xff, 0x21, 0x0c, 0x0e, 0xff, 0x29, 0x15, 0x16, 0xff, 0x2f, 0x1b, 0x1b, 0xff, 0x26, 0x12, 0x13, 0xff, 0x2f, 0x1b, 0x16, 0xff, 0x3e, 0x27, 0x20, 0xff, 0x43, 0x2b, 0x20, 0xff, 0x3f, 0x29, 0x1d, 0xff, 0x37, 0x20, 0x1a, 0xff, 0x39, 0x21, 0x1a, 0xff, 0x37, 0x22, 0x19, 0xff, 0x39, 0x22, 0x1e, 0xff, 0x3e, 0x26, 0x20, 0xff, 0x3a, 0x23, 0x1d, 0xff, 0x33, 0x1c, 0x19, 0xff, 0x2a, 0x16, 0x16, 0xff, 0x27, 0x14, 0x13, 0xff, 0x2b, 0x17, 0x15, 0xff, 0x5f, 0x50, 0x50, 0xff, 0xdc, 0xd9, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf9, 0xfc, 0xff, 0xd9, 0xea, 0xf0, 0xff, 0xd4, 0xeb, 0xed, 0xff, 0xd1, 0xe9, 0xeb, 0xff, 0xd3, 0xe8, 0xef, 0xff, 0xd4, 0xe8, 0xee, 0xff, 0xd1, 0xe8, 0xea, 0xff, 0xd0, 0xe7, 0xec, 0xff, 0xd1, 0xe7, 0xec, 0xff, 0xd4, 0xeb, 0xed, 0xff, 0xd6, 0xef, 0xec, 0xff, 0xd3, 0xec, 0xea, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xd2, 0xe8, 0xee, 0xff, 0xd1, 0xe7, 0xec, 0xff, 0xd0, 0xe7, 0xed, 0xff, 0xcf, 0xe6, 0xec, 0xff, 0xcf, 0xe6, 0xec, 0xff, 0xd4, 0xea, 0xef, 0xff, 0xbf, 0xd2, 0xe0, 0xff, 0xb8, 0xca, 0xda, 0xff, 0xda, 0xf3, 0xf0, 0xff, 0xcf, 0xe4, 0xea, 0xff, 0xcd, 0xe4, 0xec, 0xff, 0xcc, 0xe5, 0xeb, 0xff, 0xca, 0xe0, 0xe8, 0xff, 0xc7, 0xdb, 0xe4, 0xff, 0xc5, 0xda, 0xdf, 0xff, 0xc2, 0xd5, 0xe2, 0xff, 0xc6, 0xd8, 0xe7, 0xff, 0x9c, 0xa7, 0xbe, 0xff, 0x71, 0x78, 0x95, 0xff, 0xda, 0xf1, 0xf2, 0xff, 0xc9, 0xe6, 0xe9, 0xff, 0xbf, 0xdb, 0xe0, 0xff, 0xbc, 0xd0, 0xdc, 0xff, 0xb6, 0xc9, 0xd7, 0xff, 0xaf, 0xc2, 0xd2, 0xff, 0xab, 0xba, 0xce, 0xff, 0xa7, 0xb9, 0xc4, 0xff, 0x9b, 0xac, 0xb9, 0xff, 0x7f, 0x7e, 0x95, 0xff, 0x7c, 0x7f, 0x78, 0xff, 0x96, 0xa2, 0x88, 0xff, 0xbf, 0xc9, 0xb5, 0xff, 0xbd, 0xc0, 0xad, 0xff, 0x82, 0x72, 0x63, 0xff, 0x3e, 0x2b, 0x18, 0xff, 0x91, 0x96, 0x85, 0xff, 0xd9, 0xf3, 0xe9, 0xff, 0xd7, 0xec, 0xdb, 0xff, 0xa0, 0x99, 0x84, 0xff, 0x4b, 0x30, 0x1f, 0xff, 0x69, 0x5b, 0x46, 0xff, 0xd7, 0xe3, 0xbf, 0xff, 0xa9, 0xa7, 0x90, 0xff, 0x33, 0x15, 0x0c, 0xff, 0x52, 0x39, 0x2a, 0xff, 0xbc, 0xbb, 0x9f, 0xff, 0x95, 0x96, 0x84, 0xff, 0x45, 0x3a, 0x33, 0xff, 0x24, 0x0e, 0x0d, 0xff, 0x59, 0x4a, 0x3e, 0xff, 0x4f, 0x40, 0x35, 0xff, 0x1f, 0x04, 0x0c, 0xff, 0x22, 0x0e, 0x10, 0xff, 0x1e, 0x0c, 0x11, 0xff, 0x1c, 0x07, 0x0d, 0xff, 0x22, 0x09, 0x08, 0xff, 0x39, 0x24, 0x24, 0xff, 0x2c, 0x18, 0x17, 0xff, 0x25, 0x12, 0x0d, 0xff, 0x23, 0x0d, 0x12, 0xff, 0x27, 0x12, 0x13, 0xff, 0x2c, 0x17, 0x12, 0xff, 0x26, 0x10, 0x0d, 0xff, 0x2b, 0x15, 0x14, 0xff, 0x2c, 0x15, 0x16, 0xff, 0x21, 0x0c, 0x0e, 0xff, 0x27, 0x16, 0x16, 0xff, 0x1c, 0x0a, 0x0e, 0xff, 0x20, 0x0d, 0x0e, 0xff, 0x26, 0x14, 0x12, 0xff, 0x27, 0x15, 0x16, 0xff, 0x2d, 0x18, 0x19, 0xff, 0x29, 0x14, 0x14, 0xff, 0x2a, 0x16, 0x14, 0xff, 0x33, 0x1e, 0x19, 0xff, 0x3b, 0x25, 0x1b, 0xff, 0x3d, 0x27, 0x18, 0xff, 0x40, 0x2b, 0x1b, 0xff, 0x4e, 0x36, 0x23, 0xff, 0x4e, 0x35, 0x21, 0xff, 0x44, 0x2d, 0x1c, 0xff, 0x44, 0x2f, 0x20, 0xff, 0x44, 0x2d, 0x21, 0xff, 0x70, 0x5f, 0x57, 0xff, 0xde, 0xda, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xeb, 0xeb, 0x19, 0xf6, 0xed, 0xe7, 0xc6, 0xf6, 0xed, 0xe7, 0xff, 0xf8, 0xf1, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xf8, 0xf9, 0xff, 0xd6, 0xec, 0xed, 0xff, 0xd0, 0xe8, 0xe9, 0xff, 0xd1, 0xe7, 0xec, 0xff, 0xd1, 0xe8, 0xed, 0xff, 0xd2, 0xe8, 0xed, 0xff, 0xd2, 0xe6, 0xed, 0xff, 0xd2, 0xe7, 0xed, 0xff, 0xd4, 0xe9, 0xed, 0xff, 0xd4, 0xeb, 0xeb, 0xff, 0xd3, 0xea, 0xea, 0xff, 0xd3, 0xe8, 0xec, 0xff, 0xd2, 0xe8, 0xed, 0xff, 0xd2, 0xe6, 0xed, 0xff, 0xd1, 0xe6, 0xed, 0xff, 0xd0, 0xe5, 0xeb, 0xff, 0xcf, 0xe4, 0xeb, 0xff, 0xd0, 0xe6, 0xeb, 0xff, 0xcc, 0xe1, 0xe9, 0xff, 0xd5, 0xea, 0xf3, 0xff, 0xd3, 0xea, 0xec, 0xff, 0xd0, 0xe6, 0xeb, 0xff, 0xcb, 0xe3, 0xea, 0xff, 0xcc, 0xe3, 0xe9, 0xff, 0xca, 0xdf, 0xe7, 0xff, 0xc6, 0xdb, 0xe3, 0xff, 0xc3, 0xd5, 0xe2, 0xff, 0xbf, 0xd2, 0xde, 0xff, 0xc5, 0xd7, 0xe5, 0xff, 0x88, 0x91, 0xac, 0xff, 0xac, 0xbc, 0xc5, 0xff, 0xde, 0xf9, 0xfb, 0xff, 0xc1, 0xdb, 0xe2, 0xff, 0xbe, 0xd3, 0xdf, 0xff, 0xb9, 0xc9, 0xd8, 0xff, 0xb1, 0xc2, 0xd2, 0xff, 0xa9, 0xbc, 0xcd, 0xff, 0xa4, 0xb5, 0xc9, 0xff, 0x9c, 0xa9, 0xbc, 0xff, 0x78, 0x7e, 0x92, 0xff, 0x7f, 0x84, 0x8c, 0xff, 0xb3, 0xbe, 0xb6, 0xff, 0xb5, 0xbd, 0xaf, 0xff, 0xa0, 0x9c, 0x8a, 0xff, 0x55, 0x4a, 0x3c, 0xff, 0x34, 0x25, 0x1f, 0xff, 0x92, 0x92, 0x8d, 0xff, 0xdb, 0xf3, 0xec, 0xff, 0xbc, 0xd2, 0xc4, 0xff, 0xb0, 0xb2, 0x98, 0xff, 0x6a, 0x59, 0x40, 0xff, 0x57, 0x3e, 0x2b, 0xff, 0x96, 0x91, 0x7b, 0xff, 0x9f, 0x9e, 0x90, 0xff, 0x41, 0x33, 0x1f, 0xff, 0x59, 0x40, 0x2a, 0xff, 0x75, 0x62, 0x51, 0xff, 0x4c, 0x44, 0x3b, 0xff, 0x25, 0x19, 0x12, 0xff, 0x1f, 0x0b, 0x0c, 0xff, 0x1e, 0x0a, 0x0e, 0xff, 0x86, 0x7e, 0x6e, 0xff, 0x4b, 0x3c, 0x33, 0xff, 0x1a, 0x02, 0x0a, 0xff, 0x1f, 0x10, 0x12, 0xff, 0x20, 0x0f, 0x0c, 0xff, 0x4e, 0x39, 0x32, 0xff, 0x72, 0x6c, 0x5e, 0xff, 0x37, 0x26, 0x22, 0xff, 0x47, 0x34, 0x30, 0xff, 0x34, 0x22, 0x1e, 0xff, 0x24, 0x0e, 0x14, 0xff, 0x23, 0x0e, 0x10, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x24, 0x11, 0x14, 0xff, 0x28, 0x13, 0x14, 0xff, 0x25, 0x11, 0x10, 0xff, 0x2c, 0x19, 0x18, 0xff, 0x29, 0x17, 0x19, 0xff, 0x25, 0x12, 0x14, 0xff, 0x2c, 0x19, 0x17, 0xff, 0x2e, 0x1a, 0x19, 0xff, 0x36, 0x20, 0x1b, 0xff, 0x48, 0x31, 0x21, 0xff, 0x52, 0x3a, 0x27, 0xff, 0x49, 0x2f, 0x21, 0xff, 0x46, 0x2e, 0x1d, 0xff, 0x58, 0x3d, 0x27, 0xff, 0x62, 0x45, 0x28, 0xff, 0x68, 0x4b, 0x2c, 0xff, 0x66, 0x4c, 0x29, 0xff, 0x64, 0x4b, 0x25, 0xff, 0x67, 0x4e, 0x29, 0xff, 0x6d, 0x54, 0x2f, 0xff, 0x94, 0x81, 0x67, 0xff, 0xe6, 0xe2, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xed, 0xe7, 0x2a, 0xf6, 0xed, 0xe6, 0xdf, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xf8, 0xf9, 0xff, 0xd9, 0xeb, 0xee, 0xff, 0xd1, 0xe7, 0xeb, 0xff, 0xcf, 0xe4, 0xea, 0xff, 0xd0, 0xe3, 0xeb, 0xff, 0xd2, 0xe4, 0xeb, 0xff, 0xd3, 0xe6, 0xeb, 0xff, 0xd3, 0xe7, 0xec, 0xff, 0xd4, 0xe8, 0xec, 0xff, 0xd5, 0xe9, 0xed, 0xff, 0xd3, 0xe7, 0xec, 0xff, 0xd2, 0xe6, 0xeb, 0xff, 0xd4, 0xe7, 0xed, 0xff, 0xd2, 0xe5, 0xed, 0xff, 0xd1, 0xe3, 0xea, 0xff, 0xd2, 0xe4, 0xeb, 0xff, 0xd3, 0xe6, 0xec, 0xff, 0xd5, 0xec, 0xef, 0xff, 0xd4, 0xea, 0xef, 0xff, 0xd0, 0xe6, 0xeb, 0xff, 0xcf, 0xe5, 0xeb, 0xff, 0xcc, 0xe2, 0xe8, 0xff, 0xcb, 0xe1, 0xe7, 0xff, 0xc8, 0xdd, 0xe5, 0xff, 0xc4, 0xd8, 0xdf, 0xff, 0xc1, 0xd2, 0xe1, 0xff, 0xc0, 0xd2, 0xdc, 0xff, 0xaa, 0xb8, 0xcb, 0xff, 0x8c, 0x96, 0xb2, 0xff, 0xd3, 0xeb, 0xea, 0xff, 0xc5, 0xe0, 0xe5, 0xff, 0xc3, 0xd9, 0xe3, 0xff, 0xba, 0xcb, 0xda, 0xff, 0xb5, 0xc4, 0xd5, 0xff, 0xb0, 0xc0, 0xd0, 0xff, 0xa5, 0xb6, 0xc7, 0xff, 0x9f, 0xad, 0xc3, 0xff, 0x93, 0x9f, 0xb4, 0xff, 0x86, 0x8f, 0x9f, 0xff, 0xc0, 0xd6, 0xcb, 0xff, 0xbf, 0xd8, 0xcd, 0xff, 0xa8, 0xad, 0xa6, 0xff, 0x61, 0x51, 0x45, 0xff, 0x44, 0x2f, 0x28, 0xff, 0x70, 0x69, 0x5c, 0xff, 0xb9, 0xc2, 0xb4, 0xff, 0xc5, 0xd6, 0xc4, 0xff, 0xbe, 0xc3, 0xa9, 0xff, 0x79, 0x6a, 0x52, 0xff, 0x2d, 0x1b, 0x10, 0xff, 0x68, 0x5b, 0x4e, 0xff, 0x7d, 0x6b, 0x59, 0xff, 0x31, 0x19, 0x14, 0xff, 0x7c, 0x66, 0x51, 0xff, 0x8b, 0x7c, 0x61, 0xff, 0x37, 0x28, 0x1d, 0xff, 0x16, 0x01, 0x09, 0xff, 0x2f, 0x19, 0x17, 0xff, 0x27, 0x10, 0x10, 0xff, 0x5e, 0x53, 0x4e, 0xff, 0x8e, 0x8a, 0x77, 0xff, 0x3f, 0x2f, 0x29, 0xff, 0x2e, 0x18, 0x17, 0xff, 0x20, 0x0d, 0x0e, 0xff, 0x0d, 0x00, 0x00, 0xff, 0x8e, 0x92, 0x80, 0xff, 0xdc, 0xf0, 0xd0, 0xff, 0x65, 0x53, 0x42, 0xff, 0x47, 0x31, 0x25, 0xff, 0x47, 0x35, 0x2e, 0xff, 0x2b, 0x17, 0x17, 0xff, 0x22, 0x0d, 0x11, 0xff, 0x25, 0x11, 0x15, 0xff, 0x26, 0x13, 0x14, 0xff, 0x24, 0x10, 0x0f, 0xff, 0x31, 0x1e, 0x1a, 0xff, 0x38, 0x25, 0x21, 0xff, 0x30, 0x1c, 0x19, 0xff, 0x35, 0x21, 0x1a, 0xff, 0x3a, 0x25, 0x1c, 0xff, 0x3d, 0x27, 0x1d, 0xff, 0x4a, 0x31, 0x21, 0xff, 0x5f, 0x47, 0x28, 0xff, 0x63, 0x48, 0x25, 0xff, 0x5c, 0x3f, 0x24, 0xff, 0x56, 0x40, 0x26, 0xff, 0x64, 0x4c, 0x2d, 0xff, 0x72, 0x58, 0x32, 0xff, 0x78, 0x5f, 0x36, 0xff, 0x71, 0x59, 0x31, 0xff, 0x72, 0x5b, 0x32, 0xff, 0x7c, 0x64, 0x3b, 0xff, 0xa1, 0x8f, 0x71, 0xff, 0xec, 0xe8, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xf8, 0xfa, 0xff, 0xd5, 0xe4, 0xea, 0xff, 0xc7, 0xd9, 0xe4, 0xff, 0xc8, 0xda, 0xe6, 0xff, 0xce, 0xe0, 0xe8, 0xff, 0xd1, 0xe5, 0xea, 0xff, 0xd2, 0xe5, 0xeb, 0xff, 0xd2, 0xe5, 0xec, 0xff, 0xd3, 0xe6, 0xed, 0xff, 0xd3, 0xe6, 0xec, 0xff, 0xd3, 0xe7, 0xed, 0xff, 0xd3, 0xe6, 0xed, 0xff, 0xd0, 0xe3, 0xea, 0xff, 0xcd, 0xe0, 0xe7, 0xff, 0xd2, 0xe4, 0xeb, 0xff, 0xd3, 0xe7, 0xee, 0xff, 0xd1, 0xe8, 0xed, 0xff, 0xd0, 0xe6, 0xeb, 0xff, 0xce, 0xe4, 0xeb, 0xff, 0xcd, 0xe3, 0xe9, 0xff, 0xcc, 0xe2, 0xe7, 0xff, 0xca, 0xe0, 0xe7, 0xff, 0xc8, 0xdc, 0xe4, 0xff, 0xc5, 0xd8, 0xdf, 0xff, 0xbf, 0xd1, 0xd9, 0xff, 0xc5, 0xd5, 0xe1, 0xff, 0x8d, 0x97, 0xb2, 0xff, 0xa2, 0xb1, 0xc3, 0xff, 0xda, 0xf3, 0xf5, 0xff, 0xc2, 0xd9, 0xe0, 0xff, 0xbc, 0xcf, 0xdc, 0xff, 0xb5, 0xc6, 0xd6, 0xff, 0xb0, 0xc0, 0xd1, 0xff, 0xac, 0xbb, 0xcd, 0xff, 0xa5, 0xb5, 0xc5, 0xff, 0x95, 0xa3, 0xb6, 0xff, 0x90, 0x9d, 0xaa, 0xff, 0xc1, 0xd4, 0xd4, 0xff, 0xc2, 0xd8, 0xd1, 0xff, 0xb9, 0xc9, 0xc0, 0xff, 0x96, 0x94, 0x87, 0xff, 0x35, 0x28, 0x20, 0xff, 0x53, 0x47, 0x40, 0xff, 0x9b, 0x98, 0x89, 0xff, 0xb8, 0xc9, 0xba, 0xff, 0xc5, 0xd6, 0xc2, 0xff, 0x86, 0x80, 0x6c, 0xff, 0x46, 0x30, 0x26, 0xff, 0x63, 0x50, 0x44, 0xff, 0x72, 0x61, 0x53, 0xff, 0x36, 0x23, 0x18, 0xff, 0x8e, 0x85, 0x65, 0xff, 0xa4, 0x97, 0x7e, 0xff, 0x36, 0x22, 0x1e, 0xff, 0x24, 0x10, 0x10, 0xff, 0x2e, 0x17, 0x17, 0xff, 0x2b, 0x10, 0x12, 0xff, 0x46, 0x36, 0x2c, 0xff, 0xb4, 0xb3, 0x9a, 0xff, 0x7b, 0x76, 0x66, 0xff, 0x31, 0x1d, 0x17, 0xff, 0x47, 0x31, 0x26, 0xff, 0x51, 0x3c, 0x33, 0xff, 0x34, 0x1f, 0x1a, 0xff, 0x52, 0x49, 0x3f, 0xff, 0x7c, 0x73, 0x64, 0xff, 0x52, 0x40, 0x2f, 0xff, 0x43, 0x2d, 0x1c, 0xff, 0x3e, 0x27, 0x1e, 0xff, 0x3e, 0x29, 0x1f, 0xff, 0x3d, 0x28, 0x22, 0xff, 0x28, 0x12, 0x11, 0xff, 0x31, 0x1d, 0x1a, 0xff, 0x2f, 0x1a, 0x16, 0xff, 0x34, 0x1f, 0x1a, 0xff, 0x3b, 0x25, 0x1e, 0xff, 0x40, 0x28, 0x1d, 0xff, 0x4b, 0x34, 0x23, 0xff, 0x4c, 0x32, 0x20, 0xff, 0x52, 0x39, 0x23, 0xff, 0x57, 0x41, 0x24, 0xff, 0x60, 0x46, 0x29, 0xff, 0x6d, 0x53, 0x2f, 0xff, 0x75, 0x5e, 0x36, 0xff, 0x71, 0x5b, 0x33, 0xff, 0x75, 0x5e, 0x33, 0xff, 0x82, 0x6c, 0x3b, 0xff, 0x86, 0x71, 0x3f, 0xff, 0x84, 0x70, 0x3d, 0xff, 0x85, 0x70, 0x40, 0xff, 0xb4, 0xa5, 0x87, 0xff, 0xf2, 0xef, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0xf0, 0xf0, 0xe1, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xf7, 0xf9, 0xff, 0xd5, 0xe0, 0xe8, 0xff, 0xc5, 0xd6, 0xe3, 0xff, 0xcb, 0xde, 0xe7, 0xff, 0xcf, 0xe3, 0xe8, 0xff, 0xd1, 0xe5, 0xeb, 0xff, 0xd2, 0xe4, 0xec, 0xff, 0xd2, 0xe4, 0xec, 0xff, 0xd2, 0xe5, 0xec, 0xff, 0xd2, 0xe5, 0xec, 0xff, 0xd0, 0xe3, 0xea, 0xff, 0xcb, 0xde, 0xe5, 0xff, 0xcc, 0xdf, 0xe6, 0xff, 0xd1, 0xe4, 0xeb, 0xff, 0xd3, 0xe5, 0xec, 0xff, 0xd1, 0xe5, 0xeb, 0xff, 0xd0, 0xe4, 0xea, 0xff, 0xcf, 0xe2, 0xea, 0xff, 0xce, 0xe2, 0xe8, 0xff, 0xcd, 0xe1, 0xe7, 0xff, 0xcb, 0xde, 0xe7, 0xff, 0xc8, 0xd9, 0xe5, 0xff, 0xc3, 0xd6, 0xdf, 0xff, 0xc2, 0xd4, 0xd8, 0xff, 0xb6, 0xc3, 0xd1, 0xff, 0x94, 0xa1, 0xb5, 0xff, 0xcd, 0xe4, 0xea, 0xff, 0xc5, 0xde, 0xe4, 0xff, 0xbf, 0xd3, 0xdd, 0xff, 0xb5, 0xc5, 0xd5, 0xff, 0xb3, 0xc3, 0xd3, 0xff, 0xad, 0xbd, 0xce, 0xff, 0xa5, 0xb4, 0xc5, 0xff, 0xa5, 0xb0, 0xbe, 0xff, 0x8b, 0x95, 0xa8, 0xff, 0xae, 0xc0, 0xc5, 0xff, 0xce, 0xe0, 0xd9, 0xff, 0xb5, 0xc4, 0xbe, 0xff, 0x89, 0x8a, 0x7e, 0xff, 0x5c, 0x4d, 0x40, 0xff, 0x62, 0x5c, 0x50, 0xff, 0xab, 0xb5, 0xa8, 0xff, 0xdc, 0xf3, 0xe9, 0xff, 0xc1, 0xd9, 0xcf, 0xff, 0xa3, 0xad, 0xa2, 0xff, 0x89, 0x88, 0x7d, 0xff, 0x73, 0x6e, 0x67, 0xff, 0x92, 0x88, 0x78, 0xff, 0x64, 0x54, 0x41, 0xff, 0x78, 0x7c, 0x69, 0xff, 0xbd, 0xc3, 0xab, 0xff, 0x2b, 0x1d, 0x16, 0xff, 0x44, 0x34, 0x2e, 0xff, 0x42, 0x2d, 0x27, 0xff, 0x53, 0x42, 0x33, 0xff, 0x4a, 0x39, 0x32, 0xff, 0x65, 0x5b, 0x4c, 0xff, 0xc3, 0xc9, 0xac, 0xff, 0x85, 0x80, 0x74, 0xff, 0x4e, 0x3d, 0x31, 0xff, 0x6f, 0x64, 0x52, 0xff, 0xa4, 0xa0, 0x8f, 0xff, 0x7c, 0x78, 0x6a, 0xff, 0x21, 0x05, 0x02, 0xff, 0x28, 0x07, 0x0a, 0xff, 0x2d, 0x1f, 0x18, 0xff, 0x65, 0x58, 0x48, 0xff, 0x46, 0x2f, 0x25, 0xff, 0x45, 0x2d, 0x20, 0xff, 0x4e, 0x37, 0x2e, 0xff, 0x33, 0x20, 0x19, 0xff, 0x33, 0x21, 0x19, 0xff, 0x3a, 0x25, 0x1d, 0xff, 0x3b, 0x25, 0x1e, 0xff, 0x44, 0x2a, 0x20, 0xff, 0x4f, 0x34, 0x23, 0xff, 0x57, 0x3d, 0x26, 0xff, 0x5e, 0x45, 0x29, 0xff, 0x62, 0x4b, 0x2c, 0xff, 0x5e, 0x4b, 0x27, 0xff, 0x67, 0x51, 0x2d, 0xff, 0x7c, 0x65, 0x3c, 0xff, 0x79, 0x61, 0x35, 0xff, 0x7b, 0x61, 0x39, 0xff, 0x7c, 0x61, 0x38, 0xff, 0x6c, 0x52, 0x2f, 0xff, 0x53, 0x3b, 0x27, 0xff, 0x58, 0x44, 0x31, 0xff, 0xa2, 0x96, 0x8d, 0xff, 0xf3, 0xf2, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0xf7, 0xf0, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xfb, 0xfc, 0xff, 0xdf, 0xe7, 0xed, 0xff, 0xca, 0xdb, 0xe2, 0xff, 0xcd, 0xe2, 0xe6, 0xff, 0xd0, 0xe4, 0xe9, 0xff, 0xce, 0xe1, 0xe7, 0xff, 0xd0, 0xe3, 0xe8, 0xff, 0xd2, 0xe4, 0xea, 0xff, 0xd0, 0xe3, 0xe9, 0xff, 0xcb, 0xde, 0xe4, 0xff, 0xc9, 0xdc, 0xe3, 0xff, 0xce, 0xe0, 0xe7, 0xff, 0xd1, 0xe3, 0xea, 0xff, 0xd1, 0xe3, 0xea, 0xff, 0xd1, 0xe4, 0xeb, 0xff, 0xd1, 0xe4, 0xeb, 0xff, 0xd1, 0xe4, 0xeb, 0xff, 0xce, 0xe1, 0xe7, 0xff, 0xcd, 0xdf, 0xe7, 0xff, 0xc8, 0xd9, 0xe3, 0xff, 0xc4, 0xd5, 0xe2, 0xff, 0xc3, 0xd4, 0xde, 0xff, 0xce, 0xdd, 0xe5, 0xff, 0xa2, 0xae, 0xbe, 0xff, 0xab, 0xba, 0xca, 0xff, 0xd7, 0xee, 0xf5, 0xff, 0xbe, 0xd5, 0xdd, 0xff, 0xba, 0xcd, 0xd9, 0xff, 0xb2, 0xc2, 0xd1, 0xff, 0xaf, 0xbe, 0xce, 0xff, 0xac, 0xba, 0xca, 0xff, 0xa5, 0xaf, 0xc1, 0xff, 0x90, 0x96, 0xb0, 0xff, 0x86, 0x89, 0xa5, 0xff, 0xa7, 0xaf, 0xb5, 0xff, 0xa0, 0xa8, 0x9f, 0xff, 0x8c, 0x88, 0x7e, 0xff, 0x7d, 0x77, 0x6a, 0xff, 0x5f, 0x5c, 0x50, 0xff, 0xb9, 0xc0, 0xb6, 0xff, 0xe1, 0xfb, 0xf1, 0xff, 0xb3, 0xcf, 0xc7, 0xff, 0xae, 0xc2, 0xb8, 0xff, 0xa8, 0xb2, 0xa5, 0xff, 0x86, 0x90, 0x84, 0xff, 0xac, 0xbb, 0xb2, 0xff, 0xc3, 0xd2, 0xc7, 0xff, 0xb6, 0xc2, 0xb5, 0xff, 0xd3, 0xe4, 0xd4, 0xff, 0x82, 0x82, 0x77, 0xff, 0x4e, 0x3e, 0x3b, 0xff, 0x9f, 0xa5, 0x97, 0xff, 0x69, 0x63, 0x54, 0xff, 0xa2, 0x9c, 0x87, 0xff, 0x8d, 0x8e, 0x7f, 0xff, 0x90, 0x95, 0x86, 0xff, 0xcb, 0xd8, 0xc2, 0xff, 0x8a, 0x8c, 0x7e, 0xff, 0x8e, 0x89, 0x79, 0xff, 0xa3, 0xa5, 0x91, 0xff, 0xab, 0xb6, 0xa4, 0xff, 0xbd, 0xc9, 0xb7, 0xff, 0x65, 0x56, 0x4b, 0xff, 0x50, 0x38, 0x26, 0xff, 0x49, 0x34, 0x2d, 0xff, 0x4a, 0x3a, 0x35, 0xff, 0x3c, 0x28, 0x1d, 0xff, 0x4c, 0x34, 0x29, 0xff, 0x3c, 0x26, 0x1d, 0xff, 0x40, 0x2c, 0x24, 0xff, 0x3d, 0x28, 0x1f, 0xff, 0x42, 0x2c, 0x1f, 0xff, 0x45, 0x2e, 0x20, 0xff, 0x4a, 0x33, 0x22, 0xff, 0x50, 0x39, 0x24, 0xff, 0x5b, 0x46, 0x28, 0xff, 0x66, 0x52, 0x2d, 0xff, 0x6b, 0x52, 0x2f, 0xff, 0x7a, 0x5e, 0x3a, 0xff, 0x7c, 0x65, 0x37, 0xff, 0x6f, 0x59, 0x2f, 0xff, 0x61, 0x48, 0x2a, 0xff, 0x5f, 0x48, 0x2a, 0xff, 0x50, 0x38, 0x21, 0xff, 0x3a, 0x23, 0x1a, 0xff, 0x3b, 0x2c, 0x2e, 0xff, 0xa8, 0xa1, 0xa5, 0xff, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0xf7, 0xf0, 0xe8, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xdf, 0xf6, 0xed, 0xe7, 0xff, 0xf8, 0xf1, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xe7, 0xee, 0xf1, 0xff, 0xd0, 0xe0, 0xe7, 0xff, 0xc9, 0xdc, 0xe6, 0xff, 0xca, 0xdd, 0xe5, 0xff, 0xc9, 0xdb, 0xe3, 0xff, 0xc9, 0xdb, 0xe3, 0xff, 0xcb, 0xdb, 0xe3, 0xff, 0xc7, 0xd7, 0xdf, 0xff, 0xc7, 0xd8, 0xe0, 0xff, 0xcc, 0xde, 0xe5, 0xff, 0xcf, 0xe2, 0xe8, 0xff, 0xce, 0xe2, 0xe9, 0xff, 0xd1, 0xe4, 0xeb, 0xff, 0xd0, 0xe4, 0xeb, 0xff, 0xcf, 0xe3, 0xea, 0xff, 0xce, 0xe2, 0xe8, 0xff, 0xcc, 0xdc, 0xe5, 0xff, 0xc7, 0xd7, 0xe2, 0xff, 0xc3, 0xd3, 0xe0, 0xff, 0xc2, 0xd3, 0xdc, 0xff, 0xc7, 0xd7, 0xe1, 0xff, 0x91, 0x9f, 0xb0, 0xff, 0xb9, 0xc8, 0xd8, 0xff, 0xca, 0xe0, 0xe8, 0xff, 0xbc, 0xd1, 0xda, 0xff, 0xb7, 0xc8, 0xd4, 0xff, 0xb1, 0xc2, 0xce, 0xff, 0xaf, 0xbd, 0xcd, 0xff, 0xa3, 0xaf, 0xbe, 0xff, 0x93, 0x9d, 0xb4, 0xff, 0x77, 0x80, 0xa8, 0xff, 0x68, 0x6c, 0x8c, 0xff, 0x81, 0x7f, 0x84, 0xff, 0x73, 0x73, 0x63, 0xff, 0x9a, 0x98, 0x88, 0xff, 0x75, 0x6e, 0x63, 0xff, 0x82, 0x88, 0x7d, 0xff, 0xda, 0xf4, 0xea, 0xff, 0xb4, 0xcf, 0xc9, 0xff, 0xae, 0xc4, 0xbe, 0xff, 0xc4, 0xd2, 0xca, 0xff, 0x85, 0x8a, 0x80, 0xff, 0x83, 0x89, 0x80, 0xff, 0xc9, 0xda, 0xd5, 0xff, 0xc6, 0xdb, 0xd8, 0xff, 0xbc, 0xd5, 0xcf, 0xff, 0xc5, 0xd7, 0xcf, 0xff, 0x5b, 0x51, 0x4c, 0xff, 0x6e, 0x64, 0x5e, 0xff, 0xc8, 0xda, 0xce, 0xff, 0xa4, 0xb8, 0xa6, 0xff, 0xa3, 0xae, 0x99, 0xff, 0xae, 0xb6, 0xa5, 0xff, 0x90, 0x92, 0x83, 0xff, 0x89, 0x8f, 0x80, 0xff, 0x91, 0x94, 0x87, 0xff, 0x7d, 0x7c, 0x71, 0xff, 0xbc, 0xbf, 0xb4, 0xff, 0x65, 0x67, 0x5d, 0xff, 0x95, 0x94, 0x7c, 0xff, 0x7d, 0x6e, 0x58, 0xff, 0x50, 0x39, 0x26, 0xff, 0x3f, 0x28, 0x1e, 0xff, 0x1a, 0x06, 0x06, 0xff, 0x40, 0x2d, 0x25, 0xff, 0x4c, 0x36, 0x2a, 0xff, 0x3e, 0x28, 0x1c, 0xff, 0x43, 0x2b, 0x22, 0xff, 0x48, 0x2f, 0x25, 0xff, 0x4d, 0x34, 0x23, 0xff, 0x50, 0x38, 0x23, 0xff, 0x54, 0x3c, 0x25, 0xff, 0x5a, 0x41, 0x28, 0xff, 0x64, 0x4c, 0x2d, 0xff, 0x6e, 0x55, 0x32, 0xff, 0x74, 0x5b, 0x36, 0xff, 0x73, 0x5b, 0x35, 0xff, 0x5b, 0x43, 0x28, 0xff, 0x42, 0x2b, 0x1f, 0xff, 0x2e, 0x19, 0x13, 0xff, 0x1e, 0x0a, 0x0d, 0xff, 0x1a, 0x06, 0x0e, 0xff, 0x55, 0x44, 0x49, 0xff, 0xcc, 0xc7, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe7, 0xbe, 0xf7, 0xf0, 0xe8, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xec, 0xe8, 0x42, 0xf6, 0xed, 0xe7, 0xdf, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf4, 0xf7, 0xff, 0xcf, 0xd9, 0xe7, 0xff, 0xb6, 0xc5, 0xda, 0xff, 0xb7, 0xc5, 0xda, 0xff, 0xbd, 0xcb, 0xdd, 0xff, 0xbe, 0xcb, 0xd9, 0xff, 0xc4, 0xd1, 0xde, 0xff, 0xc9, 0xd7, 0xe1, 0xff, 0xcc, 0xdb, 0xe4, 0xff, 0xce, 0xe1, 0xe8, 0xff, 0xce, 0xe1, 0xe9, 0xff, 0xd0, 0xe2, 0xea, 0xff, 0xd0, 0xe2, 0xe9, 0xff, 0xce, 0xe1, 0xe7, 0xff, 0xcd, 0xe0, 0xe7, 0xff, 0xcb, 0xdc, 0xe6, 0xff, 0xc3, 0xd3, 0xe1, 0xff, 0xc0, 0xd0, 0xdd, 0xff, 0xc1, 0xd3, 0xdb, 0xff, 0xbe, 0xcf, 0xd5, 0xff, 0xb1, 0xc1, 0xcd, 0xff, 0xc4, 0xd9, 0xe3, 0xff, 0xc3, 0xd9, 0xe1, 0xff, 0xbb, 0xcc, 0xd8, 0xff, 0xb4, 0xc4, 0xd1, 0xff, 0xad, 0xbd, 0xcb, 0xff, 0xa6, 0xb1, 0xc4, 0xff, 0x92, 0x9b, 0xb3, 0xff, 0x77, 0x7f, 0xa1, 0xff, 0x57, 0x5b, 0x87, 0xff, 0x4a, 0x4b, 0x63, 0xff, 0x71, 0x6d, 0x6a, 0xff, 0xa4, 0xa2, 0x93, 0xff, 0x9a, 0xa1, 0x90, 0xff, 0x72, 0x71, 0x65, 0xff, 0xc0, 0xcd, 0xc3, 0xff, 0xb1, 0xc3, 0xbb, 0xff, 0x8b, 0x93, 0x8c, 0xff, 0xa9, 0xae, 0xaa, 0xff, 0x8b, 0x8f, 0x8b, 0xff, 0x73, 0x70, 0x6d, 0xff, 0x7c, 0x77, 0x73, 0xff, 0x99, 0xa0, 0x9b, 0xff, 0x8b, 0x93, 0x90, 0xff, 0x8d, 0x94, 0x8d, 0xff, 0x83, 0x7e, 0x77, 0xff, 0x39, 0x2a, 0x29, 0xff, 0x66, 0x67, 0x5d, 0xff, 0xa5, 0xaa, 0xa1, 0xff, 0x8b, 0x95, 0x8c, 0xff, 0x8d, 0x93, 0x86, 0xff, 0xa9, 0xab, 0x99, 0xff, 0x66, 0x5a, 0x4e, 0xff, 0x40, 0x33, 0x27, 0xff, 0x77, 0x6c, 0x57, 0xff, 0x58, 0x4b, 0x3e, 0xff, 0x68, 0x5e, 0x56, 0xff, 0x51, 0x42, 0x34, 0xff, 0x44, 0x2e, 0x21, 0xff, 0x46, 0x34, 0x25, 0xff, 0x36, 0x21, 0x1d, 0xff, 0x3d, 0x29, 0x23, 0xff, 0x2b, 0x17, 0x15, 0xff, 0x36, 0x23, 0x1d, 0xff, 0x3e, 0x28, 0x1e, 0xff, 0x49, 0x31, 0x28, 0xff, 0x46, 0x2d, 0x23, 0xff, 0x44, 0x2b, 0x1e, 0xff, 0x53, 0x3a, 0x27, 0xff, 0x5d, 0x45, 0x2b, 0xff, 0x5d, 0x45, 0x27, 0xff, 0x66, 0x4e, 0x2d, 0xff, 0x62, 0x4c, 0x2d, 0xff, 0x5c, 0x45, 0x2c, 0xff, 0x48, 0x33, 0x20, 0xff, 0x30, 0x1d, 0x12, 0xff, 0x31, 0x1b, 0x17, 0xff, 0x39, 0x21, 0x1c, 0xff, 0x3e, 0x29, 0x1b, 0xff, 0x53, 0x42, 0x2e, 0xff, 0xa2, 0x99, 0x8b, 0xff, 0xeb, 0xe9, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe7, 0xb5, 0xf5, 0xeb, 0xeb, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xeb, 0xe5, 0x32, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf7, 0xfa, 0xff, 0xcf, 0xd5, 0xe4, 0xff, 0xb7, 0xc2, 0xd9, 0xff, 0xb3, 0xbe, 0xd3, 0xff, 0xb5, 0xc2, 0xd0, 0xff, 0xc1, 0xce, 0xdb, 0xff, 0xca, 0xd7, 0xe3, 0xff, 0xcf, 0xdf, 0xe7, 0xff, 0xd0, 0xe3, 0xe8, 0xff, 0xd0, 0xe4, 0xe6, 0xff, 0xcf, 0xe3, 0xe7, 0xff, 0xcf, 0xe2, 0xe8, 0xff, 0xcd, 0xdf, 0xe7, 0xff, 0xc8, 0xda, 0xe1, 0xff, 0xc6, 0xd7, 0xe0, 0xff, 0xc0, 0xd0, 0xda, 0xff, 0xc0, 0xd0, 0xda, 0xff, 0xc2, 0xd2, 0xda, 0xff, 0xc3, 0xd5, 0xda, 0xff, 0xc5, 0xda, 0xe0, 0xff, 0xc5, 0xda, 0xe1, 0xff, 0xc0, 0xd4, 0xdb, 0xff, 0xb8, 0xc9, 0xd2, 0xff, 0xb2, 0xc2, 0xcc, 0xff, 0xaa, 0xb5, 0xc4, 0xff, 0x90, 0x96, 0xaf, 0xff, 0x74, 0x77, 0x9c, 0xff, 0x62, 0x64, 0x8a, 0xff, 0x4c, 0x48, 0x67, 0xff, 0x69, 0x63, 0x6b, 0xff, 0x73, 0x6d, 0x65, 0xff, 0x81, 0x7e, 0x72, 0xff, 0x97, 0x9d, 0x91, 0xff, 0xa6, 0xaf, 0xa5, 0xff, 0x9c, 0x9c, 0x97, 0xff, 0x52, 0x4b, 0x4d, 0xff, 0x3b, 0x2c, 0x31, 0xff, 0x70, 0x5f, 0x58, 0xff, 0x4b, 0x45, 0x3a, 0xff, 0x58, 0x4e, 0x4e, 0xff, 0x49, 0x3e, 0x3b, 0xff, 0x8d, 0x90, 0x84, 0xff, 0x88, 0x85, 0x78, 0xff, 0x47, 0x39, 0x32, 0xff, 0x40, 0x2b, 0x29, 0xff, 0x58, 0x49, 0x48, 0xff, 0x71, 0x6d, 0x62, 0xff, 0x5b, 0x50, 0x4e, 0xff, 0x45, 0x36, 0x36, 0xff, 0x69, 0x5e, 0x55, 0xff, 0x76, 0x6c, 0x5f, 0xff, 0x3f, 0x2f, 0x26, 0xff, 0x38, 0x23, 0x1c, 0xff, 0x49, 0x35, 0x27, 0xff, 0x4c, 0x36, 0x2a, 0xff, 0x48, 0x31, 0x23, 0xff, 0x5f, 0x4a, 0x37, 0xff, 0x32, 0x1e, 0x1a, 0xff, 0x25, 0x13, 0x15, 0xff, 0x2d, 0x1a, 0x1a, 0xff, 0x31, 0x1c, 0x1a, 0xff, 0x38, 0x24, 0x1c, 0xff, 0x37, 0x23, 0x1d, 0xff, 0x3b, 0x25, 0x20, 0xff, 0x3e, 0x27, 0x1f, 0xff, 0x43, 0x2d, 0x22, 0xff, 0x4d, 0x38, 0x27, 0xff, 0x56, 0x41, 0x29, 0xff, 0x57, 0x3f, 0x27, 0xff, 0x4b, 0x34, 0x25, 0xff, 0x39, 0x25, 0x1f, 0xff, 0x27, 0x13, 0x14, 0xff, 0x1f, 0x0b, 0x0d, 0xff, 0x34, 0x1e, 0x19, 0xff, 0x59, 0x42, 0x2e, 0xff, 0x6e, 0x59, 0x3b, 0xff, 0x7a, 0x65, 0x3c, 0xff, 0x93, 0x82, 0x5b, 0xff, 0xd3, 0xca, 0xb9, 0xff, 0xfa, 0xf9, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0xf0, 0xf0, 0xe1, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xf0, 0xe8, 0x21, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xe9, 0xec, 0xf2, 0xff, 0xc6, 0xcd, 0xdc, 0xff, 0xbc, 0xc8, 0xd6, 0xff, 0xc1, 0xd0, 0xd9, 0xff, 0xc9, 0xd9, 0xe0, 0xff, 0xcf, 0xdf, 0xe7, 0xff, 0xcf, 0xe0, 0xe8, 0xff, 0xce, 0xdf, 0xe8, 0xff, 0xd0, 0xe2, 0xea, 0xff, 0xcf, 0xe0, 0xe7, 0xff, 0xcc, 0xdd, 0xe3, 0xff, 0xc7, 0xd6, 0xdd, 0xff, 0xc0, 0xcd, 0xd7, 0xff, 0xbf, 0xcd, 0xd6, 0xff, 0xbf, 0xce, 0xd6, 0xff, 0xc3, 0xd1, 0xda, 0xff, 0xc5, 0xd7, 0xde, 0xff, 0xc7, 0xdb, 0xe1, 0xff, 0xc5, 0xd8, 0xe0, 0xff, 0xbd, 0xce, 0xd7, 0xff, 0xb9, 0xc9, 0xd2, 0xff, 0xb3, 0xc1, 0xcb, 0xff, 0x9d, 0xa4, 0xb6, 0xff, 0x7a, 0x7c, 0x9c, 0xff, 0x63, 0x63, 0x8e, 0xff, 0x51, 0x4f, 0x72, 0xff, 0x35, 0x33, 0x3f, 0xff, 0x61, 0x5d, 0x5b, 0xff, 0x69, 0x62, 0x5d, 0xff, 0x8f, 0x94, 0x89, 0xff, 0xbb, 0xcb, 0xc1, 0xff, 0x97, 0x9c, 0x99, 0xff, 0x59, 0x4e, 0x50, 0xff, 0x27, 0x17, 0x1e, 0xff, 0x3f, 0x2b, 0x2c, 0xff, 0x55, 0x3e, 0x33, 0xff, 0x4f, 0x42, 0x37, 0xff, 0x4b, 0x43, 0x3d, 0xff, 0x5a, 0x53, 0x4a, 0xff, 0x98, 0x97, 0x86, 0xff, 0x5f, 0x53, 0x46, 0xff, 0x1a, 0x03, 0x03, 0xff, 0x34, 0x22, 0x22, 0xff, 0x38, 0x27, 0x27, 0xff, 0x4a, 0x3b, 0x34, 0xff, 0x56, 0x48, 0x46, 0xff, 0x57, 0x48, 0x46, 0xff, 0x33, 0x25, 0x1a, 0xff, 0x56, 0x46, 0x39, 0xff, 0x47, 0x35, 0x2c, 0xff, 0x2e, 0x1a, 0x1b, 0xff, 0x1f, 0x09, 0x12, 0xff, 0x24, 0x0d, 0x11, 0xff, 0x32, 0x1b, 0x19, 0xff, 0x2f, 0x19, 0x15, 0xff, 0x26, 0x13, 0x10, 0xff, 0x27, 0x15, 0x17, 0xff, 0x28, 0x16, 0x16, 0xff, 0x2f, 0x1b, 0x18, 0xff, 0x36, 0x1f, 0x1c, 0xff, 0x3b, 0x24, 0x1f, 0xff, 0x43, 0x2e, 0x24, 0xff, 0x44, 0x30, 0x20, 0xff, 0x4d, 0x38, 0x25, 0xff, 0x4c, 0x37, 0x25, 0xff, 0x3d, 0x2a, 0x1f, 0xff, 0x29, 0x15, 0x16, 0xff, 0x1c, 0x08, 0x0d, 0xff, 0x16, 0x04, 0x09, 0xff, 0x1f, 0x0c, 0x10, 0xff, 0x2e, 0x19, 0x19, 0xff, 0x45, 0x2f, 0x23, 0xff, 0x54, 0x3b, 0x22, 0xff, 0x5e, 0x47, 0x2e, 0xff, 0x9b, 0x8d, 0x7c, 0xff, 0xe4, 0xe0, 0xdb, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf8, 0xfa, 0xff, 0xe1, 0xe5, 0xec, 0xff, 0xcd, 0xd6, 0xe0, 0xff, 0xc6, 0xd6, 0xdd, 0xff, 0xc9, 0xd9, 0xe0, 0xff, 0xca, 0xda, 0xe2, 0xff, 0xcb, 0xdc, 0xe4, 0xff, 0xd0, 0xe0, 0xe6, 0xff, 0xcc, 0xda, 0xe0, 0xff, 0xc8, 0xd4, 0xdc, 0xff, 0xc4, 0xcd, 0xd8, 0xff, 0xc1, 0xcc, 0xd9, 0xff, 0xbf, 0xcb, 0xd7, 0xff, 0xc1, 0xce, 0xd7, 0xff, 0xc3, 0xd3, 0xda, 0xff, 0xc9, 0xdb, 0xe0, 0xff, 0xc7, 0xdb, 0xe0, 0xff, 0xc2, 0xd5, 0xdd, 0xff, 0xbc, 0xcc, 0xd9, 0xff, 0xb8, 0xc4, 0xd0, 0xff, 0xad, 0xb7, 0xc5, 0xff, 0x8a, 0x8e, 0xa9, 0xff, 0x6d, 0x70, 0x96, 0xff, 0x5c, 0x59, 0x82, 0xff, 0x26, 0x1e, 0x38, 0xff, 0x39, 0x36, 0x36, 0xff, 0x81, 0x7f, 0x75, 0xff, 0x89, 0x8a, 0x85, 0xff, 0xb3, 0xc2, 0xbb, 0xff, 0xbb, 0xcd, 0xc8, 0xff, 0x72, 0x72, 0x74, 0xff, 0x28, 0x19, 0x22, 0xff, 0x46, 0x34, 0x36, 0xff, 0x7a, 0x6a, 0x55, 0xff, 0x42, 0x2e, 0x22, 0xff, 0x46, 0x36, 0x36, 0xff, 0x4c, 0x48, 0x3e, 0xff, 0x7b, 0x79, 0x6d, 0xff, 0x78, 0x6b, 0x60, 0xff, 0x1c, 0x05, 0x00, 0xff, 0x2a, 0x14, 0x16, 0xff, 0x42, 0x2f, 0x2b, 0xff, 0x29, 0x13, 0x14, 0xff, 0x26, 0x13, 0x12, 0xff, 0x4c, 0x3c, 0x34, 0xff, 0x3d, 0x29, 0x2a, 0xff, 0x1c, 0x05, 0x0a, 0xff, 0x33, 0x1e, 0x1e, 0xff, 0x38, 0x21, 0x23, 0xff, 0x1f, 0x09, 0x0d, 0xff, 0x22, 0x0c, 0x10, 0xff, 0x27, 0x13, 0x19, 0xff, 0x1f, 0x0c, 0x11, 0xff, 0x28, 0x14, 0x16, 0xff, 0x2d, 0x17, 0x19, 0xff, 0x28, 0x15, 0x17, 0xff, 0x2c, 0x19, 0x1a, 0xff, 0x31, 0x1c, 0x1b, 0xff, 0x34, 0x1d, 0x1c, 0xff, 0x3e, 0x29, 0x1f, 0xff, 0x51, 0x3c, 0x2b, 0xff, 0x4e, 0x37, 0x2b, 0xff, 0x3b, 0x26, 0x1e, 0xff, 0x25, 0x10, 0x0f, 0xff, 0x1c, 0x05, 0x0b, 0xff, 0x22, 0x0f, 0x12, 0xff, 0x34, 0x1e, 0x18, 0xff, 0x4e, 0x36, 0x24, 0xff, 0x5f, 0x4a, 0x2f, 0xff, 0x5d, 0x47, 0x2b, 0xff, 0x57, 0x40, 0x2a, 0xff, 0x7c, 0x6e, 0x5b, 0xff, 0xc6, 0xbf, 0xb5, 0xff, 0xfb, 0xfa, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf4, 0xf7, 0xff, 0xdd, 0xe4, 0xec, 0xff, 0xca, 0xd6, 0xe0, 0xff, 0xc5, 0xd4, 0xdb, 0xff, 0xc3, 0xd2, 0xdc, 0xff, 0xb7, 0xc4, 0xd6, 0xff, 0xbb, 0xc7, 0xd4, 0xff, 0xc1, 0xcb, 0xd7, 0xff, 0xbf, 0xc8, 0xd7, 0xff, 0xbe, 0xc8, 0xd5, 0xff, 0xbe, 0xc9, 0xd5, 0xff, 0xbf, 0xcb, 0xd5, 0xff, 0xc2, 0xd2, 0xd8, 0xff, 0xc8, 0xd8, 0xde, 0xff, 0xc6, 0xd8, 0xdd, 0xff, 0xbc, 0xce, 0xd6, 0xff, 0xb8, 0xc7, 0xd2, 0xff, 0xb2, 0xbd, 0xc7, 0xff, 0x9a, 0xa0, 0xb0, 0xff, 0x7f, 0x81, 0xa1, 0xff, 0x71, 0x6f, 0x95, 0xff, 0x52, 0x4a, 0x69, 0xff, 0x26, 0x18, 0x26, 0xff, 0x5f, 0x59, 0x51, 0xff, 0x90, 0x8f, 0x82, 0xff, 0x9c, 0xa4, 0x9d, 0xff, 0xbe, 0xcd, 0xc8, 0xff, 0x9a, 0xa4, 0xa4, 0xff, 0x50, 0x47, 0x4f, 0xff, 0x20, 0x0c, 0x19, 0xff, 0x4e, 0x3b, 0x3c, 0xff, 0x52, 0x3f, 0x35, 0xff, 0x2a, 0x16, 0x12, 0xff, 0x4e, 0x3d, 0x3a, 0xff, 0x54, 0x4a, 0x41, 0xff, 0x4a, 0x3f, 0x37, 0xff, 0x31, 0x1f, 0x19, 0xff, 0x44, 0x2c, 0x27, 0xff, 0x39, 0x27, 0x24, 0xff, 0x41, 0x2d, 0x2b, 0xff, 0x35, 0x20, 0x1e, 0xff, 0x1a, 0x05, 0x07, 0xff, 0x4a, 0x36, 0x32, 0xff, 0x43, 0x30, 0x2c, 0xff, 0x33, 0x1f, 0x1e, 0xff, 0x44, 0x31, 0x2e, 0xff, 0x3b, 0x28, 0x24, 0xff, 0x3a, 0x27, 0x23, 0xff, 0x38, 0x25, 0x21, 0xff, 0x2d, 0x1a, 0x19, 0xff, 0x28, 0x16, 0x16, 0xff, 0x29, 0x16, 0x15, 0xff, 0x2d, 0x16, 0x19, 0xff, 0x2c, 0x1a, 0x19, 0xff, 0x2e, 0x1b, 0x1a, 0xff, 0x34, 0x20, 0x1c, 0xff, 0x3e, 0x2b, 0x1f, 0xff, 0x45, 0x32, 0x26, 0xff, 0x3e, 0x29, 0x21, 0xff, 0x2b, 0x13, 0x12, 0xff, 0x23, 0x0f, 0x10, 0xff, 0x2e, 0x1b, 0x18, 0xff, 0x47, 0x30, 0x23, 0xff, 0x5f, 0x47, 0x2c, 0xff, 0x78, 0x60, 0x3c, 0xff, 0x80, 0x68, 0x3e, 0xff, 0x7f, 0x67, 0x38, 0xff, 0x98, 0x85, 0x5e, 0xff, 0xc7, 0xbc, 0xa7, 0xff, 0xf3, 0xf1, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0xd6, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xe6, 0xe8, 0xf1, 0xff, 0xbe, 0xc2, 0xdb, 0xff, 0x8f, 0x92, 0xc0, 0xff, 0x72, 0x73, 0xad, 0xff, 0x8a, 0x8d, 0xb6, 0xff, 0xa1, 0xa6, 0xbe, 0xff, 0xb0, 0xb6, 0xc6, 0xff, 0xb7, 0xbd, 0xcc, 0xff, 0xb7, 0xc0, 0xcc, 0xff, 0xbd, 0xc8, 0xd1, 0xff, 0xc2, 0xd1, 0xd8, 0xff, 0xc2, 0xd2, 0xda, 0xff, 0xbf, 0xcf, 0xd6, 0xff, 0xbb, 0xcb, 0xd3, 0xff, 0xba, 0xc8, 0xcf, 0xff, 0xa9, 0xb1, 0xb9, 0xff, 0x8a, 0x8c, 0x9f, 0xff, 0x7d, 0x7b, 0x9b, 0xff, 0x70, 0x68, 0x88, 0xff, 0x4e, 0x42, 0x52, 0xff, 0x30, 0x21, 0x22, 0xff, 0x67, 0x59, 0x53, 0xff, 0x97, 0x9b, 0x8d, 0xff, 0xc0, 0xcc, 0xc2, 0xff, 0x81, 0x84, 0x82, 0xff, 0x65, 0x62, 0x65, 0xff, 0x3e, 0x2f, 0x3a, 0xff, 0x38, 0x25, 0x30, 0xff, 0x2f, 0x1d, 0x20, 0xff, 0x16, 0x00, 0x06, 0xff, 0x2b, 0x18, 0x17, 0xff, 0x47, 0x39, 0x30, 0xff, 0x43, 0x31, 0x2c, 0xff, 0x21, 0x0e, 0x0d, 0xff, 0x40, 0x2f, 0x2d, 0xff, 0x40, 0x2f, 0x27, 0xff, 0x2c, 0x1a, 0x18, 0xff, 0x2a, 0x17, 0x17, 0xff, 0x43, 0x31, 0x29, 0xff, 0x29, 0x13, 0x16, 0xff, 0x25, 0x10, 0x14, 0xff, 0x31, 0x1e, 0x1b, 0xff, 0x3d, 0x2a, 0x27, 0xff, 0x3e, 0x2b, 0x28, 0xff, 0x34, 0x22, 0x1f, 0xff, 0x3f, 0x2e, 0x28, 0xff, 0x3c, 0x29, 0x25, 0xff, 0x30, 0x1c, 0x1b, 0xff, 0x2f, 0x1b, 0x18, 0xff, 0x2d, 0x19, 0x17, 0xff, 0x2e, 0x1a, 0x19, 0xff, 0x2f, 0x1d, 0x1b, 0xff, 0x34, 0x22, 0x1e, 0xff, 0x3b, 0x28, 0x23, 0xff, 0x38, 0x25, 0x21, 0xff, 0x2e, 0x1a, 0x16, 0xff, 0x2e, 0x1a, 0x13, 0xff, 0x3c, 0x29, 0x1e, 0xff, 0x51, 0x3b, 0x26, 0xff, 0x69, 0x51, 0x35, 0xff, 0x6b, 0x55, 0x33, 0xff, 0x69, 0x53, 0x2f, 0xff, 0x74, 0x60, 0x39, 0xff, 0x8b, 0x79, 0x58, 0xff, 0xb6, 0xa9, 0x95, 0xff, 0xe8, 0xe4, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe7, 0xb5, 0xf5, 0xeb, 0xeb, 0x19, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xe1, 0x11, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf9, 0xfc, 0xff, 0xcf, 0xcf, 0xe3, 0xff, 0x9d, 0x9f, 0xc4, 0xff, 0x8c, 0x8c, 0xb7, 0xff, 0x8d, 0x8e, 0xaf, 0xff, 0x9a, 0x9b, 0xb3, 0xff, 0xa8, 0xab, 0xbe, 0xff, 0xb4, 0xbc, 0xc7, 0xff, 0xbc, 0xc7, 0xce, 0xff, 0xc1, 0xcd, 0xd5, 0xff, 0xbf, 0xcc, 0xd6, 0xff, 0xbb, 0xc8, 0xd1, 0xff, 0xbb, 0xc6, 0xd0, 0xff, 0xb2, 0xbc, 0xc7, 0xff, 0xa1, 0xa7, 0xb1, 0xff, 0x8e, 0x8d, 0xa0, 0xff, 0x71, 0x6e, 0x8a, 0xff, 0x55, 0x4a, 0x63, 0xff, 0x3f, 0x32, 0x3a, 0xff, 0x37, 0x27, 0x25, 0xff, 0x59, 0x4a, 0x45, 0xff, 0xb7, 0xc0, 0xb1, 0xff, 0xa1, 0xa5, 0x9e, 0xff, 0x48, 0x3c, 0x40, 0xff, 0x52, 0x47, 0x4b, 0xff, 0x31, 0x23, 0x2e, 0xff, 0x3b, 0x2a, 0x33, 0xff, 0x25, 0x14, 0x16, 0xff, 0x21, 0x0d, 0x10, 0xff, 0x33, 0x1f, 0x1b, 0xff, 0x4b, 0x3c, 0x32, 0xff, 0x38, 0x28, 0x24, 0xff, 0x4e, 0x3f, 0x3e, 0xff, 0x47, 0x38, 0x34, 0xff, 0x13, 0x03, 0x01, 0xff, 0x29, 0x15, 0x19, 0xff, 0x28, 0x14, 0x13, 0xff, 0x3a, 0x29, 0x21, 0xff, 0x36, 0x22, 0x22, 0xff, 0x1f, 0x0a, 0x0e, 0xff, 0x25, 0x0f, 0x13, 0xff, 0x24, 0x0e, 0x12, 0xff, 0x20, 0x0a, 0x0e, 0xff, 0x20, 0x0a, 0x0f, 0xff, 0x20, 0x0b, 0x0c, 0xff, 0x22, 0x0d, 0x10, 0xff, 0x28, 0x12, 0x17, 0xff, 0x29, 0x14, 0x17, 0xff, 0x2b, 0x16, 0x17, 0xff, 0x2e, 0x1a, 0x19, 0xff, 0x33, 0x1f, 0x1e, 0xff, 0x36, 0x23, 0x1f, 0xff, 0x2a, 0x17, 0x15, 0xff, 0x21, 0x0c, 0x0f, 0xff, 0x2e, 0x19, 0x17, 0xff, 0x44, 0x31, 0x25, 0xff, 0x4d, 0x3a, 0x28, 0xff, 0x52, 0x3c, 0x27, 0xff, 0x58, 0x40, 0x29, 0xff, 0x56, 0x3f, 0x27, 0xff, 0x74, 0x62, 0x4d, 0xff, 0xa6, 0x9a, 0x8b, 0xff, 0xdf, 0xda, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf9, 0xfb, 0xff, 0xe1, 0xe1, 0xec, 0xff, 0xb6, 0xb7, 0xcd, 0xff, 0x98, 0x99, 0xb7, 0xff, 0x97, 0x9a, 0xb3, 0xff, 0xaa, 0xb1, 0xc0, 0xff, 0xb6, 0xbf, 0xc7, 0xff, 0xb9, 0xc4, 0xca, 0xff, 0xb7, 0xc2, 0xca, 0xff, 0xb8, 0xc3, 0xcb, 0xff, 0xb5, 0xbf, 0xc8, 0xff, 0xad, 0xb5, 0xc0, 0xff, 0xa9, 0xae, 0xb9, 0xff, 0x8c, 0x8d, 0x9c, 0xff, 0x60, 0x5b, 0x71, 0xff, 0x40, 0x32, 0x44, 0xff, 0x30, 0x1e, 0x25, 0xff, 0x37, 0x24, 0x25, 0xff, 0x7a, 0x76, 0x6c, 0xff, 0xc4, 0xcd, 0xbc, 0xff, 0x60, 0x58, 0x56, 0xff, 0x30, 0x1f, 0x25, 0xff, 0x4c, 0x3f, 0x43, 0xff, 0x47, 0x37, 0x43, 0xff, 0x35, 0x25, 0x2d, 0xff, 0x23, 0x12, 0x13, 0xff, 0x22, 0x0e, 0x0f, 0xff, 0x3b, 0x28, 0x23, 0xff, 0x37, 0x26, 0x1f, 0xff, 0x22, 0x12, 0x0e, 0xff, 0x41, 0x30, 0x2c, 0xff, 0x2f, 0x1c, 0x1b, 0xff, 0x20, 0x0b, 0x0f, 0xff, 0x2e, 0x18, 0x1a, 0xff, 0x37, 0x23, 0x22, 0xff, 0x27, 0x13, 0x14, 0xff, 0x36, 0x25, 0x1c, 0xff, 0x24, 0x10, 0x10, 0xff, 0x29, 0x12, 0x17, 0xff, 0x29, 0x14, 0x15, 0xff, 0x29, 0x14, 0x16, 0xff, 0x2a, 0x14, 0x17, 0xff, 0x2a, 0x16, 0x17, 0xff, 0x2b, 0x17, 0x18, 0xff, 0x2d, 0x18, 0x1a, 0xff, 0x2d, 0x18, 0x1a, 0xff, 0x2f, 0x1a, 0x1a, 0xff, 0x31, 0x1d, 0x1a, 0xff, 0x2e, 0x19, 0x18, 0xff, 0x25, 0x10, 0x11, 0xff, 0x23, 0x0d, 0x0f, 0xff, 0x2f, 0x1b, 0x1a, 0xff, 0x3a, 0x26, 0x23, 0xff, 0x39, 0x25, 0x1f, 0xff, 0x39, 0x25, 0x1d, 0xff, 0x3d, 0x29, 0x20, 0xff, 0x67, 0x57, 0x50, 0xff, 0xa1, 0x96, 0x93, 0xff, 0xdd, 0xd8, 0xd7, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xf0, 0xea, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xfa, 0xfb, 0xff, 0xe5, 0xe4, 0xed, 0xff, 0xca, 0xcb, 0xda, 0xff, 0xb3, 0xb7, 0xc9, 0xff, 0xaa, 0xb2, 0xc1, 0xff, 0xb3, 0xbc, 0xcb, 0xff, 0xb7, 0xc0, 0xca, 0xff, 0xb7, 0xc0, 0xc8, 0xff, 0xb1, 0xb9, 0xc1, 0xff, 0xad, 0xb3, 0xba, 0xff, 0x9e, 0xa1, 0xad, 0xff, 0x80, 0x7d, 0x8f, 0xff, 0x61, 0x54, 0x65, 0xff, 0x41, 0x31, 0x3b, 0xff, 0x2b, 0x17, 0x1b, 0xff, 0x3e, 0x2d, 0x2e, 0xff, 0xa9, 0xa9, 0x9c, 0xff, 0xa9, 0xad, 0x9c, 0xff, 0x48, 0x3b, 0x40, 0xff, 0x47, 0x35, 0x42, 0xff, 0x4a, 0x3c, 0x44, 0xff, 0x4b, 0x3b, 0x48, 0xff, 0x2f, 0x1d, 0x23, 0xff, 0x2c, 0x18, 0x18, 0xff, 0x28, 0x14, 0x14, 0xff, 0x41, 0x2e, 0x29, 0xff, 0x37, 0x24, 0x20, 0xff, 0x1f, 0x0d, 0x0e, 0xff, 0x23, 0x10, 0x10, 0xff, 0x28, 0x12, 0x15, 0xff, 0x28, 0x11, 0x16, 0xff, 0x2a, 0x14, 0x16, 0xff, 0x30, 0x1b, 0x1c, 0xff, 0x22, 0x0c, 0x11, 0xff, 0x30, 0x1d, 0x18, 0xff, 0x2b, 0x16, 0x16, 0xff, 0x26, 0x10, 0x14, 0xff, 0x2a, 0x15, 0x16, 0xff, 0x2b, 0x16, 0x18, 0xff, 0x2b, 0x16, 0x18, 0xff, 0x2a, 0x14, 0x17, 0xff, 0x2b, 0x16, 0x17, 0xff, 0x2f, 0x1a, 0x1a, 0xff, 0x31, 0x1b, 0x1d, 0xff, 0x2d, 0x18, 0x1a, 0xff, 0x26, 0x13, 0x12, 0xff, 0x21, 0x0e, 0x0f, 0xff, 0x25, 0x12, 0x14, 0xff, 0x2e, 0x1b, 0x1d, 0xff, 0x30, 0x1c, 0x1b, 0xff, 0x2e, 0x1a, 0x19, 0xff, 0x3e, 0x2b, 0x2b, 0xff, 0x73, 0x65, 0x65, 0xff, 0xb1, 0xab, 0xab, 0xff, 0xe3, 0xe1, 0xe1, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe7, 0x74, 0xf6, 0xec, 0xe7, 0xce, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xf4, 0xf4, 0xf7, 0xff, 0xe0, 0xe0, 0xe8, 0xff, 0xc2, 0xc2, 0xd1, 0xff, 0xa3, 0xa3, 0xba, 0xff, 0x96, 0x96, 0xb0, 0xff, 0x91, 0x91, 0xa8, 0xff, 0x91, 0x93, 0xa5, 0xff, 0x96, 0x95, 0xa8, 0xff, 0x7a, 0x70, 0x82, 0xff, 0x5c, 0x4a, 0x58, 0xff, 0x3e, 0x2b, 0x31, 0xff, 0x26, 0x14, 0x16, 0xff, 0x61, 0x54, 0x52, 0xff, 0xaa, 0xa8, 0x9c, 0xff, 0x74, 0x72, 0x68, 0xff, 0x3e, 0x32, 0x41, 0xff, 0x5d, 0x50, 0x64, 0xff, 0x52, 0x47, 0x52, 0xff, 0x42, 0x33, 0x3d, 0xff, 0x34, 0x22, 0x23, 0xff, 0x38, 0x24, 0x21, 0xff, 0x2c, 0x17, 0x17, 0xff, 0x3b, 0x27, 0x21, 0xff, 0x2e, 0x19, 0x17, 0xff, 0x24, 0x10, 0x14, 0xff, 0x24, 0x11, 0x15, 0xff, 0x24, 0x0f, 0x14, 0xff, 0x28, 0x12, 0x16, 0xff, 0x27, 0x12, 0x15, 0xff, 0x23, 0x0e, 0x11, 0xff, 0x26, 0x11, 0x12, 0xff, 0x25, 0x0f, 0x15, 0xff, 0x2b, 0x15, 0x18, 0xff, 0x28, 0x14, 0x15, 0xff, 0x29, 0x13, 0x16, 0xff, 0x2a, 0x15, 0x16, 0xff, 0x2a, 0x15, 0x16, 0xff, 0x2a, 0x16, 0x17, 0xff, 0x31, 0x1c, 0x1d, 0xff, 0x2f, 0x1b, 0x1b, 0xff, 0x29, 0x14, 0x16, 0xff, 0x22, 0x0e, 0x12, 0xff, 0x1e, 0x0d, 0x11, 0xff, 0x23, 0x12, 0x13, 0xff, 0x28, 0x17, 0x16, 0xff, 0x36, 0x26, 0x25, 0xff, 0x5d, 0x50, 0x4f, 0xff, 0x98, 0x8f, 0x8f, 0xff, 0xd1, 0xcd, 0xce, 0xff, 0xf3, 0xf3, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfa, 0xf8, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xec, 0xe7, 0x95, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfa, 0xf7, 0xf5, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfc, 0xff, 0xec, 0xeb, 0xef, 0xff, 0xce, 0xcc, 0xd7, 0xff, 0xb1, 0xaf, 0xbf, 0xff, 0xa5, 0xa3, 0xb6, 0xff, 0x94, 0x8d, 0x9e, 0xff, 0x5f, 0x50, 0x5c, 0xff, 0x4c, 0x39, 0x3f, 0xff, 0x3e, 0x29, 0x2d, 0xff, 0x2e, 0x1b, 0x1e, 0xff, 0x70, 0x64, 0x61, 0xff, 0x81, 0x7a, 0x76, 0xff, 0x5f, 0x58, 0x5a, 0xff, 0x47, 0x39, 0x47, 0xff, 0x51, 0x42, 0x55, 0xff, 0x57, 0x4b, 0x52, 0xff, 0x32, 0x21, 0x26, 0xff, 0x3e, 0x2b, 0x29, 0xff, 0x42, 0x30, 0x29, 0xff, 0x29, 0x15, 0x13, 0xff, 0x3d, 0x29, 0x22, 0xff, 0x2f, 0x1a, 0x18, 0xff, 0x25, 0x0f, 0x13, 0xff, 0x25, 0x10, 0x13, 0xff, 0x26, 0x11, 0x15, 0xff, 0x25, 0x10, 0x13, 0xff, 0x2b, 0x16, 0x18, 0xff, 0x28, 0x13, 0x15, 0xff, 0x23, 0x0e, 0x0f, 0xff, 0x27, 0x11, 0x15, 0xff, 0x2a, 0x15, 0x18, 0xff, 0x2c, 0x15, 0x19, 0xff, 0x29, 0x12, 0x17, 0xff, 0x28, 0x14, 0x16, 0xff, 0x2d, 0x18, 0x17, 0xff, 0x32, 0x1d, 0x1c, 0xff, 0x2e, 0x19, 0x1b, 0xff, 0x22, 0x0e, 0x12, 0xff, 0x21, 0x0f, 0x11, 0xff, 0x27, 0x16, 0x16, 0xff, 0x42, 0x33, 0x34, 0xff, 0x67, 0x5a, 0x5c, 0xff, 0x9a, 0x91, 0x93, 0xff, 0xcd, 0xc8, 0xc9, 0xff, 0xf2, 0xf1, 0xf1, 0xff, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xf7, 0xf0, 0xea, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf5, 0xed, 0xe7, 0xd6, 0xf6, 0xed, 0xe7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf4, 0xf5, 0xff, 0xdf, 0xdc, 0xdf, 0xff, 0xbd, 0xb6, 0xb8, 0xff, 0x8e, 0x83, 0x84, 0xff, 0x67, 0x58, 0x5a, 0xff, 0x55, 0x45, 0x48, 0xff, 0x5f, 0x51, 0x54, 0xff, 0x74, 0x6a, 0x6a, 0xff, 0x5d, 0x52, 0x55, 0xff, 0x3d, 0x2e, 0x37, 0xff, 0x48, 0x39, 0x41, 0xff, 0x3d, 0x2d, 0x31, 0xff, 0x21, 0x0c, 0x11, 0xff, 0x50, 0x3d, 0x35, 0xff, 0x47, 0x34, 0x2c, 0xff, 0x2a, 0x15, 0x15, 0xff, 0x4a, 0x38, 0x2f, 0xff, 0x3c, 0x29, 0x20, 0xff, 0x2a, 0x16, 0x16, 0xff, 0x25, 0x10, 0x13, 0xff, 0x25, 0x10, 0x13, 0xff, 0x25, 0x10, 0x13, 0xff, 0x24, 0x0f, 0x11, 0xff, 0x2f, 0x1a, 0x1c, 0xff, 0x2a, 0x15, 0x17, 0xff, 0x24, 0x10, 0x11, 0xff, 0x2b, 0x16, 0x18, 0xff, 0x2c, 0x17, 0x18, 0xff, 0x2d, 0x18, 0x18, 0xff, 0x35, 0x21, 0x22, 0xff, 0x33, 0x20, 0x21, 0xff, 0x34, 0x21, 0x24, 0xff, 0x42, 0x30, 0x34, 0xff, 0x65, 0x5a, 0x59, 0xff, 0x8f, 0x85, 0x86, 0xff, 0xb8, 0xb1, 0xb2, 0xff, 0xdd, 0xdb, 0xdb, 0xff, 0xf5, 0xf5, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xe7, 0xf5, 0xee, 0xe8, 0x84, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf7, 0xf7, 0xff, 0xe6, 0xe4, 0xe4, 0xff, 0xc9, 0xc4, 0xc5, 0xff, 0xba, 0xb3, 0xb5, 0xff, 0x9b, 0x92, 0x95, 0xff, 0x73, 0x68, 0x6a, 0xff, 0x58, 0x4b, 0x4d, 0xff, 0x3e, 0x2c, 0x32, 0xff, 0x43, 0x30, 0x30, 0xff, 0x68, 0x56, 0x48, 0xff, 0x42, 0x2e, 0x28, 0xff, 0x28, 0x13, 0x14, 0xff, 0x4a, 0x37, 0x2a, 0xff, 0x44, 0x33, 0x26, 0xff, 0x31, 0x1d, 0x1b, 0xff, 0x27, 0x11, 0x15, 0xff, 0x24, 0x0f, 0x13, 0xff, 0x25, 0x10, 0x13, 0xff, 0x26, 0x11, 0x13, 0xff, 0x2e, 0x1a, 0x1b, 0xff, 0x42, 0x2e, 0x30, 0xff, 0x40, 0x2d, 0x2f, 0xff, 0x4b, 0x38, 0x3b, 0xff, 0x5b, 0x4c, 0x4c, 0xff, 0x70, 0x62, 0x61, 0xff, 0x8d, 0x82, 0x83, 0xff, 0xa8, 0xa0, 0xa1, 0xff, 0xc9, 0xc5, 0xc5, 0xff, 0xea, 0xe8, 0xe9, 0xff, 0xf9, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe7, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, 0xf4, 0xf4, 0xf5, 0xff, 0xed, 0xeb, 0xeb, 0xff, 0xd0, 0xcd, 0xcd, 0xff, 0xb1, 0xab, 0xac, 0xff, 0x9e, 0x95, 0x97, 0xff, 0x9d, 0x94, 0x8e, 0xff, 0x98, 0x8e, 0x84, 0xff, 0x6f, 0x60, 0x60, 0xff, 0x5e, 0x4d, 0x4e, 0xff, 0x71, 0x61, 0x56, 0xff, 0x67, 0x57, 0x4f, 0xff, 0x50, 0x3e, 0x40, 0xff, 0x49, 0x37, 0x3a, 0xff, 0x4f, 0x3e, 0x40, 0xff, 0x57, 0x46, 0x48, 0xff, 0x60, 0x50, 0x52, 0xff, 0x6b, 0x5d, 0x5e, 0xff, 0x81, 0x74, 0x75, 0xff, 0x96, 0x8b, 0x8c, 0xff, 0xa5, 0x9c, 0x9d, 0xff, 0xbb, 0xb4, 0xb6, 0xff, 0xd8, 0xd4, 0xd5, 0xff, 0xee, 0xec, 0xec, 0xff, 0xf6, 0xf6, 0xf6, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfa, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfb, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe6, 0xff, 0xf6, 0xed, 0xe7, 0xad, 0xf7, 0xed, 0xe8, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xfb, 0xf7, 0xf5, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf5, 0xec, 0xe7, 0x6b, 0xf7, 0xed, 0xe7, 0xb5, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xad, 0xf7, 0xed, 0xe8, 0x63, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xee, 0xe7, 0x4b, 0xf5, 0xec, 0xe7, 0x6b, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xee, 0xe8, 0xff, 0xf7, 0xef, 0xe9, 0xff, 0xf8, 0xf1, 0xec, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xfa, 0xf5, 0xf2, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xfc, 0xf9, 0xf8, 0xff, 0xfd, 0xfa, 0xf9, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfd, 0xfc, 0xfb, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xfd, 0xfb, 0xfa, 0xff, 0xfd, 0xfa, 0xf9, 0xff, 0xfc, 0xf9, 0xf7, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xfb, 0xf6, 0xf3, 0xff, 0xfa, 0xf5, 0xf1, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf8, 0xf0, 0xeb, 0xff, 0xf7, 0xee, 0xe9, 0xff, 0xf6, 0xee, 0xe8, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0xf7, 0xec, 0xe8, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xee, 0xe6, 0x5b, 0xf6, 0xec, 0xe6, 0xa5, 0xf6, 0xec, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf7, 0xed, 0xe8, 0xef, 0xf6, 0xec, 0xe6, 0xa5, 0xf7, 0xee, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xe9, 0x3a, 0xf7, 0xec, 0xe8, 0x42, 0xf6, 0xed, 0xe6, 0x53, 0xf6, 0xed, 0xe7, 0x74, 0xf6, 0xed, 0xe7, 0x8c, 0xf6, 0xed, 0xe7, 0xad, 0xf6, 0xed, 0xe7, 0xc6, 0xf5, 0xed, 0xe7, 0xd6, 0xf6, 0xed, 0xe8, 0xef, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xff, 0xf6, 0xed, 0xe7, 0xf7, 0xf6, 0xed, 0xe7, 0xe7, 0xf5, 0xed, 0xe7, 0xd6, 0xf6, 0xee, 0xe7, 0xbe, 0xf6, 0xec, 0xe6, 0xa5, 0xf5, 0xee, 0xe8, 0x84, 0xf7, 0xed, 0xe8, 0x63, 0xf5, 0xee, 0xe7, 0x4b, 0xf7, 0xec, 0xe8, 0x42, 0xf6, 0xed, 0xe9, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0x09, 0xf0, 0xf0, 0xe1, 0x11, 0xf0, 0xf0, 0xe1, 0x11, 0xf0, 0xf0, 0xe1, 0x11, 0xff, 0xe3, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_image_dsc_t img_demo_vector_avatar = { + .header.cf = LV_COLOR_FORMAT_ARGB8888, + .header.always_zero = 0, + .header.w = 160, + .header.h = 154, + .data = img_demo_vector_avatar_map, +}; diff --git a/demos/vector_graphic/lv_demo_vector_graphic.c b/demos/vector_graphic/lv_demo_vector_graphic.c new file mode 100644 index 000000000..c84d17c7d --- /dev/null +++ b/demos/vector_graphic/lv_demo_vector_graphic.c @@ -0,0 +1,245 @@ +/** + * @file lv_demo_vector_graphic.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_demo_vector_graphic.h" + +#if LV_USE_DEMO_VECTOR_GRAPHIC + +/********************* + * DEFINES + *********************/ +#define WIDTH 640 +#define HEIGHT 480 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void draw_pattern(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_fpoint_t pts[] = {{200, 200}, {300, 200}, {300, 300}, {200, 300}}; + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_line_to(path, &pts[1]); + lv_vector_path_quad_to(path, &pts[2], &pts[3]); + lv_vector_path_close(path); + + lv_draw_image_dsc_t img_dsc; + lv_draw_image_dsc_init(&img_dsc); + + LV_IMAGE_DECLARE(img_demo_vector_avatar); + img_dsc.header = img_demo_vector_avatar.header; + img_dsc.src = &img_demo_vector_avatar; + + lv_vector_dsc_set_fill_image(ctx, &img_dsc); + lv_vector_dsc_translate(ctx, 250, 250); + lv_vector_dsc_rotate(ctx, 25); + lv_vector_dsc_translate(ctx, -250, -250); + lv_vector_dsc_add_path(ctx, path); // draw a path +} + +static void draw_gradient(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_fpoint_t pts[] = {{400, 200}, {600, 200}, {400, 400}}; + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_quad_to(path, &pts[1], &pts[2]); + lv_vector_path_close(path); + + lv_grad_dsc_t grad; + grad.dir = LV_GRAD_DIR_HOR; + grad.stops_count = 2; + grad.stops[0].color = lv_color_hex(0xff0000); + grad.stops[0].opa = LV_OPA_COVER; + grad.stops[0].frac = 0; + grad.stops[1].color = lv_color_hex(0x00ff00); + grad.stops[1].opa = LV_OPA_COVER; + grad.stops[1].frac = 255; + // grad.stops[2].color = lv_color_hex(0x0000ff); + // grad.stops[2].opa = LV_OPA_COVER; + // grad.stops[2].frac = 255; + + lv_matrix_t mt; + lv_matrix_identity(&mt); + lv_matrix_rotate(&mt, 30); + lv_vector_dsc_set_fill_transform(ctx, &mt); + + lv_vector_dsc_set_fill_linear_gradient(ctx, &grad, LV_VECTOR_GRADIENT_SPREAD_PAD); + lv_vector_dsc_add_path(ctx, path); // draw a path +} + +static void draw_radial_gradient(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_fpoint_t pts[] = {{400, 50}, {500, 50}, {500, 200}, {400, 200}}; + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_line_to(path, &pts[1]); + lv_vector_path_line_to(path, &pts[2]); + lv_vector_path_line_to(path, &pts[3]); + lv_vector_path_close(path); + + lv_grad_dsc_t grad; + grad.dir = LV_GRAD_DIR_HOR; + grad.stops_count = 2; + grad.stops[0].color = lv_color_hex(0xff0000); + grad.stops[0].opa = LV_OPA_COVER; + grad.stops[0].frac = 0; + grad.stops[1].color = lv_color_hex(0x0000ff); + grad.stops[1].opa = LV_OPA_COVER; + grad.stops[1].frac = 255; + // grad.stops[2].color = lv_color_hex(0x0000ff); + // grad.stops[2].opa = LV_OPA_COVER; + // grad.stops[2].frac = 255; + + lv_vector_dsc_set_fill_radial_gradient(ctx, &grad, 50, 50, 20, LV_VECTOR_GRADIENT_SPREAD_REFLECT); + lv_vector_dsc_add_path(ctx, path); // draw a path +} + +static void draw_shapes(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_fpoint_t pts[] = {{50, 50}, {200, 200}, {50, 200}}; + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_line_to(path, &pts[1]); + lv_vector_path_line_to(path, &pts[2]); + lv_vector_path_close(path); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xFF, 0x00, 0x00)); + lv_vector_dsc_scale(ctx, 0.5, 0.5); + lv_vector_dsc_add_path(ctx, path); // draw a path + + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_area_t rect = {300, 300, 400, 400}; + lv_vector_path_append_rect(path, &rect, 50, 60); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0x00, 0x80, 0xff)); + lv_vector_dsc_skew(ctx, 5, 0); + lv_vector_dsc_add_path(ctx, path); // draw a path + + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_area_t rect2 = {100, 300, 200, 400}; + lv_vector_path_append_rect(path, &rect2, 10, 10); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0x80, 0x00, 0x80)); + + lv_vector_path_t * path2 = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); + lv_fpoint_t p = {50, 420}; + lv_vector_path_append_circle(path2, &p, 50, 30); + lv_vector_path_append_path(path, path2); + + lv_vector_dsc_add_path(ctx, path); // draw a path + + lv_vector_path_delete(path2); +} + +static void draw_lines(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_fpoint_t pts[] = {{50, 50}, {200, 200}, {250, 300}, {350, 150}}; + + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_cubic_to(path, &pts[1], &pts[2], &pts[3]); + + lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0x00, 0xff, 0x00)); + lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_COVER); + lv_vector_dsc_set_fill_opa(ctx, LV_OPA_0); + lv_vector_dsc_set_stroke_width(ctx, 8.0f); + + float dashes[] = {10, 15, 20, 12}; + lv_vector_dsc_set_stroke_dash(ctx, dashes, 4); + + lv_vector_dsc_add_path(ctx, path); // draw a path + + lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_0); + lv_vector_dsc_set_fill_opa(ctx, LV_OPA_COVER); +} + +static void draw_blend(lv_vector_dsc_t * ctx, lv_vector_path_t * path) +{ + lv_vector_path_clear(path); + lv_vector_dsc_identity(ctx); + + lv_fpoint_t pts[] = {{200, 200}, {400, 200}, {450, 350}, {350, 150}}; + + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_cubic_to(path, &pts[1], &pts[2], &pts[3]); + lv_vector_path_close(path); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xFF, 0x00, 0xFF)); + lv_vector_dsc_set_blend_mode(ctx, LV_VECTOR_BLEND_SCREEN); + + lv_vector_dsc_add_path(ctx, path); // draw a path +} + +static void draw_vector(lv_layer_t * layer) +{ + lv_vector_dsc_t * ctx = lv_vector_dsc_create(layer); + + lv_area_t rect = {0, 100, 300, 300}; + lv_vector_dsc_set_fill_color(ctx, lv_color_lighten(lv_color_black(), 50)); + lv_vector_clear_area(ctx, &rect); // clear screen + + lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); + + draw_shapes(ctx, path); + draw_lines(ctx, path); + draw_pattern(ctx, path); + draw_radial_gradient(ctx, path); + draw_gradient(ctx, path); + draw_blend(ctx, path); + + lv_draw_vector(ctx); // submit draw + lv_vector_path_delete(path); + lv_vector_dsc_delete(ctx); +} + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_demo_vector_graphic(void) +{ + static uint8_t canvas_buf[WIDTH * HEIGHT * 4]; + + lv_obj_t * canvas = lv_canvas_create(lv_scr_act()); + lv_canvas_set_buffer(canvas, canvas_buf, WIDTH, HEIGHT, LV_COLOR_FORMAT_ARGB8888); + + lv_layer_t layer; + lv_canvas_init_layer(canvas, &layer); + draw_vector(&layer); + + lv_canvas_finish_layer(canvas, &layer); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +#endif diff --git a/demos/vector_graphic/lv_demo_vector_graphic.h b/demos/vector_graphic/lv_demo_vector_graphic.h new file mode 100644 index 000000000..caf2acc4c --- /dev/null +++ b/demos/vector_graphic/lv_demo_vector_graphic.h @@ -0,0 +1,40 @@ + +/** + * @file lv_demo_vector_graphic.h + * + */ + +#ifndef LV_DEMO_VECTOR_GRAPHIC_H +#define LV_DEMO_VECTOR_GRAPHIC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_demos.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_demo_vector_graphic(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_DEMO_VECTOR_GRAPHIC_H*/ diff --git a/env_support/cmake/custom.cmake b/env_support/cmake/custom.cmake index 636c1803c..ab4c9611a 100644 --- a/env_support/cmake/custom.cmake +++ b/env_support/cmake/custom.cmake @@ -19,6 +19,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries" OFF) file(GLOB_RECURSE SOURCES ${LVGL_ROOT_DIR}/src/*.c) file(GLOB_RECURSE EXAMPLE_SOURCES ${LVGL_ROOT_DIR}/examples/*.c) file(GLOB_RECURSE DEMO_SOURCES ${LVGL_ROOT_DIR}/demos/*.c) +file(GLOB_RECURSE THORVG_SOURCES ${LVGL_ROOT_DIR}/src/libs/thorvg/*.cpp) # Build LVGL library add_library(lvgl ${SOURCES}) @@ -31,11 +32,19 @@ target_compile_definitions( # Add definition of LV_CONF_PATH only if needed if(LV_CONF_PATH) target_compile_definitions(lvgl PUBLIC LV_CONF_PATH=${LV_CONF_PATH}) + target_compile_definitions(lvgl_thorvg PUBLIC LV_CONF_PATH=${LV_CONF_PATH}) endif() # Include root and optional parent path of LV_CONF_PATH target_include_directories(lvgl SYSTEM PUBLIC ${LVGL_ROOT_DIR} ${LV_CONF_DIR}) + +if(NOT LV_CONF_BUILD_DISABLE_THORVG_INTERNAL) + add_library(lvgl_thorvg ${THORVG_SOURCES}) + add_library(lvgl::thorvg ALIAS lvgl_thorvg) + target_include_directories(lvgl_thorvg SYSTEM PUBLIC ${LVGL_ROOT_DIR}/src/libs/thorvg) +endif() + # Build LVGL example library if(NOT LV_CONF_BUILD_DISABLE_EXAMPLES) add_library(lvgl_examples ${EXAMPLE_SOURCES}) diff --git a/examples/test.py b/examples/test.py new file mode 100755 index 000000000..e69de29bb diff --git a/examples/widgets/canvas/index.rst b/examples/widgets/canvas/index.rst index b1184ca29..89e8a8713 100644 --- a/examples/widgets/canvas/index.rst +++ b/examples/widgets/canvas/index.rst @@ -45,3 +45,9 @@ Draw a line to the canvas .. lv_example:: widgets/canvas/lv_example_canvas_7 :language: c + +Draw a vector graphic to the canvas +------------------------- + +.. lv_example:: widgets/canvas/lv_example_canvas_8 + :language: c diff --git a/examples/widgets/canvas/lv_example_canvas_8.c b/examples/widgets/canvas/lv_example_canvas_8.c new file mode 100644 index 000000000..abc628491 --- /dev/null +++ b/examples/widgets/canvas/lv_example_canvas_8.c @@ -0,0 +1,42 @@ +#include "../../lv_examples.h" +#if LV_USE_CANVAS && LV_BUILD_EXAMPLES && LV_USE_VECTOR_GRAPHIC + +#define CANVAS_WIDTH 150 +#define CANVAS_HEIGHT 150 + +/** + * Draw a path to the canvas + */ +void lv_example_canvas_8(void) +{ + /*Create a buffer for the canvas*/ + static uint8_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)]; + + /*Create a canvas and initialize its palette*/ + lv_obj_t * canvas = lv_canvas_create(lv_screen_active()); + lv_canvas_set_buffer(canvas, cbuf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_NATIVE); + lv_canvas_fill_bg(canvas, lv_color_hex3(0xccc), LV_OPA_COVER); + lv_obj_center(canvas); + + lv_layer_t layer; + lv_canvas_init_layer(canvas, &layer); + + lv_vector_dsc_t * dsc = lv_vector_dsc_create(&layer); + lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); + + lv_fpoint_t pts[] = {{10, 10}, {130, 130}, {10, 130}}; + lv_vector_path_move_to(path, &pts[0]); + lv_vector_path_line_to(path, &pts[1]); + lv_vector_path_line_to(path, &pts[2]); + lv_vector_path_close(path); + + lv_vector_dsc_set_fill_color(dsc, lv_color_make(0x00, 0x80, 0xff)); + lv_vector_dsc_add_path(dsc, path); + + lv_draw_vector(dsc); + lv_vector_path_delete(path); + lv_vector_dsc_delete(dsc); + + lv_canvas_finish_layer(canvas, &layer); +} +#endif diff --git a/examples/widgets/lv_example_widgets.h b/examples/widgets/lv_example_widgets.h index 03d4cc57a..92d43fbce 100644 --- a/examples/widgets/lv_example_widgets.h +++ b/examples/widgets/lv_example_widgets.h @@ -55,6 +55,7 @@ void lv_example_canvas_4(void); void lv_example_canvas_5(void); void lv_example_canvas_6(void); void lv_example_canvas_7(void); +void lv_example_canvas_8(void); void lv_example_chart_1(void); void lv_example_chart_2(void); diff --git a/lv_conf_template.h b/lv_conf_template.h index 2c44f399e..c854bdb8d 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -653,6 +653,15 @@ /*Rlottie library*/ #define LV_USE_RLOTTIE 0 +/*Enable Vector Graphic APIs*/ +#define LV_USE_VECTOR_GRAPHIC 0 + +/* Enable ThorVG (vector graphics library) from the src/libs folder */ +#define LV_USE_THORVG_INTERNAL 0 + +/* Enable ThorVG by assuming that its installed and linked to the project */ +#define LV_USE_THORVG_EXTERNAL 0 + /*FFmpeg library for image decoding and playing videos *Supports all major image formats so do not enable other image decoder with it*/ #define LV_USE_FFMPEG 0 @@ -851,6 +860,9 @@ /*Demonstrate scroll settings*/ #define LV_USE_DEMO_SCROLL 0 + +/*Vector graphic demo*/ +#define LV_USE_DEMO_VECTOR_GRAPHIC 0 /*--END OF LV_CONF_H--*/ #endif /*LV_CONF_H*/ diff --git a/lvgl.h b/lvgl.h index cf8b53a98..482153143 100644 --- a/lvgl.h +++ b/lvgl.h @@ -30,6 +30,7 @@ extern "C" { #include "src/misc/lv_log.h" #include "src/misc/lv_timer.h" #include "src/misc/lv_math.h" +#include "src/misc/lv_array.h" #include "src/misc/lv_async.h" #include "src/misc/lv_anim_timeline.h" #include "src/misc/lv_profiler_builtin.h" @@ -105,6 +106,7 @@ extern "C" { #include "src/layouts/lv_layout.h" #include "src/draw/lv_draw.h" +#include "src/draw/lv_draw_vector.h" #include "src/themes/lv_theme.h" diff --git a/lvgl.mk b/lvgl.mk index af6e6c242..a48529f07 100644 --- a/lvgl.mk +++ b/lvgl.mk @@ -3,4 +3,6 @@ LVGL_PATH ?= ${shell pwd}/lvgl CSRCS += $(shell find $(LVGL_PATH)/src -type f -name '*.c') CSRCS += $(shell find $(LVGL_PATH)/demos -type f -name '*.c') CSRCS += $(shell find $(LVGL_PATH)/examples -type f -name '*.c') +CXXSRCS += $(shell find $(LVGL_PATH)/src/libs/thorvg -type f -name '*.cpp') CFLAGS += "-I$(LVGL_PATH)" + diff --git a/scripts/code-format.cfg b/scripts/code-format.cfg index dd4052a6e..184ef9805 100644 --- a/scripts/code-format.cfg +++ b/scripts/code-format.cfg @@ -30,15 +30,16 @@ --exclude=../src/lv_conf_internal.h --exclude=../src/core/lv_obj_style_gen.c --exclude=../src/core/lv_obj_style_gen.h ---exclude=../src/extra/libs/gif/gifdec.c ---exclude=../src/extra/libs/gif/gifdec.h ---exclude=../src/extra/libs/lodepng/lodepng.c ---exclude=../src/extra/libs/lodepng/lodepng.h ---exclude=../src/extra/libs/qrcode/qrcodegen.c ---exclude=../src/extra/libs/qrcode/qrcodegen.h ---exclude=../src/extra/libs/tjpgd/tjpgd.c ---exclude=../src/extra/libs/tjpgd/tjpgd.h ---exclude=../src/extra/libs/tjpgd/tjpgdcnf.h +--exclude=../src/libs/gif/gifdec.c +--exclude=../src/libs/gif/gifdec.h +--exclude=../src/libs/lodepng/lodepng.c +--exclude=../src/libs/lodepng/lodepng.h +--exclude=../src/libs/qrcode/qrcodegen.c +--exclude=../src/libs/qrcode/qrcodegen.h +--exclude=../src/libs/tjpgd/tjpgd.c +--exclude=../src/libs/tjpgd/tjpgd.h +--exclude=../src/libs/tjpgd/tjpgdcnf.h +--exclude=../src/libs/thorvg --exclude=../tests/unity/unity.c --exclude=../tests/unity/unity_internals.h --exclude=../tests/unity/unity_support.c diff --git a/src/draw/lv_draw.h b/src/draw/lv_draw.h index 2d81af0ac..7ea0ac5fa 100644 --- a/src/draw/lv_draw.h +++ b/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 { diff --git a/src/draw/lv_draw_vector.c b/src/draw/lv_draw_vector.c new file mode 100644 index 000000000..c0ba95ee3 --- /dev/null +++ b/src/draw/lv_draw_vector.c @@ -0,0 +1,752 @@ +/** +* @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/src/draw/lv_draw_vector.h b/src/draw/lv_draw_vector.h new file mode 100644 index 000000000..ea6d63ca3 --- /dev/null +++ b/src/draw/lv_draw_vector.h @@ -0,0 +1,541 @@ +/** + * @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/src/draw/sw/lv_draw_sw.c b/src/draw/sw/lv_draw_sw.c index d8ff61b94..d07d4ecdb 100644 --- a/src/draw/sw/lv_draw_sw.c +++ b/src/draw/sw/lv_draw_sw.c @@ -15,6 +15,13 @@ #include "../../stdlib/lv_string.h" #include "../../core/lv_global.h" +#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL) + #if LV_USE_THORVG_EXTERNAL + #include + #else + #include "../../libs/thorvg/thorvg_capi.h" + #endif +#endif /********************* * DEFINES *********************/ @@ -67,10 +74,18 @@ void lv_draw_sw_init(void) lv_thread_init(&draw_sw_unit->thread, LV_THREAD_PRIO_HIGH, render_thread_cb, 8 * 1024, draw_sw_unit); #endif } + +#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL) + tvg_engine_init(TVG_ENGINE_SW, 0); +#endif } void lv_draw_sw_deinit(void) { +#if LV_USE_VECTOR_GRAPHIC && (LV_USE_THORVG_EXTERNAL || LV_USE_THORVG_INTERNAL) + tvg_engine_term(TVG_ENGINE_SW); +#endif + #if LV_DRAW_SW_COMPLEX == 1 lv_draw_sw_mask_deinit(); #endif @@ -189,6 +204,11 @@ static void execute_drawing(lv_draw_sw_unit_t * u) case LV_DRAW_TASK_TYPE_MASK_RECTANGLE: lv_draw_sw_mask_rect((lv_draw_unit_t *)u, t->draw_dsc, &t->area); break; +#if LV_USE_VECTOR_GRAPHIC + case LV_DRAW_TASK_TYPE_VECTOR: + lv_draw_sw_vector((lv_draw_unit_t *)u, t->draw_dsc); + break; +#endif default: break; } diff --git a/src/draw/sw/lv_draw_sw.h b/src/draw/sw/lv_draw_sw.h index ce09a653a..19b507afe 100644 --- a/src/draw/sw/lv_draw_sw.h +++ b/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 *********************/ @@ -82,6 +83,9 @@ void lv_draw_sw_transform(lv_draw_unit_t * draw_unit, const lv_area_t * dest_are int32_t src_w, int32_t src_h, int32_t src_stride, const lv_draw_image_dsc_t * draw_dsc, const lv_draw_image_sup_t * sup, lv_color_format_t cf, void * dest_buf); +#if LV_USE_VECTOR_GRAPHIC +void lv_draw_sw_vector(lv_draw_unit_t * draw_unit, const lv_draw_vector_task_dsc_t * dsc); +#endif /** * Swap the upper and lower byte of an RGB565 buffer. diff --git a/src/draw/sw/lv_draw_sw_vector.c b/src/draw/sw/lv_draw_sw_vector.c new file mode 100644 index 000000000..bf024fccc --- /dev/null +++ b/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/src/libs/thorvg/add_lvgl_if.sh b/src/libs/thorvg/add_lvgl_if.sh new file mode 100755 index 000000000..43ce90306 --- /dev/null +++ b/src/libs/thorvg/add_lvgl_if.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +#Add LVGL #if LV_USE_THORVG_INTERNAL guard +#Usage +# find -name "*.cpp" | xargs ./add_lvgl_if.sh +# find -name "t*.h" | xargs ./add_lvgl_if.sh + +sed '0,/\*\/$/ {/\*\/$/ {n; s|^|\n#include "../../lv_conf_internal.h"\n#if LV_USE_THORVG_INTERNAL\n|}}' $@ -i + +sed -i -e '$a\ +\ +#endif /* LV_USE_THORVG_INTERNAL */\ +' $@ -i diff --git a/src/libs/thorvg/config.h b/src/libs/thorvg/config.h new file mode 100644 index 000000000..abeed9415 --- /dev/null +++ b/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/src/libs/thorvg/thorvg.h b/src/libs/thorvg/thorvg.h new file mode 100644 index 000000000..7f5a84b94 --- /dev/null +++ b/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/src/libs/thorvg/thorvg_capi.h b/src/libs/thorvg/thorvg_capi.h new file mode 100644 index 000000000..1f31cb71d --- /dev/null +++ b/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/src/libs/thorvg/tvgAnimation.cpp b/src/libs/thorvg/tvgAnimation.cpp new file mode 100644 index 000000000..ae5e8f94e --- /dev/null +++ b/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/src/libs/thorvg/tvgArray.h b/src/libs/thorvg/tvgArray.h new file mode 100644 index 000000000..41accc13b --- /dev/null +++ b/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/src/libs/thorvg/tvgBezier.cpp b/src/libs/thorvg/tvgBezier.cpp new file mode 100644 index 000000000..e00fc1855 --- /dev/null +++ b/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/src/libs/thorvg/tvgBezier.h b/src/libs/thorvg/tvgBezier.h new file mode 100644 index 000000000..24f70f336 --- /dev/null +++ b/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/src/libs/thorvg/tvgCanvas.cpp b/src/libs/thorvg/tvgCanvas.cpp new file mode 100644 index 000000000..4da0e5e12 --- /dev/null +++ b/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/src/libs/thorvg/tvgCanvas.h b/src/libs/thorvg/tvgCanvas.h new file mode 100644 index 000000000..852f7db43 --- /dev/null +++ b/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/src/libs/thorvg/tvgCapi.cpp b/src/libs/thorvg/tvgCapi.cpp new file mode 100644 index 000000000..e8affb53d --- /dev/null +++ b/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/src/libs/thorvg/tvgCommon.h b/src/libs/thorvg/tvgCommon.h new file mode 100644 index 000000000..d70681170 --- /dev/null +++ b/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/src/libs/thorvg/tvgCompressor.cpp b/src/libs/thorvg/tvgCompressor.cpp new file mode 100644 index 000000000..20941712f --- /dev/null +++ b/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/src/libs/thorvg/tvgCompressor.h b/src/libs/thorvg/tvgCompressor.h new file mode 100644 index 000000000..6d9dfce1b --- /dev/null +++ b/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/src/libs/thorvg/tvgFill.cpp b/src/libs/thorvg/tvgFill.cpp new file mode 100644 index 000000000..152883445 --- /dev/null +++ b/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/src/libs/thorvg/tvgFill.h b/src/libs/thorvg/tvgFill.h new file mode 100644 index 000000000..159540398 --- /dev/null +++ b/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/src/libs/thorvg/tvgFrameModule.h b/src/libs/thorvg/tvgFrameModule.h new file mode 100644 index 000000000..726b84c84 --- /dev/null +++ b/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/src/libs/thorvg/tvgInitializer.cpp b/src/libs/thorvg/tvgInitializer.cpp new file mode 100644 index 000000000..779d97bcd --- /dev/null +++ b/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/src/libs/thorvg/tvgIteratorAccessor.h b/src/libs/thorvg/tvgIteratorAccessor.h new file mode 100644 index 000000000..ac7015fe4 --- /dev/null +++ b/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/src/libs/thorvg/tvgLoadModule.h b/src/libs/thorvg/tvgLoadModule.h new file mode 100644 index 000000000..7ccf94850 --- /dev/null +++ b/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/src/libs/thorvg/tvgLoader.cpp b/src/libs/thorvg/tvgLoader.cpp new file mode 100644 index 000000000..3a2cb32a4 --- /dev/null +++ b/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/src/libs/thorvg/tvgLoader.h b/src/libs/thorvg/tvgLoader.h new file mode 100644 index 000000000..7acdc943a --- /dev/null +++ b/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/src/libs/thorvg/tvgMath.cpp b/src/libs/thorvg/tvgMath.cpp new file mode 100644 index 000000000..cf43490df --- /dev/null +++ b/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/src/libs/thorvg/tvgMath.h b/src/libs/thorvg/tvgMath.h new file mode 100644 index 000000000..3f48e3cac --- /dev/null +++ b/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/src/libs/thorvg/tvgPaint.cpp b/src/libs/thorvg/tvgPaint.cpp new file mode 100644 index 000000000..50ba3e6cc --- /dev/null +++ b/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/src/libs/thorvg/tvgPaint.h b/src/libs/thorvg/tvgPaint.h new file mode 100644 index 000000000..ea5878d85 --- /dev/null +++ b/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/src/libs/thorvg/tvgPicture.cpp b/src/libs/thorvg/tvgPicture.cpp new file mode 100644 index 000000000..e58d68ea3 --- /dev/null +++ b/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/src/libs/thorvg/tvgPicture.h b/src/libs/thorvg/tvgPicture.h new file mode 100644 index 000000000..a9a52a98f --- /dev/null +++ b/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/src/libs/thorvg/tvgRawLoader.cpp b/src/libs/thorvg/tvgRawLoader.cpp new file mode 100644 index 000000000..b20c7fda8 --- /dev/null +++ b/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/src/libs/thorvg/tvgRawLoader.h b/src/libs/thorvg/tvgRawLoader.h new file mode 100644 index 000000000..bc3851f55 --- /dev/null +++ b/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/src/libs/thorvg/tvgRender.cpp b/src/libs/thorvg/tvgRender.cpp new file mode 100644 index 000000000..9994c8cbb --- /dev/null +++ b/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/src/libs/thorvg/tvgRender.h b/src/libs/thorvg/tvgRender.h new file mode 100644 index 000000000..d95c37cc0 --- /dev/null +++ b/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/src/libs/thorvg/tvgSaveModule.h b/src/libs/thorvg/tvgSaveModule.h new file mode 100644 index 000000000..cff1a81db --- /dev/null +++ b/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/src/libs/thorvg/tvgSaver.cpp b/src/libs/thorvg/tvgSaver.cpp new file mode 100644 index 000000000..4fd9848e3 --- /dev/null +++ b/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/src/libs/thorvg/tvgScene.cpp b/src/libs/thorvg/tvgScene.cpp new file mode 100644 index 000000000..122cd5354 --- /dev/null +++ b/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/src/libs/thorvg/tvgScene.h b/src/libs/thorvg/tvgScene.h new file mode 100644 index 000000000..f626e993e --- /dev/null +++ b/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/src/libs/thorvg/tvgShape.cpp b/src/libs/thorvg/tvgShape.cpp new file mode 100644 index 000000000..e43c07f2f --- /dev/null +++ b/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/src/libs/thorvg/tvgShape.h b/src/libs/thorvg/tvgShape.h new file mode 100644 index 000000000..161932d11 --- /dev/null +++ b/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/src/libs/thorvg/tvgStr.cpp b/src/libs/thorvg/tvgStr.cpp new file mode 100644 index 000000000..25630c7e9 --- /dev/null +++ b/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/src/libs/thorvg/tvgStr.h b/src/libs/thorvg/tvgStr.h new file mode 100644 index 000000000..eb664e2f5 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgCssStyle.cpp b/src/libs/thorvg/tvgSvgCssStyle.cpp new file mode 100644 index 000000000..89e45d401 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgCssStyle.h b/src/libs/thorvg/tvgSvgCssStyle.h new file mode 100644 index 000000000..d718ede64 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgLoader.cpp b/src/libs/thorvg/tvgSvgLoader.cpp new file mode 100644 index 000000000..efca81245 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgLoader.h b/src/libs/thorvg/tvgSvgLoader.h new file mode 100644 index 000000000..38ead0ebc --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgLoaderCommon.h b/src/libs/thorvg/tvgSvgLoaderCommon.h new file mode 100644 index 000000000..6df7237d7 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgPath.cpp b/src/libs/thorvg/tvgSvgPath.cpp new file mode 100644 index 000000000..46ec157f3 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgPath.h b/src/libs/thorvg/tvgSvgPath.h new file mode 100644 index 000000000..5ac02cd6a --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgSceneBuilder.cpp b/src/libs/thorvg/tvgSvgSceneBuilder.cpp new file mode 100644 index 000000000..ace3d32e5 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgSceneBuilder.h b/src/libs/thorvg/tvgSvgSceneBuilder.h new file mode 100644 index 000000000..5a401e713 --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgUtil.cpp b/src/libs/thorvg/tvgSvgUtil.cpp new file mode 100644 index 000000000..3c5da6cac --- /dev/null +++ b/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/src/libs/thorvg/tvgSvgUtil.h b/src/libs/thorvg/tvgSvgUtil.h new file mode 100644 index 000000000..c32298387 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwCanvas.cpp b/src/libs/thorvg/tvgSwCanvas.cpp new file mode 100644 index 000000000..b53fe26ed --- /dev/null +++ b/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/src/libs/thorvg/tvgSwCommon.h b/src/libs/thorvg/tvgSwCommon.h new file mode 100644 index 000000000..591b1c96f --- /dev/null +++ b/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/src/libs/thorvg/tvgSwFill.cpp b/src/libs/thorvg/tvgSwFill.cpp new file mode 100644 index 000000000..c4a697d35 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwImage.cpp b/src/libs/thorvg/tvgSwImage.cpp new file mode 100644 index 000000000..040c38930 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwMath.cpp b/src/libs/thorvg/tvgSwMath.cpp new file mode 100644 index 000000000..b636c04cf --- /dev/null +++ b/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/src/libs/thorvg/tvgSwMemPool.cpp b/src/libs/thorvg/tvgSwMemPool.cpp new file mode 100644 index 000000000..efe780af6 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRaster.cpp b/src/libs/thorvg/tvgSwRaster.cpp new file mode 100644 index 000000000..dc8c76eb1 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRasterAvx.h b/src/libs/thorvg/tvgSwRasterAvx.h new file mode 100644 index 000000000..633abf1db --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRasterC.h b/src/libs/thorvg/tvgSwRasterC.h new file mode 100644 index 000000000..c7f2840f8 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRasterNeon.h b/src/libs/thorvg/tvgSwRasterNeon.h new file mode 100644 index 000000000..97e4706a3 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRasterTexmap.h b/src/libs/thorvg/tvgSwRasterTexmap.h new file mode 100644 index 000000000..e6e87782d --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRenderer.cpp b/src/libs/thorvg/tvgSwRenderer.cpp new file mode 100644 index 000000000..d8e00dc86 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRenderer.h b/src/libs/thorvg/tvgSwRenderer.h new file mode 100644 index 000000000..8df5a2143 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwRle.cpp b/src/libs/thorvg/tvgSwRle.cpp new file mode 100644 index 000000000..e541e943b --- /dev/null +++ b/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/src/libs/thorvg/tvgSwShape.cpp b/src/libs/thorvg/tvgSwShape.cpp new file mode 100644 index 000000000..6a9ea5330 --- /dev/null +++ b/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/src/libs/thorvg/tvgSwStroke.cpp b/src/libs/thorvg/tvgSwStroke.cpp new file mode 100644 index 000000000..c097c6063 --- /dev/null +++ b/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/src/libs/thorvg/tvgTaskScheduler.cpp b/src/libs/thorvg/tvgTaskScheduler.cpp new file mode 100644 index 000000000..970369029 --- /dev/null +++ b/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/src/libs/thorvg/tvgTaskScheduler.h b/src/libs/thorvg/tvgTaskScheduler.h new file mode 100644 index 000000000..f45ab2915 --- /dev/null +++ b/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/src/libs/thorvg/tvgXmlParser.cpp b/src/libs/thorvg/tvgXmlParser.cpp new file mode 100644 index 000000000..6bd35ba81 --- /dev/null +++ b/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/src/libs/thorvg/tvgXmlParser.h b/src/libs/thorvg/tvgXmlParser.h new file mode 100644 index 000000000..e1cf2de2e --- /dev/null +++ b/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 //!< \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/src/misc/lv_array.h b/src/misc/lv_array.h new file mode 100644 index 000000000..1e8c7ddb2 --- /dev/null +++ b/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/tests/CMakeLists.txt b/tests/CMakeLists.txt index cedcc9980..2756b5afe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -80,6 +80,12 @@ else() message(FATAL_ERROR "Must provide a known options value (check main.py?).") endif() + +if ($ENV{NON_AMD64_BUILD}) + set(BUILD_TARGET_DEF -DNON_AMD64_BUILD) + message("Non AMD64 target is specified") +endif() + # Options lvgl and examples are compiled with. set(COMPILE_OPTIONS -DLV_CONF_PATH=${LVGL_TEST_DIR}/src/lv_test_conf.h @@ -110,6 +116,7 @@ set(COMPILE_OPTIONS -Werror=float-conversion -Werror=strict-aliasing ${BUILD_OPTIONS} + ${BUILD_TARGET_DEF} ) # Options test cases are compiled with. @@ -118,11 +125,42 @@ set(LVGL_TESTFILE_COMPILE_OPTIONS -Wno-missing-prototypes ) +set(LVGL_THORVG_COMPILE_OPTIONS + -DLV_CONF_PATH=${LVGL_TEST_DIR}/src/lv_test_conf.h + -DLV_BUILD_TEST + -pedantic-errors + -Wall + -Wclobbered + -Wdeprecated + -Wdouble-promotion + -Wempty-body + -Werror + -Wextra + -Wformat-security + -Wmaybe-uninitialized + -Wpointer-arith + -Wmultichar + -Wno-pedantic + -Wno-unused-parameter + -Wreturn-type + -Wshift-negative-value + -Wsizeof-pointer-memaccess + -Wtype-limits + -Wundef + -Wuninitialized + -Wunreachable-code + -Werror=float-conversion + -Werror=strict-aliasing + ${BUILD_OPTIONS} + ${BUILD_TARGET_DEF} +) + get_filename_component(LVGL_DIR ${LVGL_TEST_DIR} DIRECTORY) # Include lvgl project file. include(${LVGL_DIR}/CMakeLists.txt) target_compile_options(lvgl PUBLIC ${COMPILE_OPTIONS}) +target_compile_options(lvgl_thorvg PUBLIC ${LVGL_THORVG_COMPILE_OPTIONS}) if (TARGET lvgl_examples) target_compile_options(lvgl_examples PUBLIC ${COMPILE_OPTIONS}) endif() @@ -209,7 +247,7 @@ foreach( test_case_fname ${TEST_CASE_FILES} ) ${test_case_fname} ${test_runner_fname} ) - target_link_libraries(${test_name} test_common lvgl_demos lvgl png ${JPEG_LIBRARIES} m ${TEST_LIBS}) + target_link_libraries(${test_name} PRIVATE test_common lvgl_demos lvgl lvgl_thorvg png ${JPEG_LIBRARIES} m ${TEST_LIBS}) target_include_directories(${test_name} PUBLIC ${TEST_INCLUDE_DIRS}) target_compile_options(${test_name} PUBLIC ${LVGL_TESTFILE_COMPILE_OPTIONS}) diff --git a/tests/makefile/test_file b/tests/makefile/test_file new file mode 100755 index 000000000..e69de29bb diff --git a/tests/ref_imgs/draw/vector_draw_lines.png b/tests/ref_imgs/draw/vector_draw_lines.png new file mode 100644 index 000000000..c0868d786 Binary files /dev/null and b/tests/ref_imgs/draw/vector_draw_lines.png differ diff --git a/tests/ref_imgs/draw/vector_draw_shapes.png b/tests/ref_imgs/draw/vector_draw_shapes.png new file mode 100644 index 000000000..a42c3c7a3 Binary files /dev/null and b/tests/ref_imgs/draw/vector_draw_shapes.png differ diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index aea975fbd..c100b60f2 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -76,6 +76,8 @@ #define LV_USE_TINY_TTF 1 #define LV_USE_SYSMON 1 #define LV_USE_SNAPSHOT 1 +#define LV_USE_THORVG_INTERNAL 1 +#define LV_USE_VECTOR_GRAPHIC 1 #define LV_BUILD_EXAMPLES 1 #define LV_USE_DEMO_WIDGETS 1 @@ -85,6 +87,7 @@ #define LV_USE_DEMO_RENDER 1 #define LV_USE_DEMO_BENCHMARK 1 #define LV_USE_DEMO_SCROLL 1 +#define LV_USE_DEMO_VECTOR_GRAPHIC 1 #define LV_USE_OBJ_ID 1 #define LV_USE_OBJ_ID_BUILTIN 1 diff --git a/tests/src/test_cases/draw/test_draw_vector.c b/tests/src/test_cases/draw/test_draw_vector.c new file mode 100644 index 000000000..d19818cf7 --- /dev/null +++ b/tests/src/test_cases/draw/test_draw_vector.c @@ -0,0 +1,239 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" +#include "lv_test_helpers.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ +} + +static void draw_shapes(lv_layer_t * layer) +{ + lv_vector_dsc_t * ctx = lv_vector_dsc_create(layer); + + lv_area_t rect = {0, 0, 640, 480}; + lv_vector_dsc_set_fill_color(ctx, lv_color_white()); + lv_vector_clear_area(ctx, &rect); + + lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); + + lv_area_t rect1 = {50, 50, 150, 150}; + lv_vector_dsc_identity(ctx); + lv_vector_path_clear(path); + lv_vector_path_append_rect(path, &rect1, 0, 0); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xff, 0x00, 0x00)); + lv_vector_dsc_add_path(ctx, path); + + + lv_vector_path_clear(path); + lv_vector_path_append_rect(path, &rect1, 20, 20); + lv_vector_dsc_translate(ctx, 150, 0); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0x00, 0x00, 0xff)); + lv_vector_dsc_add_path(ctx, path); + + + lv_fpoint_t pc = {100, 100}; + lv_vector_path_clear(path); + lv_vector_path_append_circle(path, &pc, 50, 50); + lv_vector_dsc_translate(ctx, 150, 0); + lv_vector_dsc_set_fill_color32(ctx, lv_color_to_32(lv_color_make(0x00, 0xff, 0x00), 0x80)); + lv_vector_dsc_add_path(ctx, path); + + + lv_vector_dsc_identity(ctx); + lv_vector_dsc_translate(ctx, 0, 150); + + lv_grad_dsc_t grad; + grad.dir = LV_GRAD_DIR_HOR; + grad.stops_count = 2; + grad.stops[0].color = lv_color_hex(0xffffff); + grad.stops[0].opa = LV_OPA_COVER; + grad.stops[0].frac = 0; + grad.stops[1].color = lv_color_hex(0x000000); + grad.stops[1].opa = LV_OPA_COVER; + grad.stops[1].frac = 255; + + lv_matrix_t mt; + lv_matrix_identity(&mt); + lv_vector_dsc_set_fill_transform(ctx, &mt); + lv_vector_dsc_set_fill_radial_gradient(ctx, &grad, 50, 50, 50, LV_VECTOR_GRADIENT_SPREAD_PAD); + lv_vector_dsc_add_path(ctx, path); + + + lv_vector_dsc_identity(ctx); + lv_vector_dsc_translate(ctx, 150, 150); + lv_draw_image_dsc_t img_dsc; + lv_draw_image_dsc_init(&img_dsc); + LV_IMAGE_DECLARE(test_image_cogwheel_argb8888); + img_dsc.header = test_image_cogwheel_argb8888.header; + img_dsc.src = &test_image_cogwheel_argb8888; + lv_vector_dsc_set_fill_image(ctx, &img_dsc); + lv_matrix_identity(&mt); + lv_matrix_translate(&mt, 50, 50); + //lv_matrix_rotate(&mt, 45); + //lv_matrix_scale(&mt, 1.5f, 1.5f); + lv_matrix_translate(&mt, -50, -50); + lv_vector_dsc_set_fill_transform(ctx, &mt); + lv_vector_dsc_add_path(ctx, path); + + + lv_vector_dsc_identity(ctx); + lv_vector_dsc_translate(ctx, 300, 150); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0xff, 0x00, 0x00)); + lv_vector_dsc_add_path(ctx, path); + + lv_vector_path_clear(path); + lv_vector_path_append_rect(path, &rect1, 20, 20); + lv_area_t rect2 = {80, 80, 120, 120}; + lv_vector_path_append_rect(path, &rect2, 15, 15); + lv_vector_dsc_translate(ctx, 50, 0); + lv_vector_dsc_set_fill_color(ctx, lv_color_make(0x00, 0x80, 0xff)); + lv_vector_dsc_set_blend_mode(ctx, LV_VECTOR_BLEND_MULTIPLY); + lv_vector_dsc_set_fill_rule(ctx, LV_VECTOR_FILL_EVENODD); + lv_vector_dsc_add_path(ctx, path); + + lv_draw_vector(ctx); + lv_vector_path_delete(path); + lv_vector_dsc_delete(ctx); +} + +static void draw_lines(lv_layer_t * layer) +{ + lv_vector_dsc_t * ctx = lv_vector_dsc_create(layer); + + lv_area_t rect = {0, 0, 640, 480}; + lv_vector_dsc_set_fill_color(ctx, lv_color_white()); + lv_vector_clear_area(ctx, &rect); + + lv_vector_path_t * path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM); + + lv_vector_dsc_set_stroke_opa(ctx, LV_OPA_COVER); + lv_vector_dsc_set_fill_opa(ctx, LV_OPA_0); + + lv_fpoint_t pts1[] = {{50, 50}, {400, 50}}; + lv_vector_path_clear(path); + lv_vector_path_move_to(path, &pts1[0]); + lv_vector_path_line_to(path, &pts1[1]); + lv_vector_dsc_add_path(ctx, path); + + lv_fpoint_t pts2[] = {{50, 80}, {400, 80}}; + lv_vector_path_clear(path); + lv_vector_path_move_to(path, &pts2[0]); + lv_vector_path_line_to(path, &pts2[1]); + lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0xff, 0x00, 0x00)); + lv_vector_dsc_set_stroke_width(ctx, 3.0f); + lv_vector_dsc_add_path(ctx, path); + + lv_fpoint_t pts3[] = {{50, 120}, {400, 120}}; + lv_vector_path_clear(path); + lv_vector_path_move_to(path, &pts3[0]); + lv_vector_path_line_to(path, &pts3[1]); + lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0x00, 0xff, 0x00)); + lv_vector_dsc_set_stroke_width(ctx, 5.0f); + float dashes[] = {10, 15, 20, 12}; + lv_vector_dsc_set_stroke_dash(ctx, dashes, 4); + lv_vector_dsc_add_path(ctx, path); + + lv_fpoint_t pts4[] = {{100, 150}, {50, 250}, {150, 250}}; + lv_vector_path_clear(path); + lv_vector_path_move_to(path, &pts4[0]); + lv_vector_path_line_to(path, &pts4[1]); + lv_vector_path_line_to(path, &pts4[2]); + lv_vector_dsc_set_stroke_color(ctx, lv_color_make(0x00, 0x00, 0x00)); + lv_vector_dsc_set_stroke_width(ctx, 10.0f); + lv_vector_dsc_set_stroke_dash(ctx, NULL, 0); + lv_vector_dsc_set_stroke_join(ctx, LV_VECTOR_STROKE_JOIN_MITER); + lv_vector_dsc_add_path(ctx, path); + + lv_vector_dsc_translate(ctx, 150, 0); + lv_vector_dsc_set_stroke_join(ctx, LV_VECTOR_STROKE_JOIN_BEVEL); + lv_vector_dsc_add_path(ctx, path); + + lv_vector_dsc_translate(ctx, 150, 0); + lv_vector_dsc_set_stroke_join(ctx, LV_VECTOR_STROKE_JOIN_ROUND); + lv_vector_dsc_add_path(ctx, path); + + lv_fpoint_t pts5[] = {{50, 300}, {150, 300}}; + lv_vector_dsc_identity(ctx); + lv_vector_path_clear(path); + lv_vector_path_move_to(path, &pts5[0]); + lv_vector_path_line_to(path, &pts5[1]); + lv_vector_dsc_set_stroke_cap(ctx, LV_VECTOR_STROKE_CAP_BUTT); + lv_vector_dsc_add_path(ctx, path); + + lv_vector_dsc_translate(ctx, 0, 40); + lv_vector_dsc_set_stroke_cap(ctx, LV_VECTOR_STROKE_CAP_SQUARE); + lv_vector_dsc_add_path(ctx, path); + + lv_vector_dsc_translate(ctx, 0, 40); + lv_vector_dsc_set_stroke_cap(ctx, LV_VECTOR_STROKE_CAP_ROUND); + lv_vector_dsc_add_path(ctx, path); + + + lv_area_t rect1 = {250, 300, 350, 400}; + lv_vector_dsc_identity(ctx); + lv_vector_path_clear(path); + lv_vector_path_append_rect(path, &rect1, 0, 0); + + lv_grad_dsc_t grad; + grad.dir = LV_GRAD_DIR_HOR; + grad.stops_count = 2; + grad.stops[0].color = lv_color_hex(0xff0000); + grad.stops[0].opa = LV_OPA_COVER; + grad.stops[0].frac = 0; + grad.stops[1].color = lv_color_hex(0x00ff00); + grad.stops[1].opa = LV_OPA_COVER; + grad.stops[1].frac = 255; + + lv_matrix_t mt; + lv_matrix_identity(&mt); + lv_matrix_rotate(&mt, 5); + lv_matrix_translate(&mt, 20, 20); + lv_vector_dsc_set_stroke_transform(ctx, &mt); + lv_vector_dsc_set_stroke_join(ctx, LV_VECTOR_STROKE_JOIN_MITER); + lv_vector_dsc_set_stroke_linear_gradient(ctx, &grad, LV_VECTOR_GRADIENT_SPREAD_REFLECT); + lv_vector_dsc_add_path(ctx, path); // draw a path + + lv_draw_vector(ctx); + lv_vector_path_delete(path); + lv_vector_dsc_delete(ctx); +} + +static void canvas_draw(const char * name, void (*draw_cb)(lv_layer_t *)) +{ + static uint8_t canvas_buf[CANVAS_WIDTH_TO_STRIDE(640, 4) * 480 + LV_DRAW_BUF_ALIGN]; + lv_obj_t * canvas = lv_canvas_create(lv_screen_active()); + lv_canvas_set_buffer(canvas, canvas_buf, 640, 480, LV_COLOR_FORMAT_ARGB8888); + + lv_layer_t layer; + lv_canvas_init_layer(canvas, &layer); + + draw_cb(&layer); + + lv_canvas_finish_layer(canvas, &layer); + +#ifndef NON_AMD64_BUILD + char fn_buf[64]; + lv_snprintf(fn_buf, sizeof(fn_buf), "draw/vector_%s.png", name); + TEST_ASSERT_EQUAL_SCREENSHOT(fn_buf); +#endif +} + +void test_draw_lines(void) +{ + canvas_draw("draw_lines", draw_lines); +} + + +void test_draw_shapes(void) +{ + canvas_draw("draw_shapes", draw_shapes); +} +#endif diff --git a/tests/src/test_cases/test_array.c b/tests/src/test_cases/test_array.c new file mode 100644 index 000000000..75a966a59 --- /dev/null +++ b/tests/src/test_cases/test_array.c @@ -0,0 +1,82 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +static lv_array_t array; + +void setUp(void) +{ + LV_ARRAY_INIT(&array, int32_t); +} + +void tearDown(void) +{ + lv_array_destroy(&array); +} + +void test_array_append_values(void) +{ + for(int32_t i = 0; i < 10; i++) { + LV_ARRAY_APPEND_VALUE(&array, i); + } + + TEST_ASSERT_EQUAL_UINT32(8, lv_array_length(&array)); +} + + +void test_array_set_get(void) +{ + uint32_t v = 100; + LV_ARRAY_SET(&array, 0, &v, uint32_t); + uint32_t * r = LV_ARRAY_GET(&array, 0, uint32_t); + TEST_ASSERT_EQUAL_UINT32(100, *r); +} + + +void test_array_len(void) +{ + for(int32_t i = 0; i < 10; i++) { + LV_ARRAY_APPEND_VALUE(&array, i); + } + + TEST_ASSERT_EQUAL_UINT32(1, lv_array_is_full(&array) ? 1 : 0); + TEST_ASSERT_EQUAL_UINT32(8, lv_array_length(&array)); + lv_array_clear(&array); + TEST_ASSERT_EQUAL_UINT32(1, lv_array_is_empty(&array) ? 1 : 0); + TEST_ASSERT_EQUAL_UINT32(0, lv_array_length(&array)); +} + + +void test_array_resize(void) +{ + for(int32_t i = 0; i < 10; i++) { + LV_ARRAY_APPEND_VALUE(&array, i); + } + + lv_array_resize(&array, 12); + TEST_ASSERT_EQUAL_UINT32(8, lv_array_length(&array)); + lv_array_resize(&array, 6); + TEST_ASSERT_EQUAL_UINT32(6, lv_array_length(&array)); +} + + +void test_array_copy(void) +{ + for(int32_t i = 0; i < 10; i++) { + LV_ARRAY_APPEND_VALUE(&array, i); + } + + lv_array_t array2; + lv_memset(&array2, 0, sizeof(lv_array_t)); + TEST_ASSERT_EQUAL_UINT32(8, lv_array_length(&array)); + lv_array_copy(&array2, &array); + TEST_ASSERT_EQUAL_UINT32(8, lv_array_length(&array2)); + uint32_t * r = LV_ARRAY_GET(&array2, 1, uint32_t); + TEST_ASSERT_EQUAL_UINT32(1, *r); + lv_array_destroy(&array2); +} + + + +#endif diff --git a/tests/unity/unity_support.c b/tests/unity/unity_support.c index 2cb7222b9..935e14a25 100644 --- a/tests/unity/unity_support.c +++ b/tests/unity/unity_support.c @@ -103,11 +103,12 @@ bool lv_test_assert_image_eq(const char * fn_ref) uint32_t act_px = 0; memcpy(&ref_px, ptr_ref, 3); memcpy(&act_px, ptr_act, 3); - //printf("0xFF%06x, ", act_px); uint8_t act_swap[3] = {ptr_act[2], ptr_act[1], ptr_act[0]}; if(memcmp(act_swap, ptr_ref, 3) != 0) { + TEST_PRINTF("Error on x:%d, y:%d. Expected %X, Actual %X", x, y, ref_px, act_px); + fflush(stderr); err = true; break; } @@ -130,6 +131,7 @@ bool lv_test_assert_image_eq(const char * fn_ref) png_release(&p); + fflush(stdout); return !err; }