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