]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/anidecod.cpp
Add some version checks to help compiling on OSX.
[wxWidgets.git] / src / common / anidecod.cpp
... / ...
CommitLineData
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_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::DoCanRead(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 if ( stream.IsSeekable() && stream.SeekI(0) == wxInvalidOffset )
131 {
132 return false;
133 }
134
135 if ( !stream.Read(&FCC1, 4) )
136 return false;
137
138 if ( FCC1 != riff32 )
139 return false;
140
141 // we have a riff file:
142 while ( stream.IsOk() )
143 {
144 if ( FCC1 == anih32 )
145 return true; // found the ANIH chunk - this should be an ANI file
146
147 // we always have a data size:
148 stream.Read(&datalen, 4);
149 datalen = wxINT32_SWAP_ON_BE(datalen) ;
150
151 // data should be padded to make even number of bytes
152 if (datalen % 2 == 1) datalen ++ ;
153
154 // now either data or a FCC:
155 if ( (FCC1 == riff32) || (FCC1 == list32) )
156 {
157 stream.Read(&FCC2, 4);
158 }
159 else
160 {
161 if ( stream.SeekI(stream.TellI() + datalen) == wxInvalidOffset )
162 return false;
163 }
164
165 // try to read next data chunk:
166 if ( !stream.Read(&FCC1, 4) )
167 {
168 // reading failed -- either EOF or IO error, bail out anyhow
169 return false;
170 }
171 }
172
173 return false;
174}
175
176// the "anih" RIFF chunk
177struct wxANIHeader
178{
179 wxInt32 cbSizeOf; // Num bytes in AniHeader (36 bytes)
180 wxInt32 cFrames; // Number of unique Icons in this cursor
181 wxInt32 cSteps; // Number of Blits before the animation cycles
182 wxInt32 cx; // width of the frames
183 wxInt32 cy; // height of the frames
184 wxInt32 cBitCount; // bit depth
185 wxInt32 cPlanes; // 1
186 wxInt32 JifRate; // Default Jiffies (1/60th of a second) if rate chunk not present.
187 wxInt32 flags; // Animation Flag (see AF_ constants)
188
189 // ANI files are always little endian so we need to swap bytes on big
190 // endian architectures
191#ifdef WORDS_BIGENDIAN
192 void AdjustEndianness()
193 {
194 // this works because all our fields are wxInt32 and they must be
195 // packed without holes between them (if they're not, they wouldn't map
196 // to the file header!)
197 wxInt32 * const start = (wxInt32 *)this;
198 wxInt32 * const end = start + sizeof(wxANIHeader)/sizeof(wxInt32);
199 for ( wxInt32 *p = start; p != end; p++ )
200 {
201 *p = wxINT32_SWAP_ALWAYS(*p);
202 }
203 }
204#else
205 void AdjustEndianness() { }
206#endif
207};
208
209bool wxANIDecoder::Load( wxInputStream& stream )
210{
211 wxInt32 FCC1, FCC2;
212 wxUint32 datalen;
213 unsigned int globaldelay=0;
214
215 wxInt32 riff32;
216 memcpy( &riff32, "RIFF", 4 );
217 wxInt32 list32;
218 memcpy( &list32, "LIST", 4 );
219 wxInt32 ico32;
220 memcpy( &ico32, "icon", 4 );
221 wxInt32 anih32;
222 memcpy( &anih32, "anih", 4 );
223 wxInt32 rate32;
224 memcpy( &rate32, "rate", 4 );
225 wxInt32 seq32;
226 memcpy( &seq32, "seq ", 4 );
227
228 if ( stream.IsSeekable() && stream.SeekI(0) == wxInvalidOffset )
229 {
230 return false;
231 }
232
233 if ( !stream.Read(&FCC1, 4) )
234 return false;
235 if ( FCC1 != riff32 )
236 return false;
237
238 m_nFrames = 0;
239 m_szAnimation = wxDefaultSize;
240
241 m_images.Clear();
242 m_info.Clear();
243
244 // we have a riff file:
245 while ( !stream.Eof() )
246 {
247 // we always have a data size:
248 if (!stream.Read(&datalen, 4))
249 return false;
250
251 datalen = wxINT32_SWAP_ON_BE(datalen);
252
253 //data should be padded to make even number of bytes
254 if (datalen % 2 == 1) datalen++;
255
256 // now either data or a FCC:
257 if ( (FCC1 == riff32) || (FCC1 == list32) )
258 {
259 if (!stream.Read(&FCC2, 4))
260 return false;
261 }
262 else if ( FCC1 == anih32 )
263 {
264 if ( datalen != sizeof(wxANIHeader) )
265 return false;
266
267 if (m_nFrames > 0)
268 return false; // already parsed an ani header?
269
270 struct wxANIHeader header;
271 if (!stream.Read(&header, sizeof(wxANIHeader)))
272 return false;
273 header.AdjustEndianness();
274
275 // we should have a global frame size
276 m_szAnimation = wxSize(header.cx, header.cy);
277
278 // save interesting info from the header
279 m_nFrames = header.cSteps; // NB: not cFrames!!
280 if ( m_nFrames == 0 )
281 return false;
282
283 globaldelay = header.JifRate * 1000 / 60;
284
285 m_images.Alloc(header.cFrames);
286 m_info.Add(wxANIFrameInfo(), m_nFrames);
287 }
288 else if ( FCC1 == rate32 )
289 {
290 // did we already process the anih32 chunk?
291 if (m_nFrames == 0)
292 return false; // rate chunks should always be placed after anih chunk
293
294 wxASSERT(m_info.GetCount() == m_nFrames);
295 for (unsigned int i=0; i<m_nFrames; i++)
296 {
297 if (!stream.Read(&FCC2, 4))
298 return false;
299 m_info[i].m_delay = wxINT32_SWAP_ON_BE(FCC2) * 1000 / 60;
300 }
301 }
302 else if ( FCC1 == seq32 )
303 {
304 // did we already process the anih32 chunk?
305 if (m_nFrames == 0)
306 return false; // seq chunks should always be placed after anih chunk
307
308 wxASSERT(m_info.GetCount() == m_nFrames);
309 for (unsigned int i=0; i<m_nFrames; i++)
310 {
311 if (!stream.Read(&FCC2, 4))
312 return false;
313 m_info[i].m_imageIndex = wxINT32_SWAP_ON_BE(FCC2);
314 }
315 }
316 else if ( FCC1 == ico32 )
317 {
318 // use DoLoadFile() and not LoadFile()!
319 wxImage image;
320 if (!sm_handler.DoLoadFile(&image, stream, false /* verbose */, -1))
321 return false;
322
323 image.SetType(wxBITMAP_TYPE_ANI);
324 m_images.Add(image);
325 }
326 else
327 {
328 if ( stream.SeekI(stream.TellI() + datalen) == wxInvalidOffset )
329 return false;
330 }
331
332 // try to read next data chunk:
333 if ( !stream.Read(&FCC1, 4) && !stream.Eof())
334 {
335 // we didn't reach the EOF! An other kind of error has occurred...
336 return false;
337 }
338 //else: proceed with the parsing of the next header block or
339 // exiting this loop (if stream.Eof() == true)
340 }
341
342 if (m_nFrames==0)
343 return false;
344
345 if (m_nFrames==m_images.GetCount())
346 {
347 // if no SEQ chunk is available, display the frames in the order
348 // they were loaded
349 for (unsigned int i=0; i<m_nFrames; i++)
350 if (m_info[i].m_imageIndex == -1)
351 m_info[i].m_imageIndex = i;
352 }
353
354 // if some frame has an invalid delay, use the global delay given in the
355 // ANI header
356 for (unsigned int i=0; i<m_nFrames; i++)
357 if (m_info[i].m_delay == 0)
358 m_info[i].m_delay = globaldelay;
359
360 // if the header did not contain a valid frame size, try to grab
361 // it from the size of the first frame (all frames are of the same size)
362 if (m_szAnimation.GetWidth() == 0 ||
363 m_szAnimation.GetHeight() == 0)
364 m_szAnimation = wxSize(m_images[0].GetWidth(), m_images[0].GetHeight());
365
366 return m_szAnimation != wxDefaultSize;
367}
368
369#endif // wxUSE_STREAMS && wxUSE_ICO_CUR