Merge commit '79ff784721f4467450f37310322ecff59d3f1ce9' into lottie-support

This commit is contained in:
Leon Styhre 2022-01-07 18:43:06 +01:00
commit 7430a8b5f5
60 changed files with 1641 additions and 383 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -12,7 +12,7 @@ install:
- git submodule update --init --recursive - git submodule update --init --recursive
- set PATH=%cd%;%PYTHON%;%PYTHON%\Scripts;%PATH% - set PATH=%cd%;%PYTHON%;%PYTHON%\Scripts;%PATH%
- pip install meson==0.50.0 ninja - 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: build_script:
- meson -Dwerror=false --backend=ninja --prefix=%cd% build - meson -Dwerror=false --backend=ninja --prefix=%cd% build
- where link - where link

View file

@ -11,3 +11,4 @@ tmp
.project .project
.vs .vs
**/*.DS_Store **/*.DS_Store
builddir_wasm

View file

@ -10,3 +10,9 @@ Shinwoo Kim <cinoo.kim@samsung.com>
Vincent Torri <vincent.torri@gmail.com> Vincent Torri <vincent.torri@gmail.com>
Nicholas Guriev <guriev-ns@ya.ru> Nicholas Guriev <guriev-ns@ya.ru>
John Preston <johnprestonmail@gmail.com> John Preston <johnprestonmail@gmail.com>
Anatoly Korniltsev <korniltsev.anatoly@gmail.com>
Александр Першин <a.pershin@corp.mail.ru>
Anton Sergeev <antonsergeev88@gmail.com>
HyunGu Lee <start3a@gmail.com>
ChaeLin Shin <chaelinshin96@gmail.com>
Seungjae Yoon <artium59@gmail.com>

View file

@ -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) configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in config.h)
target_include_directories(rlottie target_include_directories(rlottie
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
PRIVATE PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
) )
#declare common target compilation options #declare common target compilation options
target_compile_options(rlottie if(MSVC)
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)
target_compile_options(rlottie target_compile_options(rlottie
PUBLIC PUBLIC
PRIVATE 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 -Wnon-virtual-dtor
-Woverloaded-virtual -Woverloaded-virtual
-Wno-unused-parameter -Wno-unused-parameter
@ -71,6 +75,8 @@ endif()
if (WIN32 AND NOT BUILD_SHARED_LIBS) if (WIN32 AND NOT BUILD_SHARED_LIBS)
target_compile_definitions(rlottie PUBLIC -DRLOTTIE_BUILD=0) 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() endif()
#declare dependancy #declare dependancy
@ -85,7 +91,7 @@ target_link_libraries(rlottie
if (NOT APPLE AND NOT WIN32) if (NOT APPLE AND NOT WIN32)
target_link_libraries(rlottie target_link_libraries(rlottie
PRIVATE PRIVATE
"-Wl,--version-script=${CMAKE_SOURCE_DIR}/rlottie.expmap" "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/rlottie.expmap"
) )
endif() endif()
@ -100,7 +106,7 @@ if (NOT LOTTIE_ASAN)
PUBLIC PUBLIC
"-Wl, -undefined error" "-Wl, -undefined error"
) )
else() elseif(NOT MSVC)
target_link_libraries(rlottie target_link_libraries(rlottie
PUBLIC PUBLIC
"-Wl,--no-undefined" "-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 #Register package in user's package registry
export(PACKAGE rlottie) export(PACKAGE rlottie)

View file

@ -3,6 +3,7 @@
[![Build Status](https://travis-ci.org/Samsung/rlottie.svg?branch=master)](https://travis-ci.org/Samsung/rlottie) [![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) [![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)
<p align="center"> <p align="center">
<img width="240" height="240" src="https://github.com/Samsung/rlottie/blob/master/.Gifs/logo.png"> <img width="240" height="240" src="https://github.com/Samsung/rlottie/blob/master/.Gifs/logo.png">
@ -35,7 +36,7 @@ Here are small samples of the power of Lottie.
- [Issues or Feature Requests?](#issues-or-feature-requests) - [Issues or Feature Requests?](#issues-or-feature-requests)
## Building Lottie ## 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 ### Meson Build
install [meson](http://mesonbuild.com/Getting-meson.html) and [ninja](https://ninja-build.org/) if not already installed. 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 ## 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. 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 ### 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 ### Usage

View file

@ -1,8 +1,14 @@
add_executable(lottie2gif "lottie2gif.cpp") add_executable(lottie2gif "lottie2gif.cpp")
target_compile_options(lottie2gif if(MSVC)
PRIVATE target_compile_options(lottie2gif
-std=c++14) PRIVATE
/std:c++14)
else()
target_compile_options(lottie2gif
PRIVATE
-std=c++14)
endif()
target_link_libraries(lottie2gif rlottie) target_link_libraries(lottie2gif rlottie)

View file

@ -0,0 +1,60 @@
#include "rlottie.h"
#include "animation.h"
using namespace rlottie;
std::unique_ptr<Animation> 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);
}

View file

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
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();

View file

@ -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 <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

View file

@ -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

Binary file not shown.

Binary file not shown.

View file

@ -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<HWND>(lParam) == hSliderPlay))
{
UINT frameNum = SendDlgItemMessage(hWnd, SLIDER_PLAY, TBM_GETPOS, 0, 0);
renderAnimation(frameNum);
}
else if ((lParam != 0) && (reinterpret_cast<HWND>(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);
}

Binary file not shown.

View file

@ -0,0 +1,32 @@
#pragma once
#include "resource.h"
#include "animation.h"
#include <Commdlg.h> // OPENFILENAME
#include "atlconv.h" // String cast. (ex. LPWSTR <-> LPSTR)
#include <gdiplus.h>
#include <CommCtrl.h> // 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

View file

@ -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

View file

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{cc7ad634-4d29-4d8e-997d-9727dabb44ee}</ProjectGuid>
<RootNamespace>rlottiePlayer</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\..\inc;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>..\..\inc;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>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)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>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)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="animation.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="rlottiePlayer.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="rlottiePlayer.cpp" />
<ClCompile Include="Source.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="rlottiePlayer.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="rlottiePlayer.ico" />
<Image Include="small.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rlottiePlayer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="animation.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="rlottiePlayer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Source.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="rlottiePlayer.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="small.ico">
<Filter>Resource Files</Filter>
</Image>
<Image Include="rlottiePlayer.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
</Project>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View file

@ -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 <SDKDDKVer.h>

1
external/rlottie/format vendored Executable file
View file

@ -0,0 +1 @@
git diff -U0 --no-color HEAD^ | clang-format-diff -i -p1

View file

@ -108,11 +108,11 @@ enum class Property {
FillOpacity, /*!< Opacity property of Fill object , value type is float [ 0 .. 100] */ FillOpacity, /*!< Opacity property of Fill object , value type is float [ 0 .. 100] */
StrokeColor, /*!< Color property of Stroke object , value type is rlottie::Color */ StrokeColor, /*!< Color property of Stroke object , value type is rlottie::Color */
StrokeOpacity, /*!< Opacity property of Stroke object , value type is float [ 0 .. 100] */ 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 */ 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 */ 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] */ 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] */ TrOpacity /*!< Transform Opacity property of Layer and Group object , value type is float [ 0 .. 100] */
}; };

View file

@ -25,7 +25,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <rlottiecommon.h> #include "rlottiecommon.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -46,6 +46,36 @@ typedef enum {
typedef struct Lottie_Animation_S Lottie_Animation; 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. * @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); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -10,7 +10,7 @@ Version: 0.2
Release: %mkrel %{?git:-c git%{git}} %{rel} Release: %mkrel %{?git:-c git%{git}} %{rel}
Summary: Platform independent standalone library that plays Lottie Animation Summary: Platform independent standalone library that plays Lottie Animation
Group: System/Libraries Group: System/Libraries
License: MIT and BSD License: MIT and BSD and MPL-2.0
URL: http://www.tizen.org/ URL: http://www.tizen.org/
Source0: https://github.com/Samsung/rlottie/archive/master/%{name}-%{version}%{?git:-git%{git}}.tar.gz Source0: https://github.com/Samsung/rlottie/archive/master/%{name}-%{version}%{?git:-git%{git}}.tar.gz

View file

@ -26,6 +26,9 @@
using namespace rlottie; using namespace rlottie;
extern void lottie_init_impl();
extern void lottie_shutdown_impl();
extern "C" { extern "C" {
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
@ -38,6 +41,34 @@ struct Lottie_Animation_S
LOTMarkerList *mMarkerList; 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) RLOTTIE_API Lottie_Animation_S *lottie_animation_from_file(const char *path)
{ {
if (auto animation = Animation::loadFromFile(path) ) { if (auto animation = Animation::loadFromFile(path) ) {
@ -275,4 +306,10 @@ lottie_animation_get_markerlist(Lottie_Animation_S *animation)
return (const LOTMarkerList*)animation->mMarkerList; return (const LOTMarkerList*)animation->mMarkerList;
} }
RLOTTIE_API void
lottie_configure_model_cache_size(size_t cacheSize)
{
rlottie::configureModelCacheSize(cacheSize);
}
} }

View file

@ -178,20 +178,29 @@ class RenderTaskScheduler {
for (unsigned n = 0; n != _count; ++n) { for (unsigned n = 0; n != _count; ++n) {
_threads.emplace_back([&, n] { run(n); }); _threads.emplace_back([&, n] { run(n); });
} }
IsRunning = true;
} }
public: public:
static bool IsRunning;
static RenderTaskScheduler &instance() static RenderTaskScheduler &instance()
{ {
static RenderTaskScheduler singleton; static RenderTaskScheduler singleton;
return singleton; return singleton;
} }
~RenderTaskScheduler() ~RenderTaskScheduler() { stop(); }
{
for (auto &e : _q) e.done();
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<Surface> process(SharedRenderTask task) std::future<Surface> process(SharedRenderTask task)
@ -214,12 +223,16 @@ public:
#else #else
class RenderTaskScheduler { class RenderTaskScheduler {
public: public:
static bool IsRunning;
static RenderTaskScheduler &instance() static RenderTaskScheduler &instance()
{ {
static RenderTaskScheduler singleton; static RenderTaskScheduler singleton;
return singleton; return singleton;
} }
void stop() {}
std::future<Surface> process(SharedRenderTask task) std::future<Surface> process(SharedRenderTask task)
{ {
auto result = task->playerImpl->render(task->frameNo, task->surface, auto result = task->playerImpl->render(task->frameNo, task->surface,
@ -228,8 +241,11 @@ public:
return std::move(task->receiver); return std::move(task->receiver);
} }
}; };
#endif #endif
bool RenderTaskScheduler::IsRunning{false};
std::future<Surface> AnimationImpl::renderAsync(size_t frameNo, std::future<Surface> AnimationImpl::renderAsync(size_t frameNo,
Surface &&surface, Surface &&surface,
bool keepAspectRatio) bool keepAspectRatio)
@ -441,6 +457,29 @@ void Surface::setDrawRegion(size_t x, size_t y, size_t width, size_t height)
mDrawArea.h = 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 #ifdef LOTTIE_LOGGING_SUPPORT
void initLogging() void initLogging()
{ {

View file

@ -238,7 +238,7 @@ class FilterData {
public: public:
void addValue(LOTVariant& value) void addValue(LOTVariant& value)
{ {
uint index = static_cast<uint>(value.property()); uint32_t index = static_cast<uint32_t>(value.property());
if (mBitset.test(index)) { if (mBitset.test(index)) {
std::replace_if(mFilters.begin(), mFilters.end(), std::replace_if(mFilters.begin(), mFilters.end(),
[&value](const LOTVariant& e) { [&value](const LOTVariant& e) {
@ -253,7 +253,7 @@ public:
void removeValue(LOTVariant& value) void removeValue(LOTVariant& value)
{ {
uint index = static_cast<uint>(value.property()); uint32_t index = static_cast<uint32_t>(value.property());
if (mBitset.test(index)) { if (mBitset.test(index)) {
mBitset.reset(index); mBitset.reset(index);
mFilters.erase(std::remove_if(mFilters.begin(), mFilters.end(), mFilters.erase(std::remove_if(mFilters.begin(), mFilters.end(),
@ -266,7 +266,7 @@ public:
} }
bool hasFilter(rlottie::Property prop) const bool hasFilter(rlottie::Property prop) const
{ {
return mBitset.test(static_cast<uint>(prop)); return mBitset.test(static_cast<uint32_t>(prop));
} }
model::Color color(rlottie::Property prop, int frame) const model::Color color(rlottie::Property prop, int frame) const
{ {

View file

@ -149,9 +149,9 @@ bool renderer::Composition::update(int frameNo, const VSize &size,
bool renderer::Composition::render(const rlottie::Surface &surface) bool renderer::Composition::render(const rlottie::Surface &surface)
{ {
mSurface.reset(reinterpret_cast<uchar *>(surface.buffer()), mSurface.reset(reinterpret_cast<uint8_t *>(surface.buffer()),
uint(surface.width()), uint(surface.height()), uint32_t(surface.width()), uint32_t(surface.height()),
uint(surface.bytesPerLine()), uint32_t(surface.bytesPerLine()),
VBitmap::Format::ARGB32_Premultiplied); VBitmap::Format::ARGB32_Premultiplied);
/* schedule all preprocess task for this frame at once. /* schedule all preprocess task for this frame at once.
@ -200,7 +200,7 @@ VRle renderer::Mask::rle()
{ {
if (!vCompare(mCombinedAlpha, 1.0f)) { if (!vCompare(mCombinedAlpha, 1.0f)) {
VRle obj = mRasterizer.rle(); VRle obj = mRasterizer.rle();
obj *= uchar(mCombinedAlpha * 255); obj *= uint8_t(mCombinedAlpha * 255);
return obj; return obj;
} else { } else {
return mRasterizer.rle(); return mRasterizer.rle();
@ -343,7 +343,7 @@ renderer::Layer::Layer(model::Layer *layerData) : mLayerData(layerData)
mLayerMask = std::make_unique<renderer::LayerMask>(mLayerData); mLayerMask = std::make_unique<renderer::LayerMask>(mLayerData);
} }
bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) LOTVariant &value)
{ {
if (!keyPath.matches(name(), depth)) { if (!keyPath.matches(name(), depth)) {
@ -359,12 +359,12 @@ bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return true; return true;
} }
bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) LOTVariant &value)
{ {
if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) { if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) {
if (keyPath.propagate(name(), depth)) { if (keyPath.propagate(name(), depth)) {
uint newDepth = keyPath.nextDepth(name(), depth); uint32_t newDepth = keyPath.nextDepth(name(), depth);
mRoot->resolveKeyPath(keyPath, newDepth, value); mRoot->resolveKeyPath(keyPath, newDepth, value);
} }
return true; return true;
@ -372,12 +372,12 @@ bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return false; return false;
} }
bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) LOTVariant &value)
{ {
if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) { if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) {
if (keyPath.propagate(name(), depth)) { if (keyPath.propagate(name(), depth)) {
uint newDepth = keyPath.nextDepth(name(), depth); uint32_t newDepth = keyPath.nextDepth(name(), depth);
for (const auto &layer : mLayers) { for (const auto &layer : mLayers) {
layer->resolveKeyPath(keyPath, newDepth, value); layer->resolveKeyPath(keyPath, newDepth, value);
} }
@ -507,7 +507,7 @@ void renderer::CompLayer::render(VPainter *painter, const VRle &inheritMask,
renderHelper(&srcPainter, inheritMask, matteRle, cache); renderHelper(&srcPainter, inheritMask, matteRle, cache);
srcPainter.end(); srcPainter.end();
painter->drawBitmap(VPoint(), srcBitmap, painter->drawBitmap(VPoint(), srcBitmap,
uchar(combinedAlpha() * 255.0f)); uint8_t(combinedAlpha() * 255.0f));
cache.release_surface(srcBitmap); cache.release_surface(srcBitmap);
} else { } else {
renderHelper(painter, inheritMask, matteRle, cache); renderHelper(painter, inheritMask, matteRle, cache);
@ -597,11 +597,21 @@ void renderer::CompLayer::renderMatteLayer(VPainter *painter, const VRle &mask,
srcBitmap.updateLuma(); 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 // 2.3 draw src buffer as mask
layerPainter.drawBitmap(VPoint(), srcBitmap); layerPainter.drawBitmap(clip, srcBitmap, clip);
layerPainter.end(); layerPainter.end();
// 3. draw the result buffer into painter // 3. draw the result buffer into painter
painter->drawBitmap(VPoint(), layerBitmap); painter->drawBitmap(clip, layerBitmap, clip);
cache.release_surface(srcBitmap); cache.release_surface(srcBitmap);
cache.release_surface(layerBitmap); cache.release_surface(layerBitmap);
@ -853,7 +863,7 @@ renderer::DrawableList renderer::ShapeLayer::renderList()
return {mDrawableList.data(), mDrawableList.size()}; return {mDrawableList.data(), mDrawableList.size()};
} }
bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) LOTVariant &value)
{ {
if (!keyPath.skip(name())) { if (!keyPath.skip(name())) {
@ -870,7 +880,7 @@ bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
} }
if (keyPath.propagate(name(), depth)) { if (keyPath.propagate(name(), depth)) {
uint newDepth = keyPath.nextDepth(name(), depth); uint32_t newDepth = keyPath.nextDepth(name(), depth);
for (auto &child : mContents) { for (auto &child : mContents) {
child->resolveKeyPath(keyPath, newDepth, value); child->resolveKeyPath(keyPath, newDepth, value);
} }
@ -878,7 +888,7 @@ bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return true; return true;
} }
bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) LOTVariant &value)
{ {
if (!keyPath.matches(mModel.name(), depth)) { if (!keyPath.matches(mModel.name(), depth)) {
@ -893,7 +903,7 @@ bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return false; return false;
} }
bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) LOTVariant &value)
{ {
if (!keyPath.matches(mModel.name(), depth)) { 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 pos = mData->mPos.value(frameNo);
VPointF size = mData->mSize.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(), VRectF r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
size.y()); size.y());

View file

@ -80,7 +80,7 @@ namespace renderer {
using DrawableList = VSpan<VDrawable *>; using DrawableList = VSpan<VDrawable *>;
enum class DirtyFlagBit : uchar { enum class DirtyFlagBit : uint8_t {
None = 0x00, None = 0x00,
Matrix = 0x01, Matrix = 0x01,
Alpha = 0x02, Alpha = 0x02,
@ -239,8 +239,8 @@ public:
std::vector<LOTMask> & cmasks() { return mCApiData->mMasks; } std::vector<LOTMask> & cmasks() { return mCApiData->mMasks; }
std::vector<LOTNode *> & cnodes() { return mCApiData->mCNodeList; } std::vector<LOTNode *> & cnodes() { return mCApiData->mCNodeList; }
const char * name() const { return mLayerData->name(); } const char * name() const { return mLayerData->name(); }
virtual bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, virtual bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value); LOTVariant &value);
protected: protected:
virtual void preprocessStage(const VRect &clip) = 0; virtual void preprocessStage(const VRect &clip) = 0;
@ -275,7 +275,7 @@ public:
void render(VPainter *painter, const VRle &mask, const VRle &matteRle, void render(VPainter *painter, const VRle &mask, const VRle &matteRle,
SurfaceCache &cache) final; SurfaceCache &cache) final;
void buildLayerNode() final; void buildLayerNode() final;
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) override; LOTVariant &value) override;
protected: protected:
@ -317,7 +317,7 @@ public:
explicit ShapeLayer(model::Layer *layerData, VArenaAlloc *allocator); explicit ShapeLayer(model::Layer *layerData, VArenaAlloc *allocator);
DrawableList renderList() final; DrawableList renderList() final;
void buildLayerNode() final; void buildLayerNode() final;
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) override; LOTVariant &value) override;
protected: protected:
@ -355,13 +355,13 @@ private:
class Object { class Object {
public: public:
enum class Type : uchar { Unknown, Group, Shape, Paint, Trim }; enum class Type : uint8_t { Unknown, Group, Shape, Paint, Trim };
virtual ~Object() = default; virtual ~Object() = default;
Object & operator=(Object &&) noexcept = delete; Object & operator=(Object &&) noexcept = delete;
virtual void update(int frameNo, const VMatrix &parentMatrix, virtual void update(int frameNo, const VMatrix &parentMatrix,
float parentAlpha, const DirtyFlag &flag) = 0; float parentAlpha, const DirtyFlag &flag) = 0;
virtual void renderList(std::vector<VDrawable *> &) {} virtual void renderList(std::vector<VDrawable *> &) {}
virtual bool resolveKeyPath(LOTKeyPath &, uint, LOTVariant &) virtual bool resolveKeyPath(LOTKeyPath &, uint32_t, LOTVariant &)
{ {
return false; return false;
} }
@ -387,7 +387,7 @@ public:
static const char *TAG = "__"; static const char *TAG = "__";
return mModel.hasModel() ? mModel.name() : TAG; return mModel.hasModel() ? mModel.name() : TAG;
} }
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth, bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value) override; LOTVariant &value) override;
protected: protected:
@ -449,7 +449,7 @@ protected:
{ {
return (mData->mPos.changed(prevFrame, curFrame) || return (mData->mPos.changed(prevFrame, curFrame) ||
mData->mSize.changed(prevFrame, curFrame) || mData->mSize.changed(prevFrame, curFrame) ||
mData->mRound.changed(prevFrame, curFrame)); mData->roundnessChanged(prevFrame, curFrame));
} }
}; };
@ -532,7 +532,7 @@ public:
protected: protected:
bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final; 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; LOTVariant &value) final;
private: private:
@ -557,7 +557,7 @@ public:
protected: protected:
bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final; 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; LOTVariant &value) final;
private: private:

View file

@ -88,7 +88,7 @@ void renderer::Layer::buildLayerNode()
mCApiData = std::make_unique<renderer::CApiData>(); mCApiData = std::make_unique<renderer::CApiData>();
clayer().keypath = name(); clayer().keypath = name();
} }
if (complexContent()) clayer().mAlpha = uchar(combinedAlpha() * 255.f); if (complexContent()) clayer().mAlpha = uint8_t(combinedAlpha() * 255.f);
clayer().mVisible = visible(); clayer().mVisible = visible();
// update matte // update matte
if (hasMatte()) { if (hasMatte()) {
@ -121,10 +121,10 @@ void renderer::Layer::buildLayerNode()
auto ptPtr = reinterpret_cast<const float *>(pts.data()); auto ptPtr = reinterpret_cast<const float *>(pts.data());
auto elmPtr = reinterpret_cast<const char *>(elm.data()); auto elmPtr = reinterpret_cast<const char *>(elm.data());
cNode.mPath.ptPtr = ptPtr; cNode.mPath.ptPtr = ptPtr;
cNode.mPath.ptCount = pts.size(); cNode.mPath.ptCount = 2 * pts.size();
cNode.mPath.elmPtr = elmPtr; cNode.mPath.elmPtr = elmPtr;
cNode.mPath.elmCount = elm.size(); cNode.mPath.elmCount = elm.size();
cNode.mAlpha = uchar(mask.mCombinedAlpha * 255.0f); cNode.mAlpha = uint8_t(mask.mCombinedAlpha * 255.0f);
switch (mask.maskMode()) { switch (mask.maskMode()) {
case model::Mask::Mode::Add: case model::Mask::Mode::Add:
cNode.mMode = MaskAdd; cNode.mMode = MaskAdd;
@ -196,7 +196,7 @@ void renderer::ImageLayer::buildLayerNode()
// Alpha calculation already combined. // Alpha calculation already combined.
lotDrawable->mCNode->mImageInfo.mAlpha = lotDrawable->mCNode->mImageInfo.mAlpha =
uchar(lotDrawable->mBrush.mTexture->mAlpha); uint8_t(lotDrawable->mBrush.mTexture->mAlpha);
cnodes().push_back(lotDrawable->mCNode.get()); cnodes().push_back(lotDrawable->mCNode.get());
} }
@ -216,7 +216,7 @@ static void updateGStops(LOTNode *n, const VGradient *grad)
LOTGradientStop *ptr = n->mGradient.stopPtr; LOTGradientStop *ptr = n->mGradient.stopPtr;
for (const auto &i : grad->mStops) { for (const auto &i : grad->mStops) {
ptr->pos = i.first; 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->r = i.second.red();
ptr->g = i.second.green(); ptr->g = i.second.green();
ptr->b = i.second.blue(); ptr->b = i.second.blue();

View file

@ -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)) { if (skip(key)) {
// This is an object we programatically create. // This is an object we programatically create.
@ -28,7 +28,7 @@ bool LOTKeyPath::matches(const std::string &key, uint depth)
return false; 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 (skip(key)) {
// If it's a container then we added programatically and it isn't a part // 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; 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()) { if (depth > mKeys.size()) {
return false; return false;

View file

@ -30,19 +30,19 @@
class LOTKeyPath { class LOTKeyPath {
public: public:
LOTKeyPath(const std::string &keyPath); LOTKeyPath(const std::string &keyPath);
bool matches(const std::string &key, uint depth); bool matches(const std::string &key, uint32_t depth);
uint nextDepth(const std::string key, uint depth); uint32_t nextDepth(const std::string key, uint32_t depth);
bool fullyResolvesTo(const std::string key, uint 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] == "**"); return skip(key) ? true : (depth < size()) || (mKeys[depth] == "**");
} }
bool skip(const std::string &key) const { return key == "__"; } bool skip(const std::string &key) const { return key == "__"; }
private: private:
bool isGlobstar(uint depth) const { return mKeys[depth] == "**"; } bool isGlobstar(uint32_t depth) const { return mKeys[depth] == "**"; }
bool isGlob(uint depth) const { return mKeys[depth] == "*"; } bool isGlob(uint32_t depth) const { return mKeys[depth] == "*"; }
bool endsWithGlobstar() const { return mKeys.back() == "**"; } bool endsWithGlobstar() const { return mKeys.back() == "**"; }
size_t size() const { return mKeys.size() - 1; } size_t size() const { return mKeys.size() - 1; }

View file

@ -205,7 +205,7 @@ void model::Dash::getDashInfo(int frameNo, std::vector<float> &result) const
{ {
result.clear(); result.clear();
if (mData.empty()) return; if (mData.size() <= 1) return;
if (result.capacity() < mData.size()) result.reserve(mData.size() + 1); 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; auto opacityArraySize = size - colorPoints * 4;
float *opacityPtr = ptr + (colorPoints * 4); float *opacityPtr = ptr + (colorPoints * 4);
stops.clear(); stops.clear();
size_t j = 0;
for (int i = 0; i < colorPoints; i++) { for (int i = 0; i < colorPoints; i++) {
float colorStop = ptr[0]; float colorStop = ptr[0];
model::Color color = model::Color(ptr[1], ptr[2], ptr[3]); model::Color color = model::Color(ptr[1], ptr[2], ptr[3]);
if (opacityArraySize) { if (opacityArraySize) {
if (j == opacityArraySize) { float opacity = getOpacityAtPosition(opacityPtr, opacityArraySize, colorStop);
// already reached the end stops.push_back(std::make_pair(colorStop, color.toColor(opacity)));
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;
}
} else { } else {
stops.push_back(std::make_pair(colorStop, color.toColor())); 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<VGradient> &grad, int frameNo) void model::Gradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
{ {
bool init = false; bool init = false;

View file

@ -57,9 +57,9 @@ inline T lerp(const T &start, const T &end, float t)
namespace model { 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, Normal = 0,
Multiply = 1, Multiply = 1,
Screen = 2, Screen = 2,
@ -72,8 +72,8 @@ public:
Color(float red, float green, float blue) : r(red), g(green), b(blue) {} Color(float red, float green, float blue) : r(red), g(green), b(blue) {}
VColor toColor(float a = 1) VColor toColor(float a = 1)
{ {
return VColor(uchar(255 * r), uchar(255 * g), uchar(255 * b), return VColor(uint8_t(255 * r), uint8_t(255 * g), uint8_t(255 * b),
uchar(255 * a)); uint8_t(255 * a));
} }
friend inline Color operator+(const Color &c1, const Color &c2); friend inline Color operator+(const Color &c1, const Color &c2);
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) VPath &result)
{ {
result.reset(); 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()); auto size = std::min(start.mPoints.size(), end.mPoints.size());
/* reserve exact memory requirement at once /* reserve exact memory requirement at once
* ptSize = size + 1(size + close) * ptSize = size + 1(size + close)
@ -440,7 +445,8 @@ public:
Path, Path,
Polystar, Polystar,
Trim, Trim,
Repeater Repeater,
RoundedCorner
}; };
explicit Object(Object::Type type) : mPtr(nullptr) explicit Object(Object::Type type) : mPtr(nullptr)
@ -661,7 +667,7 @@ public:
class Layer : public Group { class Layer : public Group {
public: public:
enum class Type : uchar { enum class Type : uint8_t {
Precomp = 0, Precomp = 0,
Solid = 1, Solid = 1,
Image = 2, Image = 2,
@ -670,6 +676,7 @@ public:
Text = 5 Text = 5
}; };
Layer() : Group(Object::Type::Layer) {} Layer() : Group(Object::Type::Layer) {}
bool hasRoundedCorner() const noexcept { return mHasRoundedCorner; }
bool hasPathOperator() const noexcept { return mHasPathOperator; } bool hasPathOperator() const noexcept { return mHasPathOperator; }
bool hasGradient() const noexcept { return mHasGradient; } bool hasGradient() const noexcept { return mHasGradient; }
bool hasMask() const noexcept { return mHasMask; } bool hasMask() const noexcept { return mHasMask; }
@ -680,7 +687,10 @@ public:
int inFrame() const noexcept { return mInFrame; } int inFrame() const noexcept { return mInFrame; }
int outFrame() const noexcept { return mOutFrame; } int outFrame() const noexcept { return mOutFrame; }
int startFrame() const noexcept { return mStartFrame; } 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; } bool autoOrient() const noexcept { return mAutoOrient; }
int timeRemap(int frameNo) const; int timeRemap(int frameNo) const;
VSize layerSize() const { return mLayerSize; } VSize layerSize() const { return mLayerSize; }
@ -694,10 +704,7 @@ public:
{ {
return mTransform ? mTransform->opacity(frameNo) : 1.0f; return mTransform ? mTransform->opacity(frameNo) : 1.0f;
} }
Asset *asset() const Asset *asset() const { return mExtra ? mExtra->mAsset : nullptr; }
{
return (mExtra && mExtra->mAsset) ? mExtra->mAsset : nullptr;
}
struct Extra { struct Extra {
Color mSolidColor; Color mSolidColor;
std::string mPreCompRefId; std::string mPreCompRefId;
@ -717,6 +724,7 @@ public:
MatteType mMatteType{MatteType::None}; MatteType mMatteType{MatteType::None};
Type mLayerType{Layer::Type::Null}; Type mLayerType{Layer::Type::Null};
BlendMode mBlendMode{BlendMode::Normal}; BlendMode mBlendMode{BlendMode::Normal};
bool mHasRoundedCorner{false};
bool mHasPathOperator{false}; bool mHasPathOperator{false};
bool mHasMask{false}; bool mHasMask{false};
bool mHasRepeater{false}; bool mHasRepeater{false};
@ -813,6 +821,7 @@ public:
private: private:
void populate(VGradientStops &stops, int frameNo); void populate(VGradientStops &stops, int frameNo);
float getOpacityAtPosition(float *opacities, size_t opacityArraySize, float position);
public: public:
int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/ int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/
@ -893,11 +902,30 @@ public:
Property<PathData> mShape; Property<PathData> mShape;
}; };
class RoundedCorner : public Object {
public:
RoundedCorner() : Object(Object::Type::RoundedCorner) {}
float radius(int frameNo) const { return mRadius.value(frameNo);}
public:
Property<float> mRadius{0};
};
class Rect : public Shape { class Rect : public Shape {
public: public:
Rect() : Shape(Object::Type::Rect) {} 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: public:
RoundedCorner* mRoundedCorner{nullptr};
Property<VPointF> mPos; Property<VPointF> mPos;
Property<VPointF> mSize; Property<VPointF> mSize;
Property<float> mRound{0}; Property<float> mRound{0};

View file

@ -145,6 +145,10 @@ public:
return true; return true;
} }
void Error()
{
st_ = kError;
}
protected: protected:
explicit LookaheadParserHandler(char *str); explicit LookaheadParserHandler(char *str);
@ -191,6 +195,7 @@ public:
int GetInt(); int GetInt();
double GetDouble(); double GetDouble();
const char * GetString(); const char * GetString();
std::string GetStringObject();
bool GetBool(); bool GetBool();
void GetNull(); void GetNull();
@ -228,6 +233,9 @@ public:
model::Object * parseObjectTypeAttr(); model::Object * parseObjectTypeAttr();
model::Object * parseGroupObject(); model::Object * parseGroupObject();
model::Rect * parseRectObject(); model::Rect * parseRectObject();
model::RoundedCorner * parseRoundedCorner();
void updateRoundedCorner(model::Group *parent, model::RoundedCorner *rc);
model::Ellipse * parseEllipseObject(); model::Ellipse * parseEllipseObject();
model::Path * parseShapeObject(); model::Path * parseShapeObject();
model::Polystar *parsePolystarObject(); model::Polystar *parsePolystarObject();
@ -396,7 +404,6 @@ bool LottieParserImpl::EnterObject()
{ {
if (st_ != kEnteringObject) { if (st_ != kEnteringObject) {
st_ = kError; st_ = kError;
RAPIDJSON_ASSERT(false);
return false; return false;
} }
@ -408,7 +415,6 @@ bool LottieParserImpl::EnterArray()
{ {
if (st_ != kEnteringArray) { if (st_ != kEnteringArray) {
st_ = kError; st_ = kError;
RAPIDJSON_ASSERT(false);
return false; return false;
} }
@ -438,7 +444,6 @@ const char *LottieParserImpl::NextObjectKey()
} }
if (st_ != kExitingObject) { if (st_ != kExitingObject) {
RAPIDJSON_ASSERT(false);
st_ = kError; st_ = kError;
return nullptr; return nullptr;
} }
@ -462,7 +467,6 @@ bool LottieParserImpl::NextArrayValue()
} }
if (st_ == kError || st_ == kHasKey) { if (st_ == kError || st_ == kHasKey) {
RAPIDJSON_ASSERT(false);
st_ = kError; st_ = kError;
return false; return false;
} }
@ -474,7 +478,6 @@ int LottieParserImpl::GetInt()
{ {
if (st_ != kHasNumber || !v_.IsInt()) { if (st_ != kHasNumber || !v_.IsInt()) {
st_ = kError; st_ = kError;
RAPIDJSON_ASSERT(false);
return 0; return 0;
} }
@ -487,7 +490,6 @@ double LottieParserImpl::GetDouble()
{ {
if (st_ != kHasNumber) { if (st_ != kHasNumber) {
st_ = kError; st_ = kError;
RAPIDJSON_ASSERT(false);
return 0.; return 0.;
} }
@ -500,7 +502,6 @@ bool LottieParserImpl::GetBool()
{ {
if (st_ != kHasBool) { if (st_ != kHasBool) {
st_ = kError; st_ = kError;
RAPIDJSON_ASSERT(false);
return false; return false;
} }
@ -523,7 +524,6 @@ const char *LottieParserImpl::GetString()
{ {
if (st_ != kHasString) { if (st_ != kHasString) {
st_ = kError; st_ = kError;
RAPIDJSON_ASSERT(false);
return nullptr; return nullptr;
} }
@ -532,6 +532,17 @@ const char *LottieParserImpl::GetString()
return result; return result;
} }
std::string LottieParserImpl::GetStringObject()
{
auto str = GetString();
if (str) {
return std::string(str);
}
return {};
}
void LottieParserImpl::SkipOut(int depth) void LottieParserImpl::SkipOut(int depth)
{ {
do { do {
@ -540,7 +551,6 @@ void LottieParserImpl::SkipOut(int depth)
} else if (st_ == kExitingArray || st_ == kExitingObject) { } else if (st_ == kExitingArray || st_ == kExitingObject) {
--depth; --depth;
} else if (st_ == kError) { } else if (st_ == kError) {
RAPIDJSON_ASSERT(false);
return; return;
} }
@ -606,7 +616,6 @@ void LottieParserImpl::Skip(const char * /*key*/)
model::BlendMode LottieParserImpl::getBlendMode() model::BlendMode LottieParserImpl::getBlendMode()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
auto mode = model::BlendMode::Normal; auto mode = model::BlendMode::Normal;
switch (GetInt()) { switch (GetInt()) {
@ -643,7 +652,6 @@ void LottieParserImpl::resolveLayerRefs()
void LottieParserImpl::parseComposition() void LottieParserImpl::parseComposition()
{ {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
std::shared_ptr<model::Composition> sharedComposition = std::shared_ptr<model::Composition> sharedComposition =
std::make_shared<model::Composition>(); std::make_shared<model::Composition>();
@ -651,22 +659,16 @@ void LottieParserImpl::parseComposition()
compRef = comp; compRef = comp;
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "v")) { if (0 == strcmp(key, "v")) {
RAPIDJSON_ASSERT(PeekType() == kStringType); comp->mVersion = GetStringObject();
comp->mVersion = std::string(GetString());
} else if (0 == strcmp(key, "w")) { } else if (0 == strcmp(key, "w")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
comp->mSize.setWidth(GetInt()); comp->mSize.setWidth(GetInt());
} else if (0 == strcmp(key, "h")) { } else if (0 == strcmp(key, "h")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
comp->mSize.setHeight(GetInt()); comp->mSize.setHeight(GetInt());
} else if (0 == strcmp(key, "ip")) { } else if (0 == strcmp(key, "ip")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
comp->mStartFrame = GetDouble(); comp->mStartFrame = GetDouble();
} else if (0 == strcmp(key, "op")) { } else if (0 == strcmp(key, "op")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
comp->mEndFrame = GetDouble(); comp->mEndFrame = GetDouble();
} else if (0 == strcmp(key, "fr")) { } else if (0 == strcmp(key, "fr")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
comp->mFrameRate = GetDouble(); comp->mFrameRate = GetDouble();
} else if (0 == strcmp(key, "assets")) { } else if (0 == strcmp(key, "assets")) {
parseAssets(comp); parseAssets(comp);
@ -686,6 +688,10 @@ void LottieParserImpl::parseComposition()
// don't have a valid bodymovin header // don't have a valid bodymovin header
return; return;
} }
if (comp->mStartFrame > comp->mEndFrame) {
// reveresed animation? missing data?
return;
}
if (!IsValid()) { if (!IsValid()) {
return; return;
} }
@ -700,20 +706,16 @@ void LottieParserImpl::parseComposition()
void LottieParserImpl::parseMarker() void LottieParserImpl::parseMarker()
{ {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
std::string comment; std::string comment;
int timeframe{0}; int timeframe{0};
int duration{0}; int duration{0};
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "cm")) { if (0 == strcmp(key, "cm")) {
RAPIDJSON_ASSERT(PeekType() == kStringType); comment = GetStringObject();
comment = std::string(GetString());
} else if (0 == strcmp(key, "tm")) { } else if (0 == strcmp(key, "tm")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
timeframe = GetDouble(); timeframe = GetDouble();
} else if (0 == strcmp(key, "dr")) { } else if (0 == strcmp(key, "dr")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
duration = GetDouble(); duration = GetDouble();
} else { } else {
@ -729,7 +731,6 @@ void LottieParserImpl::parseMarker()
void LottieParserImpl::parseMarkers() void LottieParserImpl::parseMarkers()
{ {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
parseMarker(); parseMarker();
@ -739,7 +740,6 @@ void LottieParserImpl::parseMarkers()
void LottieParserImpl::parseAssets(model::Composition *composition) void LottieParserImpl::parseAssets(model::Composition *composition)
{ {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
auto asset = parseAsset(); auto asset = parseAsset();
@ -815,8 +815,6 @@ static std::string toString(const T &value)
*/ */
model::Asset *LottieParserImpl::parseAsset() model::Asset *LottieParserImpl::parseAsset()
{ {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
auto asset = allocator().make<model::Asset>(); auto asset = allocator().make<model::Asset>();
std::string filename; std::string filename;
std::string relativePath; std::string relativePath;
@ -824,30 +822,24 @@ model::Asset *LottieParserImpl::parseAsset()
EnterObject(); EnterObject();
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "w")) { if (0 == strcmp(key, "w")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
asset->mWidth = GetInt(); asset->mWidth = GetInt();
} else if (0 == strcmp(key, "h")) { } else if (0 == strcmp(key, "h")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
asset->mHeight = GetInt(); asset->mHeight = GetInt();
} else if (0 == strcmp(key, "p")) { /* image name */ } else if (0 == strcmp(key, "p")) { /* image name */
asset->mAssetType = model::Asset::Type::Image; asset->mAssetType = model::Asset::Type::Image;
RAPIDJSON_ASSERT(PeekType() == kStringType); filename = GetStringObject();
filename = std::string(GetString());
} else if (0 == strcmp(key, "u")) { /* relative image path */ } else if (0 == strcmp(key, "u")) { /* relative image path */
RAPIDJSON_ASSERT(PeekType() == kStringType); relativePath = GetStringObject();
relativePath = std::string(GetString());
} else if (0 == strcmp(key, "e")) { /* relative image path */ } else if (0 == strcmp(key, "e")) { /* relative image path */
embededResource = GetInt(); embededResource = GetInt();
} else if (0 == strcmp(key, "id")) { /* reference id*/ } else if (0 == strcmp(key, "id")) { /* reference id*/
if (PeekType() == kStringType) { if (PeekType() == kStringType) {
asset->mRefId = std::string(GetString()); asset->mRefId = GetStringObject();
} else { } else {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
asset->mRefId = toString(GetInt()); asset->mRefId = toString(GetInt());
} }
} else if (0 == strcmp(key, "layers")) { } else if (0 == strcmp(key, "layers")) {
asset->mAssetType = model::Asset::Type::Precomp; asset->mAssetType = model::Asset::Type::Precomp;
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
bool staticFlag = true; bool staticFlag = true;
while (NextArrayValue()) { while (NextArrayValue()) {
@ -886,7 +878,6 @@ void LottieParserImpl::parseLayers(model::Composition *comp)
comp->mRootLayer->mLayerType = model::Layer::Type::Precomp; comp->mRootLayer->mLayerType = model::Layer::Type::Precomp;
comp->mRootLayer->setName("__"); comp->mRootLayer->setName("__");
bool staticFlag = true; bool staticFlag = true;
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
auto layer = parseLayer(); auto layer = parseLayer();
@ -900,6 +891,8 @@ void LottieParserImpl::parseLayers(model::Composition *comp)
model::Color LottieParserImpl::toColor(const char *str) model::Color LottieParserImpl::toColor(const char *str)
{ {
if (!str) return {};
model::Color color; model::Color color;
auto len = strlen(str); auto len = strlen(str);
@ -925,7 +918,6 @@ model::Color LottieParserImpl::toColor(const char *str)
model::MatteType LottieParserImpl::getMatteType() model::MatteType LottieParserImpl::getMatteType()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
switch (GetInt()) { switch (GetInt()) {
case 1: case 1:
return model::MatteType::Alpha; return model::MatteType::Alpha;
@ -947,7 +939,6 @@ model::MatteType LottieParserImpl::getMatteType()
model::Layer::Type LottieParserImpl::getLayerType() model::Layer::Type LottieParserImpl::getLayerType()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
switch (GetInt()) { switch (GetInt()) {
case 0: case 0:
return model::Layer::Type::Precomp; return model::Layer::Type::Precomp;
@ -979,7 +970,6 @@ model::Layer::Type LottieParserImpl::getLayerType()
*/ */
model::Layer *LottieParserImpl::parseLayer() model::Layer *LottieParserImpl::parseLayer()
{ {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
model::Layer *layer = allocator().make<model::Layer>(); model::Layer *layer = allocator().make<model::Layer>();
curLayerRef = layer; curLayerRef = layer;
bool ddd = true; bool ddd = true;
@ -988,43 +978,33 @@ model::Layer *LottieParserImpl::parseLayer()
if (0 == strcmp(key, "ty")) { /* Type of layer*/ if (0 == strcmp(key, "ty")) { /* Type of layer*/
layer->mLayerType = getLayerType(); layer->mLayerType = getLayerType();
} else if (0 == strcmp(key, "nm")) { /*Layer name*/ } else if (0 == strcmp(key, "nm")) { /*Layer name*/
RAPIDJSON_ASSERT(PeekType() == kStringType);
layer->setName(GetString()); layer->setName(GetString());
} else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for } else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for
parenting and expressions.*/ parenting and expressions.*/
RAPIDJSON_ASSERT(PeekType() == kNumberType);
layer->mId = GetInt(); layer->mId = GetInt();
} else if (0 == strcmp(key, "ddd")) { /*3d layer */ } else if (0 == strcmp(key, "ddd")) { /*3d layer */
RAPIDJSON_ASSERT(PeekType() == kNumberType);
ddd = GetInt(); ddd = GetInt();
} else if (0 == } else if (0 ==
strcmp(key, strcmp(key,
"parent")) { /*Layer Parent. Uses "ind" of parent.*/ "parent")) { /*Layer Parent. Uses "ind" of parent.*/
RAPIDJSON_ASSERT(PeekType() == kNumberType);
layer->mParentId = GetInt(); layer->mParentId = GetInt();
} else if (0 == strcmp(key, "refId")) { /*preComp Layer reference id*/ } else if (0 == strcmp(key, "refId")) { /*preComp Layer reference id*/
RAPIDJSON_ASSERT(PeekType() == kStringType); layer->extra()->mPreCompRefId = GetStringObject();
layer->extra()->mPreCompRefId = std::string(GetString());
layer->mHasGradient = true; layer->mHasGradient = true;
mLayersToUpdate.push_back(layer); mLayersToUpdate.push_back(layer);
} else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching" } else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching"
RAPIDJSON_ASSERT(PeekType() == kNumberType);
layer->mTimeStreatch = GetDouble(); layer->mTimeStreatch = GetDouble();
} else if (0 == strcmp(key, "tm")) { // time remapping } else if (0 == strcmp(key, "tm")) { // time remapping
parseProperty(layer->extra()->mTimeRemap); parseProperty(layer->extra()->mTimeRemap);
} else if (0 == strcmp(key, "ip")) { } else if (0 == strcmp(key, "ip")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
layer->mInFrame = std::lround(GetDouble()); layer->mInFrame = std::lround(GetDouble());
} else if (0 == strcmp(key, "op")) { } else if (0 == strcmp(key, "op")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
layer->mOutFrame = std::lround(GetDouble()); layer->mOutFrame = std::lround(GetDouble());
} else if (0 == strcmp(key, "st")) { } else if (0 == strcmp(key, "st")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
layer->mStartFrame = GetDouble(); layer->mStartFrame = GetDouble();
} else if (0 == strcmp(key, "bm")) { } else if (0 == strcmp(key, "bm")) {
layer->mBlendMode = getBlendMode(); layer->mBlendMode = getBlendMode();
} else if (0 == strcmp(key, "ks")) { } else if (0 == strcmp(key, "ks")) {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
layer->mTransform = parseTransformObject(ddd); layer->mTransform = parseTransformObject(ddd);
} else if (0 == strcmp(key, "shapes")) { } else if (0 == strcmp(key, "shapes")) {
@ -1084,7 +1064,7 @@ model::Layer *LottieParserImpl::parseLayer()
staticFlag &= child->isStatic(); staticFlag &= child->isStatic();
} }
if (layer->hasMask()) { if (layer->hasMask() && layer->mExtra) {
for (const auto &mask : layer->mExtra->mMasks) { for (const auto &mask : layer->mExtra->mMasks) {
staticFlag &= mask->isStatic(); staticFlag &= mask->isStatic();
} }
@ -1097,7 +1077,6 @@ model::Layer *LottieParserImpl::parseLayer()
void LottieParserImpl::parseMaskProperty(model::Layer *layer) void LottieParserImpl::parseMaskProperty(model::Layer *layer)
{ {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
layer->extra()->mMasks.push_back(parseMaskObject()); layer->extra()->mMasks.push_back(parseMaskObject());
@ -1108,7 +1087,6 @@ model::Mask *LottieParserImpl::parseMaskObject()
{ {
auto obj = allocator().make<model::Mask>(); auto obj = allocator().make<model::Mask>();
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "inv")) { if (0 == strcmp(key, "inv")) {
@ -1153,7 +1131,6 @@ model::Mask *LottieParserImpl::parseMaskObject()
void LottieParserImpl::parseShapesAttr(model::Layer *layer) void LottieParserImpl::parseShapesAttr(model::Layer *layer)
{ {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
parseObject(layer); parseObject(layer);
@ -1162,13 +1139,19 @@ void LottieParserImpl::parseShapesAttr(model::Layer *layer)
model::Object *LottieParserImpl::parseObjectTypeAttr() model::Object *LottieParserImpl::parseObjectTypeAttr()
{ {
RAPIDJSON_ASSERT(PeekType() == kStringType);
const char *type = GetString(); const char *type = GetString();
if (!type) {
vWarning << "No object type specified";
return nullptr;
}
if (0 == strcmp(type, "gr")) { if (0 == strcmp(type, "gr")) {
return parseGroupObject(); return parseGroupObject();
} else if (0 == strcmp(type, "rc")) { } else if (0 == strcmp(type, "rc")) {
return parseRectObject(); 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(); return parseEllipseObject();
} else if (0 == strcmp(type, "tr")) { } else if (0 == strcmp(type, "tr")) {
return parseTransformObject(); return parseTransformObject();
@ -1205,18 +1188,39 @@ model::Object *LottieParserImpl::parseObjectTypeAttr()
void LottieParserImpl::parseObject(model::Group *parent) void LottieParserImpl::parseObject(model::Group *parent)
{ {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "ty")) { if (0 == strcmp(key, "ty")) {
auto child = parseObjectTypeAttr(); 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<model::RoundedCorner *>(child));
}
parent->mChildren.push_back(child);
}
} else { } else {
Skip(key); 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<model::Rect *>(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<model::Group *>(e), rc);
}
}
}
model::Object *LottieParserImpl::parseGroupObject() model::Object *LottieParserImpl::parseGroupObject()
{ {
auto group = allocator().make<model::Group>(); auto group = allocator().make<model::Group>();
@ -1225,14 +1229,13 @@ model::Object *LottieParserImpl::parseGroupObject()
if (0 == strcmp(key, "nm")) { if (0 == strcmp(key, "nm")) {
group->setName(GetString()); group->setName(GetString());
} else if (0 == strcmp(key, "it")) { } else if (0 == strcmp(key, "it")) {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
parseObject(group); parseObject(group);
} }
if (group->mChildren.back()->type() == if (!group->mChildren.empty()
model::Object::Type::Transform) { && group->mChildren.back()->type()
== model::Object::Type::Transform) {
group->mTransform = group->mTransform =
static_cast<model::Transform *>(group->mChildren.back()); static_cast<model::Transform *>(group->mChildren.back());
group->mChildren.pop_back(); group->mChildren.pop_back();
@ -1282,6 +1285,28 @@ model::Rect *LottieParserImpl::parseRectObject()
return obj; return obj;
} }
/*
* https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
*/
model::RoundedCorner *LottieParserImpl::parseRoundedCorner()
{
auto obj = allocator().make<model::RoundedCorner>();
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 * 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() model::Trim::TrimType LottieParserImpl::getTrimType()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
switch (GetInt()) { switch (GetInt()) {
case 1: case 1:
return model::Trim::TrimType::Simultaneously; return model::Trim::TrimType::Simultaneously;
@ -1396,7 +1420,7 @@ model::Trim::TrimType LottieParserImpl::getTrimType()
return model::Trim::TrimType::Individually; return model::Trim::TrimType::Individually;
break; break;
default: default:
RAPIDJSON_ASSERT(0); Error();
return model::Trim::TrimType::Simultaneously; return model::Trim::TrimType::Simultaneously;
break; break;
} }
@ -1544,10 +1568,13 @@ model::Transform *LottieParserImpl::parseTransformObject(bool ddd)
} else if (0 == strcmp(key, "hd")) { } else if (0 == strcmp(key, "hd")) {
objT->setHidden(GetBool()); objT->setHidden(GetBool());
} else if (0 == strcmp(key, "rx")) { } else if (0 == strcmp(key, "rx")) {
if (!obj->mExtra) return nullptr;
parseProperty(obj->mExtra->m3DRx); parseProperty(obj->mExtra->m3DRx);
} else if (0 == strcmp(key, "ry")) { } else if (0 == strcmp(key, "ry")) {
if (!obj->mExtra) return nullptr;
parseProperty(obj->mExtra->m3DRy); parseProperty(obj->mExtra->m3DRy);
} else if (0 == strcmp(key, "rz")) { } else if (0 == strcmp(key, "rz")) {
if (!obj->mExtra) return nullptr;
parseProperty(obj->mExtra->m3DRz); parseProperty(obj->mExtra->m3DRz);
} else { } else {
Skip(key); Skip(key);
@ -1606,7 +1633,6 @@ model::Fill *LottieParserImpl::parseFillObject()
*/ */
CapStyle LottieParserImpl::getLineCap() CapStyle LottieParserImpl::getLineCap()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
switch (GetInt()) { switch (GetInt()) {
case 1: case 1:
return CapStyle::Flat; return CapStyle::Flat;
@ -1622,7 +1648,6 @@ CapStyle LottieParserImpl::getLineCap()
FillRule LottieParserImpl::getFillRule() FillRule LottieParserImpl::getFillRule()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
switch (GetInt()) { switch (GetInt()) {
case 1: case 1:
return FillRule::Winding; return FillRule::Winding;
@ -1641,7 +1666,6 @@ FillRule LottieParserImpl::getFillRule()
*/ */
JoinStyle LottieParserImpl::getLineJoin() JoinStyle LottieParserImpl::getLineJoin()
{ {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
switch (GetInt()) { switch (GetInt()) {
case 1: case 1:
return JoinStyle::Miter; return JoinStyle::Miter;
@ -1678,7 +1702,6 @@ model::Stroke *LottieParserImpl::parseStrokeObject()
} else if (0 == strcmp(key, "lj")) { } else if (0 == strcmp(key, "lj")) {
obj->mJoinStyle = getLineJoin(); obj->mJoinStyle = getLineJoin();
} else if (0 == strcmp(key, "ml")) { } else if (0 == strcmp(key, "ml")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
obj->mMiterLimit = GetDouble(); obj->mMiterLimit = GetDouble();
} else if (0 == strcmp(key, "d")) { } else if (0 == strcmp(key, "d")) {
parseDashProperty(obj->mDash); parseDashProperty(obj->mDash);
@ -1700,7 +1723,6 @@ void LottieParserImpl::parseGradientProperty(model::Gradient *obj,
const char * key) const char * key)
{ {
if (0 == strcmp(key, "t")) { if (0 == strcmp(key, "t")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
obj->mGradientType = GetInt(); obj->mGradientType = GetInt();
} else if (0 == strcmp(key, "o")) { } else if (0 == strcmp(key, "o")) {
parseProperty(obj->mOpacity); parseProperty(obj->mOpacity);
@ -1758,10 +1780,8 @@ model::GradientFill *LottieParserImpl::parseGFillObject()
void LottieParserImpl::parseDashProperty(model::Dash &dash) void LottieParserImpl::parseDashProperty(model::Dash &dash)
{ {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "v")) { if (0 == strcmp(key, "v")) {
@ -1791,7 +1811,6 @@ model::GradientStroke *LottieParserImpl::parseGStrokeObject()
} else if (0 == strcmp(key, "lj")) { } else if (0 == strcmp(key, "lj")) {
obj->mJoinStyle = getLineJoin(); obj->mJoinStyle = getLineJoin();
} else if (0 == strcmp(key, "ml")) { } else if (0 == strcmp(key, "ml")) {
RAPIDJSON_ASSERT(PeekType() == kNumberType);
obj->mMiterLimit = GetDouble(); obj->mMiterLimit = GetDouble();
} else if (0 == strcmp(key, "d")) { } else if (0 == strcmp(key, "d")) {
parseDashProperty(obj->mDash); parseDashProperty(obj->mDash);
@ -1807,10 +1826,8 @@ model::GradientStroke *LottieParserImpl::parseGStrokeObject()
void LottieParserImpl::getValue(std::vector<VPointF> &v) void LottieParserImpl::getValue(std::vector<VPointF> &v)
{ {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
VPointF pt; VPointF pt;
getValue(pt); getValue(pt);
@ -1847,7 +1864,7 @@ void LottieParserImpl::getValue(float &val)
} else if (PeekType() == kNumberType) { } else if (PeekType() == kNumberType) {
val = GetDouble(); val = GetDouble();
} else { } else {
RAPIDJSON_ASSERT(0); Error();
} }
} }
@ -1890,7 +1907,7 @@ void LottieParserImpl::getValue(int &val)
} else if (PeekType() == kNumberType) { } else if (PeekType() == kNumberType) {
val = GetInt(); val = GetInt();
} else { } else {
RAPIDJSON_ASSERT(0); Error();
} }
} }
@ -1905,7 +1922,6 @@ void LottieParserImpl::parsePathInfo()
bool arrayWrapper = (PeekType() == kArrayType); bool arrayWrapper = (PeekType() == kArrayType);
if (arrayWrapper) EnterArray(); if (arrayWrapper) EnterArray();
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "i")) { if (0 == strcmp(key, "i")) {
@ -1917,7 +1933,7 @@ void LottieParserImpl::parsePathInfo()
} else if (0 == strcmp(key, "c")) { } else if (0 == strcmp(key, "c")) {
mPathInfo.mClosed = GetBool(); mPathInfo.mClosed = GetBool();
} else { } else {
RAPIDJSON_ASSERT(0); Error();
Skip(nullptr); Skip(nullptr);
} }
} }
@ -1937,7 +1953,6 @@ void LottieParserImpl::getValue(model::PathData &obj)
VPointF LottieParserImpl::parseInperpolatorPoint() VPointF LottieParserImpl::parseInperpolatorPoint()
{ {
VPointF cp; VPointF cp;
RAPIDJSON_ASSERT(PeekType() == kObjectType);
EnterObject(); EnterObject();
while (const char *key = NextObjectKey()) { while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "x")) { if (0 == strcmp(key, "x")) {
@ -2026,17 +2041,15 @@ void LottieParserImpl::parseKeyFrame(model::KeyFrames<T, Tag> &obj)
continue; continue;
} else if (0 == strcmp(key, "n")) { } else if (0 == strcmp(key, "n")) {
if (PeekType() == kStringType) { if (PeekType() == kStringType) {
parsed.interpolatorKey = GetString(); parsed.interpolatorKey = GetStringObject();
} else { } else {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
RAPIDJSON_ASSERT(PeekType() == kStringType);
if (parsed.interpolatorKey.empty()) { if (parsed.interpolatorKey.empty()) {
parsed.interpolatorKey = GetString(); parsed.interpolatorKey = GetStringObject();
} else { } else {
// skip rest of the string // skip rest of the string
GetString(); Skip(nullptr);
} }
} }
} }
@ -2092,12 +2105,10 @@ void LottieParserImpl::parseShapeProperty(model::Property<model::PathData> &obj)
if (PeekType() == kArrayType) { if (PeekType() == kArrayType) {
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
RAPIDJSON_ASSERT(PeekType() == kObjectType);
parseKeyFrame(obj.animation()); parseKeyFrame(obj.animation());
} }
} else { } else {
if (!obj.isStatic()) { if (!obj.isStatic()) {
RAPIDJSON_ASSERT(false);
st_ = kError; st_ = kError;
return; return;
} }
@ -2118,14 +2129,12 @@ void LottieParserImpl::parsePropertyHelper(model::Property<T, Tag> &obj)
{ {
if (PeekType() == kNumberType) { if (PeekType() == kNumberType) {
if (!obj.isStatic()) { if (!obj.isStatic()) {
RAPIDJSON_ASSERT(false);
st_ = kError; st_ = kError;
return; return;
} }
/*single value property with no animation*/ /*single value property with no animation*/
getValue(obj.value()); getValue(obj.value());
} else { } else {
RAPIDJSON_ASSERT(PeekType() == kArrayType);
EnterArray(); EnterArray();
while (NextArrayValue()) { while (NextArrayValue()) {
/* property with keyframe info*/ /* property with keyframe info*/
@ -2138,9 +2147,7 @@ void LottieParserImpl::parsePropertyHelper(model::Property<T, Tag> &obj)
* or array of object without entering the array * or array of object without entering the array
* thats why this hack is there * thats why this hack is there
*/ */
RAPIDJSON_ASSERT(PeekType() == kNumberType);
if (!obj.isStatic()) { if (!obj.isStatic()) {
RAPIDJSON_ASSERT(false);
st_ = kError; st_ = kError;
return; return;
} }
@ -2252,6 +2259,11 @@ public:
<< " , a:" << !obj->isStatic() << " }"; << " , a:" << !obj->isStatic() << " }";
break; break;
} }
case model::Object::Type::RoundedCorner: {
vDebug << level << "{ RoundedCorner: name: " << obj->name()
<< " , a:" << !obj->isStatic() << " }";
break;
}
case model::Object::Type::Ellipse: { case model::Object::Type::Ellipse: {
vDebug << level << "{ Ellipse: name: " << obj->name() vDebug << level << "{ Ellipse: name: " << obj->name()
<< " , a:" << !obj->isStatic() << " }"; << " , a:" << !obj->isStatic() << " }";

View file

@ -2,9 +2,11 @@ if(LOTTIE_MODULE)
add_library(rlottie-image-loader SHARED add_library(rlottie-image-loader SHARED
stb_image.cpp stb_image.cpp
) )
target_compile_options(rlottie-image-loader PRIVATE if(NOT MSVC)
-fvisibility=hidden 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_FILENAME ${LOTTIE_MODULE_PATH} NAME)
get_filename_component(LOTTIE_MODULE_DIR ${LOTTIE_MODULE_PATH} DIRECTORY) get_filename_component(LOTTIE_MODULE_DIR ${LOTTIE_MODULE_PATH} DIRECTORY)

View file

@ -3,14 +3,14 @@ source_file = ['stb_image.cpp']
if get_option('module') == true if get_option('module') == true
rlottie_image_loader_dir = get_option('moduledir') != '' ? get_option('moduledir') : get_option('libdir') rlottie_image_loader_dir = get_option('moduledir') != '' ? get_option('moduledir') : get_option('libdir')
rlottie_image_loader_lib = shared_library('rlottie-image-loader', rlottie_image_loader_lib = shared_module('rlottie-image-loader',
source_file, source_file,
include_directories : [include_directories('.'), config_dir], include_directories : [include_directories('.'), config_dir],
install : true, install : true,
install_dir : rlottie_image_loader_dir, install_dir : rlottie_image_loader_dir,
cpp_args : compiler_flags, cpp_args : compiler_flags,
gnu_symbol_visibility : 'hidden', gnu_symbol_visibility : 'hidden',
) )
cc = meson.get_compiler('cpp') cc = meson.get_compiler('cpp')
stb_dep = cc.find_library('dl', required : false) stb_dep = cc.find_library('dl', required : false)
else else

View file

@ -307,6 +307,10 @@ RECENT REVISION HISTORY:
#include <stdio.h> #include <stdio.h>
#endif // STBI_NO_STDIO #endif // STBI_NO_STDIO
#if defined _WIN32 || defined __CYGWIN__
#include <windows.h>
#endif // defined _WIN32 || defined __CYGWIN__
#define STBI_VERSION 1 #define STBI_VERSION 1
enum 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) static FILE *stbi__fopen(char const *filename, char const *mode)
{ {
FILE *f; FILE *f;
#if defined(_MSC_VER) && _MSC_VER >= 1400 #if defined(_MSC_VER)
if (0 != fopen_s(&f, filename, mode)) DWORD cch =
f=0; MultiByteToWideChar(CP_UTF8, 0, filename, -1, nullptr, 0);
#else wchar_t *filenameU = new wchar_t[cch];
f = fopen(filename, mode); if(!filenameU)
#endif {
return f; 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;
} }

View file

@ -81,12 +81,12 @@ float VBezier::tAtLength(float l, float totalLength) const
t *= 0.5; t *= 0.5;
float lastBigger = 1.0; float lastBigger = 1.0;
while (1) { for (int num = 0; num < 100500; num++) {
VBezier right = *this; VBezier right = *this;
VBezier left; VBezier left;
right.parameterSplitLeft(t, &left); right.parameterSplitLeft(t, &left);
float lLen = left.length(); float lLen = left.length();
if (fabs(lLen - l) < error) break; if (fabs(lLen - l) < error) return t;
if (lLen < l) { if (lLen < l) {
t += (lastBigger - t) * 0.5f; t += (lastBigger - t) * 0.5f;
@ -95,6 +95,7 @@ float VBezier::tAtLength(float l, float totalLength) const
t -= t * 0.5f; t -= t * 0.5f;
} }
} }
vWarning << "no convergence";
return t; return t;
} }

View file

@ -31,31 +31,31 @@ V_BEGIN_NAMESPACE
void VBitmap::Impl::reset(size_t width, size_t height, VBitmap::Format format) void VBitmap::Impl::reset(size_t width, size_t height, VBitmap::Format format)
{ {
mRoData = nullptr; mRoData = nullptr;
mWidth = uint(width); mWidth = uint32_t(width);
mHeight = uint(height); mHeight = uint32_t(height);
mFormat = format; mFormat = format;
mDepth = depth(format); mDepth = depth(format);
mStride = ((mWidth * mDepth + 31) >> 5) mStride = ((mWidth * mDepth + 31) >> 5)
<< 2; // bytes per scanline (must be multiple of 4) << 2; // bytes per scanline (must be multiple of 4)
mOwnData = std::make_unique<uchar[]>(mStride * mHeight); mOwnData = std::make_unique<uint8_t[]>(mStride * mHeight);
} }
void VBitmap::Impl::reset(uchar *data, size_t width, size_t height, size_t bytesPerLine, void VBitmap::Impl::reset(uint8_t *data, size_t width, size_t height,
VBitmap::Format format) size_t bytesPerLine, VBitmap::Format format)
{ {
mRoData = data; mRoData = data;
mWidth = uint(width); mWidth = uint32_t(width);
mHeight = uint(height); mHeight = uint32_t(height);
mStride = uint(bytesPerLine); mStride = uint32_t(bytesPerLine);
mFormat = format; mFormat = format;
mDepth = depth(format); mDepth = depth(format);
mOwnData = nullptr; 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) { switch (format) {
case VBitmap::Format::Alpha8: case VBitmap::Format::Alpha8:
depth = 8; depth = 8;
@ -70,7 +70,7 @@ uchar VBitmap::Impl::depth(VBitmap::Format format)
return depth; return depth;
} }
void VBitmap::Impl::fill(uint /*pixel*/) void VBitmap::Impl::fill(uint32_t /*pixel*/)
{ {
//@TODO //@TODO
} }
@ -79,9 +79,9 @@ void VBitmap::Impl::updateLuma()
{ {
if (mFormat != VBitmap::Format::ARGB32_Premultiplied) return; if (mFormat != VBitmap::Format::ARGB32_Premultiplied) return;
auto dataPtr = data(); auto dataPtr = data();
for (uint col = 0; col < mHeight; col++) { for (uint32_t col = 0; col < mHeight; col++) {
uint *pixel = (uint *)(dataPtr + mStride * col); uint32_t *pixel = (uint32_t *)(dataPtr + mStride * col);
for (uint row = 0; row < mWidth; row++) { for (uint32_t row = 0; row < mWidth; row++) {
int alpha = vAlpha(*pixel); int alpha = vAlpha(*pixel);
if (alpha == 0) { if (alpha == 0) {
pixel++; pixel++;
@ -112,8 +112,8 @@ VBitmap::VBitmap(size_t width, size_t height, VBitmap::Format format)
mImpl = rc_ptr<Impl>(width, height, format); mImpl = rc_ptr<Impl>(width, height, format);
} }
VBitmap::VBitmap(uchar *data, size_t width, size_t height, size_t bytesPerLine, VBitmap::VBitmap(uint8_t *data, size_t width, size_t height,
VBitmap::Format format) size_t bytesPerLine, VBitmap::Format format)
{ {
if (!data || width <= 0 || height <= 0 || bytesPerLine <= 0 || if (!data || width <= 0 || height <= 0 || bytesPerLine <= 0 ||
format == Format::Invalid) format == Format::Invalid)
@ -122,7 +122,7 @@ VBitmap::VBitmap(uchar *data, size_t width, size_t height, size_t bytesPerLine,
mImpl = rc_ptr<Impl>(data, width, height, bytesPerLine, format); mImpl = rc_ptr<Impl>(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) VBitmap::Format format)
{ {
if (mImpl) { if (mImpl) {
@ -165,12 +165,12 @@ size_t VBitmap::depth() const
return mImpl ? mImpl->mDepth : 0; return mImpl ? mImpl->mDepth : 0;
} }
uchar *VBitmap::data() uint8_t *VBitmap::data()
{ {
return mImpl ? mImpl->data() : nullptr; return mImpl ? mImpl->data() : nullptr;
} }
uchar *VBitmap::data() const uint8_t *VBitmap::data() const
{ {
return mImpl ? mImpl->data() : nullptr; return mImpl ? mImpl->data() : nullptr;
} }
@ -195,7 +195,7 @@ VBitmap::Format VBitmap::format() const
return mImpl ? mImpl->format() : VBitmap::Format::Invalid; return mImpl ? mImpl->format() : VBitmap::Format::Invalid;
} }
void VBitmap::fill(uint pixel) void VBitmap::fill(uint32_t pixel)
{ {
if (mImpl) mImpl->fill(pixel); if (mImpl) mImpl->fill(pixel);
} }

View file

@ -30,7 +30,7 @@ V_BEGIN_NAMESPACE
class VBitmap { class VBitmap {
public: public:
enum class Format: uchar { enum class Format : uint8_t {
Invalid, Invalid,
Alpha8, Alpha8,
ARGB32, ARGB32,
@ -39,8 +39,10 @@ public:
VBitmap() = default; VBitmap() = default;
VBitmap(size_t w, size_t h, VBitmap::Format format); 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); VBitmap(uint8_t *data, size_t w, size_t h, size_t bytesPerLine,
void reset(uchar *data, size_t w, size_t h, size_t stride, VBitmap::Format format); 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); void reset(size_t w, size_t h, VBitmap::Format format=Format::ARGB32_Premultiplied);
size_t stride() const; size_t stride() const;
size_t width() const; size_t width() const;
@ -48,27 +50,28 @@ public:
size_t depth() const; size_t depth() const;
VBitmap::Format format() const; VBitmap::Format format() const;
bool valid() const; bool valid() const;
uchar * data(); uint8_t * data();
uchar * data() const; uint8_t * data() const;
VRect rect() const; VRect rect() const;
VSize size() const; VSize size() const;
void fill(uint pixel); void fill(uint32_t pixel);
void updateLuma(); void updateLuma();
private: private:
struct Impl { struct Impl {
std::unique_ptr<uchar[]> mOwnData{nullptr}; std::unique_ptr<uint8_t[]> mOwnData{nullptr};
uchar * mRoData{nullptr}; uint8_t * mRoData{nullptr};
uint mWidth{0}; uint32_t mWidth{0};
uint mHeight{0}; uint32_t mHeight{0};
uint mStride{0}; uint32_t mStride{0};
uchar mDepth{0}; uint8_t mDepth{0};
VBitmap::Format mFormat{VBitmap::Format::Invalid}; VBitmap::Format mFormat{VBitmap::Format::Invalid};
explicit Impl(size_t width, size_t height, VBitmap::Format format) explicit Impl(size_t width, size_t height, VBitmap::Format format)
{ {
reset(width, height, 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); reset(data, w, h, bytesPerLine, format);
} }
@ -77,12 +80,12 @@ private:
size_t stride() const { return mStride; } size_t stride() const { return mStride; }
size_t width() const { return mWidth; } size_t width() const { return mWidth; }
size_t height() const { return mHeight; } 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; } 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); void reset(size_t, size_t, VBitmap::Format);
static uchar depth(VBitmap::Format format); static uint8_t depth(VBitmap::Format format);
void fill(uint); void fill(uint32_t);
void updateLuma(); void updateLuma();
}; };

View file

@ -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) : mType(VBrush::Type::Solid), mColor(r, g, b, a)
{ {

View file

@ -75,7 +75,7 @@ public:
VBrush():mType(Type::NoBrush),mColor(){}; VBrush():mType(Type::NoBrush),mColor(){};
explicit VBrush(const VColor &color); explicit VBrush(const VColor &color);
explicit VBrush(const VGradient *gradient); 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); explicit VBrush(const VTexture *texture);
inline VBrush::Type type() const { return mType; } inline VBrush::Type type() const { return mType; }
public: public:

View file

@ -106,7 +106,7 @@ void VDrawable::setDashInfo(std::vector<float> &dashInfo)
bool hasChanged = false; bool hasChanged = false;
if (obj->mDash.size() == dashInfo.size()) { 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])) { if (!vCompare(obj->mDash[i], dashInfo[i])) {
hasChanged = true; hasChanged = true;
break; break;

View file

@ -56,7 +56,7 @@ public:
VCacheKey hash_val = 0; VCacheKey hash_val = 0;
VCacheData info; VCacheData info;
const VGradientStops &stops = gradient.mStops; 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 += hash_val +=
VCacheKey(stops[i].second.premulARGB() * gradient.alpha()); VCacheKey(stops[i].second.premulARGB() * gradient.alpha());
@ -100,11 +100,11 @@ public:
} }
protected: protected:
uint maxCacheSize() const { return 60; } uint32_t maxCacheSize() const { return 60; }
VCacheData addCacheElement(VCacheKey hash_val, const VGradient &gradient) VCacheData addCacheElement(VCacheKey hash_val, const VGradient &gradient)
{ {
if (mCache.size() == maxCacheSize()) { if (mCache.size() == maxCacheSize()) {
uint count = maxCacheSize() / 10; uint32_t count = maxCacheSize() / 10;
while (count--) { while (count--) {
mCache.erase(mCache.begin()); mCache.erase(mCache.begin());
} }
@ -147,7 +147,7 @@ bool VGradientCache::generateGradientColorTable(const VGradientStops &stops,
colorTable[pos++] = curColor; colorTable[pos++] = curColor;
while (fpos <= curr->first) { while (fpos <= curr->first && pos < size) {
colorTable[pos] = colorTable[pos - 1]; colorTable[pos] = colorTable[pos - 1];
pos++; pos++;
fpos += incr; fpos += incr;
@ -519,7 +519,7 @@ static void blend_color(size_t size, const VRle::Span *array, void *userData)
{ {
VSpanData *data = (VSpanData *)(userData); VSpanData *data = (VSpanData *)(userData);
Operator op = getOperator(data); Operator op = getOperator(data);
const uint color = data->mSolid; const uint32_t color = data->mSolid;
for (size_t i = 0 ; i < size; ++i) { for (size_t i = 0 ; i < size; ++i) {
const auto &span = array[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 // 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 <class Process> template <class Process>
static inline void process_in_chunk(const VRle::Span *array, size_t size, static inline void process_in_chunk(const VRle::Span *array, size_t size,
Process process) Process process)
{ {
std::array<uint, 2048> buf; std::array<uint32_t, 2048> buf;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
const auto &span = array[i]; const auto &span = array[i];
size_t len = span.len; size_t len = span.len;
size_t x = span.x; auto x = span.x;
while (len) { while (len) {
auto l = std::min(len, buf.size()); auto l = std::min(len, buf.size());
process(buf.data(), x, span.y, l, span.coverage); 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( process_in_chunk(
array, size, 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.srcFetch(scratch, &op, data, (int)y, (int)x, (int)len);
op.func(data->buffer((int)x, (int)y), (int)len, scratch, cov); 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; 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); return ((a * b) >> 8);
} }
@ -590,7 +590,7 @@ static void blend_image_xform(size_t size, const VRle::Span *array,
process_in_chunk( process_in_chunk(
array, size, 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 auto coverage = (cov * src.alpha()) >> 8;
const float xfactor = y * data->m21 + data->dx + data->m11; const float xfactor = y * data->m21 + data->dx + data->m11;
const float yfactor = y * data->m22 + data->dy + data->m12; const float yfactor = y * data->m22 + data->dy + data->m12;

View file

@ -127,7 +127,7 @@ public:
void resetBuffer(int val = 0); void resetBuffer(int val = 0);
inline uchar *scanLine(int y) inline uint8_t *scanLine(int y)
{ {
assert(y >= 0); assert(y >= 0);
assert(size_t(y) < mHeight); assert(size_t(y) < mHeight);
@ -150,7 +150,7 @@ private:
size_t mHeight{0}; size_t mHeight{0};
size_t mBytesPerLine{0}; size_t mBytesPerLine{0};
size_t mBytesPerPixel{0}; size_t mBytesPerPixel{0};
mutable uchar * mBuffer{nullptr}; mutable uint8_t *mBuffer{nullptr};
}; };
struct VGradientData { struct VGradientData {
@ -171,8 +171,8 @@ struct VGradientData {
struct VTextureData : public VRasterBuffer { struct VTextureData : public VRasterBuffer {
uint32_t pixel(int x, int y) const { return *pixelRef(x, y); }; uint32_t pixel(int x, int y) const { return *pixelRef(x, y); };
uchar alpha() const { return mAlpha; } uint8_t alpha() const { return mAlpha; }
void setAlpha(uchar alpha) { mAlpha = alpha; } void setAlpha(uint8_t alpha) { mAlpha = alpha; }
void setClip(const VRect &clip); void setClip(const VRect &clip);
// clip rect // clip rect
int left; int left;
@ -180,7 +180,7 @@ struct VTextureData : public VRasterBuffer {
int top; int top;
int bottom; int bottom;
bool hasAlpha; bool hasAlpha;
uchar mAlpha; uint8_t mAlpha;
}; };
struct VColorTable { struct VColorTable {
@ -208,7 +208,7 @@ struct VSpanData {
mDrawableSize = VSize(region.width(), region.height()); 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()); return mRasterBuffer->pixelRef(x + mOffset.x(), y + mOffset.y());
} }
@ -256,9 +256,10 @@ inline constexpr int vAlpha(uint32_t c)
return c >> 24; 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 >>= 8;
t &= 0xff00ff; t &= 0xff00ff;
x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;

View file

@ -65,10 +65,10 @@ static void color_SourceOver(uint32_t *dest, int length, uint32_t color,
dest = d * sa * ca + d * cia dest = d * sa * ca + d * cia
= d * (sa * ca + cia) = d * (sa * ca + cia)
*/ */
static void color_DestinationIn(uint *dest, int length, uint color, static void color_DestinationIn(uint32_t *dest, int length, uint32_t color,
uint alpha) uint32_t alpha)
{ {
uint a = vAlpha(color); uint32_t a = vAlpha(color);
if (alpha != 255) { if (alpha != 255) {
a = BYTE_MUL(a, alpha) + 255 - alpha; 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 dest = d * sia * ca + d * cia
= d * (sia * ca + cia) = d * (sia * ca + cia)
*/ */
static void color_DestinationOut(uint *dest, int length, uint color, static void color_DestinationOut(uint32_t *dest, int length, uint32_t color,
uint alpha) uint32_t alpha)
{ {
uint a = vAlpha(~color); uint32_t a = vAlpha(~color);
if (alpha != 255) a = BYTE_MUL(a, alpha) + 255 - alpha; if (alpha != 255) a = BYTE_MUL(a, alpha) + 255 - alpha;
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
dest[i] = BYTE_MUL(dest[i], a); 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) uint32_t alpha)
{ {
if (alpha == 255) { if (alpha == 255) {
memcpy(dest, src, size_t(length) * sizeof(uint)); memcpy(dest, src, size_t(length) * sizeof(uint32_t));
} else { } else {
uint ialpha = 255 - alpha; uint32_t ialpha = 255 - alpha;
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
dest[i] = dest[i] =
interpolate_pixel(src[i], alpha, dest[i], ialpha); 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, static void src_SourceOver(uint32_t *dest, int length, const uint32_t *src,
uint32_t alpha) uint32_t alpha)
{ {
uint s, sia; uint32_t s, sia;
if (alpha == 255) { if (alpha == 255) {
for (int i = 0; i < length; ++i) { 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, static void src_DestinationIn(uint32_t *dest, int length, const uint32_t *src,
uint alpha) uint32_t alpha)
{ {
if (alpha == 255) { if (alpha == 255) {
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
dest[i] = BYTE_MUL(dest[i], vAlpha(src[i])); dest[i] = BYTE_MUL(dest[i], vAlpha(src[i]));
} }
} else { } else {
uint cia = 255 - alpha; uint32_t cia = 255 - alpha;
for (int i = 0; i < length; ++i) { 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); dest[i] = BYTE_MUL(dest[i], a);
} }
} }
} }
static void src_DestinationOut(uint *dest, int length, const uint *src, static void src_DestinationOut(uint32_t *dest, int length, const uint32_t *src,
uint alpha) uint32_t alpha)
{ {
if (alpha == 255) { if (alpha == 255) {
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
dest[i] = BYTE_MUL(dest[i], vAlpha(~src[i])); dest[i] = BYTE_MUL(dest[i], vAlpha(~src[i]));
} }
} else { } else {
uint cia = 255 - alpha; uint32_t cia = 255 - alpha;
for (int i = 0; i < length; ++i) { 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); dest[i] = BYTE_MUL(dest[i], sia);
} }
} }

View file

@ -29,9 +29,6 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
using uint = uint32_t;
using ushort = uint16_t;
using uchar = uint8_t;
#if !defined(V_NAMESPACE) #if !defined(V_NAMESPACE)
@ -122,10 +119,13 @@ public:
explicit constexpr inline vFlagHelper(int ai) noexcept : i(ai) {} explicit constexpr inline vFlagHelper(int ai) noexcept : i(ai) {}
constexpr inline operator int() const noexcept { return i; } 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(short ai) noexcept : i(int(ai)) {}
explicit constexpr inline vFlagHelper(ushort ai) noexcept : i(int(uint(ai))) {} explicit constexpr inline vFlagHelper(uint16_t ai) noexcept
constexpr inline operator uint() const noexcept { return uint(i); } : i(int(uint32_t(ai)))
{
}
constexpr inline operator uint32_t() const noexcept { return uint32_t(i); }
}; };
template <typename Enum> template <typename Enum>
@ -139,7 +139,7 @@ public:
using Int = typename std::conditional< using Int = typename std::conditional<
std::is_unsigned<typename std::underlying_type<Enum>::type>::value, std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
unsigned int, signed int>::type; uint32_t, signed int>::type;
using enum_type = Enum; using enum_type = Enum;
// compiler-generated copy/move ctor/assignment operators are fine! // compiler-generated copy/move ctor/assignment operators are fine!
@ -153,7 +153,7 @@ public:
i &= mask; i &= mask;
return *this; return *this;
} }
inline vFlag &operator&=(uint mask) noexcept inline vFlag &operator&=(uint32_t mask) noexcept
{ {
i &= mask; i &= mask;
return *this; return *this;
@ -206,7 +206,7 @@ public:
{ {
return vFlag(vFlagHelper(i & mask)); 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)); return vFlag(vFlagHelper(i & mask));
} }
@ -236,44 +236,47 @@ public:
class VColor { class VColor {
public: public:
VColor() = default; VColor() = default;
explicit VColor(uchar red, uchar green, uchar blue, uchar alpha = 255) noexcept explicit VColor(uint8_t red, uint8_t green, uint8_t blue,
:a(alpha), r(red), g(green), b(blue){} uint8_t alpha = 255) noexcept
inline uchar red() const noexcept { return r; } : a(alpha), r(red), g(green), b(blue)
inline uchar green() const noexcept { return g; } {
inline uchar blue() const noexcept { return b; } }
inline uchar alpha() const noexcept { return a; } inline uint8_t red() const noexcept { return r; }
inline void setRed(uchar red) noexcept { r = red; } inline uint8_t green() const noexcept { return g; }
inline void setGreen(uchar green) noexcept { g = green; } inline uint8_t blue() const noexcept { return b; }
inline void setBlue(uchar blue) noexcept { b = blue; } inline uint8_t alpha() const noexcept { return a; }
inline void setAlpha(uchar alpha) noexcept { a = alpha; } 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 isOpaque() const { return a == 255; }
inline bool isTransparent() const { return a == 0; } inline bool isTransparent() const { return a == 0; }
inline bool operator==(const VColor &o) const inline bool operator==(const VColor &o) const
{ {
return ((a == o.a) && (r == o.r) && (g == o.g) && (b == o.b)); 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 pr = (r * a) / 255;
int pg = (g * a) / 255; int pg = (g * a) / 255;
int pb = (b * 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 alpha = int(a * opacity);
int pr = (r * alpha) / 255; int pr = (r * alpha) / 255;
int pg = (g * alpha) / 255; int pg = (g * alpha) / 255;
int pb = (b * 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: public:
uchar a{0}; uint8_t a{0};
uchar r{0}; uint8_t r{0};
uchar g{0}; uint8_t g{0};
uchar b{0}; uint8_t b{0};
}; };
enum class FillRule: unsigned char { EvenOdd, Winding }; enum class FillRule: unsigned char { EvenOdd, Winding };

View file

@ -66,7 +66,7 @@ static void fillRect(const VRect &r, VSpanData *data)
int i = 0; int i = 0;
while (i < n) { while (i < n) {
spans[i].x = short(x1); 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].y = short(y + i);
spans[i].coverage = 255; spans[i].coverage = 255;
++i; ++i;
@ -84,12 +84,12 @@ void VPainter::drawBitmapUntransform(const VRect & target,
{ {
mSpanData.initTexture(&bitmap, const_alpha, source); mSpanData.initTexture(&bitmap, const_alpha, source);
if (!mSpanData.mUnclippedBlendFunc) return; 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) VPainter::VPainter(VBitmap *buffer)

View file

@ -219,13 +219,13 @@ void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir)
void VPath::VPathData::addRect(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 x = rect.x();
float y = rect.y(); float y = rect.y();
float w = rect.width(); float w = rect.width();
float h = rect.height(); float h = rect.height();
if (vCompare(w, 0.f) && vCompare(h, 0.f)) return;
reserve(5, 6); // 1Move + 4Line + 1Close reserve(5, 6); // 1Move + 4Line + 1Close
if (dir == VPath::Direction::CW) { if (dir == VPath::Direction::CW) {
moveTo(x + w, y); moveTo(x + w, y);

View file

@ -35,7 +35,7 @@ class VPath {
public: public:
enum class Direction { CCW, CW }; 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 empty() const;
bool null() const; bool null() const;
void moveTo(const VPointF &p); void moveTo(const VPointF &p);

View file

@ -380,7 +380,7 @@ struct VRleTask {
outRef.convert(mPath); outRef.convert(mPath);
outRef.convert(mCap, mJoin, mStrokeWidth, mMiterLimit); outRef.convert(mCap, mJoin, mStrokeWidth, mMiterLimit);
uint points, contors; uint32_t points, contors;
SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap,
outRef.ftJoin, outRef.ftMiterLimit); outRef.ftJoin, outRef.ftMiterLimit);
@ -461,20 +461,29 @@ class RleTaskScheduler {
for (unsigned n = 0; n != _count; ++n) { for (unsigned n = 0; n != _count; ++n) {
_threads.emplace_back([&, n] { run(n); }); _threads.emplace_back([&, n] { run(n); });
} }
IsRunning = true;
} }
public: public:
static bool IsRunning;
static RleTaskScheduler &instance() static RleTaskScheduler &instance()
{ {
static RleTaskScheduler singleton; static RleTaskScheduler singleton;
return singleton; return singleton;
} }
~RleTaskScheduler() ~RleTaskScheduler() { stop(); }
{
for (auto &e : _q) e.done();
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) void process(VTask task)
@ -499,12 +508,16 @@ public:
SW_FT_Stroker stroker; SW_FT_Stroker stroker;
public: public:
static bool IsRunning;
static RleTaskScheduler &instance() static RleTaskScheduler &instance()
{ {
static RleTaskScheduler singleton; static RleTaskScheduler singleton;
return singleton; return singleton;
} }
void stop() {}
RleTaskScheduler() { SW_FT_Stroker_New(&stroker); } RleTaskScheduler() { SW_FT_Stroker_New(&stroker); }
~RleTaskScheduler() { SW_FT_Stroker_Done(stroker); } ~RleTaskScheduler() { SW_FT_Stroker_Done(stroker); }
@ -513,6 +526,8 @@ public:
}; };
#endif #endif
bool RleTaskScheduler::IsRunning{false};
struct VRasterizer::VRasterizerImpl { struct VRasterizer::VRasterizerImpl {
VRleTask mTask; VRleTask mTask;
@ -560,4 +575,11 @@ void VRasterizer::rasterize(VPath path, CapStyle cap, JoinStyle join,
updateRequest(); updateRequest();
} }
void lottieShutdownRasterTaskScheduler()
{
if (RleTaskScheduler::IsRunning) {
RleTaskScheduler::instance().stop();
}
}
V_END_NAMESPACE V_END_NAMESPACE

View file

@ -27,6 +27,7 @@
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <limits>
#include <vector> #include <vector>
#include "vdebug.h" #include "vdebug.h"
#include "vglobal.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(const VRect &, rle_view &, Result &);
static size_t _opIntersect(rle_view &, 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; 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) { for (auto &i : mSpans) {
i.coverage = divBy255(i.coverage * alpha); 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()); mSpans.reserve(a.size() + b.size());
// if two rle are disjoint // 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) { if (a.data()[0].y < b.data()[0].y) {
copy(a.data(), a.size(), mSpans); copy(a.data(), a.size(), mSpans);
copy(b.data(), b.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; out->x = minx;
} else { } else {
out->x = span.x; 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) { if (out->len != 0) {
out->y = span.y; 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; 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--) { while (count--) {
int x = spans->x + offsetX; int x = spans->x + offsetX;
int l = spans->len; int l = spans->len;
uchar *ptr = buffer + x; uint8_t *ptr = buffer + x;
while (l--) { while (l--) {
int da = *ptr; int da = *ptr;
*ptr = divBy255((255 - spans->coverage) * (da) + *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) int offsetX)
{ {
while (count--) { while (count--) {
int x = spans->x + offsetX; int x = spans->x + offsetX;
int l = spans->len; int l = spans->len;
uchar *ptr = buffer + x; uint8_t *ptr = buffer + x;
while (l--) { while (l--) {
*ptr = divBy255((255 - spans->coverage) * (*ptr)); *ptr = divBy255((255 - spans->coverage) * (*ptr));
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) int offsetX)
{ {
while (count--) { while (count--) {
int x = spans->x + offsetX; int x = spans->x + offsetX;
int l = spans->len; int l = spans->len;
uchar *ptr = buffer + x; uint8_t *ptr = buffer + x;
while (l--) { while (l--) {
*ptr = spans->coverage + divBy255((255 - spans->coverage) * (*ptr)); *ptr = spans->coverage + divBy255((255 - spans->coverage) * (*ptr));
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--) { while (count--) {
int x = spans->x + offsetX; int x = spans->x + offsetX;
int l = spans->len; int l = spans->len;
uchar *ptr = buffer + x; uint8_t *ptr = buffer + x;
while (l--) { while (l--) {
*ptr = std::max(spans->coverage, *ptr); *ptr = std::max(spans->coverage, *ptr);
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; size_t count = 0;
uchar value = buffer[0]; uint8_t value = buffer[0];
int curIndex = 0; int curIndex = 0;
// size = offsetX < 0 ? size + offsetX : size; // size = offsetX < 0 ? size + offsetX : size;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
uchar curValue = buffer[0]; uint8_t curValue = buffer[0];
if (value != curValue) { if (value != curValue) {
if (value) { if (value) {
out->y = y; out->y = y;
@ -549,10 +551,10 @@ struct SpanMerger {
break; break;
} }
} }
using blitter = void (*)(VRle::Span *, int, uchar *, int); using blitter = void (*)(VRle::Span *, int, uint8_t *, int);
blitter _blitter; blitter _blitter;
std::array<VRle::Span, 256> _result; std::array<VRle::Span, 256> _result;
std::array<uchar, 1024> _buffer; std::array<uint8_t, 1024> _buffer;
VRle::Span * _aStart{nullptr}; VRle::Span * _aStart{nullptr};
VRle::Span * _bStart{nullptr}; VRle::Span * _bStart{nullptr};

View file

@ -36,8 +36,8 @@ public:
struct Span { struct Span {
short x{0}; short x{0};
short y{0}; short y{0};
ushort len{0}; uint16_t len{0};
uchar coverage{0}; uint8_t coverage{0};
}; };
using VRleSpanCb = void (*)(size_t count, const VRle::Span *spans, using VRleSpanCb = void (*)(size_t count, const VRle::Span *spans,
void *userData); void *userData);
@ -52,7 +52,7 @@ public:
void reset() { d.write().reset(); } void reset() { d.write().reset(); }
void translate(const VPoint &p) { d.write().translate(p); } 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 VRect &r, VRleSpanCb cb, void *userData) const;
void intersect(const VRle &rle, 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 setBbox(const VRect &bbox) const;
void reset(); void reset();
void translate(const VPoint &p); 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 opGeneric(const VRle::Data &, const VRle::Data &, Op code);
void opSubstract(const VRle::Data &, const VRle::Data &); void opSubstract(const VRle::Data &, const VRle::Data &);
void opIntersect(VRle::View a, VRle::View b); void opIntersect(VRle::View a, VRle::View b);

View file

@ -27,6 +27,81 @@ public:
return mPlayer ? true : false; return mPlayer ? true : false;
} }
void setFillColor(std::string keypath, float r, float g, float b)
{
if (!mPlayer) return;
mPlayer->setValue<rlottie::Property::FillColor>(keypath,
rlottie::Color(r, g, b));
}
void setStrokeColor(std::string keypath, float r, float g, float b)
{
if (!mPlayer) return;
mPlayer->setValue<rlottie::Property::StrokeColor>(keypath,
rlottie::Color(r, g, b));
}
void setFillOpacity(std::string keypath, float opacity)
{
if (!mPlayer || opacity > 100 || opacity < 0) return;
mPlayer->setValue<rlottie::Property::FillOpacity>(keypath, opacity);
}
void setStrokeOpacity(std::string keypath, float opacity)
{
if (!mPlayer || opacity > 100 || opacity < 0) return;
mPlayer->setValue<rlottie::Property::StrokeOpacity>(keypath, opacity);
}
void setStrokeWidth(std::string keypath, float width)
{
if (!mPlayer || width < 0) return;
mPlayer->setValue<rlottie::Property::StrokeWidth>(keypath, width);
}
void setAnchor(std::string keypath, float x, float y)
{
if (!mPlayer) return;
mPlayer->setValue<rlottie::Property::TrAnchor>(keypath,
rlottie::Point(x, y));
}
void setPosition(std::string keypath, float x, float y)
{
if (!mPlayer) return;
mPlayer->setValue<rlottie::Property::TrPosition>(keypath,
rlottie::Point(x, y));
}
void setScale(std::string keypath, float width, float height)
{
if (!mPlayer) return;
mPlayer->setValue<rlottie::Property::TrScale>(keypath,
rlottie::Size(width, height));
}
void setRotation(std::string keypath, float degree)
{
if (!mPlayer || degree > 360 || degree < 0) return;
mPlayer->setValue<rlottie::Property::TrRotation>(keypath, degree);
}
void setOpacity(std::string keypath, float opacity)
{
if (!mPlayer || opacity > 100 || opacity < 0) return;
mPlayer->setValue<rlottie::Property::TrOpacity>(keypath, opacity);
}
// canvas pixel pix[0] pix[1] pix[2] pix[3] {B G R A} // 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} // lottie pixel pix[0] pix[1] pix[2] pix[3] {R G B A}
val render(int frame, int width, int height) val render(int frame, int width, int height)
@ -105,5 +180,15 @@ EMSCRIPTEN_BINDINGS(rlottie_bindings)
.constructor(&RlottieWasm::create) .constructor(&RlottieWasm::create)
.function("load", &RlottieWasm::load, allow_raw_pointers()) .function("load", &RlottieWasm::load, allow_raw_pointers())
.function("frames", &RlottieWasm::frames) .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);
} }

View file

@ -45,7 +45,7 @@
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
@ -118,6 +118,9 @@
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../inc;./;../src/lottie;../src/vector;../src/vector/pixman;../src/vector/freetype;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-DRLOTTIE_BUILD %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4251;4244;4996</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>