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