]> git.saurik.com Git - wxWidgets.git/blame - src/common/anidecod.cpp
Remove unused NO_SORT constant.
[wxWidgets.git] / src / common / anidecod.cpp
CommitLineData
72045d57
VZ
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
d18868bb 17#if wxUSE_STREAMS && wxUSE_ICO_CUR
72045d57 18
14c0d834
PC
19#include "wx/anidecod.h"
20
72045d57
VZ
21#ifndef WX_PRECOMP
22 #include "wx/palette.h"
23#endif
24
25#include <stdlib.h>
26#include <string.h>
72045d57
VZ
27
28// static
29wxCURHandler wxANIDecoder::sm_handler;
30
72045d57
VZ
31//---------------------------------------------------------------------------
32// wxANIFrameInfo
33//---------------------------------------------------------------------------
34
35class wxANIFrameInfo
36{
37public:
870cf35c 38 wxANIFrameInfo(unsigned int delay = 0, int idx = -1)
72045d57
VZ
39 { m_delay=delay; m_imageIndex=idx; }
40
870cf35c 41 unsigned int m_delay;
72045d57
VZ
42 int m_imageIndex;
43};
44
14c0d834
PC
45#include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
46WX_DEFINE_OBJARRAY(wxImageArray)
72045d57 47
14c0d834
PC
48#include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
49WX_DEFINE_OBJARRAY(wxANIFrameInfoArray)
72045d57
VZ
50
51
52//---------------------------------------------------------------------------
53// wxANIDecoder
54//---------------------------------------------------------------------------
55
56wxANIDecoder::wxANIDecoder()
57{
58}
59
60wxANIDecoder::~wxANIDecoder()
61{
62}
63
870cf35c 64bool wxANIDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
72045d57 65{
870cf35c 66 unsigned int idx = m_info[frame].m_imageIndex;
72045d57 67 *image = m_images[idx]; // copy
9d9e3858 68 return image->IsOk();
72045d57
VZ
69}
70
71
72//---------------------------------------------------------------------------
73// Data accessors
74//---------------------------------------------------------------------------
75
870cf35c 76wxSize wxANIDecoder::GetFrameSize(unsigned int WXUNUSED(frame)) const
72045d57
VZ
77{
78 // all frames are of the same size...
79 return m_szAnimation;
80}
81
870cf35c 82wxPoint wxANIDecoder::GetFramePosition(unsigned int WXUNUSED(frame)) const
72045d57
VZ
83{
84 // all frames are of the same size...
85 return wxPoint(0,0);
86}
87
870cf35c 88wxAnimationDisposal wxANIDecoder::GetDisposalMethod(unsigned int WXUNUSED(frame)) const
72045d57
VZ
89{
90 // this disposal is implicit for all frames inside an ANI file
91 return wxANIM_TOBACKGROUND;
92}
93
870cf35c 94long wxANIDecoder::GetDelay(unsigned int frame) const
72045d57
VZ
95{
96 return m_info[frame].m_delay;
97}
98
870cf35c 99wxColour wxANIDecoder::GetTransparentColour(unsigned int frame) const
05a98b6d 100{
870cf35c 101 unsigned int idx = m_info[frame].m_imageIndex;
05a98b6d
RR
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
72045d57
VZ
111
112//---------------------------------------------------------------------------
113// ANI reading and decoding
114//---------------------------------------------------------------------------
115
8faef7cc 116bool wxANIDecoder::DoCanRead(wxInputStream& stream) const
72045d57
VZ
117{
118 wxInt32 FCC1, FCC2;
8faef7cc 119 wxUint32 datalen;
72045d57
VZ
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
72045d57
VZ
130 if ( !stream.Read(&FCC1, 4) )
131 return false;
132
133 if ( FCC1 != riff32 )
134 return false;
135
136 // we have a riff file:
137 while ( stream.IsOk() )
138 {
139 if ( FCC1 == anih32 )
140 return true; // found the ANIH chunk - this should be an ANI file
141
142 // we always have a data size:
143 stream.Read(&datalen, 4);
144 datalen = wxINT32_SWAP_ON_BE(datalen) ;
9d9e3858 145
72045d57
VZ
146 // data should be padded to make even number of bytes
147 if (datalen % 2 == 1) datalen ++ ;
148
149 // now either data or a FCC:
150 if ( (FCC1 == riff32) || (FCC1 == list32) )
151 {
152 stream.Read(&FCC2, 4);
153 }
154 else
155 {
b81e4506
FM
156 if ( stream.SeekI(stream.TellI() + datalen) == wxInvalidOffset )
157 return false;
72045d57
VZ
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
9d9e3858 172struct wxANIHeader
72045d57
VZ
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)
ce7efe2d
VZ
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
72045d57
VZ
202};
203
204bool wxANIDecoder::Load( wxInputStream& stream )
205{
206 wxInt32 FCC1, FCC2;
207 wxUint32 datalen;
870cf35c 208 unsigned int globaldelay=0;
72045d57
VZ
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
b81e4506
FM
223 if ( !stream.Read(&FCC1, 4) )
224 return false;
72045d57
VZ
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:
604898dc 235 while ( !stream.Eof() )
72045d57
VZ
236 {
237 // we always have a data size:
604898dc
FM
238 if (!stream.Read(&datalen, 4))
239 return false;
240
72045d57
VZ
241 datalen = wxINT32_SWAP_ON_BE(datalen);
242
243 //data should be padded to make even number of bytes
244 if (datalen % 2 == 1) datalen++;
245
246 // now either data or a FCC:
247 if ( (FCC1 == riff32) || (FCC1 == list32) )
248 {
604898dc
FM
249 if (!stream.Read(&FCC2, 4))
250 return false;
72045d57
VZ
251 }
252 else if ( FCC1 == anih32 )
253 {
254 if ( datalen != sizeof(wxANIHeader) )
255 return false;
256
257 if (m_nFrames > 0)
258 return false; // already parsed an ani header?
259
260 struct wxANIHeader header;
604898dc
FM
261 if (!stream.Read(&header, sizeof(wxANIHeader)))
262 return false;
ce7efe2d 263 header.AdjustEndianness();
72045d57
VZ
264
265 // we should have a global frame size
266 m_szAnimation = wxSize(header.cx, header.cy);
267
268 // save interesting info from the header
269 m_nFrames = header.cSteps; // NB: not cFrames!!
9d9e3858 270 if ( m_nFrames == 0 )
72045d57
VZ
271 return false;
272
ce7efe2d 273 globaldelay = header.JifRate * 1000 / 60;
72045d57
VZ
274
275 m_images.Alloc(header.cFrames);
276 m_info.Add(wxANIFrameInfo(), m_nFrames);
277 }
278 else if ( FCC1 == rate32 )
279 {
280 // did we already process the anih32 chunk?
281 if (m_nFrames == 0)
282 return false; // rate chunks should always be placed after anih chunk
9d9e3858 283
72045d57 284 wxASSERT(m_info.GetCount() == m_nFrames);
870cf35c 285 for (unsigned int i=0; i<m_nFrames; i++)
72045d57 286 {
604898dc
FM
287 if (!stream.Read(&FCC2, 4))
288 return false;
72045d57
VZ
289 m_info[i].m_delay = wxINT32_SWAP_ON_BE(FCC2) * 1000 / 60;
290 }
291 }
292 else if ( FCC1 == seq32 )
293 {
294 // did we already process the anih32 chunk?
295 if (m_nFrames == 0)
296 return false; // seq chunks should always be placed after anih chunk
297
298 wxASSERT(m_info.GetCount() == m_nFrames);
870cf35c 299 for (unsigned int i=0; i<m_nFrames; i++)
72045d57 300 {
604898dc
FM
301 if (!stream.Read(&FCC2, 4))
302 return false;
72045d57
VZ
303 m_info[i].m_imageIndex = wxINT32_SWAP_ON_BE(FCC2);
304 }
305 }
306 else if ( FCC1 == ico32 )
307 {
308 // use DoLoadFile() and not LoadFile()!
309 wxImage image;
310 if (!sm_handler.DoLoadFile(&image, stream, false /* verbose */, -1))
311 return false;
312
9d1c7e84 313 image.SetType(wxBITMAP_TYPE_ANI);
72045d57
VZ
314 m_images.Add(image);
315 }
316 else
ce7efe2d 317 {
b81e4506
FM
318 if ( stream.SeekI(stream.TellI() + datalen) == wxInvalidOffset )
319 return false;
ce7efe2d 320 }
72045d57
VZ
321
322 // try to read next data chunk:
604898dc
FM
323 if ( !stream.Read(&FCC1, 4) && !stream.Eof())
324 {
325 // we didn't reach the EOF! An other kind of error has occurred...
b81e4506 326 return false;
604898dc
FM
327 }
328 //else: proceed with the parsing of the next header block or
329 // exiting this loop (if stream.Eof() == true)
72045d57
VZ
330 }
331
332 if (m_nFrames==0)
333 return false;
334
335 if (m_nFrames==m_images.GetCount())
336 {
337 // if no SEQ chunk is available, display the frames in the order
338 // they were loaded
870cf35c 339 for (unsigned int i=0; i<m_nFrames; i++)
72045d57
VZ
340 if (m_info[i].m_imageIndex == -1)
341 m_info[i].m_imageIndex = i;
342 }
343
344 // if some frame has an invalid delay, use the global delay given in the
345 // ANI header
870cf35c 346 for (unsigned int i=0; i<m_nFrames; i++)
72045d57
VZ
347 if (m_info[i].m_delay == 0)
348 m_info[i].m_delay = globaldelay;
349
350 // if the header did not contain a valid frame size, try to grab
351 // it from the size of the first frame (all frames are of the same size)
352 if (m_szAnimation.GetWidth() == 0 ||
353 m_szAnimation.GetHeight() == 0)
354 m_szAnimation = wxSize(m_images[0].GetWidth(), m_images[0].GetHeight());
355
14c0d834 356 return m_szAnimation != wxDefaultSize;
72045d57
VZ
357}
358
d18868bb 359#endif // wxUSE_STREAMS && wxUSE_ICO_CUR