ES-DE/external/rlottie/example/win32Player/rlottiePlayer.cpp

521 lines
16 KiB
C++
Raw Normal View History

Squashed 'external/rlottie/' changes from bf3d272df..875626965 875626965 Check border of color table while generating gradient d1636c7b4 Ignore animations with objects of unspecified type f3eed9a33 add formal parameter void to lottie_init() and lottie_shutdown() 519bd08b0 Add lottie_init() and lottie_shutdown() c api. 447cb7dd1 replace unsigned int with uint32_t in Int() d7cf0a5ab replace uint ushort and uchar typedefs with uint32 uint16 and uint8 -- added format script to run clang format on latest commit. 16beec845 lottiemodel: Improve opacity calculation for gradient stop 327fb7dba Reject reversed frames 625bc4c48 Finite loop in VBezier::tAtLength() ddbc9dce7 rlottie/capi: Add missing multiply for Path c0e16e571 Check Layer.mExtra and Transform.mExtra before dereferencing 9f78532c8 Avoid access to last element of empty mChildren vector 4e08acd8c remove RAPIDJSON_ASSERT() by placing the parser in error state 7affa78fe Fix dereferencing of null pointer in model::Layer::solidColor() getter 9d17ff8e6 Skip dash array of size one or zero f379dfe15 add the /inc folder as an include path at build time 194b31736 Improve matte rendering performance for simple layer 7c5b40cbb CMake: fix MSVC warnings fa44b753e fix compileing error 41f906480 On windows, stbi__fopen opens a path containing non-English characters or symbols will fail due to encoding problems, so the filename parameter is assumed to be utf-8 and converted to utf-16, and then call _wfopen_s or _wfopen 29b391b95 add lottie_configure_model_cache_size() c api 3cd00151c Fix comparison bd4c4e1f8 MT and MTd c593b57eb Remove link warnings on MSVC 132ca0a00 #include "fix" for rust-bindgen c9780b46d Fix CMake build in subdirectory 1cb2021d6 Fix crash when path animation data is empty ad9beaec6 Added support for RoundedCornor objects. a41bd7383 rlottie: draw line if width or height is 0 ed94baf14 resource: polish player png image. 9b4c4d2af resource: add rlottie_player image. 2d7b1fa2b Fixed build with GCC 11. 9cd9a6e95 vs2019: Add release build option d733e953b updated AUTHORS fc760ef92 updated AUTHORS d92d4aaf6 wasm: Add extra functions for lottie animation to use them in rlottie viewer 09662b89d updated AUTHORS 71fbff5d5 example/win32Player: removed unnecessary files. d25911e6c add app executables 538dfd9aa rename directory rlottiePlayer to win32Player 6fbb67d49 fix openJSONFileDialog 85856dff3 fix animation replace error f86e97d65 fix animation bitmap size to be set dynamically 55815b2cb move function prototype to animation.h To prevent function prototype duplicate code in source.cpp. 9a71eeca9 add an example of win32 app that play rlottie animation by vs2019 619320cbb add rlottiePlayer Project 4884e0e9b Modify: Update .gitignore 3ebf0c7e3 Docs: Fix README typo 502cc3638 updated license in spec file. e812ba9e9 Docs: Fix README typo c98ec565d Update README.md 33295c22f Fixed README typo 073224e69 fixed readme typo ff8ddfc49 ci: upgrade windows build chain to VS2017 10c1055a3 fixed sign extension warning 98ed2620a build: use shared_module instead of shared_library git-subtree-dir: external/rlottie git-subtree-split: 875626965959d8e269ca22175c8e1ad190696c43
2022-01-07 17:43:06 +00:00
// 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);
}