1///////////////////////////////////////////////////////////////////////////// 2// Name: src/common/anidecod.cpp 3// Purpose: wxANIDecoder, ANI reader for wxImage and wxAnimation 4// Author: Francesco Montorsi 5// RCS-ID: $Id: anidecod.cpp 43898 2006-12-10 14:18:37Z VZ $ 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::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 stream.SeekI(0); 131 if ( !stream.Read(&FCC1, 4) ) 132 return false; 133 134 if ( FCC1 != riff32 ) 135 return false; 136 137 // we have a riff file: 138 while ( stream.IsOk() ) 139 { 140 if ( FCC1 == anih32 ) 141 return true; // found the ANIH chunk - this should be an ANI file 142 143 // we always have a data size: 144 stream.Read(&datalen, 4); 145 datalen = wxINT32_SWAP_ON_BE(datalen) ; 146 147 // data should be padded to make even number of bytes 148 if (datalen % 2 == 1) datalen ++ ; 149 150 // now either data or a FCC: 151 if ( (FCC1 == riff32) || (FCC1 == list32) ) 152 { 153 stream.Read(&FCC2, 4); 154 } 155 else 156 { 157 stream.SeekI(stream.TellI() + datalen); 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 172struct wxANIHeader 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) 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 202}; 203 204bool wxANIDecoder::Load( wxInputStream& stream ) 205{ 206 wxInt32 FCC1, FCC2; 207 wxUint32 datalen; 208 unsigned int globaldelay=0; 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 223 stream.SeekI(0); 224 stream.Read(&FCC1, 4); 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: 235 while ( stream.IsOk() ) 236 { 237 // we always have a data size: 238 stream.Read(&datalen, 4); 239 datalen = wxINT32_SWAP_ON_BE(datalen); 240 241 //data should be padded to make even number of bytes 242 if (datalen % 2 == 1) datalen++; 243 244 // now either data or a FCC: 245 if ( (FCC1 == riff32) || (FCC1 == list32) ) 246 { 247 stream.Read(&FCC2, 4); 248 } 249 else if ( FCC1 == anih32 ) 250 { 251 if ( datalen != sizeof(wxANIHeader) ) 252 return false; 253 254 if (m_nFrames > 0) 255 return false; // already parsed an ani header? 256 257 struct wxANIHeader header; 258 stream.Read(&header, sizeof(wxANIHeader)); 259 header.AdjustEndianness(); 260 261 // we should have a global frame size 262 m_szAnimation = wxSize(header.cx, header.cy); 263 264 // save interesting info from the header 265 m_nFrames = header.cSteps; // NB: not cFrames!! 266 if ( m_nFrames == 0 ) 267 return false; 268 269 globaldelay = header.JifRate * 1000 / 60; 270 271 m_images.Alloc(header.cFrames); 272 m_info.Add(wxANIFrameInfo(), m_nFrames); 273 } 274 else if ( FCC1 == rate32 ) 275 { 276 // did we already process the anih32 chunk? 277 if (m_nFrames == 0) 278 return false; // rate chunks should always be placed after anih chunk 279 280 wxASSERT(m_info.GetCount() == m_nFrames); 281 for (unsigned int i=0; i<m_nFrames; i++) 282 { 283 stream.Read(&FCC2, 4); 284 m_info[i].m_delay = wxINT32_SWAP_ON_BE(FCC2) * 1000 / 60; 285 } 286 } 287 else if ( FCC1 == seq32 ) 288 { 289 // did we already process the anih32 chunk? 290 if (m_nFrames == 0) 291 return false; // seq 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 stream.Read(&FCC2, 4); 297 m_info[i].m_imageIndex = wxINT32_SWAP_ON_BE(FCC2); 298 } 299 } 300 else if ( FCC1 == ico32 ) 301 { 302 // use DoLoadFile() and not LoadFile()! 303 wxImage image; 304 if (!sm_handler.DoLoadFile(&image, stream, false /* verbose */, -1)) 305 return false; 306 307 m_images.Add(image); 308 } 309 else 310 { 311 stream.SeekI(stream.TellI() + datalen); 312 } 313 314 // try to read next data chunk: 315 stream.Read(&FCC1, 4); 316 } 317 318 if (m_nFrames==0) 319 return false; 320 321 if (m_nFrames==m_images.GetCount()) 322 { 323 // if no SEQ chunk is available, display the frames in the order 324 // they were loaded 325 for (unsigned int i=0; i<m_nFrames; i++) 326 if (m_info[i].m_imageIndex == -1) 327 m_info[i].m_imageIndex = i; 328 } 329 330 // if some frame has an invalid delay, use the global delay given in the 331 // ANI header 332 for (unsigned int i=0; i<m_nFrames; i++) 333 if (m_info[i].m_delay == 0) 334 m_info[i].m_delay = globaldelay; 335 336 // if the header did not contain a valid frame size, try to grab 337 // it from the size of the first frame (all frames are of the same size) 338 if (m_szAnimation.GetWidth() == 0 || 339 m_szAnimation.GetHeight() == 0) 340 m_szAnimation = wxSize(m_images[0].GetWidth(), m_images[0].GetHeight()); 341 342 return m_szAnimation != wxDefaultSize; 343} 344 345#endif // wxUSE_STREAMS && wxUSE_ICO_CUR 346