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