[SHIMGVW] Split animation code to anime.c (#6144)

Improve code flexibility.
Add anime.c and move animation code to anime.c.
CORE-19358
This commit is contained in:
Katayama Hirofumi MZ 2023-12-10 14:07:46 +09:00 committed by GitHub
parent 5a2c451696
commit 0fe0b40ee1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 231 additions and 195 deletions

View file

@ -2,6 +2,7 @@
spec2def(shimgvw.dll shimgvw.spec)
list(APPEND SOURCE
anime.c
shimgvw.c
comsup.c
shimgvw.rc

156
dll/win32/shimgvw/anime.c Normal file
View file

@ -0,0 +1,156 @@
/*
* PROJECT: ReactOS Picture and Fax Viewer
* LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
* PURPOSE: Animation of shimgvw.dll
* COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
#include "shimgvw.h"
#define ANIME_TIMER_ID 9999
void Anime_FreeInfo(PANIME pAnime)
{
if (pAnime->m_pDelayItem)
{
free(pAnime->m_pDelayItem);
pAnime->m_pDelayItem = NULL;
}
pAnime->m_nFrameIndex = 0;
pAnime->m_nFrameCount = 0;
pAnime->m_nLoopIndex = 0;
pAnime->m_nLoopCount = (UINT)-1;
}
void Anime_SetTimerWnd(PANIME pAnime, HWND hwndTimer)
{
pAnime->m_hwndTimer = hwndTimer;
}
void Anime_Pause(PANIME pAnime)
{
KillTimer(pAnime->m_hwndTimer, ANIME_TIMER_ID);
}
void Anime_Start(PANIME pAnime, DWORD dwDelay)
{
if (pAnime->m_pDelayItem)
SetTimer(pAnime->m_hwndTimer, ANIME_TIMER_ID, dwDelay, NULL);
}
BOOL Anime_OnTimer(PANIME pAnime, WPARAM wParam)
{
DWORD dwDelay;
if (wParam != ANIME_TIMER_ID)
return FALSE;
Anime_Pause(pAnime);
if (Anime_Step(pAnime, &dwDelay))
Anime_Start(pAnime, dwDelay);
return TRUE;
}
BOOL Anime_LoadInfo(PANIME pAnime)
{
UINT nDimCount = 0;
UINT cbItem;
UINT result;
Anime_Pause(pAnime);
Anime_FreeInfo(pAnime);
if (!g_pImage)
return FALSE;
GdipImageGetFrameDimensionsCount(g_pImage, &nDimCount);
if (nDimCount)
{
GUID *dims = (GUID *)calloc(nDimCount, sizeof(GUID));
if (dims)
{
GdipImageGetFrameDimensionsList(g_pImage, dims, nDimCount);
GdipImageGetFrameCount(g_pImage, dims, &result);
pAnime->m_nFrameCount = result;
free(dims);
}
}
result = 0;
GdipGetPropertyItemSize(g_pImage, PropertyTagFrameDelay, &result);
cbItem = result;
if (cbItem)
{
pAnime->m_pDelayItem = (PropertyItem *)malloc(cbItem);
GdipGetPropertyItem(g_pImage, PropertyTagFrameDelay, cbItem, pAnime->m_pDelayItem);
}
result = 0;
GdipGetPropertyItemSize(g_pImage, PropertyTagLoopCount, &result);
cbItem = result;
if (cbItem)
{
PropertyItem *pItem = (PropertyItem *)malloc(cbItem);
if (pItem)
{
if (GdipGetPropertyItem(g_pImage, PropertyTagLoopCount, cbItem, pItem) == Ok)
{
pAnime->m_nLoopCount = *(WORD *)pItem->value;
}
free(pItem);
}
}
Anime_Start(pAnime, 0);
return pAnime->m_pDelayItem != NULL;
}
void Anime_SetFrameIndex(PANIME pAnime, UINT nFrameIndex)
{
if (nFrameIndex < pAnime->m_nFrameCount)
{
GUID guid = FrameDimensionTime;
if (Ok != GdipImageSelectActiveFrame(g_pImage, &guid, nFrameIndex))
{
guid = FrameDimensionPage;
GdipImageSelectActiveFrame(g_pImage, &guid, nFrameIndex);
}
}
pAnime->m_nFrameIndex = nFrameIndex;
}
DWORD Anime_GetFrameDelay(PANIME pAnime, UINT nFrameIndex)
{
if (nFrameIndex < pAnime->m_nFrameCount && pAnime->m_pDelayItem)
{
return ((DWORD *)pAnime->m_pDelayItem->value)[pAnime->m_nFrameIndex] * 10;
}
return 0;
}
BOOL Anime_Step(PANIME pAnime, DWORD *pdwDelay)
{
*pdwDelay = INFINITE;
if (pAnime->m_nLoopCount == (UINT)-1)
return FALSE;
if (pAnime->m_nFrameIndex + 1 < pAnime->m_nFrameCount)
{
*pdwDelay = Anime_GetFrameDelay(pAnime, pAnime->m_nFrameIndex);
Anime_SetFrameIndex(pAnime, pAnime->m_nFrameIndex);
++pAnime->m_nFrameIndex;
return TRUE;
}
if (pAnime->m_nLoopCount == 0 || pAnime->m_nLoopIndex < pAnime->m_nLoopCount)
{
*pdwDelay = Anime_GetFrameDelay(pAnime, pAnime->m_nFrameIndex);
Anime_SetFrameIndex(pAnime, pAnime->m_nFrameIndex);
pAnime->m_nFrameIndex = 0;
++pAnime->m_nLoopIndex;
return TRUE;
}
return FALSE;
}

View file

@ -1,37 +1,19 @@
/*
* PROJECT: ReactOS Picture and Fax Viewer
* FILE: dll/win32/shimgvw/shimgvw.c
* PURPOSE: shimgvw.dll
* PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
* Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
* PROJECT: ReactOS Picture and Fax Viewer
* LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
* PURPOSE: Image file browsing and manipulation
* COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org)
* Copyright 2018-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
#define WIN32_NO_STATUS
#define _INC_WINDOWS
#define COM_NO_WINDOWS_H
#define INITGUID
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include <winreg.h>
#include <wingdi.h>
#include <wincon.h>
#include "shimgvw.h"
#include <windowsx.h>
#include <objbase.h>
#include <commctrl.h>
#include <commdlg.h>
#include <gdiplus.h>
#include <shlobj.h>
#include <strsafe.h>
#include <shlwapi.h>
#include <shellapi.h>
#define NDEBUG
#include <debug.h>
#include "shimgvw.h"
HINSTANCE g_hInstance;
SHIMGVW_SETTINGS g_Settings;
SHIMGVW_FILENODE *g_pCurrentFile;
@ -41,6 +23,8 @@ WNDPROC g_fnPrevProc = NULL;
HWND g_hDispWnd = NULL;
HWND g_hToolBar = NULL;
ANIME g_Anime; /* Animation */
/* zooming */
static UINT s_nZoomPercents = 100;
@ -109,135 +93,6 @@ static const TB_BUTTON_CONFIG s_ButtonConfig[] =
DEFINE_BTN_CONFIG(HELP_TOC)
};
/* animation */
UINT m_nFrameIndex = 0;
UINT m_nFrameCount = 0;
UINT m_nLoopIndex = 0;
UINT m_nLoopCount = (UINT)-1;
PropertyItem *m_pDelayItem = NULL;
#define ANIME_TIMER_ID 9999
static void Anime_FreeInfo(void)
{
if (m_pDelayItem)
{
free(m_pDelayItem);
m_pDelayItem = NULL;
}
m_nFrameIndex = 0;
m_nFrameCount = 0;
m_nLoopIndex = 0;
m_nLoopCount = (UINT)-1;
}
static BOOL Anime_LoadInfo(void)
{
UINT nDimCount = 0;
UINT cbItem;
UINT result;
Anime_FreeInfo();
KillTimer(g_hDispWnd, ANIME_TIMER_ID);
if (!g_pImage)
return FALSE;
GdipImageGetFrameDimensionsCount(g_pImage, &nDimCount);
if (nDimCount)
{
GUID *dims = (GUID *)calloc(nDimCount, sizeof(GUID));
if (dims)
{
GdipImageGetFrameDimensionsList(g_pImage, dims, nDimCount);
GdipImageGetFrameCount(g_pImage, dims, &result);
m_nFrameCount = result;
free(dims);
}
}
result = 0;
GdipGetPropertyItemSize(g_pImage, PropertyTagFrameDelay, &result);
cbItem = result;
if (cbItem)
{
m_pDelayItem = (PropertyItem *)malloc(cbItem);
GdipGetPropertyItem(g_pImage, PropertyTagFrameDelay, cbItem, m_pDelayItem);
}
result = 0;
GdipGetPropertyItemSize(g_pImage, PropertyTagLoopCount, &result);
cbItem = result;
if (cbItem)
{
PropertyItem *pItem = (PropertyItem *)malloc(cbItem);
if (pItem)
{
if (GdipGetPropertyItem(g_pImage, PropertyTagLoopCount, cbItem, pItem) == Ok)
{
m_nLoopCount = *(WORD *)pItem->value;
}
free(pItem);
}
}
if (m_pDelayItem)
{
SetTimer(g_hDispWnd, ANIME_TIMER_ID, 0, NULL);
}
return m_pDelayItem != NULL;
}
static void Anime_SetFrameIndex(UINT nFrameIndex)
{
if (nFrameIndex < m_nFrameCount)
{
GUID guid = FrameDimensionTime;
if (Ok != GdipImageSelectActiveFrame(g_pImage, &guid, nFrameIndex))
{
guid = FrameDimensionPage;
GdipImageSelectActiveFrame(g_pImage, &guid, nFrameIndex);
}
}
m_nFrameIndex = nFrameIndex;
}
DWORD Anime_GetFrameDelay(UINT nFrameIndex)
{
if (nFrameIndex < m_nFrameCount && m_pDelayItem)
{
return ((DWORD *)m_pDelayItem->value)[m_nFrameIndex] * 10;
}
return 0;
}
BOOL Anime_Step(DWORD *pdwDelay)
{
*pdwDelay = INFINITE;
if (m_nLoopCount == (UINT)-1)
return FALSE;
if (m_nFrameIndex + 1 < m_nFrameCount)
{
*pdwDelay = Anime_GetFrameDelay(m_nFrameIndex);
Anime_SetFrameIndex(m_nFrameIndex);
++m_nFrameIndex;
return TRUE;
}
if (m_nLoopCount == 0 || m_nLoopIndex < m_nLoopCount)
{
*pdwDelay = Anime_GetFrameDelay(m_nFrameIndex);
Anime_SetFrameIndex(m_nFrameIndex);
m_nFrameIndex = 0;
++m_nLoopIndex;
return TRUE;
}
return FALSE;
}
static void UpdateZoom(UINT NewZoom, BOOL bEnableBestFit, BOOL bEnableRealSize)
{
BOOL bEnableZoomIn, bEnableZoomOut;
@ -356,7 +211,8 @@ static void pLoadImage(LPCWSTR szOpenFileName)
DPRINT1("GdipLoadImageFromFile() failed\n");
return;
}
Anime_LoadInfo();
Anime_LoadInfo(&g_Anime);
if (szOpenFileName && szOpenFileName[0])
SHAddToRecentDocs(SHARD_PATHW, szOpenFileName);
@ -447,27 +303,14 @@ static void pSaveImageAs(HWND hwnd)
if (GetSaveFileNameW(&sfn))
{
if (m_pDelayItem)
{
/* save animation */
KillTimer(g_hDispWnd, ANIME_TIMER_ID);
Anime_Pause(&g_Anime);
DPRINT1("FIXME: save animation\n");
if (GdipSaveImageToFile(g_pImage, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok)
{
DPRINT1("GdipSaveImageToFile() failed\n");
}
SetTimer(g_hDispWnd, ANIME_TIMER_ID, 0, NULL);
}
else
if (GdipSaveImageToFile(g_pImage, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok)
{
/* save non-animation */
if (GdipSaveImageToFile(g_pImage, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok)
{
DPRINT1("GdipSaveImageToFile() failed\n");
}
DPRINT1("GdipSaveImageToFile() failed\n");
}
Anime_Start(&g_Anime, 0);
}
free(szFilterMask);
@ -894,19 +737,6 @@ ImageView_CreateToolBar(HWND hwnd)
return FALSE;
}
static void ImageView_OnTimer(HWND hwnd)
{
DWORD dwDelay;
KillTimer(hwnd, ANIME_TIMER_ID);
InvalidateRect(hwnd, NULL, FALSE);
if (Anime_Step(&dwDelay))
{
SetTimer(hwnd, ANIME_TIMER_ID, dwDelay, NULL);
}
}
LRESULT CALLBACK
ImageView_DispWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@ -919,11 +749,8 @@ ImageView_DispWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
}
case WM_TIMER:
{
if (wParam == ANIME_TIMER_ID)
{
ImageView_OnTimer(hwnd);
return 0;
}
if (Anime_OnTimer(&g_Anime, wParam))
InvalidateRect(hwnd, NULL, FALSE);
break;
}
}
@ -941,6 +768,7 @@ ImageView_InitControls(HWND hwnd)
g_fnPrevProc = (WNDPROC) SetWindowLongPtr(g_hDispWnd, GWLP_WNDPROC, (LPARAM) ImageView_DispWndProc);
ImageView_CreateToolBar(hwnd);
Anime_SetTimerWnd(&g_Anime, g_hDispWnd);
}
static VOID
@ -1244,7 +1072,7 @@ ImageView_CreateWindow(HWND hwnd, LPCWSTR szFileName)
g_pImage = NULL;
}
Anime_FreeInfo();
Anime_FreeInfo(&g_Anime);
GdiplusShutdown(gdiplusToken);

View file

@ -1,9 +1,39 @@
/*
* PROJECT: ReactOS Picture and Fax Viewer
* LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
* PURPOSE: Image file browsing and manipulation
* COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org)
* Copyright 2018-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
#pragma once
#define WIN32_NO_STATUS
#define _INC_WINDOWS
#define COM_NO_WINDOWS_H
#define INITGUID
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include <winreg.h>
#include <wingdi.h>
#include <wincon.h>
#include <objbase.h>
#include <gdiplus.h>
#include <strsafe.h>
#define NDEBUG
#include <debug.h>
#include "resource.h"
#define TB_IMAGE_WIDTH 16
#define TB_IMAGE_HEIGHT 16
extern HINSTANCE hInstance;
extern HINSTANCE g_hInstance;
extern HWND g_hDispWnd;
extern GpImage *g_pImage;
typedef struct
{
@ -14,11 +44,32 @@ typedef struct
INT Height;
} SHIMGVW_SETTINGS;
typedef struct SHIMGVW_FILENODE_INTERNAL
typedef struct tagSHIMGVW_FILENODE
{
WCHAR FileName[MAX_PATH];
struct SHIMGVW_FILENODE_INTERNAL *Prev;
struct SHIMGVW_FILENODE_INTERNAL *Next;
struct tagSHIMGVW_FILENODE *Prev;
struct tagSHIMGVW_FILENODE *Next;
} SHIMGVW_FILENODE;
#define WC_SHIMGVW L"ShImgVw:CPreviewWnd"
/* Animation */
typedef struct tagANIME
{
UINT m_nFrameIndex;
UINT m_nFrameCount;
UINT m_nLoopIndex;
UINT m_nLoopCount;
PropertyItem *m_pDelayItem;
HWND m_hwndTimer;
} ANIME, *PANIME;
void Anime_FreeInfo(PANIME pAnime);
BOOL Anime_LoadInfo(PANIME pAnime);
void Anime_SetTimerWnd(PANIME pAnime, HWND hwndTimer);
void Anime_SetFrameIndex(PANIME pAnime, UINT nFrameIndex);
DWORD Anime_GetFrameDelay(PANIME pAnime, UINT nFrameIndex);
void Anime_Start(PANIME pAnime, DWORD dwDelay);
void Anime_Pause(PANIME pAnime);
BOOL Anime_Step(PANIME pAnime, DWORD *pdwDelay);
BOOL Anime_OnTimer(PANIME pAnime, WPARAM wParam);