diff --git a/.Gifs/rlottie_player.png b/.Gifs/rlottie_player.png new file mode 100644 index 000000000..f47fffa68 Binary files /dev/null and b/.Gifs/rlottie_player.png differ diff --git a/.appveyor.yml b/.appveyor.yml index 0b51e2990..254e5c777 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,7 +12,7 @@ install: - git submodule update --init --recursive - set PATH=%cd%;%PYTHON%;%PYTHON%\Scripts;%PATH% - pip install meson==0.50.0 ninja - - call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" %ARCH% + - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" build_script: - meson -Dwerror=false --backend=ninja --prefix=%cd% build - where link @@ -20,4 +20,4 @@ build_script: test_script: - ninja -C build test after_build: - - ninja -C build install \ No newline at end of file + - ninja -C build install diff --git a/.gitignore b/.gitignore index 0d9491f33..260edc852 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ tmp .project .vs **/*.DS_Store +builddir_wasm \ No newline at end of file diff --git a/AUTHORS b/AUTHORS index 8e38feee1..5f2ff9b9b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,3 +10,9 @@ Shinwoo Kim Vincent Torri Nicholas Guriev John Preston +Anatoly Korniltsev +Александр Першин +Anton Sergeev +HyunGu Lee +ChaeLin Shin +Seungjae Yoon diff --git a/CMakeLists.txt b/CMakeLists.txt index c111dac64..38a986225 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,28 +41,32 @@ set(LOTTIE_MODULE_PATH "${CMAKE_SHARED_LIBRARY_PREFIX}rlottie-image-loader${CMAK configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in config.h) target_include_directories(rlottie + PUBLIC $ PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" ) #declare common target compilation options -target_compile_options(rlottie - PUBLIC - PRIVATE - -std=c++14 - -fno-exceptions - -fno-unwind-tables - -fno-asynchronous-unwind-tables - -fno-rtti - -Wall - -fvisibility=hidden - ) - -#MSVC does not recognize these parameters -if (NOT WIN32) +if(MSVC) target_compile_options(rlottie PUBLIC PRIVATE + /std:c++14 + /EHs-c- # disable exceptions + /GR- # disable RTTI + /W3 + ) +else() + target_compile_options(rlottie + PUBLIC + PRIVATE + -std=c++14 + -fno-exceptions + -fno-unwind-tables + -fno-asynchronous-unwind-tables + -fno-rtti + -Wall + -fvisibility=hidden -Wnon-virtual-dtor -Woverloaded-virtual -Wno-unused-parameter @@ -71,6 +75,8 @@ endif() if (WIN32 AND NOT BUILD_SHARED_LIBS) target_compile_definitions(rlottie PUBLIC -DRLOTTIE_BUILD=0) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") endif() #declare dependancy @@ -85,7 +91,7 @@ target_link_libraries(rlottie if (NOT APPLE AND NOT WIN32) target_link_libraries(rlottie PRIVATE - "-Wl,--version-script=${CMAKE_SOURCE_DIR}/rlottie.expmap" + "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/rlottie.expmap" ) endif() @@ -100,7 +106,7 @@ if (NOT LOTTIE_ASAN) PUBLIC "-Wl, -undefined error" ) - else() + elseif(NOT MSVC) target_link_libraries(rlottie PUBLIC "-Wl,--no-undefined" @@ -195,4 +201,3 @@ export(EXPORT rlottie-targets FILE ${CMAKE_CURRENT_BINARY_DIR}/rlottieTargets.cm #Register package in user's package registry export(PACKAGE rlottie) - diff --git a/README.md b/README.md index e7beedc5f..3f1c5a610 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://travis-ci.org/Samsung/rlottie.svg?branch=master)](https://travis-ci.org/Samsung/rlottie) [![Build status](https://ci.appveyor.com/api/projects/status/n3xonxk1ooo6s7nr?svg=true&passingText=windows%20-%20passing)](https://ci.appveyor.com/project/smohantty/rlottie-mliua) +[![Gitter](https://badges.gitter.im/rLottie-dev/community.svg)](https://gitter.im/rLottie-dev/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

@@ -35,7 +36,7 @@ Here are small samples of the power of Lottie. - [Issues or Feature Requests?](#issues-or-feature-requests) ## Building Lottie -rottie supports [meson](https://mesonbuild.com/) and [cmake](https://cmake.org/) build system. rottie is written in `C++14`. and has a public header dependancy of `C++11` +rlottie supports [meson](https://mesonbuild.com/) and [cmake](https://cmake.org/) build system. rlottie is written in `C++14`. and has a public header dependency of `C++11` ### Meson Build install [meson](http://mesonbuild.com/Getting-meson.html) and [ninja](https://ninja-build.org/) if not already installed. @@ -98,7 +99,7 @@ ninja test # ## Demo -If you want to see rlottie librray in action without building it please visit [rlottie online viewer](http://rlottie.com) +If you want to see rlottie library in action without building it please visit [rlottie online viewer](http://rlottie.com) While building rlottie library it generates a simple lottie to GIF converter which can be used to convert lottie json file to GIF file. @@ -166,7 +167,7 @@ You can update properties dynamically at runtime. This can be used for a variety ### Understanding After Effects -To understand how to change animation properties in Lottie, you should first understand how animation properties are stored in Lottie. Animation properties are stored in a data tree that mimics the information heirarchy of After Effects. In After Effects a Composition is a collection of Layers that each have their own timelines. Layer objects have string names, and their contents can be an image, shape layers, fills, strokes, or just about anything that is drawable. Each object in After Effects has a name. Lottie can find these objects and properties by their name using a KeyPath. +To understand how to change animation properties in Lottie, you should first understand how animation properties are stored in Lottie. Animation properties are stored in a data tree that mimics the information hierarchy of After Effects. In After Effects a Composition is a collection of Layers that each have their own timelines. Layer objects have string names, and their contents can be an image, shape layers, fills, strokes, or just about anything that is drawable. Each object in After Effects has a name. Lottie can find these objects and properties by their name using a KeyPath. ### Usage diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index b42cc960c..aacdc7c02 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,8 +1,14 @@ add_executable(lottie2gif "lottie2gif.cpp") -target_compile_options(lottie2gif - PRIVATE - -std=c++14) +if(MSVC) + target_compile_options(lottie2gif + PRIVATE + /std:c++14) +else() + target_compile_options(lottie2gif + PRIVATE + -std=c++14) +endif() target_link_libraries(lottie2gif rlottie) diff --git a/example/win32Player/Source.cpp b/example/win32Player/Source.cpp new file mode 100644 index 000000000..99ea876dd --- /dev/null +++ b/example/win32Player/Source.cpp @@ -0,0 +1,60 @@ +#include "rlottie.h" +#include "animation.h" +using namespace rlottie; + +std::unique_ptr anim; +uint32_t *buffer; +size_t width, height; +size_t bytesPerLine; +uint32_t curColor = UINT32_MAX; + +void setAnimationSize(size_t w, size_t h) +{ + width = w; + height = h; + bytesPerLine = width * sizeof(uint32_t); + + if (buffer != NULL) freeAnimation(); + buffer = (uint32_t*)calloc(bytesPerLine * height, sizeof(uint32_t)); +} + +void setAnimation(char* path, size_t* w, size_t* h) +{ + anim = Animation::loadFromFile(path); + anim->size(*w, *h); + setAnimationSize(*w, *h); +} + +uint32_t* renderRLottieAnimation(uint32_t frameNum) +{ + Surface surface = Surface(buffer, width, height, bytesPerLine); + anim->renderSync(frameNum, surface); + // background color + for (int i = 0; i < height; i++) + for (int j = 0; j < width; ++j) + { + uint32_t* v = buffer + i * width + j; + if (*v == 0) *v = curColor; + } + return buffer; +} + +void setAnimationColor(int r, int g, int b) +{ + curColor = ((255 << 16) * r) + ((255 << 8) * g) + 255 * b; +} + +size_t getTotalFrame() +{ + return anim->totalFrame(); +} + +bool isAnimNULL() +{ + return anim == NULL; +} + +void freeAnimation() +{ + free(buffer); +} \ No newline at end of file diff --git a/example/win32Player/animation.h b/example/win32Player/animation.h new file mode 100644 index 000000000..483cb3faa --- /dev/null +++ b/example/win32Player/animation.h @@ -0,0 +1,10 @@ +#pragma once +#include + +void setAnimation(char* path, size_t* w, size_t* h); +void setAnimationSize(size_t w, size_t h); +uint32_t* renderRLottieAnimation(uint32_t frameNum); +size_t getTotalFrame(); +bool isAnimNULL(); +void setAnimationColor(int r, int g, int b); +void freeAnimation(); \ No newline at end of file diff --git a/example/win32Player/framework.h b/example/win32Player/framework.h new file mode 100644 index 000000000..33a6be646 --- /dev/null +++ b/example/win32Player/framework.h @@ -0,0 +1,15 @@ +// header.h : include file for standard system include files, +// or project specific include files +// + +#pragma once + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +// C RunTime Header Files +#include +#include +#include +#include diff --git a/example/win32Player/resource.h b/example/win32Player/resource.h new file mode 100644 index 000000000..ac46939a8 --- /dev/null +++ b/example/win32Player/resource.h @@ -0,0 +1,40 @@ +//{{NO_DEPENDENCIES}} +// This is an include file generated by Microsoft Visual C++. +// It is being used in rlottiePlayer.rc. +// +#define IDC_MYICON 2 +#define IDD_RLOTTIEPLAYER_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_RLOTTIEPLAYER 107 +#define IDI_SMALL 108 +#define IDC_RLOTTIEPLAYER 109 +#define IDR_MAINFRAME 128 +#define MAIN_WINDOW 130 +#define BTN_BROWSE 1004 +#define BTN_WHITE 1005 +#define BTN_BLACK 1006 +#define BTN_RED 1007 +#define BTN_GREEN 1008 +#define BTN_BLUE 1009 +#define SLIDER_CANVAS_RESIZE 1010 +#define BTN_PLAY 1011 +#define SLIDER_PLAY 1012 +#define TEXT_FILENAME 1013 +#define PICCTRL_RLOTTIE 1014 +#define TIMER_PLAY_ANIM 1015 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1017 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/example/win32Player/rlottie.dll b/example/win32Player/rlottie.dll new file mode 100644 index 000000000..296f70a0b Binary files /dev/null and b/example/win32Player/rlottie.dll differ diff --git a/example/win32Player/rlottie.lib b/example/win32Player/rlottie.lib new file mode 100644 index 000000000..2625916fd Binary files /dev/null and b/example/win32Player/rlottie.lib differ diff --git a/example/win32Player/rlottiePlayer.cpp b/example/win32Player/rlottiePlayer.cpp new file mode 100644 index 000000000..b2d0431f8 --- /dev/null +++ b/example/win32Player/rlottiePlayer.cpp @@ -0,0 +1,521 @@ +// rlottiePlayer.cpp : Defines the entry point for the application. +// + + +#include "framework.h" +#include "rlottiePlayer.h" +using namespace Gdiplus; + +#define MAX_LOADSTRING 100 + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name +HWND mainWindow; // Main Window Instance +HWND hTextFileToBeOpened; // openDialog file path +HWND hBtnPlay; +HWND hSliderPlay, hSliderCanvasResize; +UINT curFrame = 0; +RlottieBitmap anim; // rendered Animation Bitmap +RECT animRect, backRect; +size_t animWidth, animHeight; +Gdiplus::Color backColor(255, 255, 255, 255); +Gdiplus::Color borderColor(255, 0, 0, 0); +bool isViewChanged = false; + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +void openJSONFileDialog(HWND); +void initUIControl(HWND); +void dlgUICommand(HWND, WPARAM); +void resizeCanvas(HWND, int); +void changeBackgroundColor(int r, int g, int b); + +// Animation Rendering Functions +void draw(HDC); +Bitmap* CreateBitmap(void* data, unsigned int width, unsigned int height); +void renderAnimation(UINT); + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // initialize Gdiplus + Gdiplus::GdiplusStartupInput gdiplusStartUpInput; + ULONG_PTR gdiplustoken; + Gdiplus::GdiplusStartup(&gdiplustoken, &gdiplusStartUpInput, nullptr); + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_RLOTTIEPLAYER, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + if (!InitInstance(hInstance, nCmdShow)) + { + return FALSE; + } + + HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_RLOTTIEPLAYER)); + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + Gdiplus::GdiplusShutdown(gdiplustoken); + return (int)msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_RLOTTIEPLAYER)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_RLOTTIEPLAYER); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + mainWindow = CreateWindowEx(0, szWindowClass, szTitle, dwStyle, + CW_USEDEFAULT, CW_USEDEFAULT, WND_WIDTH, WND_HEIGHT, nullptr, nullptr, hInstance, nullptr); + + if (!mainWindow) + { + return FALSE; + } + + ShowWindow(mainWindow, nCmdShow); + UpdateWindow(mainWindow); + SetMenu(mainWindow, NULL); + + return TRUE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// WM_COMMAND - process the application menu +// WM_PAINT - Paint the main window +// WM_DESTROY - post a quit message and return +// +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static bool isplay = false; + int wmId = LOWORD(wParam); + + switch (message) + { + case WM_CREATE: + { + initUIControl(hWnd); + break; + } + case WM_TIMER: + { + switch (wmId) + { + case TIMER_PLAY_ANIM: + { + renderAnimation(curFrame + 1); + SendMessage(hSliderPlay, TBM_SETPOS, TRUE, curFrame); + break; + } + default: + break; + } + break; + } + case WM_COMMAND: + { + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + + case IDM_EXIT: + DestroyWindow(hWnd); + break; + + case BTN_BROWSE: + openJSONFileDialog(hWnd); + break; + + case BTN_PLAY: + { + LPWSTR textBtnPlay; + USES_CONVERSION; + if (isplay) + { + isplay = false; + textBtnPlay = A2W("Play"); + KillTimer(hWnd, TIMER_PLAY_ANIM); + } + else + { + isplay = true; + textBtnPlay = A2W("Pause"); + SetTimer(hWnd, TIMER_PLAY_ANIM, 10, NULL); + } + SetWindowText(hBtnPlay, textBtnPlay); + break; + } + + case WM_DROPFILES: + break; + case BTN_WHITE: + changeBackgroundColor(1, 1, 1); + break; + case BTN_BLACK: + changeBackgroundColor(0, 0, 0); + break; + case BTN_RED: + changeBackgroundColor(1, 0, 0); + break; + case BTN_GREEN: + changeBackgroundColor(0, 1, 0); + break; + case BTN_BLUE: + changeBackgroundColor(0, 0, 1); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + + case WM_HSCROLL: + { + if ((lParam != 0) && (reinterpret_cast(lParam) == hSliderPlay)) + { + UINT frameNum = SendDlgItemMessage(hWnd, SLIDER_PLAY, TBM_GETPOS, 0, 0); + renderAnimation(frameNum); + } + else if ((lParam != 0) && (reinterpret_cast(lParam) == hSliderCanvasResize)) + { + static int curSize = anim.width / RESIZE_LENGTH; + int newSize = SendDlgItemMessage(hWnd, SLIDER_CANVAS_RESIZE, TBM_GETPOS, 0, 0); + resizeCanvas(hWnd, (curSize - newSize) * RESIZE_LENGTH); + curSize = newSize; + } + break; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + draw(hdc); + EndPaint(hWnd, &ps); + } + break; + case WM_CTLCOLORSTATIC: + { + static HBRUSH hBrushEdit = CreateSolidBrush(RGB(255, 255, 255)); + return (LRESULT)hBrushEdit; + } + + case WM_DESTROY: + freeAnimation(); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} + +void openJSONFileDialog(HWND hDlg) +{ + OPENFILENAME ofn; // common dialog box structure + TCHAR szFile[260] = { 0 }; // if using TCHAR macros + + // Initialize OPENFILENAME + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = _T("JSON\0*.json\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + isViewChanged = true; + if (GetOpenFileName(&ofn)) + { + isViewChanged = true; + SetWindowText(hTextFileToBeOpened, ofn.lpstrFile); + // LPWSTR(w_char*) -> LPSTR(char*) + USES_CONVERSION; + LPSTR path = W2A(ofn.lpstrFile); + + setAnimation(path, &animWidth, &animHeight); + // init play slider + SendMessage(hSliderPlay, TBM_SETRANGE, FALSE, MAKELPARAM(0, getTotalFrame())); + SendMessage(hSliderPlay, TBM_SETPOS, TRUE, 0); + renderAnimation(0); + } +} + +void draw(HDC hdc) +{ + Graphics gf(hdc); + int half_interval = UI_INTERVAL / 2; + + // background + SolidBrush brush(backColor); + int back_y = half_interval + BTN_HEIGHT; + int back_height = back_y + BMP_MAX_LEN + UI_INTERVAL; + if (isViewChanged) + { + isViewChanged = false; + gf.FillRectangle(&brush, 0, back_y, WND_WIDTH, back_height); + } + + // image borderline + Pen pen(borderColor); + gf.DrawRectangle(&pen, anim.x - half_interval, anim.y - half_interval, anim.width + half_interval * 2, anim.height + half_interval * 2); + + // image + if (anim.image != NULL) + { + gf.DrawImage(anim.image, anim.x, anim.y, anim.width, anim.height); + } +} + +Bitmap* CreateBitmap(void* data, unsigned int width, unsigned int height) +{ + BITMAPINFO Info; + memset(&Info, 0, sizeof(Info)); + + Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + Info.bmiHeader.biWidth = width; + Info.bmiHeader.biHeight = height; + Info.bmiHeader.biPlanes = 1; + Info.bmiHeader.biBitCount = 32; + Info.bmiHeader.biCompression = BI_RGB; + Info.bmiHeader.biSizeImage = 0; //(((32 * width + 31) & ~31) / 8) * height; + + return new Gdiplus::Bitmap(&Info, data); +} + +void renderAnimation(UINT frameNum) +{ + if (isAnimNULL()) return; + if (anim.image != NULL) delete anim.image; + + curFrame = frameNum % getTotalFrame(); + + // render + UINT* resRender = renderRLottieAnimation(curFrame); + anim.image = CreateBitmap(resRender, animWidth, animHeight); + anim.image->RotateFlip(RotateNoneFlipY); + // call WM_PAINT message + InvalidateRect(mainWindow, &animRect, FALSE); +} + +void initUIControl(HWND hWnd) +{ + int half_ui_interval = UI_INTERVAL / 2; + + // button browse + int browse_x = UI_INTERVAL; + int browse_y = half_ui_interval; + CreateWindow(L"button", L"Browse", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + browse_x, browse_y, BTN_WIDTH, BTN_HEIGHT, hWnd, (HMENU)BTN_BROWSE, hInst, NULL); + + // textbox FilePath + int textFile_x = browse_x + BTN_WIDTH + UI_INTERVAL; + int textFile_y = browse_y; + hTextFileToBeOpened = CreateWindowEx(0, L"static", L"No file selected.", WS_CHILD | WS_VISIBLE | ES_LEFT, + textFile_x, textFile_y, WND_WIDTH * 0.6, TEXT_HEIGHT, hWnd, (HMENU)TEXT_FILENAME, hInst, 0); + + // image + anim.x = WND_WIDTH / 4; + anim.y = browse_y + BTN_HEIGHT + UI_INTERVAL * 2; + anim.width = BMP_MAX_LEN; + anim.height = anim.width; + + // animating range + SetRect(&animRect, + anim.x - UI_INTERVAL, + anim.y - UI_INTERVAL, + anim.x + anim.width + UI_INTERVAL * 2, + anim.y + anim.height + UI_INTERVAL * 2 + ); + + // background range + SetRect(&backRect, + 0, + anim.y - UI_INTERVAL, + WND_WIDTH, + anim.y + anim.height + UI_INTERVAL * 2 + ); + + // text Background Color + int textBC_x = WND_WIDTH / 20; + int textBC_y = anim.y + anim.height + UI_INTERVAL * 2; + CreateWindowEx(0, L"static", L"Background Color", WS_CHILD | WS_VISIBLE | ES_LEFT, + textBC_x, textBC_y, 120, TEXT_HEIGHT, hWnd, (HMENU)TEXT_FILENAME, hInst, 0); + + // radio button + // white + int white_x = WND_WIDTH / 20; + int white_y = textBC_y + TEXT_HEIGHT + half_ui_interval; + CreateWindowEx(0, L"button", L"White", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, + white_x, white_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_WHITE, hInst, NULL); + + // black + int black_x = white_x + RDOBTN_WIDTH + half_ui_interval; + int black_y = white_y; + CreateWindowEx(0, L"button", L"Black", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, + black_x, black_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_BLACK, hInst, NULL); + + // red + int red_x = black_x + RDOBTN_WIDTH + half_ui_interval; + int red_y = white_y; + CreateWindowEx(0, L"button", L"Red", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, + red_x, red_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_RED, hInst, NULL); + + // green + int green_x = red_x + RDOBTN_WIDTH + half_ui_interval; + int green_y = white_y; + CreateWindowEx(0, L"button", L"Green", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, + green_x, green_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_GREEN, hInst, NULL); + + // blue + int blue_x = green_x + RDOBTN_WIDTH + half_ui_interval; + int blue_y = white_y; + CreateWindowEx(0, L"button", L"Blue", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, + blue_x, blue_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_BLUE, hInst, NULL); + + CheckRadioButton(hWnd, BTN_WHITE, BTN_BLUE, BTN_WHITE); + + // text Canvas Resize + int textCR_x = WND_WIDTH / 2; + int textCR_y = textBC_y; + CreateWindowEx(0, L"static", L"Canvas Resize", WS_CHILD | WS_VISIBLE | ES_LEFT, + textCR_x, textCR_y, 120, TEXT_HEIGHT, hWnd, (HMENU)TEXT_FILENAME, hInst, 0); + + // slider Canvas Resize + int sliderCR_x = textCR_x; + int sliderCR_y = textCR_y + TEXT_HEIGHT + half_ui_interval; + hSliderCanvasResize = CreateWindowExW(0, TRACKBAR_CLASSW, NULL, WS_CHILD | WS_VISIBLE | TBS_FIXEDLENGTH | TBS_NOTICKS, + sliderCR_x, sliderCR_y, WND_WIDTH * 0.2, SLIDER_HEIGHT, hWnd, (HMENU)SLIDER_CANVAS_RESIZE, hInst, NULL); + + // init resize slider + UINT sizeSlider = anim.width / RESIZE_LENGTH; + SendMessage(hSliderCanvasResize, TBM_SETRANGE, FALSE, MAKELPARAM(0, sizeSlider)); + SendMessage(hSliderCanvasResize, TBM_SETPOS, TRUE, sizeSlider); + + // button play + int btnPlay_x = WND_WIDTH / 10; + int btnPlay_y = red_y + RDOBTN_HEIGHT + UI_INTERVAL * 2; + hBtnPlay = CreateWindow(L"button", L"Play", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + btnPlay_x, btnPlay_y, BTN_WIDTH, BTN_HEIGHT, hWnd, (HMENU)BTN_PLAY, hInst, NULL); + + // slider play + int sliderPlay_x = btnPlay_x + BTN_WIDTH + UI_INTERVAL; + int sliderPlay_y = btnPlay_y; + hSliderPlay = CreateWindowExW(0, TRACKBAR_CLASSW, NULL, WS_CHILD | WS_VISIBLE | TBS_FIXEDLENGTH | TBS_NOTICKS, + sliderPlay_x, sliderPlay_y, WND_WIDTH * 0.6, SLIDER_HEIGHT, hWnd, (HMENU)SLIDER_PLAY, hInst, NULL); +} + +void resizeCanvas(HWND hWnd, int resizeValue) +{ + isViewChanged = true; + anim.x += resizeValue / 2; + anim.y += resizeValue / 2; + anim.width -= resizeValue; + anim.height -= resizeValue; + InvalidateRect(hWnd, &animRect, TRUE); +} + +void changeBackgroundColor(int r, int g, int b) +{ + isViewChanged = true; + backColor = Gdiplus::Color(r * 255, g * 255, b * 255); + if (r + g + b == 0) borderColor = Gdiplus::Color(255, 255, 255); + else borderColor = Gdiplus::Color(0, 0, 0); + setAnimationColor(r, g, b); + renderAnimation(curFrame); + InvalidateRect(mainWindow, &backRect, FALSE); +} \ No newline at end of file diff --git a/example/win32Player/rlottiePlayer.exe b/example/win32Player/rlottiePlayer.exe new file mode 100644 index 000000000..b52a4cb77 Binary files /dev/null and b/example/win32Player/rlottiePlayer.exe differ diff --git a/example/win32Player/rlottiePlayer.h b/example/win32Player/rlottiePlayer.h new file mode 100644 index 000000000..07e5fd244 --- /dev/null +++ b/example/win32Player/rlottiePlayer.h @@ -0,0 +1,32 @@ +#pragma once + +#include "resource.h" +#include "animation.h" +#include // OPENFILENAME +#include "atlconv.h" // String cast. (ex. LPWSTR <-> LPSTR) +#include +#include // slider handle + +// interval +#define UI_INTERVAL 20 + +// length +#define WND_WIDTH 1000 +#define WND_HEIGHT 800 +#define BMP_MAX_LEN 500 +#define BTN_WIDTH 100 +#define BTN_HEIGHT 30 +#define TEXT_HEIGHT 20 +#define SLIDER_HEIGHT 25 +#define RDOBTN_WIDTH 60 +#define RDOBTN_HEIGHT 20 +#define RESIZE_LENGTH 10 + +typedef struct RlottieBitmap +{ + Gdiplus::Bitmap* image = NULL; + int x = 0; + int y = 0; + unsigned int width = 0; + unsigned int height = 0; +}RlottieBitmap; \ No newline at end of file diff --git a/example/win32Player/rlottiePlayer.ico b/example/win32Player/rlottiePlayer.ico new file mode 100644 index 000000000..b3ec03bd6 Binary files /dev/null and b/example/win32Player/rlottiePlayer.ico differ diff --git a/example/win32Player/rlottiePlayer.rc b/example/win32Player/rlottiePlayer.rc new file mode 100644 index 000000000..0c7935ff4 Binary files /dev/null and b/example/win32Player/rlottiePlayer.rc differ diff --git a/example/win32Player/rlottiePlayer.sln b/example/win32Player/rlottiePlayer.sln new file mode 100644 index 000000000..d1c8f0dc3 --- /dev/null +++ b/example/win32Player/rlottiePlayer.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlottiePlayer", "rlottiePlayer.vcxproj", "{CC7AD634-4D29-4D8E-997D-9727DABB44EE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Debug|x64.ActiveCfg = Debug|x64 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Debug|x64.Build.0 = Debug|x64 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Debug|x86.ActiveCfg = Debug|Win32 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Debug|x86.Build.0 = Debug|Win32 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Release|x64.ActiveCfg = Release|x64 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Release|x64.Build.0 = Release|x64 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Release|x86.ActiveCfg = Release|Win32 + {CC7AD634-4D29-4D8E-997D-9727DABB44EE}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {57EEA15F-28FB-4C19-96B4-7B62BFA0CFE5} + EndGlobalSection +EndGlobal diff --git a/example/win32Player/rlottiePlayer.vcxproj b/example/win32Player/rlottiePlayer.vcxproj new file mode 100644 index 000000000..e61b03b2d --- /dev/null +++ b/example/win32Player/rlottiePlayer.vcxproj @@ -0,0 +1,166 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {cc7ad634-4d29-4d8e-997d-9727dabb44ee} + rlottiePlayer + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + ..\..\inc;$(IncludePath) + + + false + ..\..\inc;$(IncludePath) + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;rlottie.lib;gdiplus.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;rlottie.lib;gdiplus.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/win32Player/rlottiePlayer.vcxproj.filters b/example/win32Player/rlottiePlayer.vcxproj.filters new file mode 100644 index 000000000..c8cf65baa --- /dev/null +++ b/example/win32Player/rlottiePlayer.vcxproj.filters @@ -0,0 +1,55 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/example/win32Player/rlottiePlayer.vcxproj.user b/example/win32Player/rlottiePlayer.vcxproj.user new file mode 100644 index 000000000..88a550947 --- /dev/null +++ b/example/win32Player/rlottiePlayer.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/example/win32Player/small.ico b/example/win32Player/small.ico new file mode 100644 index 000000000..b3ec03bd6 Binary files /dev/null and b/example/win32Player/small.ico differ diff --git a/example/win32Player/targetver.h b/example/win32Player/targetver.h new file mode 100644 index 000000000..bf75e0895 --- /dev/null +++ b/example/win32Player/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// // Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include diff --git a/format b/format new file mode 100755 index 000000000..c5630b483 --- /dev/null +++ b/format @@ -0,0 +1 @@ +git diff -U0 --no-color HEAD^ | clang-format-diff -i -p1 \ No newline at end of file diff --git a/inc/rlottie.h b/inc/rlottie.h index 12c413bf6..67565691c 100644 --- a/inc/rlottie.h +++ b/inc/rlottie.h @@ -108,11 +108,11 @@ enum class Property { FillOpacity, /*!< Opacity property of Fill object , value type is float [ 0 .. 100] */ StrokeColor, /*!< Color property of Stroke object , value type is rlottie::Color */ StrokeOpacity, /*!< Opacity property of Stroke object , value type is float [ 0 .. 100] */ - StrokeWidth, /*!< stroke with property of Stroke object , value type is float */ + StrokeWidth, /*!< stroke width property of Stroke object , value type is float */ TrAnchor, /*!< Transform Anchor property of Layer and Group object , value type is rlottie::Point */ TrPosition, /*!< Transform Position property of Layer and Group object , value type is rlottie::Point */ TrScale, /*!< Transform Scale property of Layer and Group object , value type is rlottie::Size. range[0 ..100] */ - TrRotation, /*!< Transform Scale property of Layer and Group object , value type is float. range[0 .. 360] in degrees*/ + TrRotation, /*!< Transform Rotation property of Layer and Group object , value type is float. range[0 .. 360] in degrees*/ TrOpacity /*!< Transform Opacity property of Layer and Group object , value type is float [ 0 .. 100] */ }; diff --git a/inc/rlottie_capi.h b/inc/rlottie_capi.h index 9bdf336f8..80907ae67 100644 --- a/inc/rlottie_capi.h +++ b/inc/rlottie_capi.h @@ -25,7 +25,7 @@ #include #include -#include +#include "rlottiecommon.h" #ifdef __cplusplus extern "C" { @@ -46,6 +46,36 @@ typedef enum { typedef struct Lottie_Animation_S Lottie_Animation; +/** + * @brief Runs lottie initialization code when rlottie library is loaded + * dynamically. + * + * + * This api should be called before any other api when rlottie library + * is loaded using dlopen() or equivalent. + * + * @see lottie_shutdown() + * + * @ingroup Lottie_Animation + * @internal + */ +RLOTTIE_API void lottie_init(void); + +/** + * @brief Runs lottie teardown code when rlottie library is loaded + * dynamically. + * + * This api should be called before unloading the rlottie library for + * proper cleanup of the resource without doing so will result in undefined + * behaviour. + * + * @see lottie_init() + * + * @ingroup Lottie_Animation + * @internal + */ +RLOTTIE_API void lottie_shutdown(void); + /** * @brief Constructs an animation object from file path. * @@ -274,6 +304,23 @@ RLOTTIE_API void lottie_animation_property_override(Lottie_Animation *animation, * */ RLOTTIE_API const LOTMarkerList* lottie_animation_get_markerlist(Lottie_Animation *animation); +/** + * @brief Configures rlottie model cache policy. + * + * Provides Library level control to configure model cache + * policy. Setting it to 0 will disable + * the cache as well as flush all the previously cached content. + * + * @param[in] cacheSize Maximum Model Cache size. + * + * @note to disable Caching configure with 0 size. + * @note to flush the current Cache content configure it with 0 and + * then reconfigure with the new size. + * + * @internal + */ +RLOTTIE_API void lottie_configure_model_cache_size(size_t cacheSize); + #ifdef __cplusplus } #endif diff --git a/packaging/rlottie.spec b/packaging/rlottie.spec index c757a1fee..55cdee8e9 100644 --- a/packaging/rlottie.spec +++ b/packaging/rlottie.spec @@ -10,7 +10,7 @@ Version: 0.2 Release: %mkrel %{?git:-c git%{git}} %{rel} Summary: Platform independent standalone library that plays Lottie Animation Group: System/Libraries -License: MIT and BSD +License: MIT and BSD and MPL-2.0 URL: http://www.tizen.org/ Source0: https://github.com/Samsung/rlottie/archive/master/%{name}-%{version}%{?git:-git%{git}}.tar.gz diff --git a/src/binding/c/lottieanimation_capi.cpp b/src/binding/c/lottieanimation_capi.cpp index 2434b5faa..53ca7bda5 100644 --- a/src/binding/c/lottieanimation_capi.cpp +++ b/src/binding/c/lottieanimation_capi.cpp @@ -26,6 +26,9 @@ using namespace rlottie; +extern void lottie_init_impl(); +extern void lottie_shutdown_impl(); + extern "C" { #include #include @@ -38,6 +41,34 @@ struct Lottie_Animation_S LOTMarkerList *mMarkerList; }; +static uint32_t _lottie_lib_ref_count = 0; + +RLOTTIE_API void lottie_init(void) +{ + if (_lottie_lib_ref_count > 0) { + _lottie_lib_ref_count++; + return; + } + lottie_init_impl(); + + _lottie_lib_ref_count = 1; +} + +RLOTTIE_API void lottie_shutdown(void) +{ + if (_lottie_lib_ref_count <= 0) { + // lottie_init() is not called before lottie_shutdown() + // or multiple shutdown is getting called. + return; + } + + _lottie_lib_ref_count--; + + if (_lottie_lib_ref_count == 0) { + lottie_shutdown_impl(); + } +} + RLOTTIE_API Lottie_Animation_S *lottie_animation_from_file(const char *path) { if (auto animation = Animation::loadFromFile(path) ) { @@ -275,4 +306,10 @@ lottie_animation_get_markerlist(Lottie_Animation_S *animation) return (const LOTMarkerList*)animation->mMarkerList; } +RLOTTIE_API void +lottie_configure_model_cache_size(size_t cacheSize) +{ + rlottie::configureModelCacheSize(cacheSize); +} + } diff --git a/src/lottie/lottieanimation.cpp b/src/lottie/lottieanimation.cpp index 601c065c3..afcc400bc 100644 --- a/src/lottie/lottieanimation.cpp +++ b/src/lottie/lottieanimation.cpp @@ -178,20 +178,29 @@ class RenderTaskScheduler { for (unsigned n = 0; n != _count; ++n) { _threads.emplace_back([&, n] { run(n); }); } + + IsRunning = true; } public: + static bool IsRunning; + static RenderTaskScheduler &instance() { static RenderTaskScheduler singleton; return singleton; } - ~RenderTaskScheduler() - { - for (auto &e : _q) e.done(); + ~RenderTaskScheduler() { stop(); } - for (auto &e : _threads) e.join(); + void stop() + { + if (IsRunning) { + IsRunning = false; + + for (auto &e : _q) e.done(); + for (auto &e : _threads) e.join(); + } } std::future process(SharedRenderTask task) @@ -214,12 +223,16 @@ public: #else class RenderTaskScheduler { public: + static bool IsRunning; + static RenderTaskScheduler &instance() { static RenderTaskScheduler singleton; return singleton; } + void stop() {} + std::future process(SharedRenderTask task) { auto result = task->playerImpl->render(task->frameNo, task->surface, @@ -228,8 +241,11 @@ public: return std::move(task->receiver); } }; + #endif +bool RenderTaskScheduler::IsRunning{false}; + std::future AnimationImpl::renderAsync(size_t frameNo, Surface &&surface, bool keepAspectRatio) @@ -441,6 +457,29 @@ void Surface::setDrawRegion(size_t x, size_t y, size_t width, size_t height) mDrawArea.h = height; } +namespace { +void lottieShutdownRenderTaskScheduler() +{ + if (RenderTaskScheduler::IsRunning) { + RenderTaskScheduler::instance().stop(); + } +} +} // namespace + +// private apis exposed to c interface +void lottie_init_impl() +{ + // do nothing for now. +} + +extern void lottieShutdownRasterTaskScheduler(); + +void lottie_shutdown_impl() +{ + lottieShutdownRenderTaskScheduler(); + lottieShutdownRasterTaskScheduler(); +} + #ifdef LOTTIE_LOGGING_SUPPORT void initLogging() { diff --git a/src/lottie/lottiefiltermodel.h b/src/lottie/lottiefiltermodel.h index b2b98b190..4f78c11a3 100644 --- a/src/lottie/lottiefiltermodel.h +++ b/src/lottie/lottiefiltermodel.h @@ -238,7 +238,7 @@ class FilterData { public: void addValue(LOTVariant& value) { - uint index = static_cast(value.property()); + uint32_t index = static_cast(value.property()); if (mBitset.test(index)) { std::replace_if(mFilters.begin(), mFilters.end(), [&value](const LOTVariant& e) { @@ -253,7 +253,7 @@ public: void removeValue(LOTVariant& value) { - uint index = static_cast(value.property()); + uint32_t index = static_cast(value.property()); if (mBitset.test(index)) { mBitset.reset(index); mFilters.erase(std::remove_if(mFilters.begin(), mFilters.end(), @@ -266,7 +266,7 @@ public: } bool hasFilter(rlottie::Property prop) const { - return mBitset.test(static_cast(prop)); + return mBitset.test(static_cast(prop)); } model::Color color(rlottie::Property prop, int frame) const { diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp index 7cd25ccd7..cb1bc2cdd 100644 --- a/src/lottie/lottieitem.cpp +++ b/src/lottie/lottieitem.cpp @@ -149,9 +149,9 @@ bool renderer::Composition::update(int frameNo, const VSize &size, bool renderer::Composition::render(const rlottie::Surface &surface) { - mSurface.reset(reinterpret_cast(surface.buffer()), - uint(surface.width()), uint(surface.height()), - uint(surface.bytesPerLine()), + mSurface.reset(reinterpret_cast(surface.buffer()), + uint32_t(surface.width()), uint32_t(surface.height()), + uint32_t(surface.bytesPerLine()), VBitmap::Format::ARGB32_Premultiplied); /* schedule all preprocess task for this frame at once. @@ -200,7 +200,7 @@ VRle renderer::Mask::rle() { if (!vCompare(mCombinedAlpha, 1.0f)) { VRle obj = mRasterizer.rle(); - obj *= uchar(mCombinedAlpha * 255); + obj *= uint8_t(mCombinedAlpha * 255); return obj; } else { return mRasterizer.rle(); @@ -343,7 +343,7 @@ renderer::Layer::Layer(model::Layer *layerData) : mLayerData(layerData) mLayerMask = std::make_unique(mLayerData); } -bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, +bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) { if (!keyPath.matches(name(), depth)) { @@ -359,12 +359,12 @@ bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, return true; } -bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, +bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) { if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) { if (keyPath.propagate(name(), depth)) { - uint newDepth = keyPath.nextDepth(name(), depth); + uint32_t newDepth = keyPath.nextDepth(name(), depth); mRoot->resolveKeyPath(keyPath, newDepth, value); } return true; @@ -372,12 +372,12 @@ bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, return false; } -bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, +bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) { if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) { if (keyPath.propagate(name(), depth)) { - uint newDepth = keyPath.nextDepth(name(), depth); + uint32_t newDepth = keyPath.nextDepth(name(), depth); for (const auto &layer : mLayers) { layer->resolveKeyPath(keyPath, newDepth, value); } @@ -507,7 +507,7 @@ void renderer::CompLayer::render(VPainter *painter, const VRle &inheritMask, renderHelper(&srcPainter, inheritMask, matteRle, cache); srcPainter.end(); painter->drawBitmap(VPoint(), srcBitmap, - uchar(combinedAlpha() * 255.0f)); + uint8_t(combinedAlpha() * 255.0f)); cache.release_surface(srcBitmap); } else { renderHelper(painter, inheritMask, matteRle, cache); @@ -597,11 +597,21 @@ void renderer::CompLayer::renderMatteLayer(VPainter *painter, const VRle &mask, srcBitmap.updateLuma(); } + auto clip = layerPainter.clipBoundingRect(); + + // if the layer has only one renderer then use it as the clip rect + // when blending 2 buffer and copy back to final buffer to avoid + // unnecessary pixel processing. + if (layer->renderList().size() == 1) + { + clip = layer->renderList()[0]->rle().boundingRect(); + } + // 2.3 draw src buffer as mask - layerPainter.drawBitmap(VPoint(), srcBitmap); + layerPainter.drawBitmap(clip, srcBitmap, clip); layerPainter.end(); // 3. draw the result buffer into painter - painter->drawBitmap(VPoint(), layerBitmap); + painter->drawBitmap(clip, layerBitmap, clip); cache.release_surface(srcBitmap); cache.release_surface(layerBitmap); @@ -853,7 +863,7 @@ renderer::DrawableList renderer::ShapeLayer::renderList() return {mDrawableList.data(), mDrawableList.size()}; } -bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth, +bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) { if (!keyPath.skip(name())) { @@ -870,7 +880,7 @@ bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth, } if (keyPath.propagate(name(), depth)) { - uint newDepth = keyPath.nextDepth(name(), depth); + uint32_t newDepth = keyPath.nextDepth(name(), depth); for (auto &child : mContents) { child->resolveKeyPath(keyPath, newDepth, value); } @@ -878,7 +888,7 @@ bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth, return true; } -bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth, +bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) { if (!keyPath.matches(mModel.name(), depth)) { @@ -893,7 +903,7 @@ bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth, return false; } -bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint depth, +bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) { if (!keyPath.matches(mModel.name(), depth)) { @@ -1097,7 +1107,7 @@ void renderer::Rect::updatePath(VPath &path, int frameNo) { VPointF pos = mData->mPos.value(frameNo); VPointF size = mData->mSize.value(frameNo); - float roundness = mData->mRound.value(frameNo); + float roundness = mData->roundness(frameNo); VRectF r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(), size.y()); diff --git a/src/lottie/lottieitem.h b/src/lottie/lottieitem.h index 1fcb561b9..001ce667c 100644 --- a/src/lottie/lottieitem.h +++ b/src/lottie/lottieitem.h @@ -80,7 +80,7 @@ namespace renderer { using DrawableList = VSpan; -enum class DirtyFlagBit : uchar { +enum class DirtyFlagBit : uint8_t { None = 0x00, Matrix = 0x01, Alpha = 0x02, @@ -239,8 +239,8 @@ public: std::vector & cmasks() { return mCApiData->mMasks; } std::vector & cnodes() { return mCApiData->mCNodeList; } const char * name() const { return mLayerData->name(); } - virtual bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, - LOTVariant &value); + virtual bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, + LOTVariant &value); protected: virtual void preprocessStage(const VRect &clip) = 0; @@ -275,7 +275,7 @@ public: void render(VPainter *painter, const VRle &mask, const VRle &matteRle, SurfaceCache &cache) final; void buildLayerNode() final; - bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, + bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) override; protected: @@ -317,7 +317,7 @@ public: explicit ShapeLayer(model::Layer *layerData, VArenaAlloc *allocator); DrawableList renderList() final; void buildLayerNode() final; - bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, + bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) override; protected: @@ -355,13 +355,13 @@ private: class Object { public: - enum class Type : uchar { Unknown, Group, Shape, Paint, Trim }; + enum class Type : uint8_t { Unknown, Group, Shape, Paint, Trim }; virtual ~Object() = default; Object & operator=(Object &&) noexcept = delete; virtual void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) = 0; virtual void renderList(std::vector &) {} - virtual bool resolveKeyPath(LOTKeyPath &, uint, LOTVariant &) + virtual bool resolveKeyPath(LOTKeyPath &, uint32_t, LOTVariant &) { return false; } @@ -387,7 +387,7 @@ public: static const char *TAG = "__"; return mModel.hasModel() ? mModel.name() : TAG; } - bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, + bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) override; protected: @@ -449,7 +449,7 @@ protected: { return (mData->mPos.changed(prevFrame, curFrame) || mData->mSize.changed(prevFrame, curFrame) || - mData->mRound.changed(prevFrame, curFrame)); + mData->roundnessChanged(prevFrame, curFrame)); } }; @@ -532,7 +532,7 @@ public: protected: bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final; - bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, + bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) final; private: @@ -557,7 +557,7 @@ public: protected: bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final; - bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, + bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value) final; private: diff --git a/src/lottie/lottieitem_capi.cpp b/src/lottie/lottieitem_capi.cpp index a4d8546f5..31ca54a68 100644 --- a/src/lottie/lottieitem_capi.cpp +++ b/src/lottie/lottieitem_capi.cpp @@ -88,7 +88,7 @@ void renderer::Layer::buildLayerNode() mCApiData = std::make_unique(); clayer().keypath = name(); } - if (complexContent()) clayer().mAlpha = uchar(combinedAlpha() * 255.f); + if (complexContent()) clayer().mAlpha = uint8_t(combinedAlpha() * 255.f); clayer().mVisible = visible(); // update matte if (hasMatte()) { @@ -121,10 +121,10 @@ void renderer::Layer::buildLayerNode() auto ptPtr = reinterpret_cast(pts.data()); auto elmPtr = reinterpret_cast(elm.data()); cNode.mPath.ptPtr = ptPtr; - cNode.mPath.ptCount = pts.size(); + cNode.mPath.ptCount = 2 * pts.size(); cNode.mPath.elmPtr = elmPtr; cNode.mPath.elmCount = elm.size(); - cNode.mAlpha = uchar(mask.mCombinedAlpha * 255.0f); + cNode.mAlpha = uint8_t(mask.mCombinedAlpha * 255.0f); switch (mask.maskMode()) { case model::Mask::Mode::Add: cNode.mMode = MaskAdd; @@ -196,7 +196,7 @@ void renderer::ImageLayer::buildLayerNode() // Alpha calculation already combined. lotDrawable->mCNode->mImageInfo.mAlpha = - uchar(lotDrawable->mBrush.mTexture->mAlpha); + uint8_t(lotDrawable->mBrush.mTexture->mAlpha); cnodes().push_back(lotDrawable->mCNode.get()); } @@ -216,7 +216,7 @@ static void updateGStops(LOTNode *n, const VGradient *grad) LOTGradientStop *ptr = n->mGradient.stopPtr; for (const auto &i : grad->mStops) { ptr->pos = i.first; - ptr->a = uchar(i.second.alpha() * grad->alpha()); + ptr->a = uint8_t(i.second.alpha() * grad->alpha()); ptr->r = i.second.red(); ptr->g = i.second.green(); ptr->b = i.second.blue(); diff --git a/src/lottie/lottiekeypath.cpp b/src/lottie/lottiekeypath.cpp index e8b6c6aef..21b01aac2 100644 --- a/src/lottie/lottiekeypath.cpp +++ b/src/lottie/lottiekeypath.cpp @@ -12,7 +12,7 @@ LOTKeyPath::LOTKeyPath(const std::string &keyPath) } } -bool LOTKeyPath::matches(const std::string &key, uint depth) +bool LOTKeyPath::matches(const std::string &key, uint32_t depth) { if (skip(key)) { // This is an object we programatically create. @@ -28,7 +28,7 @@ bool LOTKeyPath::matches(const std::string &key, uint depth) return false; } -uint LOTKeyPath::nextDepth(const std::string key, uint depth) +uint32_t LOTKeyPath::nextDepth(const std::string key, uint32_t depth) { if (skip(key)) { // If it's a container then we added programatically and it isn't a part @@ -51,7 +51,7 @@ uint LOTKeyPath::nextDepth(const std::string key, uint depth) return depth; } -bool LOTKeyPath::fullyResolvesTo(const std::string key, uint depth) +bool LOTKeyPath::fullyResolvesTo(const std::string key, uint32_t depth) { if (depth > mKeys.size()) { return false; diff --git a/src/lottie/lottiekeypath.h b/src/lottie/lottiekeypath.h index 0b59b161f..2c53287ef 100644 --- a/src/lottie/lottiekeypath.h +++ b/src/lottie/lottiekeypath.h @@ -30,19 +30,19 @@ class LOTKeyPath { public: LOTKeyPath(const std::string &keyPath); - bool matches(const std::string &key, uint depth); - uint nextDepth(const std::string key, uint depth); - bool fullyResolvesTo(const std::string key, uint depth); + bool matches(const std::string &key, uint32_t depth); + uint32_t nextDepth(const std::string key, uint32_t depth); + bool fullyResolvesTo(const std::string key, uint32_t depth); - bool propagate(const std::string key, uint depth) + bool propagate(const std::string key, uint32_t depth) { return skip(key) ? true : (depth < size()) || (mKeys[depth] == "**"); } bool skip(const std::string &key) const { return key == "__"; } private: - bool isGlobstar(uint depth) const { return mKeys[depth] == "**"; } - bool isGlob(uint depth) const { return mKeys[depth] == "*"; } + bool isGlobstar(uint32_t depth) const { return mKeys[depth] == "**"; } + bool isGlob(uint32_t depth) const { return mKeys[depth] == "*"; } bool endsWithGlobstar() const { return mKeys.back() == "**"; } size_t size() const { return mKeys.size() - 1; } diff --git a/src/lottie/lottiemodel.cpp b/src/lottie/lottiemodel.cpp index e2b48e64b..1bca99d2e 100644 --- a/src/lottie/lottiemodel.cpp +++ b/src/lottie/lottiemodel.cpp @@ -205,7 +205,7 @@ void model::Dash::getDashInfo(int frameNo, std::vector &result) const { result.clear(); - if (mData.empty()) return; + if (mData.size() <= 1) return; if (result.capacity() < mData.size()) result.reserve(mData.size() + 1); @@ -256,52 +256,12 @@ void model::Gradient::populate(VGradientStops &stops, int frameNo) auto opacityArraySize = size - colorPoints * 4; float *opacityPtr = ptr + (colorPoints * 4); stops.clear(); - size_t j = 0; for (int i = 0; i < colorPoints; i++) { float colorStop = ptr[0]; model::Color color = model::Color(ptr[1], ptr[2], ptr[3]); if (opacityArraySize) { - if (j == opacityArraySize) { - // already reached the end - float stop1 = opacityPtr[j - 4]; - float op1 = opacityPtr[j - 3]; - float stop2 = opacityPtr[j - 2]; - float op2 = opacityPtr[j - 1]; - if (colorStop > stop2) { - stops.push_back( - std::make_pair(colorStop, color.toColor(op2))); - } else { - float progress = (colorStop - stop1) / (stop2 - stop1); - float opacity = op1 + progress * (op2 - op1); - stops.push_back( - std::make_pair(colorStop, color.toColor(opacity))); - } - continue; - } - for (; j < opacityArraySize; j += 2) { - float opacityStop = opacityPtr[j]; - if (opacityStop < colorStop) { - // add a color using opacity stop - stops.push_back(std::make_pair( - opacityStop, color.toColor(opacityPtr[j + 1]))); - continue; - } - // add a color using color stop - if (j == 0) { - stops.push_back(std::make_pair( - colorStop, color.toColor(opacityPtr[j + 1]))); - } else { - float progress = (colorStop - opacityPtr[j - 2]) / - (opacityPtr[j] - opacityPtr[j - 2]); - float opacity = - opacityPtr[j - 1] + - progress * (opacityPtr[j + 1] - opacityPtr[j - 1]); - stops.push_back( - std::make_pair(colorStop, color.toColor(opacity))); - } - j += 2; - break; - } + float opacity = getOpacityAtPosition(opacityPtr, opacityArraySize, colorStop); + stops.push_back(std::make_pair(colorStop, color.toColor(opacity))); } else { stops.push_back(std::make_pair(colorStop, color.toColor())); } @@ -309,6 +269,21 @@ void model::Gradient::populate(VGradientStops &stops, int frameNo) } } +float model::Gradient::getOpacityAtPosition(float *opacities, size_t opacityArraySize, float position) +{ + for (size_t i = 2; i < opacityArraySize; i += 2) + { + float lastPosition = opacities[i - 2]; + float thisPosition = opacities[i]; + if (opacities[i] >= position) { + float progress = (position - lastPosition) / (thisPosition - lastPosition); + progress = progress < 0.0f ? 0.0f : 1.0f < progress ? 1.0f : progress; //clamp(progress, 0, 1) + return opacities[i - 1] + progress * (opacities[i + 1] - opacities[i - 1]); + } + } + return 0.0f; +} + void model::Gradient::update(std::unique_ptr &grad, int frameNo) { bool init = false; diff --git a/src/lottie/lottiemodel.h b/src/lottie/lottiemodel.h index 851341476..3e0430853 100644 --- a/src/lottie/lottiemodel.h +++ b/src/lottie/lottiemodel.h @@ -57,9 +57,9 @@ inline T lerp(const T &start, const T &end, float t) namespace model { -enum class MatteType : uchar { None = 0, Alpha = 1, AlphaInv, Luma, LumaInv }; +enum class MatteType : uint8_t { None = 0, Alpha = 1, AlphaInv, Luma, LumaInv }; -enum class BlendMode : uchar { +enum class BlendMode : uint8_t { Normal = 0, Multiply = 1, Screen = 2, @@ -72,8 +72,8 @@ public: Color(float red, float green, float blue) : r(red), g(green), b(blue) {} VColor toColor(float a = 1) { - return VColor(uchar(255 * r), uchar(255 * g), uchar(255 * b), - uchar(255 * a)); + return VColor(uint8_t(255 * r), uint8_t(255 * g), uint8_t(255 * b), + uint8_t(255 * a)); } friend inline Color operator+(const Color &c1, const Color &c2); friend inline Color operator-(const Color &c1, const Color &c2); @@ -111,6 +111,11 @@ struct PathData { VPath &result) { result.reset(); + // test for empty animation data. + if (start.mPoints.empty() || end.mPoints.empty()) + { + return; + } auto size = std::min(start.mPoints.size(), end.mPoints.size()); /* reserve exact memory requirement at once * ptSize = size + 1(size + close) @@ -440,7 +445,8 @@ public: Path, Polystar, Trim, - Repeater + Repeater, + RoundedCorner }; explicit Object(Object::Type type) : mPtr(nullptr) @@ -661,7 +667,7 @@ public: class Layer : public Group { public: - enum class Type : uchar { + enum class Type : uint8_t { Precomp = 0, Solid = 1, Image = 2, @@ -670,6 +676,7 @@ public: Text = 5 }; Layer() : Group(Object::Type::Layer) {} + bool hasRoundedCorner() const noexcept { return mHasRoundedCorner; } bool hasPathOperator() const noexcept { return mHasPathOperator; } bool hasGradient() const noexcept { return mHasGradient; } bool hasMask() const noexcept { return mHasMask; } @@ -680,7 +687,10 @@ public: int inFrame() const noexcept { return mInFrame; } int outFrame() const noexcept { return mOutFrame; } int startFrame() const noexcept { return mStartFrame; } - Color solidColor() const noexcept { return mExtra->mSolidColor; } + Color solidColor() const noexcept + { + return mExtra ? mExtra->mSolidColor : Color(); + } bool autoOrient() const noexcept { return mAutoOrient; } int timeRemap(int frameNo) const; VSize layerSize() const { return mLayerSize; } @@ -694,10 +704,7 @@ public: { return mTransform ? mTransform->opacity(frameNo) : 1.0f; } - Asset *asset() const - { - return (mExtra && mExtra->mAsset) ? mExtra->mAsset : nullptr; - } + Asset *asset() const { return mExtra ? mExtra->mAsset : nullptr; } struct Extra { Color mSolidColor; std::string mPreCompRefId; @@ -717,6 +724,7 @@ public: MatteType mMatteType{MatteType::None}; Type mLayerType{Layer::Type::Null}; BlendMode mBlendMode{BlendMode::Normal}; + bool mHasRoundedCorner{false}; bool mHasPathOperator{false}; bool mHasMask{false}; bool mHasRepeater{false}; @@ -813,6 +821,7 @@ public: private: void populate(VGradientStops &stops, int frameNo); + float getOpacityAtPosition(float *opacities, size_t opacityArraySize, float position); public: int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/ @@ -893,11 +902,30 @@ public: Property mShape; }; +class RoundedCorner : public Object { +public: + RoundedCorner() : Object(Object::Type::RoundedCorner) {} + float radius(int frameNo) const { return mRadius.value(frameNo);} +public: + Property mRadius{0}; +}; + class Rect : public Shape { public: Rect() : Shape(Object::Type::Rect) {} + float roundness(int frameNo) + { + return mRoundedCorner ? mRoundedCorner->radius(frameNo) : + mRound.value(frameNo); + } + bool roundnessChanged(int prevFrame, int curFrame) + { + return mRoundedCorner ? mRoundedCorner->mRadius.changed(prevFrame, curFrame) : + mRound.changed(prevFrame, curFrame); + } public: + RoundedCorner* mRoundedCorner{nullptr}; Property mPos; Property mSize; Property mRound{0}; diff --git a/src/lottie/lottieparser.cpp b/src/lottie/lottieparser.cpp index e5ef03774..83be17e39 100644 --- a/src/lottie/lottieparser.cpp +++ b/src/lottie/lottieparser.cpp @@ -145,6 +145,10 @@ public: return true; } + void Error() + { + st_ = kError; + } protected: explicit LookaheadParserHandler(char *str); @@ -191,6 +195,7 @@ public: int GetInt(); double GetDouble(); const char * GetString(); + std::string GetStringObject(); bool GetBool(); void GetNull(); @@ -228,6 +233,9 @@ public: model::Object * parseObjectTypeAttr(); model::Object * parseGroupObject(); model::Rect * parseRectObject(); + model::RoundedCorner * parseRoundedCorner(); + void updateRoundedCorner(model::Group *parent, model::RoundedCorner *rc); + model::Ellipse * parseEllipseObject(); model::Path * parseShapeObject(); model::Polystar *parsePolystarObject(); @@ -396,7 +404,6 @@ bool LottieParserImpl::EnterObject() { if (st_ != kEnteringObject) { st_ = kError; - RAPIDJSON_ASSERT(false); return false; } @@ -408,7 +415,6 @@ bool LottieParserImpl::EnterArray() { if (st_ != kEnteringArray) { st_ = kError; - RAPIDJSON_ASSERT(false); return false; } @@ -438,7 +444,6 @@ const char *LottieParserImpl::NextObjectKey() } if (st_ != kExitingObject) { - RAPIDJSON_ASSERT(false); st_ = kError; return nullptr; } @@ -462,7 +467,6 @@ bool LottieParserImpl::NextArrayValue() } if (st_ == kError || st_ == kHasKey) { - RAPIDJSON_ASSERT(false); st_ = kError; return false; } @@ -474,7 +478,6 @@ int LottieParserImpl::GetInt() { if (st_ != kHasNumber || !v_.IsInt()) { st_ = kError; - RAPIDJSON_ASSERT(false); return 0; } @@ -487,7 +490,6 @@ double LottieParserImpl::GetDouble() { if (st_ != kHasNumber) { st_ = kError; - RAPIDJSON_ASSERT(false); return 0.; } @@ -500,7 +502,6 @@ bool LottieParserImpl::GetBool() { if (st_ != kHasBool) { st_ = kError; - RAPIDJSON_ASSERT(false); return false; } @@ -523,7 +524,6 @@ const char *LottieParserImpl::GetString() { if (st_ != kHasString) { st_ = kError; - RAPIDJSON_ASSERT(false); return nullptr; } @@ -532,6 +532,17 @@ const char *LottieParserImpl::GetString() return result; } +std::string LottieParserImpl::GetStringObject() +{ + auto str = GetString(); + + if (str) { + return std::string(str); + } + + return {}; +} + void LottieParserImpl::SkipOut(int depth) { do { @@ -540,7 +551,6 @@ void LottieParserImpl::SkipOut(int depth) } else if (st_ == kExitingArray || st_ == kExitingObject) { --depth; } else if (st_ == kError) { - RAPIDJSON_ASSERT(false); return; } @@ -606,7 +616,6 @@ void LottieParserImpl::Skip(const char * /*key*/) model::BlendMode LottieParserImpl::getBlendMode() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); auto mode = model::BlendMode::Normal; switch (GetInt()) { @@ -643,7 +652,6 @@ void LottieParserImpl::resolveLayerRefs() void LottieParserImpl::parseComposition() { - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); std::shared_ptr sharedComposition = std::make_shared(); @@ -651,22 +659,16 @@ void LottieParserImpl::parseComposition() compRef = comp; while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "v")) { - RAPIDJSON_ASSERT(PeekType() == kStringType); - comp->mVersion = std::string(GetString()); + comp->mVersion = GetStringObject(); } else if (0 == strcmp(key, "w")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); comp->mSize.setWidth(GetInt()); } else if (0 == strcmp(key, "h")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); comp->mSize.setHeight(GetInt()); } else if (0 == strcmp(key, "ip")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); comp->mStartFrame = GetDouble(); } else if (0 == strcmp(key, "op")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); comp->mEndFrame = GetDouble(); } else if (0 == strcmp(key, "fr")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); comp->mFrameRate = GetDouble(); } else if (0 == strcmp(key, "assets")) { parseAssets(comp); @@ -686,6 +688,10 @@ void LottieParserImpl::parseComposition() // don't have a valid bodymovin header return; } + if (comp->mStartFrame > comp->mEndFrame) { + // reveresed animation? missing data? + return; + } if (!IsValid()) { return; } @@ -700,20 +706,16 @@ void LottieParserImpl::parseComposition() void LottieParserImpl::parseMarker() { - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); std::string comment; int timeframe{0}; int duration{0}; while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "cm")) { - RAPIDJSON_ASSERT(PeekType() == kStringType); - comment = std::string(GetString()); + comment = GetStringObject(); } else if (0 == strcmp(key, "tm")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); timeframe = GetDouble(); } else if (0 == strcmp(key, "dr")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); duration = GetDouble(); } else { @@ -729,7 +731,6 @@ void LottieParserImpl::parseMarker() void LottieParserImpl::parseMarkers() { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { parseMarker(); @@ -739,7 +740,6 @@ void LottieParserImpl::parseMarkers() void LottieParserImpl::parseAssets(model::Composition *composition) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { auto asset = parseAsset(); @@ -815,8 +815,6 @@ static std::string toString(const T &value) */ model::Asset *LottieParserImpl::parseAsset() { - RAPIDJSON_ASSERT(PeekType() == kObjectType); - auto asset = allocator().make(); std::string filename; std::string relativePath; @@ -824,30 +822,24 @@ model::Asset *LottieParserImpl::parseAsset() EnterObject(); while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "w")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); asset->mWidth = GetInt(); } else if (0 == strcmp(key, "h")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); asset->mHeight = GetInt(); } else if (0 == strcmp(key, "p")) { /* image name */ asset->mAssetType = model::Asset::Type::Image; - RAPIDJSON_ASSERT(PeekType() == kStringType); - filename = std::string(GetString()); + filename = GetStringObject(); } else if (0 == strcmp(key, "u")) { /* relative image path */ - RAPIDJSON_ASSERT(PeekType() == kStringType); - relativePath = std::string(GetString()); + relativePath = GetStringObject(); } else if (0 == strcmp(key, "e")) { /* relative image path */ embededResource = GetInt(); } else if (0 == strcmp(key, "id")) { /* reference id*/ if (PeekType() == kStringType) { - asset->mRefId = std::string(GetString()); + asset->mRefId = GetStringObject(); } else { - RAPIDJSON_ASSERT(PeekType() == kNumberType); asset->mRefId = toString(GetInt()); } } else if (0 == strcmp(key, "layers")) { asset->mAssetType = model::Asset::Type::Precomp; - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); bool staticFlag = true; while (NextArrayValue()) { @@ -886,7 +878,6 @@ void LottieParserImpl::parseLayers(model::Composition *comp) comp->mRootLayer->mLayerType = model::Layer::Type::Precomp; comp->mRootLayer->setName("__"); bool staticFlag = true; - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { auto layer = parseLayer(); @@ -900,6 +891,8 @@ void LottieParserImpl::parseLayers(model::Composition *comp) model::Color LottieParserImpl::toColor(const char *str) { + if (!str) return {}; + model::Color color; auto len = strlen(str); @@ -925,7 +918,6 @@ model::Color LottieParserImpl::toColor(const char *str) model::MatteType LottieParserImpl::getMatteType() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); switch (GetInt()) { case 1: return model::MatteType::Alpha; @@ -947,7 +939,6 @@ model::MatteType LottieParserImpl::getMatteType() model::Layer::Type LottieParserImpl::getLayerType() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); switch (GetInt()) { case 0: return model::Layer::Type::Precomp; @@ -979,7 +970,6 @@ model::Layer::Type LottieParserImpl::getLayerType() */ model::Layer *LottieParserImpl::parseLayer() { - RAPIDJSON_ASSERT(PeekType() == kObjectType); model::Layer *layer = allocator().make(); curLayerRef = layer; bool ddd = true; @@ -988,43 +978,33 @@ model::Layer *LottieParserImpl::parseLayer() if (0 == strcmp(key, "ty")) { /* Type of layer*/ layer->mLayerType = getLayerType(); } else if (0 == strcmp(key, "nm")) { /*Layer name*/ - RAPIDJSON_ASSERT(PeekType() == kStringType); layer->setName(GetString()); } else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for parenting and expressions.*/ - RAPIDJSON_ASSERT(PeekType() == kNumberType); layer->mId = GetInt(); } else if (0 == strcmp(key, "ddd")) { /*3d layer */ - RAPIDJSON_ASSERT(PeekType() == kNumberType); ddd = GetInt(); } else if (0 == strcmp(key, "parent")) { /*Layer Parent. Uses "ind" of parent.*/ - RAPIDJSON_ASSERT(PeekType() == kNumberType); layer->mParentId = GetInt(); } else if (0 == strcmp(key, "refId")) { /*preComp Layer reference id*/ - RAPIDJSON_ASSERT(PeekType() == kStringType); - layer->extra()->mPreCompRefId = std::string(GetString()); + layer->extra()->mPreCompRefId = GetStringObject(); layer->mHasGradient = true; mLayersToUpdate.push_back(layer); } else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching" - RAPIDJSON_ASSERT(PeekType() == kNumberType); layer->mTimeStreatch = GetDouble(); } else if (0 == strcmp(key, "tm")) { // time remapping parseProperty(layer->extra()->mTimeRemap); } else if (0 == strcmp(key, "ip")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); layer->mInFrame = std::lround(GetDouble()); } else if (0 == strcmp(key, "op")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); layer->mOutFrame = std::lround(GetDouble()); } else if (0 == strcmp(key, "st")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); layer->mStartFrame = GetDouble(); } else if (0 == strcmp(key, "bm")) { layer->mBlendMode = getBlendMode(); } else if (0 == strcmp(key, "ks")) { - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); layer->mTransform = parseTransformObject(ddd); } else if (0 == strcmp(key, "shapes")) { @@ -1084,7 +1064,7 @@ model::Layer *LottieParserImpl::parseLayer() staticFlag &= child->isStatic(); } - if (layer->hasMask()) { + if (layer->hasMask() && layer->mExtra) { for (const auto &mask : layer->mExtra->mMasks) { staticFlag &= mask->isStatic(); } @@ -1097,7 +1077,6 @@ model::Layer *LottieParserImpl::parseLayer() void LottieParserImpl::parseMaskProperty(model::Layer *layer) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { layer->extra()->mMasks.push_back(parseMaskObject()); @@ -1108,7 +1087,6 @@ model::Mask *LottieParserImpl::parseMaskObject() { auto obj = allocator().make(); - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "inv")) { @@ -1153,7 +1131,6 @@ model::Mask *LottieParserImpl::parseMaskObject() void LottieParserImpl::parseShapesAttr(model::Layer *layer) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { parseObject(layer); @@ -1162,13 +1139,19 @@ void LottieParserImpl::parseShapesAttr(model::Layer *layer) model::Object *LottieParserImpl::parseObjectTypeAttr() { - RAPIDJSON_ASSERT(PeekType() == kStringType); const char *type = GetString(); + if (!type) { + vWarning << "No object type specified"; + return nullptr; + } if (0 == strcmp(type, "gr")) { return parseGroupObject(); } else if (0 == strcmp(type, "rc")) { return parseRectObject(); - } else if (0 == strcmp(type, "el")) { + } else if (0 == strcmp(type, "rd")) { + curLayerRef->mHasRoundedCorner = true; + return parseRoundedCorner(); + } else if (0 == strcmp(type, "el")) { return parseEllipseObject(); } else if (0 == strcmp(type, "tr")) { return parseTransformObject(); @@ -1205,18 +1188,39 @@ model::Object *LottieParserImpl::parseObjectTypeAttr() void LottieParserImpl::parseObject(model::Group *parent) { - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "ty")) { auto child = parseObjectTypeAttr(); - if (child && !child->hidden()) parent->mChildren.push_back(child); + if (child && !child->hidden()) { + if (child->type() == model::Object::Type::RoundedCorner) { + updateRoundedCorner(parent, static_cast(child)); + } + parent->mChildren.push_back(child); + } } else { Skip(key); } } } +void LottieParserImpl::updateRoundedCorner(model::Group *group, model::RoundedCorner *rc) +{ + for(auto &e : group->mChildren) + { + if (e->type() == model::Object::Type::Rect) { + static_cast(e)->mRoundedCorner = rc; + if (!rc->isStatic()) { + e->setStatic(false); + group->setStatic(false); + //@TODO need to propagate. + } + } else if ( e->type() == model::Object::Type::Group) { + updateRoundedCorner(static_cast(e), rc); + } + } +} + model::Object *LottieParserImpl::parseGroupObject() { auto group = allocator().make(); @@ -1225,14 +1229,13 @@ model::Object *LottieParserImpl::parseGroupObject() if (0 == strcmp(key, "nm")) { group->setName(GetString()); } else if (0 == strcmp(key, "it")) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { - RAPIDJSON_ASSERT(PeekType() == kObjectType); parseObject(group); } - if (group->mChildren.back()->type() == - model::Object::Type::Transform) { + if (!group->mChildren.empty() + && group->mChildren.back()->type() + == model::Object::Type::Transform) { group->mTransform = static_cast(group->mChildren.back()); group->mChildren.pop_back(); @@ -1282,6 +1285,28 @@ model::Rect *LottieParserImpl::parseRectObject() return obj; } +/* + * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json + */ +model::RoundedCorner *LottieParserImpl::parseRoundedCorner() +{ + auto obj = allocator().make(); + + while (const char *key = NextObjectKey()) { + if (0 == strcmp(key, "nm")) { + obj->setName(GetString()); + } else if (0 == strcmp(key, "r")) { + parseProperty(obj->mRadius); + } else if (0 == strcmp(key, "hd")) { + obj->setHidden(GetBool()); + } else { + Skip(key); + } + } + obj->setStatic(obj->mRadius.isStatic()); + return obj; +} + /* * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/ellipse.json */ @@ -1387,7 +1412,6 @@ model::Polystar *LottieParserImpl::parsePolystarObject() model::Trim::TrimType LottieParserImpl::getTrimType() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); switch (GetInt()) { case 1: return model::Trim::TrimType::Simultaneously; @@ -1396,7 +1420,7 @@ model::Trim::TrimType LottieParserImpl::getTrimType() return model::Trim::TrimType::Individually; break; default: - RAPIDJSON_ASSERT(0); + Error(); return model::Trim::TrimType::Simultaneously; break; } @@ -1544,10 +1568,13 @@ model::Transform *LottieParserImpl::parseTransformObject(bool ddd) } else if (0 == strcmp(key, "hd")) { objT->setHidden(GetBool()); } else if (0 == strcmp(key, "rx")) { + if (!obj->mExtra) return nullptr; parseProperty(obj->mExtra->m3DRx); } else if (0 == strcmp(key, "ry")) { + if (!obj->mExtra) return nullptr; parseProperty(obj->mExtra->m3DRy); } else if (0 == strcmp(key, "rz")) { + if (!obj->mExtra) return nullptr; parseProperty(obj->mExtra->m3DRz); } else { Skip(key); @@ -1606,7 +1633,6 @@ model::Fill *LottieParserImpl::parseFillObject() */ CapStyle LottieParserImpl::getLineCap() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); switch (GetInt()) { case 1: return CapStyle::Flat; @@ -1622,7 +1648,6 @@ CapStyle LottieParserImpl::getLineCap() FillRule LottieParserImpl::getFillRule() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); switch (GetInt()) { case 1: return FillRule::Winding; @@ -1641,7 +1666,6 @@ FillRule LottieParserImpl::getFillRule() */ JoinStyle LottieParserImpl::getLineJoin() { - RAPIDJSON_ASSERT(PeekType() == kNumberType); switch (GetInt()) { case 1: return JoinStyle::Miter; @@ -1678,7 +1702,6 @@ model::Stroke *LottieParserImpl::parseStrokeObject() } else if (0 == strcmp(key, "lj")) { obj->mJoinStyle = getLineJoin(); } else if (0 == strcmp(key, "ml")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); obj->mMiterLimit = GetDouble(); } else if (0 == strcmp(key, "d")) { parseDashProperty(obj->mDash); @@ -1700,7 +1723,6 @@ void LottieParserImpl::parseGradientProperty(model::Gradient *obj, const char * key) { if (0 == strcmp(key, "t")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); obj->mGradientType = GetInt(); } else if (0 == strcmp(key, "o")) { parseProperty(obj->mOpacity); @@ -1758,10 +1780,8 @@ model::GradientFill *LottieParserImpl::parseGFillObject() void LottieParserImpl::parseDashProperty(model::Dash &dash) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "v")) { @@ -1791,7 +1811,6 @@ model::GradientStroke *LottieParserImpl::parseGStrokeObject() } else if (0 == strcmp(key, "lj")) { obj->mJoinStyle = getLineJoin(); } else if (0 == strcmp(key, "ml")) { - RAPIDJSON_ASSERT(PeekType() == kNumberType); obj->mMiterLimit = GetDouble(); } else if (0 == strcmp(key, "d")) { parseDashProperty(obj->mDash); @@ -1807,10 +1826,8 @@ model::GradientStroke *LottieParserImpl::parseGStrokeObject() void LottieParserImpl::getValue(std::vector &v) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); VPointF pt; getValue(pt); @@ -1847,7 +1864,7 @@ void LottieParserImpl::getValue(float &val) } else if (PeekType() == kNumberType) { val = GetDouble(); } else { - RAPIDJSON_ASSERT(0); + Error(); } } @@ -1890,7 +1907,7 @@ void LottieParserImpl::getValue(int &val) } else if (PeekType() == kNumberType) { val = GetInt(); } else { - RAPIDJSON_ASSERT(0); + Error(); } } @@ -1905,7 +1922,6 @@ void LottieParserImpl::parsePathInfo() bool arrayWrapper = (PeekType() == kArrayType); if (arrayWrapper) EnterArray(); - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "i")) { @@ -1917,7 +1933,7 @@ void LottieParserImpl::parsePathInfo() } else if (0 == strcmp(key, "c")) { mPathInfo.mClosed = GetBool(); } else { - RAPIDJSON_ASSERT(0); + Error(); Skip(nullptr); } } @@ -1937,7 +1953,6 @@ void LottieParserImpl::getValue(model::PathData &obj) VPointF LottieParserImpl::parseInperpolatorPoint() { VPointF cp; - RAPIDJSON_ASSERT(PeekType() == kObjectType); EnterObject(); while (const char *key = NextObjectKey()) { if (0 == strcmp(key, "x")) { @@ -2026,17 +2041,15 @@ void LottieParserImpl::parseKeyFrame(model::KeyFrames &obj) continue; } else if (0 == strcmp(key, "n")) { if (PeekType() == kStringType) { - parsed.interpolatorKey = GetString(); + parsed.interpolatorKey = GetStringObject(); } else { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { - RAPIDJSON_ASSERT(PeekType() == kStringType); if (parsed.interpolatorKey.empty()) { - parsed.interpolatorKey = GetString(); + parsed.interpolatorKey = GetStringObject(); } else { // skip rest of the string - GetString(); + Skip(nullptr); } } } @@ -2092,12 +2105,10 @@ void LottieParserImpl::parseShapeProperty(model::Property &obj) if (PeekType() == kArrayType) { EnterArray(); while (NextArrayValue()) { - RAPIDJSON_ASSERT(PeekType() == kObjectType); parseKeyFrame(obj.animation()); } } else { if (!obj.isStatic()) { - RAPIDJSON_ASSERT(false); st_ = kError; return; } @@ -2118,14 +2129,12 @@ void LottieParserImpl::parsePropertyHelper(model::Property &obj) { if (PeekType() == kNumberType) { if (!obj.isStatic()) { - RAPIDJSON_ASSERT(false); st_ = kError; return; } /*single value property with no animation*/ getValue(obj.value()); } else { - RAPIDJSON_ASSERT(PeekType() == kArrayType); EnterArray(); while (NextArrayValue()) { /* property with keyframe info*/ @@ -2138,9 +2147,7 @@ void LottieParserImpl::parsePropertyHelper(model::Property &obj) * or array of object without entering the array * thats why this hack is there */ - RAPIDJSON_ASSERT(PeekType() == kNumberType); if (!obj.isStatic()) { - RAPIDJSON_ASSERT(false); st_ = kError; return; } @@ -2252,6 +2259,11 @@ public: << " , a:" << !obj->isStatic() << " }"; break; } + case model::Object::Type::RoundedCorner: { + vDebug << level << "{ RoundedCorner: name: " << obj->name() + << " , a:" << !obj->isStatic() << " }"; + break; + } case model::Object::Type::Ellipse: { vDebug << level << "{ Ellipse: name: " << obj->name() << " , a:" << !obj->isStatic() << " }"; diff --git a/src/vector/stb/CMakeLists.txt b/src/vector/stb/CMakeLists.txt index 4861e993c..b668b43a8 100644 --- a/src/vector/stb/CMakeLists.txt +++ b/src/vector/stb/CMakeLists.txt @@ -2,9 +2,11 @@ if(LOTTIE_MODULE) add_library(rlottie-image-loader SHARED stb_image.cpp ) - target_compile_options(rlottie-image-loader PRIVATE - -fvisibility=hidden - ) + if(NOT MSVC) + target_compile_options(rlottie-image-loader PRIVATE + -fvisibility=hidden + ) + endif() get_filename_component(LOTTIE_MODULE_FILENAME ${LOTTIE_MODULE_PATH} NAME) get_filename_component(LOTTIE_MODULE_DIR ${LOTTIE_MODULE_PATH} DIRECTORY) diff --git a/src/vector/stb/meson.build b/src/vector/stb/meson.build index 49be07630..128114f34 100644 --- a/src/vector/stb/meson.build +++ b/src/vector/stb/meson.build @@ -3,14 +3,14 @@ source_file = ['stb_image.cpp'] if get_option('module') == true rlottie_image_loader_dir = get_option('moduledir') != '' ? get_option('moduledir') : get_option('libdir') - rlottie_image_loader_lib = shared_library('rlottie-image-loader', - source_file, - include_directories : [include_directories('.'), config_dir], - install : true, - install_dir : rlottie_image_loader_dir, - cpp_args : compiler_flags, - gnu_symbol_visibility : 'hidden', - ) + rlottie_image_loader_lib = shared_module('rlottie-image-loader', + source_file, + include_directories : [include_directories('.'), config_dir], + install : true, + install_dir : rlottie_image_loader_dir, + cpp_args : compiler_flags, + gnu_symbol_visibility : 'hidden', + ) cc = meson.get_compiler('cpp') stb_dep = cc.find_library('dl', required : false) else diff --git a/src/vector/stb/stb_image.h b/src/vector/stb/stb_image.h index ae0c1e529..56f818305 100644 --- a/src/vector/stb/stb_image.h +++ b/src/vector/stb/stb_image.h @@ -307,6 +307,10 @@ RECENT REVISION HISTORY: #include #endif // STBI_NO_STDIO +#if defined _WIN32 || defined __CYGWIN__ +#include +#endif // defined _WIN32 || defined __CYGWIN__ + #define STBI_VERSION 1 enum @@ -1149,14 +1153,38 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in static FILE *stbi__fopen(char const *filename, char const *mode) { - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; + FILE *f; +#if defined(_MSC_VER) + DWORD cch = + MultiByteToWideChar(CP_UTF8, 0, filename, -1, nullptr, 0); + wchar_t *filenameU = new wchar_t[cch]; + if(!filenameU) + { + return 0; + } + MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameU, cch); + cch = MultiByteToWideChar(CP_UTF8, 0, mode, -1, nullptr, 0); + wchar_t *modeU = new wchar_t[cch]; + if(!modeU) + { + delete[] filenameU; + return 0; + } + MultiByteToWideChar(CP_UTF8, 0, mode, -1, modeU, cch); +#if _MSC_VER >= 1400 + if(0 != _wfopen_s(&f, filenameU, modeU)) + f = 0; + delete[] filenameU; + delete[] modeU; +#else // _MSC_VER >= 1400 + f = _wfopen(filenameU, modeU); + delete[] filenameU; + delete[] modeU; +#endif // _MSC_VER >= 1400 +#else // _MSC_VER + f = fopen(filename, mode); +#endif //_MSC_VER + return f; } diff --git a/src/vector/vbezier.cpp b/src/vector/vbezier.cpp index 797d56440..15889299d 100644 --- a/src/vector/vbezier.cpp +++ b/src/vector/vbezier.cpp @@ -81,12 +81,12 @@ float VBezier::tAtLength(float l, float totalLength) const t *= 0.5; float lastBigger = 1.0; - while (1) { + for (int num = 0; num < 100500; num++) { VBezier right = *this; VBezier left; right.parameterSplitLeft(t, &left); float lLen = left.length(); - if (fabs(lLen - l) < error) break; + if (fabs(lLen - l) < error) return t; if (lLen < l) { t += (lastBigger - t) * 0.5f; @@ -95,6 +95,7 @@ float VBezier::tAtLength(float l, float totalLength) const t -= t * 0.5f; } } + vWarning << "no convergence"; return t; } diff --git a/src/vector/vbitmap.cpp b/src/vector/vbitmap.cpp index 22cb24346..378db2278 100644 --- a/src/vector/vbitmap.cpp +++ b/src/vector/vbitmap.cpp @@ -31,31 +31,31 @@ V_BEGIN_NAMESPACE void VBitmap::Impl::reset(size_t width, size_t height, VBitmap::Format format) { mRoData = nullptr; - mWidth = uint(width); - mHeight = uint(height); + mWidth = uint32_t(width); + mHeight = uint32_t(height); mFormat = format; mDepth = depth(format); mStride = ((mWidth * mDepth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 4) - mOwnData = std::make_unique(mStride * mHeight); + mOwnData = std::make_unique(mStride * mHeight); } -void VBitmap::Impl::reset(uchar *data, size_t width, size_t height, size_t bytesPerLine, - VBitmap::Format format) +void VBitmap::Impl::reset(uint8_t *data, size_t width, size_t height, + size_t bytesPerLine, VBitmap::Format format) { mRoData = data; - mWidth = uint(width); - mHeight = uint(height); - mStride = uint(bytesPerLine); + mWidth = uint32_t(width); + mHeight = uint32_t(height); + mStride = uint32_t(bytesPerLine); mFormat = format; mDepth = depth(format); mOwnData = nullptr; } -uchar VBitmap::Impl::depth(VBitmap::Format format) +uint8_t VBitmap::Impl::depth(VBitmap::Format format) { - uchar depth = 1; + uint8_t depth = 1; switch (format) { case VBitmap::Format::Alpha8: depth = 8; @@ -70,7 +70,7 @@ uchar VBitmap::Impl::depth(VBitmap::Format format) return depth; } -void VBitmap::Impl::fill(uint /*pixel*/) +void VBitmap::Impl::fill(uint32_t /*pixel*/) { //@TODO } @@ -79,9 +79,9 @@ void VBitmap::Impl::updateLuma() { if (mFormat != VBitmap::Format::ARGB32_Premultiplied) return; auto dataPtr = data(); - for (uint col = 0; col < mHeight; col++) { - uint *pixel = (uint *)(dataPtr + mStride * col); - for (uint row = 0; row < mWidth; row++) { + for (uint32_t col = 0; col < mHeight; col++) { + uint32_t *pixel = (uint32_t *)(dataPtr + mStride * col); + for (uint32_t row = 0; row < mWidth; row++) { int alpha = vAlpha(*pixel); if (alpha == 0) { pixel++; @@ -112,8 +112,8 @@ VBitmap::VBitmap(size_t width, size_t height, VBitmap::Format format) mImpl = rc_ptr(width, height, format); } -VBitmap::VBitmap(uchar *data, size_t width, size_t height, size_t bytesPerLine, - VBitmap::Format format) +VBitmap::VBitmap(uint8_t *data, size_t width, size_t height, + size_t bytesPerLine, VBitmap::Format format) { if (!data || width <= 0 || height <= 0 || bytesPerLine <= 0 || format == Format::Invalid) @@ -122,7 +122,7 @@ VBitmap::VBitmap(uchar *data, size_t width, size_t height, size_t bytesPerLine, mImpl = rc_ptr(data, width, height, bytesPerLine, format); } -void VBitmap::reset(uchar *data, size_t w, size_t h, size_t bytesPerLine, +void VBitmap::reset(uint8_t *data, size_t w, size_t h, size_t bytesPerLine, VBitmap::Format format) { if (mImpl) { @@ -165,12 +165,12 @@ size_t VBitmap::depth() const return mImpl ? mImpl->mDepth : 0; } -uchar *VBitmap::data() +uint8_t *VBitmap::data() { return mImpl ? mImpl->data() : nullptr; } -uchar *VBitmap::data() const +uint8_t *VBitmap::data() const { return mImpl ? mImpl->data() : nullptr; } @@ -195,7 +195,7 @@ VBitmap::Format VBitmap::format() const return mImpl ? mImpl->format() : VBitmap::Format::Invalid; } -void VBitmap::fill(uint pixel) +void VBitmap::fill(uint32_t pixel) { if (mImpl) mImpl->fill(pixel); } diff --git a/src/vector/vbitmap.h b/src/vector/vbitmap.h index a068d7446..bc5059a7f 100644 --- a/src/vector/vbitmap.h +++ b/src/vector/vbitmap.h @@ -30,7 +30,7 @@ V_BEGIN_NAMESPACE class VBitmap { public: - enum class Format: uchar { + enum class Format : uint8_t { Invalid, Alpha8, ARGB32, @@ -39,8 +39,10 @@ public: VBitmap() = default; VBitmap(size_t w, size_t h, VBitmap::Format format); - VBitmap(uchar *data, size_t w, size_t h, size_t bytesPerLine, VBitmap::Format format); - void reset(uchar *data, size_t w, size_t h, size_t stride, VBitmap::Format format); + VBitmap(uint8_t *data, size_t w, size_t h, size_t bytesPerLine, + VBitmap::Format format); + void reset(uint8_t *data, size_t w, size_t h, size_t stride, + VBitmap::Format format); void reset(size_t w, size_t h, VBitmap::Format format=Format::ARGB32_Premultiplied); size_t stride() const; size_t width() const; @@ -48,27 +50,28 @@ public: size_t depth() const; VBitmap::Format format() const; bool valid() const; - uchar * data(); - uchar * data() const; + uint8_t * data(); + uint8_t * data() const; VRect rect() const; VSize size() const; - void fill(uint pixel); + void fill(uint32_t pixel); void updateLuma(); private: struct Impl { - std::unique_ptr mOwnData{nullptr}; - uchar * mRoData{nullptr}; - uint mWidth{0}; - uint mHeight{0}; - uint mStride{0}; - uchar mDepth{0}; + std::unique_ptr mOwnData{nullptr}; + uint8_t * mRoData{nullptr}; + uint32_t mWidth{0}; + uint32_t mHeight{0}; + uint32_t mStride{0}; + uint8_t mDepth{0}; VBitmap::Format mFormat{VBitmap::Format::Invalid}; explicit Impl(size_t width, size_t height, VBitmap::Format format) { reset(width, height, format); } - explicit Impl(uchar *data, size_t w, size_t h, size_t bytesPerLine, VBitmap::Format format) + explicit Impl(uint8_t *data, size_t w, size_t h, size_t bytesPerLine, + VBitmap::Format format) { reset(data, w, h, bytesPerLine, format); } @@ -77,12 +80,12 @@ private: size_t stride() const { return mStride; } size_t width() const { return mWidth; } size_t height() const { return mHeight; } - uchar * data() { return mRoData ? mRoData : mOwnData.get(); } + uint8_t * data() { return mRoData ? mRoData : mOwnData.get(); } VBitmap::Format format() const { return mFormat; } - void reset(uchar *, size_t, size_t, size_t, VBitmap::Format); + void reset(uint8_t *, size_t, size_t, size_t, VBitmap::Format); void reset(size_t, size_t, VBitmap::Format); - static uchar depth(VBitmap::Format format); - void fill(uint); + static uint8_t depth(VBitmap::Format format); + void fill(uint32_t); void updateLuma(); }; diff --git a/src/vector/vbrush.cpp b/src/vector/vbrush.cpp index fc0902ba8..f2ab3387c 100644 --- a/src/vector/vbrush.cpp +++ b/src/vector/vbrush.cpp @@ -43,7 +43,7 @@ VBrush::VBrush(const VColor &color) : mType(VBrush::Type::Solid), mColor(color) { } -VBrush::VBrush(uchar r, uchar g, uchar b, uchar a) +VBrush::VBrush(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : mType(VBrush::Type::Solid), mColor(r, g, b, a) { diff --git a/src/vector/vbrush.h b/src/vector/vbrush.h index 74f8dcafe..a1abbd34e 100644 --- a/src/vector/vbrush.h +++ b/src/vector/vbrush.h @@ -75,7 +75,7 @@ public: VBrush():mType(Type::NoBrush),mColor(){}; explicit VBrush(const VColor &color); explicit VBrush(const VGradient *gradient); - explicit VBrush(uchar r, uchar g, uchar b, uchar a); + explicit VBrush(uint8_t r, uint8_t g, uint8_t b, uint8_t a); explicit VBrush(const VTexture *texture); inline VBrush::Type type() const { return mType; } public: diff --git a/src/vector/vdrawable.cpp b/src/vector/vdrawable.cpp index c8ff02bc7..55b1bc842 100644 --- a/src/vector/vdrawable.cpp +++ b/src/vector/vdrawable.cpp @@ -106,7 +106,7 @@ void VDrawable::setDashInfo(std::vector &dashInfo) bool hasChanged = false; if (obj->mDash.size() == dashInfo.size()) { - for (uint i = 0; i < dashInfo.size(); ++i) { + for (uint32_t i = 0; i < dashInfo.size(); ++i) { if (!vCompare(obj->mDash[i], dashInfo[i])) { hasChanged = true; break; diff --git a/src/vector/vdrawhelper.cpp b/src/vector/vdrawhelper.cpp index 4594ae52c..41593c862 100644 --- a/src/vector/vdrawhelper.cpp +++ b/src/vector/vdrawhelper.cpp @@ -56,7 +56,7 @@ public: VCacheKey hash_val = 0; VCacheData info; const VGradientStops &stops = gradient.mStops; - for (uint i = 0; i < stops.size() && i <= 2; i++) + for (uint32_t i = 0; i < stops.size() && i <= 2; i++) hash_val += VCacheKey(stops[i].second.premulARGB() * gradient.alpha()); @@ -100,11 +100,11 @@ public: } protected: - uint maxCacheSize() const { return 60; } + uint32_t maxCacheSize() const { return 60; } VCacheData addCacheElement(VCacheKey hash_val, const VGradient &gradient) { if (mCache.size() == maxCacheSize()) { - uint count = maxCacheSize() / 10; + uint32_t count = maxCacheSize() / 10; while (count--) { mCache.erase(mCache.begin()); } @@ -147,7 +147,7 @@ bool VGradientCache::generateGradientColorTable(const VGradientStops &stops, colorTable[pos++] = curColor; - while (fpos <= curr->first) { + while (fpos <= curr->first && pos < size) { colorTable[pos] = colorTable[pos - 1]; pos++; fpos += incr; @@ -519,7 +519,7 @@ static void blend_color(size_t size, const VRle::Span *array, void *userData) { VSpanData *data = (VSpanData *)(userData); Operator op = getOperator(data); - const uint color = data->mSolid; + const uint32_t color = data->mSolid; for (size_t i = 0 ; i < size; ++i) { const auto &span = array[i]; @@ -528,16 +528,16 @@ static void blend_color(size_t size, const VRle::Span *array, void *userData) } // Signature of Process Object -// void Pocess(uint* scratchBuffer, size_t x, size_t y, uchar cov) +// void Pocess(uint* scratchBuffer, size_t x, size_t y, uint8_t cov) template static inline void process_in_chunk(const VRle::Span *array, size_t size, Process process) { - std::array buf; + std::array buf; for (size_t i = 0; i < size; i++) { const auto &span = array[i]; size_t len = span.len; - size_t x = span.x; + auto x = span.x; while (len) { auto l = std::min(len, buf.size()); process(buf.data(), x, span.y, l, span.coverage); @@ -557,7 +557,7 @@ static void blend_gradient(size_t size, const VRle::Span *array, process_in_chunk( array, size, - [&](uint *scratch, size_t x, size_t y, size_t len, uchar cov) { + [&](uint32_t *scratch, size_t x, size_t y, size_t len, uint8_t cov) { op.srcFetch(scratch, &op, data, (int)y, (int)x, (int)len); op.func(data->buffer((int)x, (int)y), (int)len, scratch, cov); }); @@ -569,7 +569,7 @@ constexpr const T &clamp(const T &v, const T &lo, const T &hi) return v < lo ? lo : hi < v ? hi : v; } -static constexpr inline uchar alpha_mul(uchar a, uchar b) +static constexpr inline uint8_t alpha_mul(uint8_t a, uint8_t b) { return ((a * b) >> 8); } @@ -590,7 +590,7 @@ static void blend_image_xform(size_t size, const VRle::Span *array, process_in_chunk( array, size, - [&](uint *scratch, size_t x, size_t y, size_t len, uchar cov) { + [&](uint32_t *scratch, size_t x, size_t y, size_t len, uint8_t cov) { const auto coverage = (cov * src.alpha()) >> 8; const float xfactor = y * data->m21 + data->dx + data->m11; const float yfactor = y * data->m22 + data->dy + data->m12; diff --git a/src/vector/vdrawhelper.h b/src/vector/vdrawhelper.h index e654e93bc..7d31e3f37 100644 --- a/src/vector/vdrawhelper.h +++ b/src/vector/vdrawhelper.h @@ -127,7 +127,7 @@ public: void resetBuffer(int val = 0); - inline uchar *scanLine(int y) + inline uint8_t *scanLine(int y) { assert(y >= 0); assert(size_t(y) < mHeight); @@ -150,7 +150,7 @@ private: size_t mHeight{0}; size_t mBytesPerLine{0}; size_t mBytesPerPixel{0}; - mutable uchar * mBuffer{nullptr}; + mutable uint8_t *mBuffer{nullptr}; }; struct VGradientData { @@ -171,8 +171,8 @@ struct VGradientData { struct VTextureData : public VRasterBuffer { uint32_t pixel(int x, int y) const { return *pixelRef(x, y); }; - uchar alpha() const { return mAlpha; } - void setAlpha(uchar alpha) { mAlpha = alpha; } + uint8_t alpha() const { return mAlpha; } + void setAlpha(uint8_t alpha) { mAlpha = alpha; } void setClip(const VRect &clip); // clip rect int left; @@ -180,7 +180,7 @@ struct VTextureData : public VRasterBuffer { int top; int bottom; bool hasAlpha; - uchar mAlpha; + uint8_t mAlpha; }; struct VColorTable { @@ -208,7 +208,7 @@ struct VSpanData { mDrawableSize = VSize(region.width(), region.height()); } - uint *buffer(int x, int y) const + uint32_t *buffer(int x, int y) const { return mRasterBuffer->pixelRef(x + mOffset.x(), y + mOffset.y()); } @@ -256,9 +256,10 @@ inline constexpr int vAlpha(uint32_t c) return c >> 24; } -static inline uint32_t interpolate_pixel(uint x, uint a, uint y, uint b) +static inline uint32_t interpolate_pixel(uint32_t x, uint32_t a, uint32_t y, + uint32_t b) { - uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; + uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; t >>= 8; t &= 0xff00ff; x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; diff --git a/src/vector/vdrawhelper_common.cpp b/src/vector/vdrawhelper_common.cpp index b2eb6db01..8a91b0f33 100644 --- a/src/vector/vdrawhelper_common.cpp +++ b/src/vector/vdrawhelper_common.cpp @@ -65,10 +65,10 @@ static void color_SourceOver(uint32_t *dest, int length, uint32_t color, dest = d * sa * ca + d * cia = d * (sa * ca + cia) */ -static void color_DestinationIn(uint *dest, int length, uint color, - uint alpha) +static void color_DestinationIn(uint32_t *dest, int length, uint32_t color, + uint32_t alpha) { - uint a = vAlpha(color); + uint32_t a = vAlpha(color); if (alpha != 255) { a = BYTE_MUL(a, alpha) + 255 - alpha; } @@ -82,10 +82,10 @@ static void color_DestinationIn(uint *dest, int length, uint color, dest = d * sia * ca + d * cia = d * (sia * ca + cia) */ -static void color_DestinationOut(uint *dest, int length, uint color, - uint alpha) +static void color_DestinationOut(uint32_t *dest, int length, uint32_t color, + uint32_t alpha) { - uint a = vAlpha(~color); + uint32_t a = vAlpha(~color); if (alpha != 255) a = BYTE_MUL(a, alpha) + 255 - alpha; for (int i = 0; i < length; ++i) { dest[i] = BYTE_MUL(dest[i], a); @@ -96,9 +96,9 @@ static void src_Source(uint32_t *dest, int length, const uint32_t *src, uint32_t alpha) { if (alpha == 255) { - memcpy(dest, src, size_t(length) * sizeof(uint)); + memcpy(dest, src, size_t(length) * sizeof(uint32_t)); } else { - uint ialpha = 255 - alpha; + uint32_t ialpha = 255 - alpha; for (int i = 0; i < length; ++i) { dest[i] = interpolate_pixel(src[i], alpha, dest[i], ialpha); @@ -112,7 +112,7 @@ static void src_Source(uint32_t *dest, int length, const uint32_t *src, static void src_SourceOver(uint32_t *dest, int length, const uint32_t *src, uint32_t alpha) { - uint s, sia; + uint32_t s, sia; if (alpha == 255) { for (int i = 0; i < length; ++i) { @@ -136,33 +136,33 @@ static void src_SourceOver(uint32_t *dest, int length, const uint32_t *src, } } -static void src_DestinationIn(uint *dest, int length, const uint *src, - uint alpha) +static void src_DestinationIn(uint32_t *dest, int length, const uint32_t *src, + uint32_t alpha) { if (alpha == 255) { for (int i = 0; i < length; ++i) { dest[i] = BYTE_MUL(dest[i], vAlpha(src[i])); } } else { - uint cia = 255 - alpha; + uint32_t cia = 255 - alpha; for (int i = 0; i < length; ++i) { - uint a = BYTE_MUL(vAlpha(src[i]), alpha) + cia; + uint32_t a = BYTE_MUL(vAlpha(src[i]), alpha) + cia; dest[i] = BYTE_MUL(dest[i], a); } } } -static void src_DestinationOut(uint *dest, int length, const uint *src, - uint alpha) +static void src_DestinationOut(uint32_t *dest, int length, const uint32_t *src, + uint32_t alpha) { if (alpha == 255) { for (int i = 0; i < length; ++i) { dest[i] = BYTE_MUL(dest[i], vAlpha(~src[i])); } } else { - uint cia = 255 - alpha; + uint32_t cia = 255 - alpha; for (int i = 0; i < length; ++i) { - uint sia = BYTE_MUL(vAlpha(~src[i]), alpha) + cia; + uint32_t sia = BYTE_MUL(vAlpha(~src[i]), alpha) + cia; dest[i] = BYTE_MUL(dest[i], sia); } } diff --git a/src/vector/vglobal.h b/src/vector/vglobal.h index 45e95e335..0aad7e8ce 100644 --- a/src/vector/vglobal.h +++ b/src/vector/vglobal.h @@ -29,9 +29,6 @@ #include #include -using uint = uint32_t; -using ushort = uint16_t; -using uchar = uint8_t; #if !defined(V_NAMESPACE) @@ -122,10 +119,13 @@ public: explicit constexpr inline vFlagHelper(int ai) noexcept : i(ai) {} constexpr inline operator int() const noexcept { return i; } - explicit constexpr inline vFlagHelper(uint ai) noexcept : i(int(ai)) {} + explicit constexpr inline vFlagHelper(uint32_t ai) noexcept : i(int(ai)) {} explicit constexpr inline vFlagHelper(short ai) noexcept : i(int(ai)) {} - explicit constexpr inline vFlagHelper(ushort ai) noexcept : i(int(uint(ai))) {} - constexpr inline operator uint() const noexcept { return uint(i); } + explicit constexpr inline vFlagHelper(uint16_t ai) noexcept + : i(int(uint32_t(ai))) + { + } + constexpr inline operator uint32_t() const noexcept { return uint32_t(i); } }; template @@ -139,7 +139,7 @@ public: using Int = typename std::conditional< std::is_unsigned::type>::value, - unsigned int, signed int>::type; + uint32_t, signed int>::type; using enum_type = Enum; // compiler-generated copy/move ctor/assignment operators are fine! @@ -153,7 +153,7 @@ public: i &= mask; return *this; } - inline vFlag &operator&=(uint mask) noexcept + inline vFlag &operator&=(uint32_t mask) noexcept { i &= mask; return *this; @@ -206,7 +206,7 @@ public: { return vFlag(vFlagHelper(i & mask)); } - constexpr inline vFlag operator&(uint mask) const noexcept + constexpr inline vFlag operator&(uint32_t mask) const noexcept { return vFlag(vFlagHelper(i & mask)); } @@ -236,44 +236,47 @@ public: class VColor { public: VColor() = default; - explicit VColor(uchar red, uchar green, uchar blue, uchar alpha = 255) noexcept - :a(alpha), r(red), g(green), b(blue){} - inline uchar red() const noexcept { return r; } - inline uchar green() const noexcept { return g; } - inline uchar blue() const noexcept { return b; } - inline uchar alpha() const noexcept { return a; } - inline void setRed(uchar red) noexcept { r = red; } - inline void setGreen(uchar green) noexcept { g = green; } - inline void setBlue(uchar blue) noexcept { b = blue; } - inline void setAlpha(uchar alpha) noexcept { a = alpha; } + explicit VColor(uint8_t red, uint8_t green, uint8_t blue, + uint8_t alpha = 255) noexcept + : a(alpha), r(red), g(green), b(blue) + { + } + inline uint8_t red() const noexcept { return r; } + inline uint8_t green() const noexcept { return g; } + inline uint8_t blue() const noexcept { return b; } + inline uint8_t alpha() const noexcept { return a; } + inline void setRed(uint8_t red) noexcept { r = red; } + inline void setGreen(uint8_t green) noexcept { g = green; } + inline void setBlue(uint8_t blue) noexcept { b = blue; } + inline void setAlpha(uint8_t alpha) noexcept { a = alpha; } inline bool isOpaque() const { return a == 255; } inline bool isTransparent() const { return a == 0; } inline bool operator==(const VColor &o) const { return ((a == o.a) && (r == o.r) && (g == o.g) && (b == o.b)); } - uint premulARGB() const + uint32_t premulARGB() const { int pr = (r * a) / 255; int pg = (g * a) / 255; int pb = (b * a) / 255; - return uint((a << 24) | (pr << 16) | (pg << 8) | (pb)); + return uint32_t((a << 24) | (pr << 16) | (pg << 8) | (pb)); } - uint premulARGB(float opacity) const + uint32_t premulARGB(float opacity) const { int alpha = int(a * opacity); int pr = (r * alpha) / 255; int pg = (g * alpha) / 255; int pb = (b * alpha) / 255; - return uint((alpha << 24) | (pr << 16) | (pg << 8) | (pb)); + return uint32_t((alpha << 24) | (pr << 16) | (pg << 8) | (pb)); } public: - uchar a{0}; - uchar r{0}; - uchar g{0}; - uchar b{0}; + uint8_t a{0}; + uint8_t r{0}; + uint8_t g{0}; + uint8_t b{0}; }; enum class FillRule: unsigned char { EvenOdd, Winding }; diff --git a/src/vector/vpainter.cpp b/src/vector/vpainter.cpp index 4ebbc5cb6..ab5492c7f 100644 --- a/src/vector/vpainter.cpp +++ b/src/vector/vpainter.cpp @@ -66,7 +66,7 @@ static void fillRect(const VRect &r, VSpanData *data) int i = 0; while (i < n) { spans[i].x = short(x1); - spans[i].len = ushort(x2 - x1); + spans[i].len = uint16_t(x2 - x1); spans[i].y = short(y + i); spans[i].coverage = 255; ++i; @@ -84,12 +84,12 @@ void VPainter::drawBitmapUntransform(const VRect & target, { mSpanData.initTexture(&bitmap, const_alpha, source); if (!mSpanData.mUnclippedBlendFunc) return; - mSpanData.dx = float(-target.x()); - mSpanData.dy = float(-target.y()); - VRect rr = source.translated(target.x(), target.y()); + // update translation matrix for source texture. + mSpanData.dx = float(target.x() - source.x()); + mSpanData.dy = float(target.y() - source.y()); - fillRect(rr, &mSpanData); + fillRect(target, &mSpanData); } VPainter::VPainter(VBitmap *buffer) diff --git a/src/vector/vpath.cpp b/src/vector/vpath.cpp index f0dbec8ea..d49cd0094 100644 --- a/src/vector/vpath.cpp +++ b/src/vector/vpath.cpp @@ -219,13 +219,13 @@ void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir) void VPath::VPathData::addRect(const VRectF &rect, VPath::Direction dir) { - if (rect.empty()) return; - float x = rect.x(); float y = rect.y(); float w = rect.width(); float h = rect.height(); + if (vCompare(w, 0.f) && vCompare(h, 0.f)) return; + reserve(5, 6); // 1Move + 4Line + 1Close if (dir == VPath::Direction::CW) { moveTo(x + w, y); diff --git a/src/vector/vpath.h b/src/vector/vpath.h index 90c2c3e53..9273cb61b 100644 --- a/src/vector/vpath.h +++ b/src/vector/vpath.h @@ -35,7 +35,7 @@ class VPath { public: enum class Direction { CCW, CW }; - enum class Element : uchar { MoveTo, LineTo, CubicTo, Close }; + enum class Element : uint8_t { MoveTo, LineTo, CubicTo, Close }; bool empty() const; bool null() const; void moveTo(const VPointF &p); diff --git a/src/vector/vraster.cpp b/src/vector/vraster.cpp index 062431532..fdf66fb01 100644 --- a/src/vector/vraster.cpp +++ b/src/vector/vraster.cpp @@ -380,7 +380,7 @@ struct VRleTask { outRef.convert(mPath); outRef.convert(mCap, mJoin, mStrokeWidth, mMiterLimit); - uint points, contors; + uint32_t points, contors; SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin, outRef.ftMiterLimit); @@ -461,20 +461,29 @@ class RleTaskScheduler { for (unsigned n = 0; n != _count; ++n) { _threads.emplace_back([&, n] { run(n); }); } + + IsRunning = true; } public: + static bool IsRunning; + static RleTaskScheduler &instance() { static RleTaskScheduler singleton; return singleton; } - ~RleTaskScheduler() - { - for (auto &e : _q) e.done(); + ~RleTaskScheduler() { stop(); } - for (auto &e : _threads) e.join(); + void stop() + { + if (IsRunning) { + IsRunning = false; + + for (auto &e : _q) e.done(); + for (auto &e : _threads) e.join(); + } } void process(VTask task) @@ -499,12 +508,16 @@ public: SW_FT_Stroker stroker; public: + static bool IsRunning; + static RleTaskScheduler &instance() { static RleTaskScheduler singleton; return singleton; } + void stop() {} + RleTaskScheduler() { SW_FT_Stroker_New(&stroker); } ~RleTaskScheduler() { SW_FT_Stroker_Done(stroker); } @@ -513,6 +526,8 @@ public: }; #endif +bool RleTaskScheduler::IsRunning{false}; + struct VRasterizer::VRasterizerImpl { VRleTask mTask; @@ -560,4 +575,11 @@ void VRasterizer::rasterize(VPath path, CapStyle cap, JoinStyle join, updateRequest(); } +void lottieShutdownRasterTaskScheduler() +{ + if (RleTaskScheduler::IsRunning) { + RleTaskScheduler::instance().stop(); + } +} + V_END_NAMESPACE diff --git a/src/vector/vrle.cpp b/src/vector/vrle.cpp index c7a520f59..753db1a11 100644 --- a/src/vector/vrle.cpp +++ b/src/vector/vrle.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "vdebug.h" #include "vglobal.h" @@ -40,7 +41,7 @@ static size_t _opGeneric(rle_view &a, rle_view &b, Result &result, static size_t _opIntersect(const VRect &, rle_view &, Result &); static size_t _opIntersect(rle_view &, rle_view &, Result &); -static inline uchar divBy255(int x) +static inline uint8_t divBy255(int x) { return (x + (x >> 8) + 0x80) >> 8; } @@ -141,7 +142,7 @@ void VRle::Data::updateBbox() const } } -void VRle::Data::operator*=(uchar alpha) +void VRle::Data::operator*=(uint8_t alpha) { for (auto &i : mSpans) { i.coverage = divBy255(i.coverage * alpha); @@ -223,7 +224,7 @@ void VRle::Data::opGeneric(const VRle::Data &aObj, const VRle::Data &bObj, mSpans.reserve(a.size() + b.size()); // if two rle are disjoint - if (!aObj.bbox().intersects(aObj.bbox())) { + if (!aObj.bbox().intersects(bObj.bbox())) { if (a.data()[0].y < b.data()[0].y) { copy(a.data(), a.size(), mSpans); copy(b.data(), b.size(), mSpans); @@ -424,7 +425,7 @@ static size_t _opIntersect(const VRect &clip, rle_view &obj, Result &result) out->x = minx; } else { out->x = span.x; - out->len = std::min(span.len, ushort(maxx - span.x + 1)); + out->len = std::min(span.len, uint16_t(maxx - span.x + 1)); } if (out->len != 0) { out->y = span.y; @@ -441,12 +442,12 @@ static size_t _opIntersect(const VRect &clip, rle_view &obj, Result &result) return result.max_size() - available; } -static void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX) +static void blitXor(VRle::Span *spans, int count, uint8_t *buffer, int offsetX) { while (count--) { int x = spans->x + offsetX; int l = spans->len; - uchar *ptr = buffer + x; + uint8_t *ptr = buffer + x; while (l--) { int da = *ptr; *ptr = divBy255((255 - spans->coverage) * (da) + @@ -457,13 +458,13 @@ static void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX) } } -static void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer, +static void blitDestinationOut(VRle::Span *spans, int count, uint8_t *buffer, int offsetX) { while (count--) { int x = spans->x + offsetX; int l = spans->len; - uchar *ptr = buffer + x; + uint8_t *ptr = buffer + x; while (l--) { *ptr = divBy255((255 - spans->coverage) * (*ptr)); ptr++; @@ -472,13 +473,13 @@ static void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer, } } -static void blitSrcOver(VRle::Span *spans, int count, uchar *buffer, +static void blitSrcOver(VRle::Span *spans, int count, uint8_t *buffer, int offsetX) { while (count--) { int x = spans->x + offsetX; int l = spans->len; - uchar *ptr = buffer + x; + uint8_t *ptr = buffer + x; while (l--) { *ptr = spans->coverage + divBy255((255 - spans->coverage) * (*ptr)); ptr++; @@ -487,12 +488,12 @@ static void blitSrcOver(VRle::Span *spans, int count, uchar *buffer, } } -void blitSrc(VRle::Span *spans, int count, uchar *buffer, int offsetX) +void blitSrc(VRle::Span *spans, int count, uint8_t *buffer, int offsetX) { while (count--) { int x = spans->x + offsetX; int l = spans->len; - uchar *ptr = buffer + x; + uint8_t *ptr = buffer + x; while (l--) { *ptr = std::max(spans->coverage, *ptr); ptr++; @@ -501,15 +502,16 @@ void blitSrc(VRle::Span *spans, int count, uchar *buffer, int offsetX) } } -size_t bufferToRle(uchar *buffer, int size, int offsetX, int y, VRle::Span *out) +size_t bufferToRle(uint8_t *buffer, int size, int offsetX, int y, + VRle::Span *out) { size_t count = 0; - uchar value = buffer[0]; + uint8_t value = buffer[0]; int curIndex = 0; // size = offsetX < 0 ? size + offsetX : size; for (int i = 0; i < size; i++) { - uchar curValue = buffer[0]; + uint8_t curValue = buffer[0]; if (value != curValue) { if (value) { out->y = y; @@ -549,10 +551,10 @@ struct SpanMerger { break; } } - using blitter = void (*)(VRle::Span *, int, uchar *, int); + using blitter = void (*)(VRle::Span *, int, uint8_t *, int); blitter _blitter; std::array _result; - std::array _buffer; + std::array _buffer; VRle::Span * _aStart{nullptr}; VRle::Span * _bStart{nullptr}; diff --git a/src/vector/vrle.h b/src/vector/vrle.h index 761e5d1bf..1bbb8365b 100644 --- a/src/vector/vrle.h +++ b/src/vector/vrle.h @@ -36,8 +36,8 @@ public: struct Span { short x{0}; short y{0}; - ushort len{0}; - uchar coverage{0}; + uint16_t len{0}; + uint8_t coverage{0}; }; using VRleSpanCb = void (*)(size_t count, const VRle::Span *spans, void *userData); @@ -52,7 +52,7 @@ public: void reset() { d.write().reset(); } void translate(const VPoint &p) { d.write().translate(p); } - void operator*=(uchar alpha) { d.write() *= alpha; } + void operator*=(uint8_t alpha) { d.write() *= alpha; } void intersect(const VRect &r, VRleSpanCb cb, void *userData) const; void intersect(const VRle &rle, VRleSpanCb cb, void *userData) const; @@ -91,7 +91,7 @@ public: void setBbox(const VRect &bbox) const; void reset(); void translate(const VPoint &p); - void operator*=(uchar alpha); + void operator*=(uint8_t alpha); void opGeneric(const VRle::Data &, const VRle::Data &, Op code); void opSubstract(const VRle::Data &, const VRle::Data &); void opIntersect(VRle::View a, VRle::View b); diff --git a/src/wasm/rlottiewasm.cpp b/src/wasm/rlottiewasm.cpp index 4c759cc76..af163796d 100644 --- a/src/wasm/rlottiewasm.cpp +++ b/src/wasm/rlottiewasm.cpp @@ -27,6 +27,81 @@ public: return mPlayer ? true : false; } + void setFillColor(std::string keypath, float r, float g, float b) + { + if (!mPlayer) return; + + mPlayer->setValue(keypath, + rlottie::Color(r, g, b)); + } + + void setStrokeColor(std::string keypath, float r, float g, float b) + { + if (!mPlayer) return; + + mPlayer->setValue(keypath, + rlottie::Color(r, g, b)); + } + + void setFillOpacity(std::string keypath, float opacity) + { + if (!mPlayer || opacity > 100 || opacity < 0) return; + + mPlayer->setValue(keypath, opacity); + } + + void setStrokeOpacity(std::string keypath, float opacity) + { + if (!mPlayer || opacity > 100 || opacity < 0) return; + + mPlayer->setValue(keypath, opacity); + } + + void setStrokeWidth(std::string keypath, float width) + { + if (!mPlayer || width < 0) return; + + mPlayer->setValue(keypath, width); + } + + void setAnchor(std::string keypath, float x, float y) + { + if (!mPlayer) return; + + mPlayer->setValue(keypath, + rlottie::Point(x, y)); + } + + void setPosition(std::string keypath, float x, float y) + { + if (!mPlayer) return; + + mPlayer->setValue(keypath, + rlottie::Point(x, y)); + } + + void setScale(std::string keypath, float width, float height) + { + if (!mPlayer) return; + + mPlayer->setValue(keypath, + rlottie::Size(width, height)); + } + + void setRotation(std::string keypath, float degree) + { + if (!mPlayer || degree > 360 || degree < 0) return; + + mPlayer->setValue(keypath, degree); + } + + void setOpacity(std::string keypath, float opacity) + { + if (!mPlayer || opacity > 100 || opacity < 0) return; + + mPlayer->setValue(keypath, opacity); + } + // canvas pixel pix[0] pix[1] pix[2] pix[3] {B G R A} // lottie pixel pix[0] pix[1] pix[2] pix[3] {R G B A} val render(int frame, int width, int height) @@ -105,5 +180,15 @@ EMSCRIPTEN_BINDINGS(rlottie_bindings) .constructor(&RlottieWasm::create) .function("load", &RlottieWasm::load, allow_raw_pointers()) .function("frames", &RlottieWasm::frames) - .function("render", &RlottieWasm::render); + .function("render", &RlottieWasm::render) + .function("setFillColor", &RlottieWasm::setFillColor) + .function("setStrokeColor", &RlottieWasm::setStrokeColor) + .function("setFillOpacity", &RlottieWasm::setFillOpacity) + .function("setStrokeOpacity", &RlottieWasm::setStrokeOpacity) + .function("setStrokeWidth", &RlottieWasm::setStrokeWidth) + .function("setAnchor", &RlottieWasm::setAnchor) + .function("setPosition", &RlottieWasm::setPosition) + .function("setScale", &RlottieWasm::setScale) + .function("setRotation", &RlottieWasm::setRotation) + .function("setOpacity", &RlottieWasm::setOpacity); } diff --git a/vs2019/rlottie.vcxproj b/vs2019/rlottie.vcxproj index d1326af72..81a72fba6 100644 --- a/vs2019/rlottie.vcxproj +++ b/vs2019/rlottie.vcxproj @@ -45,7 +45,7 @@ MultiByte - Application + DynamicLibrary false v142 true @@ -118,6 +118,9 @@ true true true + ../inc;./;../src/lottie;../src/vector;../src/vector/pixman;../src/vector/freetype;%(AdditionalIncludeDirectories) + -DRLOTTIE_BUILD %(AdditionalOptions) + 4251;4244;4996 Console