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
- set PATH=%cd%;%PYTHON%;%PYTHON%\Scripts;%PATH%
- pip install meson==0.50.0 ninja
- call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" %ARCH%
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
build_script:
- meson -Dwerror=false --backend=ninja --prefix=%cd% build
- where link

View file

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

View file

@ -10,3 +10,9 @@ Shinwoo Kim <cinoo.kim@samsung.com>
Vincent Torri <vincent.torri@gmail.com>
Nicholas Guriev <guriev-ns@ya.ru>
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,11 +41,22 @@ set(LOTTIE_MODULE_PATH "${CMAKE_SHARED_LIBRARY_PREFIX}rlottie-image-loader${CMAK
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in config.h)
target_include_directories(rlottie
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}"
)
#declare common target compilation options
if(MSVC)
target_compile_options(rlottie
PUBLIC
PRIVATE
/std:c++14
/EHs-c- # disable exceptions
/GR- # disable RTTI
/W3
)
else()
target_compile_options(rlottie
PUBLIC
PRIVATE
@ -56,13 +67,6 @@ target_compile_options(rlottie
-fno-rtti
-Wall
-fvisibility=hidden
)
#MSVC does not recognize these parameters
if (NOT WIN32)
target_compile_options(rlottie
PUBLIC
PRIVATE
-Wnon-virtual-dtor
-Woverloaded-virtual
-Wno-unused-parameter
@ -71,6 +75,8 @@ endif()
if (WIN32 AND NOT BUILD_SHARED_LIBS)
target_compile_definitions(rlottie PUBLIC -DRLOTTIE_BUILD=0)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
endif()
#declare dependancy
@ -85,7 +91,7 @@ target_link_libraries(rlottie
if (NOT APPLE AND NOT WIN32)
target_link_libraries(rlottie
PRIVATE
"-Wl,--version-script=${CMAKE_SOURCE_DIR}/rlottie.expmap"
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/rlottie.expmap"
)
endif()
@ -100,7 +106,7 @@ if (NOT LOTTIE_ASAN)
PUBLIC
"-Wl, -undefined error"
)
else()
elseif(NOT MSVC)
target_link_libraries(rlottie
PUBLIC
"-Wl,--no-undefined"
@ -195,4 +201,3 @@ export(EXPORT rlottie-targets FILE ${CMAKE_CURRENT_BINARY_DIR}/rlottieTargets.cm
#Register package in user's package registry
export(PACKAGE rlottie)

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://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">
<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)
## Building Lottie
rottie supports [meson](https://mesonbuild.com/) and [cmake](https://cmake.org/) build system. rottie is written in `C++14`. and has a public header dependancy of `C++11`
rlottie supports [meson](https://mesonbuild.com/) and [cmake](https://cmake.org/) build system. rlottie is written in `C++14`. and has a public header dependency of `C++11`
### Meson Build
install [meson](http://mesonbuild.com/Getting-meson.html) and [ninja](https://ninja-build.org/) if not already installed.
@ -98,7 +99,7 @@ ninja test
#
## Demo
If you want to see rlottie librray in action without building it please visit [rlottie online viewer](http://rlottie.com)
If you want to see rlottie library in action without building it please visit [rlottie online viewer](http://rlottie.com)
While building rlottie library it generates a simple lottie to GIF converter which can be used to convert lottie json file to GIF file.
@ -166,7 +167,7 @@ You can update properties dynamically at runtime. This can be used for a variety
### Understanding After Effects
To understand how to change animation properties in Lottie, you should first understand how animation properties are stored in Lottie. Animation properties are stored in a data tree that mimics the information heirarchy of After Effects. In After Effects a Composition is a collection of Layers that each have their own timelines. Layer objects have string names, and their contents can be an image, shape layers, fills, strokes, or just about anything that is drawable. Each object in After Effects has a name. Lottie can find these objects and properties by their name using a KeyPath.
To understand how to change animation properties in Lottie, you should first understand how animation properties are stored in Lottie. Animation properties are stored in a data tree that mimics the information hierarchy of After Effects. In After Effects a Composition is a collection of Layers that each have their own timelines. Layer objects have string names, and their contents can be an image, shape layers, fills, strokes, or just about anything that is drawable. Each object in After Effects has a name. Lottie can find these objects and properties by their name using a KeyPath.
### Usage

View file

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

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] */
StrokeColor, /*!< Color property of Stroke object , value type is rlottie::Color */
StrokeOpacity, /*!< Opacity property of Stroke object , value type is float [ 0 .. 100] */
StrokeWidth, /*!< stroke with property of Stroke object , value type is float */
StrokeWidth, /*!< stroke width property of Stroke object , value type is float */
TrAnchor, /*!< Transform Anchor property of Layer and Group object , value type is rlottie::Point */
TrPosition, /*!< Transform Position property of Layer and Group object , value type is rlottie::Point */
TrScale, /*!< Transform Scale property of Layer and Group object , value type is rlottie::Size. range[0 ..100] */
TrRotation, /*!< Transform Scale property of Layer and Group object , value type is float. range[0 .. 360] in degrees*/
TrRotation, /*!< Transform Rotation property of Layer and Group object , value type is float. range[0 .. 360] in degrees*/
TrOpacity /*!< Transform Opacity property of Layer and Group object , value type is float [ 0 .. 100] */
};

View file

@ -25,7 +25,7 @@
#include <stddef.h>
#include <stdint.h>
#include <rlottiecommon.h>
#include "rlottiecommon.h"
#ifdef __cplusplus
extern "C" {
@ -46,6 +46,36 @@ typedef enum {
typedef struct Lottie_Animation_S Lottie_Animation;
/**
* @brief Runs lottie initialization code when rlottie library is loaded
* dynamically.
*
*
* This api should be called before any other api when rlottie library
* is loaded using dlopen() or equivalent.
*
* @see lottie_shutdown()
*
* @ingroup Lottie_Animation
* @internal
*/
RLOTTIE_API void lottie_init(void);
/**
* @brief Runs lottie teardown code when rlottie library is loaded
* dynamically.
*
* This api should be called before unloading the rlottie library for
* proper cleanup of the resource without doing so will result in undefined
* behaviour.
*
* @see lottie_init()
*
* @ingroup Lottie_Animation
* @internal
*/
RLOTTIE_API void lottie_shutdown(void);
/**
* @brief Constructs an animation object from file path.
*
@ -274,6 +304,23 @@ RLOTTIE_API void lottie_animation_property_override(Lottie_Animation *animation,
* */
RLOTTIE_API const LOTMarkerList* lottie_animation_get_markerlist(Lottie_Animation *animation);
/**
* @brief Configures rlottie model cache policy.
*
* Provides Library level control to configure model cache
* policy. Setting it to 0 will disable
* the cache as well as flush all the previously cached content.
*
* @param[in] cacheSize Maximum Model Cache size.
*
* @note to disable Caching configure with 0 size.
* @note to flush the current Cache content configure it with 0 and
* then reconfigure with the new size.
*
* @internal
*/
RLOTTIE_API void lottie_configure_model_cache_size(size_t cacheSize);
#ifdef __cplusplus
}
#endif

View file

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

View file

@ -26,6 +26,9 @@
using namespace rlottie;
extern void lottie_init_impl();
extern void lottie_shutdown_impl();
extern "C" {
#include <string.h>
#include <stdarg.h>
@ -38,6 +41,34 @@ struct Lottie_Animation_S
LOTMarkerList *mMarkerList;
};
static uint32_t _lottie_lib_ref_count = 0;
RLOTTIE_API void lottie_init(void)
{
if (_lottie_lib_ref_count > 0) {
_lottie_lib_ref_count++;
return;
}
lottie_init_impl();
_lottie_lib_ref_count = 1;
}
RLOTTIE_API void lottie_shutdown(void)
{
if (_lottie_lib_ref_count <= 0) {
// lottie_init() is not called before lottie_shutdown()
// or multiple shutdown is getting called.
return;
}
_lottie_lib_ref_count--;
if (_lottie_lib_ref_count == 0) {
lottie_shutdown_impl();
}
}
RLOTTIE_API Lottie_Animation_S *lottie_animation_from_file(const char *path)
{
if (auto animation = Animation::loadFromFile(path) ) {
@ -275,4 +306,10 @@ lottie_animation_get_markerlist(Lottie_Animation_S *animation)
return (const LOTMarkerList*)animation->mMarkerList;
}
RLOTTIE_API void
lottie_configure_model_cache_size(size_t cacheSize)
{
rlottie::configureModelCacheSize(cacheSize);
}
}

View file

@ -178,21 +178,30 @@ class RenderTaskScheduler {
for (unsigned n = 0; n != _count; ++n) {
_threads.emplace_back([&, n] { run(n); });
}
IsRunning = true;
}
public:
static bool IsRunning;
static RenderTaskScheduler &instance()
{
static RenderTaskScheduler singleton;
return singleton;
}
~RenderTaskScheduler()
{
for (auto &e : _q) e.done();
~RenderTaskScheduler() { stop(); }
void stop()
{
if (IsRunning) {
IsRunning = false;
for (auto &e : _q) e.done();
for (auto &e : _threads) e.join();
}
}
std::future<Surface> process(SharedRenderTask task)
{
@ -214,12 +223,16 @@ public:
#else
class RenderTaskScheduler {
public:
static bool IsRunning;
static RenderTaskScheduler &instance()
{
static RenderTaskScheduler singleton;
return singleton;
}
void stop() {}
std::future<Surface> process(SharedRenderTask task)
{
auto result = task->playerImpl->render(task->frameNo, task->surface,
@ -228,8 +241,11 @@ public:
return std::move(task->receiver);
}
};
#endif
bool RenderTaskScheduler::IsRunning{false};
std::future<Surface> AnimationImpl::renderAsync(size_t frameNo,
Surface &&surface,
bool keepAspectRatio)
@ -441,6 +457,29 @@ void Surface::setDrawRegion(size_t x, size_t y, size_t width, size_t height)
mDrawArea.h = height;
}
namespace {
void lottieShutdownRenderTaskScheduler()
{
if (RenderTaskScheduler::IsRunning) {
RenderTaskScheduler::instance().stop();
}
}
} // namespace
// private apis exposed to c interface
void lottie_init_impl()
{
// do nothing for now.
}
extern void lottieShutdownRasterTaskScheduler();
void lottie_shutdown_impl()
{
lottieShutdownRenderTaskScheduler();
lottieShutdownRasterTaskScheduler();
}
#ifdef LOTTIE_LOGGING_SUPPORT
void initLogging()
{

View file

@ -238,7 +238,7 @@ class FilterData {
public:
void addValue(LOTVariant& value)
{
uint index = static_cast<uint>(value.property());
uint32_t index = static_cast<uint32_t>(value.property());
if (mBitset.test(index)) {
std::replace_if(mFilters.begin(), mFilters.end(),
[&value](const LOTVariant& e) {
@ -253,7 +253,7 @@ public:
void removeValue(LOTVariant& value)
{
uint index = static_cast<uint>(value.property());
uint32_t index = static_cast<uint32_t>(value.property());
if (mBitset.test(index)) {
mBitset.reset(index);
mFilters.erase(std::remove_if(mFilters.begin(), mFilters.end(),
@ -266,7 +266,7 @@ public:
}
bool hasFilter(rlottie::Property prop) const
{
return mBitset.test(static_cast<uint>(prop));
return mBitset.test(static_cast<uint32_t>(prop));
}
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)
{
mSurface.reset(reinterpret_cast<uchar *>(surface.buffer()),
uint(surface.width()), uint(surface.height()),
uint(surface.bytesPerLine()),
mSurface.reset(reinterpret_cast<uint8_t *>(surface.buffer()),
uint32_t(surface.width()), uint32_t(surface.height()),
uint32_t(surface.bytesPerLine()),
VBitmap::Format::ARGB32_Premultiplied);
/* schedule all preprocess task for this frame at once.
@ -200,7 +200,7 @@ VRle renderer::Mask::rle()
{
if (!vCompare(mCombinedAlpha, 1.0f)) {
VRle obj = mRasterizer.rle();
obj *= uchar(mCombinedAlpha * 255);
obj *= uint8_t(mCombinedAlpha * 255);
return obj;
} else {
return mRasterizer.rle();
@ -343,7 +343,7 @@ renderer::Layer::Layer(model::Layer *layerData) : mLayerData(layerData)
mLayerMask = std::make_unique<renderer::LayerMask>(mLayerData);
}
bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value)
{
if (!keyPath.matches(name(), depth)) {
@ -359,12 +359,12 @@ bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return true;
}
bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value)
{
if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) {
if (keyPath.propagate(name(), depth)) {
uint newDepth = keyPath.nextDepth(name(), depth);
uint32_t newDepth = keyPath.nextDepth(name(), depth);
mRoot->resolveKeyPath(keyPath, newDepth, value);
}
return true;
@ -372,12 +372,12 @@ bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return false;
}
bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value)
{
if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) {
if (keyPath.propagate(name(), depth)) {
uint newDepth = keyPath.nextDepth(name(), depth);
uint32_t newDepth = keyPath.nextDepth(name(), depth);
for (const auto &layer : mLayers) {
layer->resolveKeyPath(keyPath, newDepth, value);
}
@ -507,7 +507,7 @@ void renderer::CompLayer::render(VPainter *painter, const VRle &inheritMask,
renderHelper(&srcPainter, inheritMask, matteRle, cache);
srcPainter.end();
painter->drawBitmap(VPoint(), srcBitmap,
uchar(combinedAlpha() * 255.0f));
uint8_t(combinedAlpha() * 255.0f));
cache.release_surface(srcBitmap);
} else {
renderHelper(painter, inheritMask, matteRle, cache);
@ -597,11 +597,21 @@ void renderer::CompLayer::renderMatteLayer(VPainter *painter, const VRle &mask,
srcBitmap.updateLuma();
}
auto clip = layerPainter.clipBoundingRect();
// if the layer has only one renderer then use it as the clip rect
// when blending 2 buffer and copy back to final buffer to avoid
// unnecessary pixel processing.
if (layer->renderList().size() == 1)
{
clip = layer->renderList()[0]->rle().boundingRect();
}
// 2.3 draw src buffer as mask
layerPainter.drawBitmap(VPoint(), srcBitmap);
layerPainter.drawBitmap(clip, srcBitmap, clip);
layerPainter.end();
// 3. draw the result buffer into painter
painter->drawBitmap(VPoint(), layerBitmap);
painter->drawBitmap(clip, layerBitmap, clip);
cache.release_surface(srcBitmap);
cache.release_surface(layerBitmap);
@ -853,7 +863,7 @@ renderer::DrawableList renderer::ShapeLayer::renderList()
return {mDrawableList.data(), mDrawableList.size()};
}
bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value)
{
if (!keyPath.skip(name())) {
@ -870,7 +880,7 @@ bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
}
if (keyPath.propagate(name(), depth)) {
uint newDepth = keyPath.nextDepth(name(), depth);
uint32_t newDepth = keyPath.nextDepth(name(), depth);
for (auto &child : mContents) {
child->resolveKeyPath(keyPath, newDepth, value);
}
@ -878,7 +888,7 @@ bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return true;
}
bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value)
{
if (!keyPath.matches(mModel.name(), depth)) {
@ -893,7 +903,7 @@ bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
return false;
}
bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth,
LOTVariant &value)
{
if (!keyPath.matches(mModel.name(), depth)) {
@ -1097,7 +1107,7 @@ void renderer::Rect::updatePath(VPath &path, int frameNo)
{
VPointF pos = mData->mPos.value(frameNo);
VPointF size = mData->mSize.value(frameNo);
float roundness = mData->mRound.value(frameNo);
float roundness = mData->roundness(frameNo);
VRectF r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
size.y());

View file

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

View file

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

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

View file

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

View file

@ -205,7 +205,7 @@ void model::Dash::getDashInfo(int frameNo, std::vector<float> &result) const
{
result.clear();
if (mData.empty()) return;
if (mData.size() <= 1) return;
if (result.capacity() < mData.size()) result.reserve(mData.size() + 1);
@ -256,52 +256,12 @@ void model::Gradient::populate(VGradientStops &stops, int frameNo)
auto opacityArraySize = size - colorPoints * 4;
float *opacityPtr = ptr + (colorPoints * 4);
stops.clear();
size_t j = 0;
for (int i = 0; i < colorPoints; i++) {
float colorStop = ptr[0];
model::Color color = model::Color(ptr[1], ptr[2], ptr[3]);
if (opacityArraySize) {
if (j == opacityArraySize) {
// already reached the end
float stop1 = opacityPtr[j - 4];
float op1 = opacityPtr[j - 3];
float stop2 = opacityPtr[j - 2];
float op2 = opacityPtr[j - 1];
if (colorStop > stop2) {
stops.push_back(
std::make_pair(colorStop, color.toColor(op2)));
} else {
float progress = (colorStop - stop1) / (stop2 - stop1);
float opacity = op1 + progress * (op2 - op1);
stops.push_back(
std::make_pair(colorStop, color.toColor(opacity)));
}
continue;
}
for (; j < opacityArraySize; j += 2) {
float opacityStop = opacityPtr[j];
if (opacityStop < colorStop) {
// add a color using opacity stop
stops.push_back(std::make_pair(
opacityStop, color.toColor(opacityPtr[j + 1])));
continue;
}
// add a color using color stop
if (j == 0) {
stops.push_back(std::make_pair(
colorStop, color.toColor(opacityPtr[j + 1])));
} else {
float progress = (colorStop - opacityPtr[j - 2]) /
(opacityPtr[j] - opacityPtr[j - 2]);
float opacity =
opacityPtr[j - 1] +
progress * (opacityPtr[j + 1] - opacityPtr[j - 1]);
stops.push_back(
std::make_pair(colorStop, color.toColor(opacity)));
}
j += 2;
break;
}
float opacity = getOpacityAtPosition(opacityPtr, opacityArraySize, colorStop);
stops.push_back(std::make_pair(colorStop, color.toColor(opacity)));
} else {
stops.push_back(std::make_pair(colorStop, color.toColor()));
}
@ -309,6 +269,21 @@ void model::Gradient::populate(VGradientStops &stops, int frameNo)
}
}
float model::Gradient::getOpacityAtPosition(float *opacities, size_t opacityArraySize, float position)
{
for (size_t i = 2; i < opacityArraySize; i += 2)
{
float lastPosition = opacities[i - 2];
float thisPosition = opacities[i];
if (opacities[i] >= position) {
float progress = (position - lastPosition) / (thisPosition - lastPosition);
progress = progress < 0.0f ? 0.0f : 1.0f < progress ? 1.0f : progress; //clamp(progress, 0, 1)
return opacities[i - 1] + progress * (opacities[i + 1] - opacities[i - 1]);
}
}
return 0.0f;
}
void model::Gradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
{
bool init = false;

View file

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

View file

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

View file

@ -2,9 +2,11 @@ if(LOTTIE_MODULE)
add_library(rlottie-image-loader SHARED
stb_image.cpp
)
if(NOT MSVC)
target_compile_options(rlottie-image-loader PRIVATE
-fvisibility=hidden
)
endif()
get_filename_component(LOTTIE_MODULE_FILENAME ${LOTTIE_MODULE_PATH} NAME)
get_filename_component(LOTTIE_MODULE_DIR ${LOTTIE_MODULE_PATH} DIRECTORY)

View file

@ -3,7 +3,7 @@ source_file = ['stb_image.cpp']
if get_option('module') == true
rlottie_image_loader_dir = get_option('moduledir') != '' ? get_option('moduledir') : get_option('libdir')
rlottie_image_loader_lib = shared_library('rlottie-image-loader',
rlottie_image_loader_lib = shared_module('rlottie-image-loader',
source_file,
include_directories : [include_directories('.'), config_dir],
install : true,

View file

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

View file

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

View file

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

View file

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

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)
{

View file

@ -75,7 +75,7 @@ public:
VBrush():mType(Type::NoBrush),mColor(){};
explicit VBrush(const VColor &color);
explicit VBrush(const VGradient *gradient);
explicit VBrush(uchar r, uchar g, uchar b, uchar a);
explicit VBrush(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
explicit VBrush(const VTexture *texture);
inline VBrush::Type type() const { return mType; }
public:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -66,7 +66,7 @@ static void fillRect(const VRect &r, VSpanData *data)
int i = 0;
while (i < n) {
spans[i].x = short(x1);
spans[i].len = ushort(x2 - x1);
spans[i].len = uint16_t(x2 - x1);
spans[i].y = short(y + i);
spans[i].coverage = 255;
++i;
@ -84,12 +84,12 @@ void VPainter::drawBitmapUntransform(const VRect & target,
{
mSpanData.initTexture(&bitmap, const_alpha, source);
if (!mSpanData.mUnclippedBlendFunc) return;
mSpanData.dx = float(-target.x());
mSpanData.dy = float(-target.y());
VRect rr = source.translated(target.x(), target.y());
// update translation matrix for source texture.
mSpanData.dx = float(target.x() - source.x());
mSpanData.dy = float(target.y() - source.y());
fillRect(rr, &mSpanData);
fillRect(target, &mSpanData);
}
VPainter::VPainter(VBitmap *buffer)

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

View file

@ -35,7 +35,7 @@ class VPath {
public:
enum class Direction { CCW, CW };
enum class Element : uchar { MoveTo, LineTo, CubicTo, Close };
enum class Element : uint8_t { MoveTo, LineTo, CubicTo, Close };
bool empty() const;
bool null() const;
void moveTo(const VPointF &p);

View file

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

View file

@ -27,6 +27,7 @@
#include <array>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <vector>
#include "vdebug.h"
#include "vglobal.h"
@ -40,7 +41,7 @@ static size_t _opGeneric(rle_view &a, rle_view &b, Result &result,
static size_t _opIntersect(const VRect &, rle_view &, Result &);
static size_t _opIntersect(rle_view &, rle_view &, Result &);
static inline uchar divBy255(int x)
static inline uint8_t divBy255(int x)
{
return (x + (x >> 8) + 0x80) >> 8;
}
@ -141,7 +142,7 @@ void VRle::Data::updateBbox() const
}
}
void VRle::Data::operator*=(uchar alpha)
void VRle::Data::operator*=(uint8_t alpha)
{
for (auto &i : mSpans) {
i.coverage = divBy255(i.coverage * alpha);
@ -223,7 +224,7 @@ void VRle::Data::opGeneric(const VRle::Data &aObj, const VRle::Data &bObj,
mSpans.reserve(a.size() + b.size());
// if two rle are disjoint
if (!aObj.bbox().intersects(aObj.bbox())) {
if (!aObj.bbox().intersects(bObj.bbox())) {
if (a.data()[0].y < b.data()[0].y) {
copy(a.data(), a.size(), mSpans);
copy(b.data(), b.size(), mSpans);
@ -424,7 +425,7 @@ static size_t _opIntersect(const VRect &clip, rle_view &obj, Result &result)
out->x = minx;
} else {
out->x = span.x;
out->len = std::min(span.len, ushort(maxx - span.x + 1));
out->len = std::min(span.len, uint16_t(maxx - span.x + 1));
}
if (out->len != 0) {
out->y = span.y;
@ -441,12 +442,12 @@ static size_t _opIntersect(const VRect &clip, rle_view &obj, Result &result)
return result.max_size() - available;
}
static void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX)
static void blitXor(VRle::Span *spans, int count, uint8_t *buffer, int offsetX)
{
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
uchar *ptr = buffer + x;
uint8_t *ptr = buffer + x;
while (l--) {
int da = *ptr;
*ptr = divBy255((255 - spans->coverage) * (da) +
@ -457,13 +458,13 @@ static void blitXor(VRle::Span *spans, int count, uchar *buffer, int offsetX)
}
}
static void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer,
static void blitDestinationOut(VRle::Span *spans, int count, uint8_t *buffer,
int offsetX)
{
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
uchar *ptr = buffer + x;
uint8_t *ptr = buffer + x;
while (l--) {
*ptr = divBy255((255 - spans->coverage) * (*ptr));
ptr++;
@ -472,13 +473,13 @@ static void blitDestinationOut(VRle::Span *spans, int count, uchar *buffer,
}
}
static void blitSrcOver(VRle::Span *spans, int count, uchar *buffer,
static void blitSrcOver(VRle::Span *spans, int count, uint8_t *buffer,
int offsetX)
{
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
uchar *ptr = buffer + x;
uint8_t *ptr = buffer + x;
while (l--) {
*ptr = spans->coverage + divBy255((255 - spans->coverage) * (*ptr));
ptr++;
@ -487,12 +488,12 @@ static void blitSrcOver(VRle::Span *spans, int count, uchar *buffer,
}
}
void blitSrc(VRle::Span *spans, int count, uchar *buffer, int offsetX)
void blitSrc(VRle::Span *spans, int count, uint8_t *buffer, int offsetX)
{
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
uchar *ptr = buffer + x;
uint8_t *ptr = buffer + x;
while (l--) {
*ptr = std::max(spans->coverage, *ptr);
ptr++;
@ -501,15 +502,16 @@ void blitSrc(VRle::Span *spans, int count, uchar *buffer, int offsetX)
}
}
size_t bufferToRle(uchar *buffer, int size, int offsetX, int y, VRle::Span *out)
size_t bufferToRle(uint8_t *buffer, int size, int offsetX, int y,
VRle::Span *out)
{
size_t count = 0;
uchar value = buffer[0];
uint8_t value = buffer[0];
int curIndex = 0;
// size = offsetX < 0 ? size + offsetX : size;
for (int i = 0; i < size; i++) {
uchar curValue = buffer[0];
uint8_t curValue = buffer[0];
if (value != curValue) {
if (value) {
out->y = y;
@ -549,10 +551,10 @@ struct SpanMerger {
break;
}
}
using blitter = void (*)(VRle::Span *, int, uchar *, int);
using blitter = void (*)(VRle::Span *, int, uint8_t *, int);
blitter _blitter;
std::array<VRle::Span, 256> _result;
std::array<uchar, 1024> _buffer;
std::array<uint8_t, 1024> _buffer;
VRle::Span * _aStart{nullptr};
VRle::Span * _bStart{nullptr};

View file

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

View file

@ -27,6 +27,81 @@ public:
return mPlayer ? true : false;
}
void setFillColor(std::string keypath, float r, float g, float b)
{
if (!mPlayer) return;
mPlayer->setValue<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}
// lottie pixel pix[0] pix[1] pix[2] pix[3] {R G B A}
val render(int frame, int width, int height)
@ -105,5 +180,15 @@ EMSCRIPTEN_BINDINGS(rlottie_bindings)
.constructor(&RlottieWasm::create)
.function("load", &RlottieWasm::load, allow_raw_pointers())
.function("frames", &RlottieWasm::frames)
.function("render", &RlottieWasm::render);
.function("render", &RlottieWasm::render)
.function("setFillColor", &RlottieWasm::setFillColor)
.function("setStrokeColor", &RlottieWasm::setStrokeColor)
.function("setFillOpacity", &RlottieWasm::setFillOpacity)
.function("setStrokeOpacity", &RlottieWasm::setStrokeOpacity)
.function("setStrokeWidth", &RlottieWasm::setStrokeWidth)
.function("setAnchor", &RlottieWasm::setAnchor)
.function("setPosition", &RlottieWasm::setPosition)
.function("setScale", &RlottieWasm::setScale)
.function("setRotation", &RlottieWasm::setRotation)
.function("setOpacity", &RlottieWasm::setOpacity);
}

View file

@ -45,7 +45,7 @@
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
@ -118,6 +118,9 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<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>
<Link>
<SubSystem>Console</SubSystem>