1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/common/anidecod.cpp
3// Purpose:     wxANIDecoder, ANI reader for wxImage and wxAnimation
4// Author:      Francesco Montorsi
5// RCS-ID:      $Id: anidecod.cpp 43898 2006-12-10 14:18:37Z VZ $
6// Copyright:   (c) Francesco Montorsi
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
14    #pragma hdrstop
15#endif
16
17#if wxUSE_STREAMS && wxUSE_ICO_CUR
18
19#include "wx/anidecod.h"
20
21#ifndef WX_PRECOMP
22    #include "wx/palette.h"
23#endif
24
25#include <stdlib.h>
26#include <string.h>
27
28// static
29wxCURHandler wxANIDecoder::sm_handler;
30
31//---------------------------------------------------------------------------
32// wxANIFrameInfo
33//---------------------------------------------------------------------------
34
35class wxANIFrameInfo
36{
37public:
38    wxANIFrameInfo(unsigned int delay = 0, int idx = -1)
39        { m_delay=delay; m_imageIndex=idx; }
40
41    unsigned int m_delay;
42    int m_imageIndex;
43};
44
45#include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
46WX_DEFINE_OBJARRAY(wxImageArray)
47
48#include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
49WX_DEFINE_OBJARRAY(wxANIFrameInfoArray)
50
51
52//---------------------------------------------------------------------------
53// wxANIDecoder
54//---------------------------------------------------------------------------
55
56wxANIDecoder::wxANIDecoder()
57{
58}
59
60wxANIDecoder::~wxANIDecoder()
61{
62}
63
64bool wxANIDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
65{
66    unsigned int idx = m_info[frame].m_imageIndex;
67    *image = m_images[idx];       // copy
68    return image->IsOk();
69}
70
71
72//---------------------------------------------------------------------------
73// Data accessors
74//---------------------------------------------------------------------------
75
76wxSize wxANIDecoder::GetFrameSize(unsigned int WXUNUSED(frame)) const
77{
78    // all frames are of the same size...
79    return m_szAnimation;
80}
81
82wxPoint wxANIDecoder::GetFramePosition(unsigned int WXUNUSED(frame)) const
83{
84    // all frames are of the same size...
85    return wxPoint(0,0);
86}
87
88wxAnimationDisposal wxANIDecoder::GetDisposalMethod(unsigned int WXUNUSED(frame)) const
89{
90    // this disposal is implicit for all frames inside an ANI file
91    return wxANIM_TOBACKGROUND;
92}
93
94long wxANIDecoder::GetDelay(unsigned int frame) const
95{
96    return m_info[frame].m_delay;
97}
98
99wxColour wxANIDecoder::GetTransparentColour(unsigned int frame) const
100{
101    unsigned int idx = m_info[frame].m_imageIndex;
102
103    if (!m_images[idx].HasMask())
104        return wxNullColour;
105
106    return wxColour(m_images[idx].GetMaskRed(),
107                    m_images[idx].GetMaskGreen(),
108                    m_images[idx].GetMaskBlue());
109}
110
111
112//---------------------------------------------------------------------------
113// ANI reading and decoding
114//---------------------------------------------------------------------------
115
116bool wxANIDecoder::CanRead(wxInputStream& stream) const
117{
118    wxInt32 FCC1, FCC2;
119    wxUint32 datalen ;
120
121    wxInt32 riff32;
122    memcpy( &riff32, "RIFF", 4 );
123    wxInt32 list32;
124    memcpy( &list32, "LIST", 4 );
125    wxInt32 ico32;
126    memcpy( &ico32, "icon", 4 );
127    wxInt32 anih32;
128    memcpy( &anih32, "anih", 4 );
129
130    stream.SeekI(0);
131    if ( !stream.Read(&FCC1, 4) )
132        return false;
133
134    if ( FCC1 != riff32 )
135        return false;
136
137    // we have a riff file:
138    while ( stream.IsOk() )
139    {
140        if ( FCC1 == anih32 )
141            return true;        // found the ANIH chunk - this should be an ANI file
142
143        // we always have a data size:
144        stream.Read(&datalen, 4);
145        datalen = wxINT32_SWAP_ON_BE(datalen) ;
146
147        // data should be padded to make even number of bytes
148        if (datalen % 2 == 1) datalen ++ ;
149
150        // now either data or a FCC:
151        if ( (FCC1 == riff32) || (FCC1 == list32) )
152        {
153            stream.Read(&FCC2, 4);
154        }
155        else
156        {
157            stream.SeekI(stream.TellI() + datalen);
158        }
159
160        // try to read next data chunk:
161        if ( !stream.Read(&FCC1, 4) )
162        {
163            // reading failed -- either EOF or IO error, bail out anyhow
164            return false;
165        }
166    }
167
168    return false;
169}
170
171// the "anih" RIFF chunk
172struct wxANIHeader
173{
174    wxInt32 cbSizeOf;     // Num bytes in AniHeader (36 bytes)
175    wxInt32 cFrames;      // Number of unique Icons in this cursor
176    wxInt32 cSteps;       // Number of Blits before the animation cycles
177    wxInt32 cx;           // width of the frames
178    wxInt32 cy;           // height of the frames
179    wxInt32 cBitCount;    // bit depth
180    wxInt32 cPlanes;      // 1
181    wxInt32 JifRate;      // Default Jiffies (1/60th of a second) if rate chunk not present.
182    wxInt32 flags;        // Animation Flag (see AF_ constants)
183
184    // ANI files are always little endian so we need to swap bytes on big
185    // endian architectures
186#ifdef WORDS_BIGENDIAN
187    void AdjustEndianness()
188    {
189        // this works because all our fields are wxInt32 and they must be
190        // packed without holes between them (if they're not, they wouldn't map
191        // to the file header!)
192        wxInt32 * const start = (wxInt32 *)this;
193        wxInt32 * const end = start + sizeof(wxANIHeader)/sizeof(wxInt32);
194        for ( wxInt32 *p = start; p != end; p++ )
195        {
196            *p = wxINT32_SWAP_ALWAYS(*p);
197        }
198    }
199#else
200    void AdjustEndianness() { }
201#endif
202};
203
204bool wxANIDecoder::Load( wxInputStream& stream )
205{
206    wxInt32 FCC1, FCC2;
207    wxUint32 datalen;
208    unsigned int globaldelay=0;
209
210    wxInt32 riff32;
211    memcpy( &riff32, "RIFF", 4 );
212    wxInt32 list32;
213    memcpy( &list32, "LIST", 4 );
214    wxInt32 ico32;
215    memcpy( &ico32, "icon", 4 );
216    wxInt32 anih32;
217    memcpy( &anih32, "anih", 4 );
218    wxInt32 rate32;
219    memcpy( &rate32, "rate", 4 );
220    wxInt32 seq32;
221    memcpy( &seq32, "seq ", 4 );
222
223    stream.SeekI(0);
224    stream.Read(&FCC1, 4);
225    if ( FCC1 != riff32 )
226        return false;
227
228    m_nFrames = 0;
229    m_szAnimation = wxDefaultSize;
230
231    m_images.Clear();
232    m_info.Clear();
233
234    // we have a riff file:
235    while ( stream.IsOk() )
236    {
237        // we always have a data size:
238        stream.Read(&datalen, 4);
239        datalen = wxINT32_SWAP_ON_BE(datalen);
240
241        //data should be padded to make even number of bytes
242        if (datalen % 2 == 1) datalen++;
243
244        // now either data or a FCC:
245        if ( (FCC1 == riff32) || (FCC1 == list32) )
246        {
247            stream.Read(&FCC2, 4);
248        }
249        else if ( FCC1 == anih32 )
250        {
251            if ( datalen != sizeof(wxANIHeader) )
252                return false;
253
254            if (m_nFrames > 0)
255                return false;       // already parsed an ani header?
256
257            struct wxANIHeader header;
258            stream.Read(&header, sizeof(wxANIHeader));
259            header.AdjustEndianness();
260
261            // we should have a global frame size
262            m_szAnimation = wxSize(header.cx, header.cy);
263
264            // save interesting info from the header
265            m_nFrames = header.cSteps;   // NB: not cFrames!!
266            if ( m_nFrames == 0 )
267                return false;
268
269            globaldelay = header.JifRate * 1000 / 60;
270
271            m_images.Alloc(header.cFrames);
272            m_info.Add(wxANIFrameInfo(), m_nFrames);
273        }
274        else if ( FCC1 == rate32 )
275        {
276            // did we already process the anih32 chunk?
277            if (m_nFrames == 0)
278                return false;       // rate chunks should always be placed after anih chunk
279
280            wxASSERT(m_info.GetCount() == m_nFrames);
281            for (unsigned int i=0; i<m_nFrames; i++)
282            {
283                stream.Read(&FCC2, 4);
284                m_info[i].m_delay = wxINT32_SWAP_ON_BE(FCC2) * 1000 / 60;
285            }
286        }
287        else if ( FCC1 == seq32 )
288        {
289            // did we already process the anih32 chunk?
290            if (m_nFrames == 0)
291                return false;       // seq chunks should always be placed after anih chunk
292
293            wxASSERT(m_info.GetCount() == m_nFrames);
294            for (unsigned int i=0; i<m_nFrames; i++)
295            {
296                stream.Read(&FCC2, 4);
297                m_info[i].m_imageIndex = wxINT32_SWAP_ON_BE(FCC2);
298            }
299        }
300        else if ( FCC1 == ico32 )
301        {
302            // use DoLoadFile() and not LoadFile()!
303            wxImage image;
304            if (!sm_handler.DoLoadFile(&image, stream, false /* verbose */, -1))
305                return false;
306
307            m_images.Add(image);
308        }
309        else
310        {
311            stream.SeekI(stream.TellI() + datalen);
312        }
313
314        // try to read next data chunk:
315        stream.Read(&FCC1, 4);
316    }
317
318    if (m_nFrames==0)
319        return false;
320
321    if (m_nFrames==m_images.GetCount())
322    {
323        // if no SEQ chunk is available, display the frames in the order
324        // they were loaded
325        for (unsigned int i=0; i<m_nFrames; i++)
326            if (m_info[i].m_imageIndex == -1)
327                m_info[i].m_imageIndex = i;
328    }
329
330    // if some frame has an invalid delay, use the global delay given in the
331    // ANI header
332    for (unsigned int i=0; i<m_nFrames; i++)
333        if (m_info[i].m_delay == 0)
334            m_info[i].m_delay = globaldelay;
335
336    // if the header did not contain a valid frame size, try to grab
337    // it from the size of the first frame (all frames are of the same size)
338    if (m_szAnimation.GetWidth() == 0 ||
339        m_szAnimation.GetHeight() == 0)
340        m_szAnimation = wxSize(m_images[0].GetWidth(), m_images[0].GetHeight());
341
342    return m_szAnimation != wxDefaultSize;
343}
344
345#endif // wxUSE_STREAMS && wxUSE_ICO_CUR
346