[SHIMGVW] Play gif animation (#934)
[reactos.git] / dll / win32 / shimgvw / shimgvw.c
index 8339b66..683e9b3 100644 (file)
@@ -9,6 +9,7 @@
 #define WIN32_NO_STATUS
 #define _INC_WINDOWS
 #define COM_NO_WINDOWS_H
+#define INITGUID
 
 #include <stdarg.h>
 
@@ -31,7 +32,6 @@
 
 #include "shimgvw.h"
 
-
 HINSTANCE hInstance;
 SHIMGVW_SETTINGS shiSettings;
 SHIMGVW_FILENODE *currentFile;
@@ -49,6 +49,137 @@ static const UINT ZoomSteps[] =
     10, 25, 50, 100, 200, 400, 800, 1600
 };
 
+/* 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)
+{
+    GUID *dims;
+    UINT nDimCount = 0;
+    UINT cbItem;
+    UINT result;
+    PropertyItem *pItem;
+
+    Anime_FreeInfo();
+    KillTimer(hDispWnd, ANIME_TIMER_ID);
+
+    if (!image)
+        return FALSE;
+
+    GdipImageGetFrameDimensionsCount(image, &nDimCount);
+    if (nDimCount)
+    {
+        dims = (GUID *)calloc(nDimCount, sizeof(GUID));
+        if (dims)
+        {
+            GdipImageGetFrameDimensionsList(image, dims, nDimCount);
+            GdipImageGetFrameCount(image, dims, &result);
+            m_nFrameCount = result;
+            free(dims);
+        }
+    }
+
+    result = 0;
+    GdipGetPropertyItemSize(image, PropertyTagFrameDelay, &result);
+    cbItem = result;
+    if (cbItem)
+    {
+        m_pDelayItem = (PropertyItem *)malloc(cbItem);
+        GdipGetPropertyItem(image, PropertyTagFrameDelay, cbItem, m_pDelayItem);
+    }
+
+    result = 0;
+    GdipGetPropertyItemSize(image, PropertyTagLoopCount, &result);
+    cbItem = result;
+    if (cbItem)
+    {
+        pItem = (PropertyItem *)malloc(cbItem);
+        if (pItem)
+        {
+            if (GdipGetPropertyItem(image, PropertyTagLoopCount, cbItem, pItem) == Ok)
+            {
+                m_nLoopCount = *(WORD *)pItem->value;
+            }
+            free(pItem);
+        }
+    }
+
+    if (m_pDelayItem)
+    {
+        SetTimer(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(image, &guid, nFrameIndex))
+        {
+            guid = FrameDimensionPage;
+            GdipImageSelectActiveFrame(image, &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 ZoomInOrOut(BOOL bZoomIn)
 {
     INT i;
@@ -171,6 +302,7 @@ static void pLoadImage(LPWSTR szOpenFileName)
         DPRINT1("GdipLoadImageFromFile() failed\n");
         return;
     }
+    Anime_LoadInfo();
 
     /* reset zoom */
     ResetZoom();
@@ -257,9 +389,26 @@ static void pSaveImageAs(HWND hwnd)
 
     if (GetSaveFileNameW(&sfn))
     {
-        if (GdipSaveImageToFile(image, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok)
+        if (m_pDelayItem)
+        {
+            /* save animation */
+            KillTimer(hDispWnd, ANIME_TIMER_ID);
+
+            DPRINT1("FIXME: save animation\n");
+            if (GdipSaveImageToFile(image, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok)
+            {
+                DPRINT1("GdipSaveImageToFile() failed\n");
+            }
+
+            SetTimer(hDispWnd, ANIME_TIMER_ID, 0, NULL);
+        }
+        else
         {
-            DPRINT1("GdipSaveImageToFile() failed\n");
+            /* save non-animation */
+            if (GdipSaveImageToFile(image, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok)
+            {
+                DPRINT1("GdipSaveImageToFile() failed\n");
+            }
         }
     }
 
@@ -595,6 +744,19 @@ ImageView_CreateToolBar(HWND hwnd)
     return FALSE;
 }
 
+static void ImageView_OnTimer(HWND hwnd)
+{
+    DWORD dwDelay;
+
+    KillTimer(hwnd, ANIME_TIMER_ID);
+    InvalidateRect(hwnd, NULL, TRUE);
+
+    if (Anime_Step(&dwDelay))
+    {
+        SetTimer(hwnd, ANIME_TIMER_ID, dwDelay, NULL);
+    }
+}
+
 LRESULT CALLBACK
 ImageView_DispWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
 {
@@ -605,6 +767,15 @@ ImageView_DispWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
             ImageView_DrawImage(hwnd);
             return 0L;
         }
+        case WM_TIMER:
+        {
+            if (wParam == ANIME_TIMER_ID)
+            {
+                ImageView_OnTimer(hwnd);
+                return 0;
+            }
+            break;
+        }
     }
     return CallWindowProc(PrevProc, hwnd, Message, wParam, lParam);
 }
@@ -874,6 +1045,9 @@ ImageView_CreateWindow(HWND hwnd, LPWSTR szFileName)
 
     if (image)
         GdipDisposeImage(image);
+
+    Anime_FreeInfo();
+
     GdiplusShutdown(gdiplusToken);
     return -1;
 }