]> git.saurik.com Git - wxWidgets.git/blame - src/generic/animateg.cpp
getting the wxWindow* on GTK
[wxWidgets.git] / src / generic / animateg.cpp
CommitLineData
72045d57
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: animateg.cpp
3// Purpose: wxAnimation and wxAnimationCtrl
4// Author: Julian Smart and Guillermo Rodriguez Garcia
5// Modified by: Francesco Montorsi
6// Created: 13/8/99
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Guillermo Rodriguez Garcia
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif //__BORLANDC__
17
72045d57
VZ
18#if wxUSE_ANIMATIONCTRL
19
c2f12218
PC
20#include "wx/animate.h"
21
22#ifndef WX_PRECOMP
23 #include "wx/log.h"
24 #include "wx/image.h"
25 #include "wx/dcmemory.h"
26 #include "wx/dcclient.h"
27 #include "wx/module.h"
28#endif
29
72045d57 30#include "wx/wfstream.h"
72045d57
VZ
31#include "wx/gifdecod.h"
32#include "wx/anidecod.h"
72045d57 33
c2f12218 34#include "wx/listimpl.cpp"
14c0d834 35WX_DEFINE_LIST(wxAnimationDecoderList)
72045d57
VZ
36
37wxAnimationDecoderList wxAnimation::sm_handlers;
38
39
72045d57
VZ
40// ----------------------------------------------------------------------------
41// wxAnimation
42// ----------------------------------------------------------------------------
43
44IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
45#define M_ANIMDATA wx_static_cast(wxAnimationDecoder*, m_refData)
46
47wxSize wxAnimation::GetSize() const
48{
49 wxCHECK_MSG( IsOk(), wxDefaultSize, wxT("invalid animation") );
50
51 return M_ANIMDATA->GetAnimationSize();
52}
53
54size_t wxAnimation::GetFrameCount() const
55{
56 wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
57
58 return M_ANIMDATA->GetFrameCount();
59}
60
61wxImage wxAnimation::GetFrame(size_t i) const
62{
63 wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid animation") );
64
65 wxImage ret;
66 if (!M_ANIMDATA->ConvertToImage(i, &ret))
67 return wxNullImage;
68 return ret;
69}
70
71int wxAnimation::GetDelay(size_t i) const
72{
73 wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
74
75 return M_ANIMDATA->GetDelay(i);
76}
77
78wxPoint wxAnimation::GetFramePosition(size_t frame) const
79{
80 wxCHECK_MSG( IsOk(), wxDefaultPosition, wxT("invalid animation") );
81
82 return M_ANIMDATA->GetFramePosition(frame);
83}
84
85wxAnimationDisposal wxAnimation::GetDisposalMethod(size_t frame) const
86{
87 wxCHECK_MSG( IsOk(), wxANIM_UNSPECIFIED, wxT("invalid animation") );
88
89 return M_ANIMDATA->GetDisposalMethod(frame);
90}
91
92wxColour wxAnimation::GetBackgroundColour() const
93{
94 wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") );
95
96 return M_ANIMDATA->GetBackgroundColour();
97}
98
99bool wxAnimation::LoadFile(const wxString& filename, wxAnimationType type)
100{
101 wxFileInputStream stream(filename);
9d9e3858 102 if ( !stream.IsOk() )
72045d57
VZ
103 return false;
104
105 return Load(stream, type);
106}
107
108bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
109{
110 UnRef();
111
112 const wxAnimationDecoder *handler;
113 if ( type == wxANIMATION_TYPE_ANY )
114 {
9d9e3858 115 for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
72045d57
VZ
116 node; node = node->GetNext() )
117 {
118 handler=(const wxAnimationDecoder*)node->GetData();
119
120 if ( handler->CanRead(stream) )
121 {
122 // do a copy of the handler from the static list which we will own
123 // as our reference data
124 m_refData = handler->Clone();
125 return M_ANIMDATA->Load(stream);
126 }
127
128 }
129
130 wxLogWarning( _("No handler found for animation type.") );
131 return false;
132 }
133
134 handler = FindHandler(type);
135
136 // do a copy of the handler from the static list which we will own
137 // as our reference data
138 m_refData = handler->Clone();
139
140 if (handler == NULL)
141 {
142 wxLogWarning( _("No animation handler for type %ld defined."), type );
143
144 return false;
145 }
146
147 if (stream.IsSeekable() && !M_ANIMDATA->CanRead(stream))
148 {
149 wxLogError(_("Animation file is not of type %ld."), type);
150 return false;
151 }
152 else
153 return M_ANIMDATA->Load(stream);
154}
155
156
157// ----------------------------------------------------------------------------
158// animation decoders
159// ----------------------------------------------------------------------------
160
161void wxAnimation::AddHandler( wxAnimationDecoder *handler )
162{
163 // Check for an existing handler of the type being added.
164 if (FindHandler( handler->GetType() ) == 0)
165 {
166 sm_handlers.Append( handler );
167 }
168 else
169 {
170 // This is not documented behaviour, merely the simplest 'fix'
171 // for preventing duplicate additions. If someone ever has
172 // a good reason to add and remove duplicate handlers (and they
173 // may) we should probably refcount the duplicates.
174 // also an issue in InsertHandler below.
175
176 wxLogDebug( _T("Adding duplicate animation handler for '%d' type"),
177 handler->GetType() );
178 delete handler;
179 }
180}
181
182void wxAnimation::InsertHandler( wxAnimationDecoder *handler )
183{
184 // Check for an existing handler of the type being added.
185 if (FindHandler( handler->GetType() ) == 0)
186 {
187 sm_handlers.Insert( handler );
188 }
189 else
190 {
191 // see AddHandler for additional comments.
192 wxLogDebug( _T("Inserting duplicate animation handler for '%d' type"),
193 handler->GetType() );
194 delete handler;
195 }
196}
197
198const wxAnimationDecoder *wxAnimation::FindHandler( wxAnimationType animType )
199{
200 wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
201 while (node)
202 {
203 const wxAnimationDecoder *handler = (const wxAnimationDecoder *)node->GetData();
204 if (handler->GetType() == animType) return handler;
205 node = node->GetNext();
206 }
207 return 0;
208}
209
210void wxAnimation::InitStandardHandlers()
211{
d18868bb 212#if wxUSE_GIF
72045d57 213 AddHandler(new wxGIFDecoder);
d18868bb
WS
214#endif // wxUSE_GIF
215#if wxUSE_ICO_CUR
72045d57 216 AddHandler(new wxANIDecoder);
d18868bb 217#endif // wxUSE_ICO_CUR
72045d57
VZ
218}
219
220void wxAnimation::CleanUpHandlers()
221{
222 wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
223 while (node)
224 {
225 wxAnimationDecoder *handler = (wxAnimationDecoder *)node->GetData();
226 wxAnimationDecoderList::compatibility_iterator next = node->GetNext();
227 delete handler;
228 node = next;
229 }
230
231 sm_handlers.Clear();
232}
233
234
235// A module to allow wxAnimation initialization/cleanup
236// without calling these functions from app.cpp or from
237// the user's application.
238
239class wxAnimationModule: public wxModule
240{
241DECLARE_DYNAMIC_CLASS(wxAnimationModule)
242public:
243 wxAnimationModule() {}
244 bool OnInit() { wxAnimation::InitStandardHandlers(); return true; };
245 void OnExit() { wxAnimation::CleanUpHandlers(); };
246};
247
248IMPLEMENT_DYNAMIC_CLASS(wxAnimationModule, wxModule)
249
250
72045d57
VZ
251// ----------------------------------------------------------------------------
252// wxAnimationCtrl
253// ----------------------------------------------------------------------------
254
255IMPLEMENT_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
256BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
257 EVT_PAINT(wxAnimationCtrl::OnPaint)
258 EVT_SIZE(wxAnimationCtrl::OnSize)
259 EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
260END_EVENT_TABLE()
261
c2f12218
PC
262wxAnimationCtrl::wxAnimationCtrl()
263{
264 m_currentFrame = 0;
265 m_looped = false;
266 m_isPlaying = false;
267 m_useWinBackgroundColour = false;
268}
269
72045d57
VZ
270bool wxAnimationCtrl::Create(wxWindow *parent, wxWindowID id,
271 const wxAnimation& animation, const wxPoint& pos,
272 const wxSize& size, long style, const wxString& name)
273{
274 m_animation = animation;
275 m_currentFrame = 0;
276 m_looped = true;
277 m_isPlaying = false;
278 m_useWinBackgroundColour = false;
279 m_timer.SetOwner(this);
280
c2f12218 281 if (!base_type::Create(parent, id, pos, size, style, wxDefaultValidator, name))
72045d57
VZ
282 return false;
283
284 // by default we get the same background colour of our parent
285 SetBackgroundColour(parent->GetBackgroundColour());
286 return true;
287}
288
289wxAnimationCtrl::~wxAnimationCtrl()
290{
291 Stop();
292}
293
294bool wxAnimationCtrl::LoadFile(const wxString& filename, wxAnimationType type)
295{
296 wxAnimation anim;
9d9e3858 297 if (!anim.LoadFile(filename, type) ||
72045d57
VZ
298 !anim.IsOk())
299 return false;
300
301 SetAnimation(anim);
302 return true;
303}
304
305wxSize wxAnimationCtrl::DoGetBestSize() const
306{
307 if (m_animation.IsOk() && !this->HasFlag(wxAC_NO_AUTORESIZE))
308 return m_animation.GetSize();
309
310 return wxSize(100, 100);
311}
312
313void wxAnimationCtrl::SetAnimation(const wxAnimation& animation)
314{
315 if (IsPlaying())
316 Stop();
317
318 m_animation = animation;
319
320 if (m_animation.GetBackgroundColour() == wxNullColour)
321 SetUseWindowBackgroundColour();
322 if (!this->HasFlag(wxAC_NO_AUTORESIZE))
323 FitToAnimation();
324
325 // display first frame
326 m_currentFrame = 0;
327 if (m_animation.IsOk())
328 RebuildBackingStoreUpToFrame(0);
329 else
330 {
9d9e3858 331 // clear to
72045d57
VZ
332 wxMemoryDC dc;
333 dc.SelectObject(m_backingStore);
334
335 // Draw the background
336 DisposeToBackground(dc);
337 }
338
339 Refresh();
340}
341
342void wxAnimationCtrl::FitToAnimation()
343{
344 SetSize(m_animation.GetSize());
345}
346
347
348// ----------------------------------------------------------------------------
349// wxAnimationCtrl - stop/play methods
350// ----------------------------------------------------------------------------
351
352void wxAnimationCtrl::Stop()
353{
354 // leave current frame displayed until Play() is called again
355 m_timer.Stop();
356 m_isPlaying = false;
357}
358
359bool wxAnimationCtrl::Play(bool looped)
360{
361 if (!m_animation.IsOk())
362 return false;
363
364 int oldframe = m_currentFrame;
365 m_looped = looped;
366 m_currentFrame = 0;
367 m_isPlaying = true;
368
369 // small optimization: if the back store was already updated to the
370 // first frame, don't rebuild it
371 if (oldframe != 0)
372 RebuildBackingStoreUpToFrame(0);
373
374 // DrawCurrentFrame() will use our updated backing store
375 wxClientDC clientDC(this);
376 DrawCurrentFrame(clientDC);
377
378 // start the timer
379 int delay = m_animation.GetDelay(0);
380 if (delay == 0)
381 delay = 1; // 0 is invalid timeout for wxTimer.
382 m_timer.Start(delay);
383
384 return true;
385}
386
387
388
389// ----------------------------------------------------------------------------
390// wxAnimationCtrl - rendering methods
391// ----------------------------------------------------------------------------
392
393void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame)
394{
395 // if we've not created the backing store yet or it's too
396 // small, then recreate it
397 wxSize sz = m_animation.GetSize(),
398 winsz = GetClientSize();
399 int w = wxMin(sz.GetWidth(), winsz.GetWidth());
400 int h = wxMin(sz.GetHeight(), winsz.GetHeight());
401
9d9e3858 402 if ( !m_backingStore.IsOk() ||
6003de2f
VZ
403 m_backingStore.GetWidth() < w || m_backingStore.GetHeight() < h )
404 {
72045d57 405 m_backingStore.Create(w, h);
6003de2f 406 }
72045d57
VZ
407
408 wxMemoryDC dc;
409 dc.SelectObject(m_backingStore);
410
411 // Draw the background
412 DisposeToBackground(dc);
413
414 // Draw all intermediate frames that haven't been removed from the animation
415 for (size_t i = 0; i < frame; i++)
416 {
417 if (m_animation.GetDisposalMethod(i) == wxANIM_DONOTREMOVE ||
418 m_animation.GetDisposalMethod(i) == wxANIM_UNSPECIFIED)
419 {
420 DrawFrame(dc, i);
421 }
422 }
423
424 // finally draw this frame
425 DrawFrame(dc, frame);
426 dc.SelectObject(wxNullBitmap);
427}
428
429void wxAnimationCtrl::IncrementalUpdateBackingStore()
430{
431 wxMemoryDC dc;
432 dc.SelectObject(m_backingStore);
433
434 // OPTIMIZATION:
435 // since wxAnimationCtrl can only play animations forward, without skipping
436 // frames, we can be sure that m_backingStore contains the m_currentFrame-1
437 // frame and thus we just need to dispose the m_currentFrame-1 frame and
438 // render the m_currentFrame-th one.
439
440 if (m_currentFrame == 0)
441 {
442 // before drawing the first frame always dispose to bg colour
443 DisposeToBackground(dc);
444 }
445 else
446 {
447 switch (m_animation.GetDisposalMethod(m_currentFrame-1))
448 {
449 case wxANIM_TOBACKGROUND:
450 DisposeToBackground(dc);
451 break;
452
453 case wxANIM_TOPREVIOUS:
454 // this disposal should never be used too often.
455 // E.g. GIF specification explicitely say to keep the usage of this
9d9e3858
VZ
456 // disposal limited to the minimum.
457 // In fact it may require a lot of time to restore
72045d57
VZ
458 if (m_currentFrame == 1)
459 {
460 // if 0-th frame disposal is to restore to previous frame,
461 // the best we can do is to restore to background
462 DisposeToBackground(dc);
463 }
464 else
465 RebuildBackingStoreUpToFrame(m_currentFrame-2);
466 break;
467
468 case wxANIM_DONOTREMOVE:
469 case wxANIM_UNSPECIFIED:
470 break;
471 }
472 }
473
474 // now just draw the current frame on the top of the backing store
475 DrawFrame(dc, m_currentFrame);
476 dc.SelectObject(wxNullBitmap);
477}
478
479void wxAnimationCtrl::DrawFrame(wxDC &dc, size_t frame)
480{
481 // PERFORMANCE NOTE:
482 // this draw stuff is not as fast as possible: the wxAnimationDecoder
483 // needs first to convert from its internal format to wxImage RGB24;
484 // the wxImage is then converted as a wxBitmap and finally blitted.
485 // If wxAnimationDecoder had a function to convert directly from its
486 // internal format to a port-specific wxBitmap, it would be somewhat faster.
487 wxBitmap bmp(m_animation.GetFrame(frame));
488 dc.DrawBitmap(bmp, m_animation.GetFramePosition(frame),
489 true /* use mask */);
490}
491
492void wxAnimationCtrl::DrawCurrentFrame(wxDC& dc)
493{
9d9e3858 494 wxASSERT( m_backingStore.IsOk() );
72045d57
VZ
495
496 // m_backingStore always contains the current frame
497 dc.DrawBitmap(m_backingStore, 0, 0);
498}
499
500void wxAnimationCtrl::DisposeToBackground(wxDC& dc)
87d2b3f1
CE
501{
502 wxColour col = IsUsingWindowBackgroundColour()
9d9e3858 503 ? GetBackgroundColour()
87d2b3f1
CE
504 : m_animation.GetBackgroundColour() ;
505 wxBrush brush(col);
72045d57
VZ
506 dc.SetBackground(brush);
507 dc.Clear();
508}
509
510// ----------------------------------------------------------------------------
511// wxAnimationCtrl - event handlers
512// ----------------------------------------------------------------------------
513
514void wxAnimationCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
515{
516 // VERY IMPORTANT: the wxPaintDC *must* be created in any case
517 wxPaintDC dc(this);
518
519 // both if we are playing or not, we need to refresh the current frame
9d9e3858 520 if ( m_backingStore.IsOk() )
72045d57
VZ
521 DrawCurrentFrame(dc);
522 //else: m_animation is not valid and thus we don't have a valid backing store...
523}
524
525void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event))
526{
527 m_currentFrame++;
528 if (m_currentFrame == m_animation.GetFrameCount())
529 {
530 // Should a non-looped animation display the last frame?
531 if (!m_looped)
532 {
533 m_timer.Stop();
534 m_isPlaying = false;
535 return;
536 }
537 else
538 m_currentFrame = 0; // let's restart
539 }
540
541 IncrementalUpdateBackingStore();
542
543 wxClientDC dc(this);
544 DrawCurrentFrame(dc);
545
7444cb50
VZ
546#ifdef __WXMAC__
547 // without this, the animation currently doesn't redraw under Mac
548 Refresh();
549#endif // __WXMAC__
550
72045d57
VZ
551 // Set the timer for the next frame
552 int delay = m_animation.GetDelay(m_currentFrame);
553 if (delay == 0)
554 delay = 1; // 0 is invalid timeout for wxTimer.
555 m_timer.Start(delay);
556}
557
558void wxAnimationCtrl::OnSize(wxSizeEvent &WXUNUSED(event))
559{
560 // NB: resizing an animation control may take a lot of time
561 // for big animations as the backing store must be
562 // extended and rebuilt. Try to avoid it!!
563 if (m_animation.IsOk())
564 RebuildBackingStoreUpToFrame(m_currentFrame);
565}
566
567#endif // wxUSE_ANIMATIONCTRL
568