]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/anidecod.cpp
added wxAnimationCtrl (patch 1570325)
[wxWidgets.git] / src / common / anidecod.cpp
diff --git a/src/common/anidecod.cpp b/src/common/anidecod.cpp
new file mode 100644 (file)
index 0000000..61cfb66
--- /dev/null
@@ -0,0 +1,314 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/common/anidecod.cpp
+// Purpose:     wxANIDecoder, ANI reader for wxImage and wxAnimation
+// Author:      Francesco Montorsi
+// RCS-ID:      $Id$
+// Copyright:   (c) Francesco Montorsi
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#if wxUSE_STREAMS && wxUSE_GIF
+
+#ifndef WX_PRECOMP
+    #include "wx/palette.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "wx/anidecod.h"
+
+// static
+wxCURHandler wxANIDecoder::sm_handler;
+
+
+
+//---------------------------------------------------------------------------
+// wxANIFrameInfo
+//---------------------------------------------------------------------------
+
+class wxANIFrameInfo
+{
+public:
+    wxANIFrameInfo(size_t delay = 0, int idx = -1) 
+        { m_delay=delay; m_imageIndex=idx; }
+
+    size_t m_delay;
+    int m_imageIndex;
+};
+
+#include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
+WX_DEFINE_OBJARRAY(wxImageArray);
+
+#include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
+WX_DEFINE_OBJARRAY(wxANIFrameInfoArray);
+
+
+
+
+//---------------------------------------------------------------------------
+// wxANIDecoder
+//---------------------------------------------------------------------------
+
+wxANIDecoder::wxANIDecoder()
+{
+}
+
+wxANIDecoder::~wxANIDecoder()
+{
+}
+
+bool wxANIDecoder::ConvertToImage(size_t frame, wxImage *image) const
+{
+    size_t idx = m_info[frame].m_imageIndex;
+    *image = m_images[idx];       // copy
+    return image->Ok();
+}
+
+
+//---------------------------------------------------------------------------
+// Data accessors
+//---------------------------------------------------------------------------
+
+wxSize wxANIDecoder::GetFrameSize(size_t WXUNUSED(frame)) const
+{
+    // all frames are of the same size...
+    return m_szAnimation;
+}
+
+wxPoint wxANIDecoder::GetFramePosition(size_t WXUNUSED(frame)) const
+{
+    // all frames are of the same size...
+    return wxPoint(0,0);
+}
+
+wxAnimationDisposal wxANIDecoder::GetDisposalMethod(size_t WXUNUSED(frame)) const
+{
+    // this disposal is implicit for all frames inside an ANI file
+    return wxANIM_TOBACKGROUND;
+}
+
+long wxANIDecoder::GetDelay(size_t frame) const
+{
+    return m_info[frame].m_delay;
+}
+
+
+//---------------------------------------------------------------------------
+// ANI reading and decoding
+//---------------------------------------------------------------------------
+
+bool wxANIDecoder::CanRead(wxInputStream& stream) const
+{
+    wxInt32 FCC1, FCC2;
+    wxUint32 datalen ;
+
+    wxInt32 riff32;
+    memcpy( &riff32, "RIFF", 4 );
+    wxInt32 list32;
+    memcpy( &list32, "LIST", 4 );
+    wxInt32 ico32;
+    memcpy( &ico32, "icon", 4 );
+    wxInt32 anih32;
+    memcpy( &anih32, "anih", 4 );
+
+    stream.SeekI(0);
+    if ( !stream.Read(&FCC1, 4) )
+        return false;
+
+    if ( FCC1 != riff32 )
+        return false;
+
+    // we have a riff file:
+    while ( stream.IsOk() )
+    {
+        if ( FCC1 == anih32 )
+            return true;        // found the ANIH chunk - this should be an ANI file
+
+        // we always have a data size:
+        stream.Read(&datalen, 4);
+        datalen = wxINT32_SWAP_ON_BE(datalen) ;
+        
+        // data should be padded to make even number of bytes
+        if (datalen % 2 == 1) datalen ++ ;
+
+        // now either data or a FCC:
+        if ( (FCC1 == riff32) || (FCC1 == list32) )
+        {
+            stream.Read(&FCC2, 4);
+        }
+        else
+        {
+            stream.SeekI(stream.TellI() + datalen);
+        }
+
+        // try to read next data chunk:
+        if ( !stream.Read(&FCC1, 4) )
+        {
+            // reading failed -- either EOF or IO error, bail out anyhow
+            return false;
+        }
+    }
+
+    return false;
+}
+
+// the "anih" RIFF chunk
+struct wxANIHeader 
+{
+    wxInt32 cbSizeOf;     // Num bytes in AniHeader (36 bytes)
+    wxInt32 cFrames;      // Number of unique Icons in this cursor
+    wxInt32 cSteps;       // Number of Blits before the animation cycles
+    wxInt32 cx;           // width of the frames
+    wxInt32 cy;           // height of the frames
+    wxInt32 cBitCount;    // bit depth
+    wxInt32 cPlanes;      // 1
+    wxInt32 JifRate;      // Default Jiffies (1/60th of a second) if rate chunk not present.
+    wxInt32 flags;        // Animation Flag (see AF_ constants)
+};
+
+bool wxANIDecoder::Load( wxInputStream& stream )
+{
+    wxInt32 FCC1, FCC2;
+    wxUint32 datalen;
+    size_t globaldelay=0;
+
+    wxInt32 riff32;
+    memcpy( &riff32, "RIFF", 4 );
+    wxInt32 list32;
+    memcpy( &list32, "LIST", 4 );
+    wxInt32 ico32;
+    memcpy( &ico32, "icon", 4 );
+    wxInt32 anih32;
+    memcpy( &anih32, "anih", 4 );
+    wxInt32 rate32;
+    memcpy( &rate32, "rate", 4 );
+    wxInt32 seq32;
+    memcpy( &seq32, "seq ", 4 );
+
+    stream.SeekI(0);
+    stream.Read(&FCC1, 4);
+    if ( FCC1 != riff32 )
+        return false;
+
+    m_nFrames = 0;
+    m_szAnimation = wxDefaultSize;
+
+    m_images.Clear();
+    m_info.Clear();
+
+    // we have a riff file:
+    while ( stream.IsOk() )
+    {
+        // we always have a data size:
+        stream.Read(&datalen, 4);
+        datalen = wxINT32_SWAP_ON_BE(datalen);
+
+        //data should be padded to make even number of bytes
+        if (datalen % 2 == 1) datalen++;
+
+        // now either data or a FCC:
+        if ( (FCC1 == riff32) || (FCC1 == list32) )
+        {
+            stream.Read(&FCC2, 4);
+        }
+        else if ( FCC1 == anih32 )
+        {
+            if ( datalen != sizeof(wxANIHeader) )
+                return false;
+
+            if (m_nFrames > 0)
+                return false;       // already parsed an ani header?
+
+            struct wxANIHeader header;
+            stream.Read(&header, sizeof(wxANIHeader));
+
+            // we should have a global frame size
+            m_szAnimation = wxSize(header.cx, header.cy);
+
+            // save interesting info from the header
+            m_nFrames = header.cSteps;   // NB: not cFrames!!
+            if (m_nFrames==0)
+                return false;
+
+            globaldelay = wxINT32_SWAP_ON_BE(header.JifRate) * 1000 / 60;
+
+            m_images.Alloc(header.cFrames);
+            m_info.Add(wxANIFrameInfo(), m_nFrames);
+        }
+        else if ( FCC1 == rate32 )
+        {
+            // did we already process the anih32 chunk?
+            if (m_nFrames == 0)
+                return false;       // rate chunks should always be placed after anih chunk
+            
+            wxASSERT(m_info.GetCount() == m_nFrames);
+            for (size_t i=0; i<m_nFrames; i++)
+            {
+                stream.Read(&FCC2, 4);
+                m_info[i].m_delay = wxINT32_SWAP_ON_BE(FCC2) * 1000 / 60;
+            }
+        }
+        else if ( FCC1 == seq32 )
+        {
+            // did we already process the anih32 chunk?
+            if (m_nFrames == 0)
+                return false;       // seq chunks should always be placed after anih chunk
+
+            wxASSERT(m_info.GetCount() == m_nFrames);
+            for (size_t i=0; i<m_nFrames; i++)
+            {
+                stream.Read(&FCC2, 4);
+                m_info[i].m_imageIndex = wxINT32_SWAP_ON_BE(FCC2);
+            }
+        }
+        else if ( FCC1 == ico32 )
+        {
+            // use DoLoadFile() and not LoadFile()!
+            wxImage image;
+            if (!sm_handler.DoLoadFile(&image, stream, false /* verbose */, -1))
+                return false;
+
+            m_images.Add(image);
+        }
+        else
+            stream.SeekI(stream.TellI() + datalen);
+
+        // try to read next data chunk:
+        stream.Read(&FCC1, 4);
+    }
+
+    if (m_nFrames==0)
+        return false;
+
+    if (m_nFrames==m_images.GetCount())
+    {
+        // if no SEQ chunk is available, display the frames in the order
+        // they were loaded
+        for (size_t i=0; i<m_nFrames; i++)
+            if (m_info[i].m_imageIndex == -1)
+                m_info[i].m_imageIndex = i;
+    }
+
+    // if some frame has an invalid delay, use the global delay given in the
+    // ANI header
+    for (size_t i=0; i<m_nFrames; i++)
+        if (m_info[i].m_delay == 0)
+            m_info[i].m_delay = globaldelay;
+
+    // if the header did not contain a valid frame size, try to grab
+    // it from the size of the first frame (all frames are of the same size)
+    if (m_szAnimation.GetWidth() == 0 ||
+        m_szAnimation.GetHeight() == 0)
+        m_szAnimation = wxSize(m_images[0].GetWidth(), m_images[0].GetHeight());
+
+    return m_szAnimation!=wxDefaultSize;
+}
+
+#endif // wxUSE_STREAMS && wxUSE_GIF