1 // rlottiePlayer.cpp : Defines the entry point for the application.
6 #include "rlottiePlayer.h"
7 using namespace Gdiplus;
9 #define MAX_LOADSTRING 100
12 HINSTANCE hInst; // current instance
13 WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
14 WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
15 HWND mainWindow; // Main Window Instance
16 HWND hTextFileToBeOpened; // openDialog file path
18 HWND hSliderPlay, hSliderCanvasResize;
20 RlottieBitmap anim; // rendered Animation Bitmap
21 RECT animRect, backRect;
22 Gdiplus::Color backColor(255, 255, 255, 255);
23 Gdiplus::Color borderColor(255, 0, 0, 0);
24 bool isBackgroundChanged = false;
26 // Forward declarations of functions included in this code module:
27 ATOM MyRegisterClass(HINSTANCE hInstance);
28 BOOL InitInstance(HINSTANCE, int);
29 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
30 INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
31 void openJSONFileDialog(HWND);
32 void initUIControl(HWND);
33 void dlgUICommand(HWND, WPARAM);
34 void resizeCanvas(HWND, int);
35 void changeBackgroundColor(int r, int g, int b);
37 // Animation Rendering Functions
39 Bitmap* CreateBitmap(void* data, unsigned int width, unsigned int height);
40 void renderAnimation(UINT);
42 int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
43 _In_opt_ HINSTANCE hPrevInstance,
44 _In_ LPWSTR lpCmdLine,
47 UNREFERENCED_PARAMETER(hPrevInstance);
48 UNREFERENCED_PARAMETER(lpCmdLine);
51 Gdiplus::GdiplusStartupInput gdiplusStartUpInput;
52 ULONG_PTR gdiplustoken;
53 Gdiplus::GdiplusStartup(&gdiplustoken, &gdiplusStartUpInput, nullptr);
55 // Initialize global strings
56 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
57 LoadStringW(hInstance, IDC_RLOTTIEPLAYER, szWindowClass, MAX_LOADSTRING);
58 MyRegisterClass(hInstance);
60 // Perform application initialization:
61 if (!InitInstance(hInstance, nCmdShow))
66 HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_RLOTTIEPLAYER));
71 while (GetMessage(&msg, nullptr, 0, 0))
73 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
75 TranslateMessage(&msg);
76 DispatchMessage(&msg);
80 Gdiplus::GdiplusShutdown(gdiplustoken);
81 return (int)msg.wParam;
87 // FUNCTION: MyRegisterClass()
89 // PURPOSE: Registers the window class.
91 ATOM MyRegisterClass(HINSTANCE hInstance)
95 wcex.cbSize = sizeof(WNDCLASSEX);
97 wcex.style = CS_HREDRAW | CS_VREDRAW;
98 wcex.lpfnWndProc = WndProc;
101 wcex.hInstance = hInstance;
102 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_RLOTTIEPLAYER));
103 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
104 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
105 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_RLOTTIEPLAYER);
106 wcex.lpszClassName = szWindowClass;
107 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
109 return RegisterClassExW(&wcex);
113 // FUNCTION: InitInstance(HINSTANCE, int)
115 // PURPOSE: Saves instance handle and creates main window
119 // In this function, we save the instance handle in a global variable and
120 // create and display the main program window.
122 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
124 hInst = hInstance; // Store instance handle in our global variable
126 DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
127 mainWindow = CreateWindowEx(0, szWindowClass, szTitle, dwStyle,
128 CW_USEDEFAULT, CW_USEDEFAULT, WND_WIDTH, WND_HEIGHT, nullptr, nullptr, hInstance, nullptr);
135 ShowWindow(mainWindow, nCmdShow);
136 UpdateWindow(mainWindow);
137 SetMenu(mainWindow, NULL);
143 // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
145 // PURPOSE: Processes messages for the main window.
147 // WM_COMMAND - process the application menu
148 // WM_PAINT - Paint the main window
149 // WM_DESTROY - post a quit message and return
152 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
154 static bool isplay = false;
155 int wmId = LOWORD(wParam);
162 initAnimation(BMP_MAX_LEN, BMP_MAX_LEN);
169 case TIMER_PLAY_ANIM:
171 renderAnimation(curFrame + 1);
172 SendMessage(hSliderPlay, TBM_SETPOS, TRUE, curFrame);
182 // Parse the menu selections:
186 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
194 openJSONFileDialog(hWnd);
204 textBtnPlay = A2W("Play");
205 KillTimer(hWnd, TIMER_PLAY_ANIM);
210 textBtnPlay = A2W("Pause");
211 SetTimer(hWnd, TIMER_PLAY_ANIM, 10, NULL);
213 SetWindowText(hBtnPlay, textBtnPlay);
220 changeBackgroundColor(1, 1, 1);
223 changeBackgroundColor(0, 0, 0);
226 changeBackgroundColor(1, 0, 0);
229 changeBackgroundColor(0, 1, 0);
232 changeBackgroundColor(0, 0, 1);
236 return DefWindowProc(hWnd, message, wParam, lParam);
243 if ((lParam != 0) && (reinterpret_cast<HWND>(lParam) == hSliderPlay))
245 UINT frameNum = SendDlgItemMessage(hWnd, SLIDER_PLAY, TBM_GETPOS, 0, 0);
246 renderAnimation(frameNum);
248 else if ((lParam != 0) && (reinterpret_cast<HWND>(lParam) == hSliderCanvasResize))
250 static int curSize = anim.width / RESIZE_LENGTH;
251 int newSize = SendDlgItemMessage(hWnd, SLIDER_CANVAS_RESIZE, TBM_GETPOS, 0, 0);
252 resizeCanvas(hWnd, (curSize - newSize) * RESIZE_LENGTH);
261 HDC hdc = BeginPaint(hWnd, &ps);
266 case WM_CTLCOLORSTATIC:
268 static HBRUSH hBrushEdit = CreateSolidBrush(RGB(255, 255, 255));
269 return (LRESULT)hBrushEdit;
277 return DefWindowProc(hWnd, message, wParam, lParam);
282 // Message handler for about box.
283 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
285 UNREFERENCED_PARAMETER(lParam);
289 return (INT_PTR)TRUE;
292 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
294 EndDialog(hDlg, LOWORD(wParam));
295 return (INT_PTR)TRUE;
299 return (INT_PTR)FALSE;
302 void openJSONFileDialog(HWND hDlg)
304 OPENFILENAME ofn; // common dialog box structure
305 TCHAR szFile[260] = { 0 }; // if using TCHAR macros
307 // Initialize OPENFILENAME
308 ZeroMemory(&ofn, sizeof(ofn));
309 ofn.lStructSize = sizeof(ofn);
310 ofn.hwndOwner = hDlg;
311 ofn.lpstrFile = szFile;
312 ofn.nMaxFile = sizeof(szFile);
313 ofn.lpstrFilter = _T("JSON\0*.json\0");
314 ofn.nFilterIndex = 1;
315 ofn.lpstrFileTitle = NULL;
316 ofn.nMaxFileTitle = 0;
317 ofn.lpstrInitialDir = NULL;
318 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
320 if (GetOpenFileName(&ofn))
322 SetWindowText(hTextFileToBeOpened, ofn.lpstrFile);
323 // LPWSTR(w_char*) -> LPSTR(char*)
325 LPSTR path = W2A(ofn.lpstrFile);
327 setAnimation(path, BMP_MAX_LEN, BMP_MAX_LEN);
329 SendMessage(hSliderPlay, TBM_SETRANGE, FALSE, MAKELPARAM(0, getTotalFrame()));
330 SendMessage(hSliderPlay, TBM_SETPOS, TRUE, 0);
338 int half_interval = UI_INTERVAL / 2;
341 SolidBrush brush(backColor);
342 int back_y = half_interval + BTN_HEIGHT;
343 int back_height = back_y + BMP_MAX_LEN + UI_INTERVAL;
344 if (isBackgroundChanged)
346 isBackgroundChanged = false;
347 gf.FillRectangle(&brush, 0, back_y, WND_WIDTH, back_height);
351 Pen pen(borderColor);
352 gf.DrawRectangle(&pen, anim.x - half_interval, anim.y - half_interval, anim.width + half_interval * 2, anim.height + half_interval * 2);
355 if (anim.image != NULL)
357 gf.DrawImage(anim.image, anim.x, anim.y, anim.width, anim.height);
361 Bitmap* CreateBitmap(void* data, unsigned int width, unsigned int height)
364 memset(&Info, 0, sizeof(Info));
366 Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
367 Info.bmiHeader.biWidth = 500;
368 Info.bmiHeader.biHeight = 500;
369 Info.bmiHeader.biPlanes = 1;
370 Info.bmiHeader.biBitCount = 32;
371 Info.bmiHeader.biCompression = BI_RGB;
372 Info.bmiHeader.biSizeImage = 0; //(((32 * width + 31) & ~31) / 8) * height;
374 return new Gdiplus::Bitmap(&Info, data);
377 void renderAnimation(UINT frameNum)
379 if (isAnimNULL()) return;
380 if (anim.image != NULL) delete anim.image;
382 curFrame = frameNum % getTotalFrame();
385 UINT* resRender = renderRLottieAnimation(curFrame);
386 anim.image = CreateBitmap(resRender, BMP_MAX_LEN, BMP_MAX_LEN);
387 anim.image->RotateFlip(RotateNoneFlipY);
388 // call WM_PAINT message
389 InvalidateRect(mainWindow, &animRect, FALSE);
392 void initUIControl(HWND hWnd)
394 int half_ui_interval = UI_INTERVAL / 2;
397 int browse_x = UI_INTERVAL;
398 int browse_y = half_ui_interval;
399 CreateWindow(L"button", L"Browse", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
400 browse_x, browse_y, BTN_WIDTH, BTN_HEIGHT, hWnd, (HMENU)BTN_BROWSE, hInst, NULL);
403 int textFile_x = browse_x + BTN_WIDTH + UI_INTERVAL;
404 int textFile_y = browse_y;
405 hTextFileToBeOpened = CreateWindowEx(0, L"static", L"No file selected.", WS_CHILD | WS_VISIBLE | ES_LEFT,
406 textFile_x, textFile_y, WND_WIDTH * 0.6, TEXT_HEIGHT, hWnd, (HMENU)TEXT_FILENAME, hInst, 0);
409 anim.x = WND_WIDTH / 4;
410 anim.y = browse_y + BTN_HEIGHT + UI_INTERVAL * 2;
411 anim.width = BMP_MAX_LEN;
412 anim.height = anim.width;
416 anim.x - UI_INTERVAL,
417 anim.y - UI_INTERVAL,
418 anim.x + anim.width + UI_INTERVAL * 2,
419 anim.y + anim.height + UI_INTERVAL * 2
425 anim.y - UI_INTERVAL,
427 anim.y + anim.height + UI_INTERVAL * 2);
429 // text Background Color
430 int textBC_x = WND_WIDTH / 20;
431 int textBC_y = anim.y + anim.height + UI_INTERVAL * 2;
432 CreateWindowEx(0, L"static", L"Background Color", WS_CHILD | WS_VISIBLE | ES_LEFT,
433 textBC_x, textBC_y, 120, TEXT_HEIGHT, hWnd, (HMENU)TEXT_FILENAME, hInst, 0);
437 int white_x = WND_WIDTH / 20;
438 int white_y = textBC_y + TEXT_HEIGHT + half_ui_interval;
439 CreateWindowEx(0, L"button", L"White", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
440 white_x, white_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_WHITE, hInst, NULL);
443 int black_x = white_x + RDOBTN_WIDTH + half_ui_interval;
444 int black_y = white_y;
445 CreateWindowEx(0, L"button", L"Black", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
446 black_x, black_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_BLACK, hInst, NULL);
449 int red_x = black_x + RDOBTN_WIDTH + half_ui_interval;
451 CreateWindowEx(0, L"button", L"Red", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
452 red_x, red_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_RED, hInst, NULL);
455 int green_x = red_x + RDOBTN_WIDTH + half_ui_interval;
456 int green_y = white_y;
457 CreateWindowEx(0, L"button", L"Green", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
458 green_x, green_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_GREEN, hInst, NULL);
461 int blue_x = green_x + RDOBTN_WIDTH + half_ui_interval;
462 int blue_y = white_y;
463 CreateWindowEx(0, L"button", L"Blue", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
464 blue_x, blue_y, RDOBTN_WIDTH, RDOBTN_HEIGHT, hWnd, (HMENU)BTN_BLUE, hInst, NULL);
466 CheckRadioButton(hWnd, BTN_WHITE, BTN_BLUE, BTN_WHITE);
468 // text Canvas Resize
469 int textCR_x = WND_WIDTH / 2;
470 int textCR_y = textBC_y;
471 CreateWindowEx(0, L"static", L"Canvas Resize", WS_CHILD | WS_VISIBLE | ES_LEFT,
472 textCR_x, textCR_y, 120, TEXT_HEIGHT, hWnd, (HMENU)TEXT_FILENAME, hInst, 0);
474 // slider Canvas Resize
475 int sliderCR_x = textCR_x;
476 int sliderCR_y = textCR_y + TEXT_HEIGHT + half_ui_interval;
477 hSliderCanvasResize = CreateWindowExW(0, TRACKBAR_CLASSW, NULL, WS_CHILD | WS_VISIBLE | TBS_FIXEDLENGTH | TBS_NOTICKS,
478 sliderCR_x, sliderCR_y, WND_WIDTH * 0.2, SLIDER_HEIGHT, hWnd, (HMENU)SLIDER_CANVAS_RESIZE, hInst, NULL);
480 // init resize slider
481 UINT sizeSlider = anim.width / RESIZE_LENGTH;
482 SendMessage(hSliderCanvasResize, TBM_SETRANGE, FALSE, MAKELPARAM(0, sizeSlider));
483 SendMessage(hSliderCanvasResize, TBM_SETPOS, TRUE, sizeSlider);
486 int btnPlay_x = WND_WIDTH / 10;
487 int btnPlay_y = red_y + RDOBTN_HEIGHT + UI_INTERVAL * 2;
488 hBtnPlay = CreateWindow(L"button", L"Play", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
489 btnPlay_x, btnPlay_y, BTN_WIDTH, BTN_HEIGHT, hWnd, (HMENU)BTN_PLAY, hInst, NULL);
492 int sliderPlay_x = btnPlay_x + BTN_WIDTH + UI_INTERVAL;
493 int sliderPlay_y = btnPlay_y;
494 hSliderPlay = CreateWindowExW(0, TRACKBAR_CLASSW, NULL, WS_CHILD | WS_VISIBLE | TBS_FIXEDLENGTH | TBS_NOTICKS,
495 sliderPlay_x, sliderPlay_y, WND_WIDTH * 0.6, SLIDER_HEIGHT, hWnd, (HMENU)SLIDER_PLAY, hInst, NULL);
498 void resizeCanvas(HWND hWnd, int resizeValue)
500 isBackgroundChanged = true;
501 anim.x += resizeValue / 2;
502 anim.y += resizeValue / 2;
503 anim.width -= resizeValue;
504 anim.height -= resizeValue;
505 InvalidateRect(hWnd, &animRect, TRUE);
508 void changeBackgroundColor(int r, int g, int b)
510 isBackgroundChanged = true;
511 backColor = Gdiplus::Color(r * 255, g * 255, b * 255);
512 if (r + g + b == 0) borderColor = Gdiplus::Color(255, 255, 255);
513 else borderColor = Gdiplus::Color(0, 0, 0);
514 setAnimationColor(r, g, b);
515 renderAnimation(curFrame);
516 InvalidateRect(mainWindow, &backRect, FALSE);