xref: /reactos/dll/win32/shimgvw/anime.c (revision 0c2cdcae)
1 /*
2  * PROJECT:     ReactOS Picture and Fax Viewer
3  * LICENSE:     GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
4  * PURPOSE:     Animation of shimgvw.dll
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 #include "shimgvw.h"
9 
10 #define ANIME_TIMER_ID  9999
11 
12 void Anime_FreeInfo(PANIME pAnime)
13 {
14     if (pAnime->m_pDelayItem)
15     {
16         QuickFree(pAnime->m_pDelayItem);
17         pAnime->m_pDelayItem = NULL;
18     }
19     pAnime->m_nFrameIndex = 0;
20     pAnime->m_nFrameCount = 0;
21     pAnime->m_nLoopIndex = 0;
22     pAnime->m_nLoopCount = (UINT)-1;
23 }
24 
25 void Anime_SetTimerWnd(PANIME pAnime, HWND hwndTimer)
26 {
27     pAnime->m_hwndTimer = hwndTimer;
28 }
29 
30 void Anime_Pause(PANIME pAnime)
31 {
32     KillTimer(pAnime->m_hwndTimer, ANIME_TIMER_ID);
33 }
34 
35 void Anime_Start(PANIME pAnime, DWORD dwDelay)
36 {
37     if (pAnime->m_pDelayItem)
38         SetTimer(pAnime->m_hwndTimer, ANIME_TIMER_ID, dwDelay, NULL);
39 }
40 
41 DWORD Anime_GetFrameDelay(PANIME pAnime, UINT nFrameIndex)
42 {
43     if (nFrameIndex < pAnime->m_nFrameCount && pAnime->m_pDelayItem)
44     {
45         return ((DWORD *)pAnime->m_pDelayItem->value)[pAnime->m_nFrameIndex] * 10;
46     }
47     return 0;
48 }
49 
50 BOOL Anime_Step(PANIME pAnime, DWORD *pdwDelay)
51 {
52     *pdwDelay = INFINITE;
53     if (pAnime->m_nLoopCount == (UINT)-1)
54         return FALSE;
55 
56     if (pAnime->m_nFrameIndex + 1 < pAnime->m_nFrameCount)
57     {
58         *pdwDelay = Anime_GetFrameDelay(pAnime, pAnime->m_nFrameIndex);
59         Anime_SetFrameIndex(pAnime, pAnime->m_nFrameIndex);
60         ++pAnime->m_nFrameIndex;
61         return TRUE;
62     }
63 
64     if (pAnime->m_nLoopCount == 0 || pAnime->m_nLoopIndex < pAnime->m_nLoopCount)
65     {
66         *pdwDelay = Anime_GetFrameDelay(pAnime, pAnime->m_nFrameIndex);
67         Anime_SetFrameIndex(pAnime, pAnime->m_nFrameIndex);
68         pAnime->m_nFrameIndex = 0;
69         ++pAnime->m_nLoopIndex;
70         return TRUE;
71     }
72 
73     return FALSE;
74 }
75 
76 BOOL Anime_OnTimer(PANIME pAnime, WPARAM wParam)
77 {
78     DWORD dwDelay;
79 
80     if (wParam != ANIME_TIMER_ID)
81         return FALSE;
82 
83     Anime_Pause(pAnime);
84     if (Anime_Step(pAnime, &dwDelay))
85         Anime_Start(pAnime, dwDelay);
86     return TRUE;
87 }
88 
89 BOOL Anime_LoadInfo(PANIME pAnime)
90 {
91     UINT nDimCount = 0;
92     UINT cbItem;
93     UINT result;
94 
95     Anime_Pause(pAnime);
96     Anime_FreeInfo(pAnime);
97 
98     if (!g_pImage)
99         return FALSE;
100 
101     GdipImageGetFrameDimensionsCount(g_pImage, &nDimCount);
102     if (nDimCount)
103     {
104         GUID *dims = (GUID *)QuickAlloc(nDimCount * sizeof(GUID), TRUE);
105         if (dims)
106         {
107             GdipImageGetFrameDimensionsList(g_pImage, dims, nDimCount);
108             GdipImageGetFrameCount(g_pImage, dims, &result);
109             pAnime->m_nFrameCount = result;
110             QuickFree(dims);
111         }
112     }
113 
114     result = 0;
115     GdipGetPropertyItemSize(g_pImage, PropertyTagFrameDelay, &result);
116     cbItem = result;
117     if (cbItem)
118     {
119         pAnime->m_pDelayItem = (PropertyItem *)QuickAlloc(cbItem, FALSE);
120         GdipGetPropertyItem(g_pImage, PropertyTagFrameDelay, cbItem, pAnime->m_pDelayItem);
121     }
122 
123     result = 0;
124     GdipGetPropertyItemSize(g_pImage, PropertyTagLoopCount, &result);
125     cbItem = result;
126     if (cbItem)
127     {
128         PropertyItem *pItem = (PropertyItem *)QuickAlloc(cbItem, FALSE);
129         if (pItem)
130         {
131             if (GdipGetPropertyItem(g_pImage, PropertyTagLoopCount, cbItem, pItem) == Ok)
132             {
133                 pAnime->m_nLoopCount = *(WORD *)pItem->value;
134             }
135             QuickFree(pItem);
136         }
137     }
138 
139     Anime_Start(pAnime, 0);
140 
141     return pAnime->m_pDelayItem != NULL;
142 }
143 
144 void Anime_SetFrameIndex(PANIME pAnime, UINT nFrameIndex)
145 {
146     if (nFrameIndex < pAnime->m_nFrameCount)
147     {
148         GUID guid = FrameDimensionTime;
149         if (Ok != GdipImageSelectActiveFrame(g_pImage, &guid, nFrameIndex))
150         {
151             guid = FrameDimensionPage;
152             GdipImageSelectActiveFrame(g_pImage, &guid, nFrameIndex);
153         }
154     }
155     pAnime->m_nFrameIndex = nFrameIndex;
156 }
157