]> git.saurik.com Git - wxWidgets.git/blob - src/common/anidecod.cpp
don't crash on weird line endings like \r\r\n
[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->Ok();
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
176 bool wxANIDecoder::Load( wxInputStream& stream )
177 {
178 wxInt32 FCC1, FCC2;
179 wxUint32 datalen;
180 size_t globaldelay=0;
181
182 wxInt32 riff32;
183 memcpy( &riff32, "RIFF", 4 );
184 wxInt32 list32;
185 memcpy( &list32, "LIST", 4 );
186 wxInt32 ico32;
187 memcpy( &ico32, "icon", 4 );
188 wxInt32 anih32;
189 memcpy( &anih32, "anih", 4 );
190 wxInt32 rate32;
191 memcpy( &rate32, "rate", 4 );
192 wxInt32 seq32;
193 memcpy( &seq32, "seq ", 4 );
194
195 stream.SeekI(0);
196 stream.Read(&FCC1, 4);
197 if ( FCC1 != riff32 )
198 return false;
199
200 m_nFrames = 0;
201 m_szAnimation = wxDefaultSize;
202
203 m_images.Clear();
204 m_info.Clear();
205
206 // we have a riff file:
207 while ( stream.IsOk() )
208 {
209 // we always have a data size:
210 stream.Read(&datalen, 4);
211 datalen = wxINT32_SWAP_ON_BE(datalen);
212
213 //data should be padded to make even number of bytes
214 if (datalen % 2 == 1) datalen++;
215
216 // now either data or a FCC:
217 if ( (FCC1 == riff32) || (FCC1 == list32) )
218 {
219 stream.Read(&FCC2, 4);
220 }
221 else if ( FCC1 == anih32 )
222 {
223 if ( datalen != sizeof(wxANIHeader) )
224 return false;
225
226 if (m_nFrames > 0)
227 return false; // already parsed an ani header?
228
229 struct wxANIHeader header;
230 stream.Read(&header, sizeof(wxANIHeader));
231
232 // we should have a global frame size
233 m_szAnimation = wxSize(header.cx, header.cy);
234
235 // save interesting info from the header
236 m_nFrames = header.cSteps; // NB: not cFrames!!
237 if (m_nFrames==0)
238 return false;
239
240 globaldelay = wxINT32_SWAP_ON_BE(header.JifRate) * 1000 / 60;
241
242 m_images.Alloc(header.cFrames);
243 m_info.Add(wxANIFrameInfo(), m_nFrames);
244 }
245 else if ( FCC1 == rate32 )
246 {
247 // did we already process the anih32 chunk?
248 if (m_nFrames == 0)
249 return false; // rate chunks should always be placed after anih chunk
250
251 wxASSERT(m_info.GetCount() == m_nFrames);
252 for (size_t i=0; i<m_nFrames; i++)
253 {
254 stream.Read(&FCC2, 4);
255 m_info[i].m_delay = wxINT32_SWAP_ON_BE(FCC2) * 1000 / 60;
256 }
257 }
258 else if ( FCC1 == seq32 )
259 {
260 // did we already process the anih32 chunk?
261 if (m_nFrames == 0)
262 return false; // seq chunks should always be placed after anih chunk
263
264 wxASSERT(m_info.GetCount() == m_nFrames);
265 for (size_t i=0; i<m_nFrames; i++)
266 {
267 stream.Read(&FCC2, 4);
268 m_info[i].m_imageIndex = wxINT32_SWAP_ON_BE(FCC2);
269 }
270 }
271 else if ( FCC1 == ico32 )
272 {
273 // use DoLoadFile() and not LoadFile()!
274 wxImage image;
275 if (!sm_handler.DoLoadFile(&image, stream, false /* verbose */, -1))
276 return false;
277
278 m_images.Add(image);
279 }
280 else
281 stream.SeekI(stream.TellI() + datalen);
282
283 // try to read next data chunk:
284 stream.Read(&FCC1, 4);
285 }
286
287 if (m_nFrames==0)
288 return false;
289
290 if (m_nFrames==m_images.GetCount())
291 {
292 // if no SEQ chunk is available, display the frames in the order
293 // they were loaded
294 for (size_t i=0; i<m_nFrames; i++)
295 if (m_info[i].m_imageIndex == -1)
296 m_info[i].m_imageIndex = i;
297 }
298
299 // if some frame has an invalid delay, use the global delay given in the
300 // ANI header
301 for (size_t i=0; i<m_nFrames; i++)
302 if (m_info[i].m_delay == 0)
303 m_info[i].m_delay = globaldelay;
304
305 // if the header did not contain a valid frame size, try to grab
306 // it from the size of the first frame (all frames are of the same size)
307 if (m_szAnimation.GetWidth() == 0 ||
308 m_szAnimation.GetHeight() == 0)
309 m_szAnimation = wxSize(m_images[0].GetWidth(), m_images[0].GetHeight());
310
311 return m_szAnimation!=wxDefaultSize;
312 }
313
314 #endif // wxUSE_STREAMS && wxUSE_GIF