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