]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/animate/animate.cpp
Don't use a saved label size incase the size changes. Patch from Hong Yuan.
[wxWidgets.git] / contrib / src / animate / animate.cpp
CommitLineData
4638d697
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: animate.cpp
3// Purpose: Implementation of wxAnimation classes
4// Author: Julian Smart and Guillermo Rodriguez Garcia
5// Modified by:
6// Created: 13/8/99
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Guillermo Rodriguez Garcia
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
4638d697
JS
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif //__BORLANDC__
17
426195f9 18#include "wx/log.h"
4638d697
JS
19#include "wx/wfstream.h"
20#include "wx/image.h"
21#include "wx/gifdecod.h"
cd2a4e16 22#include "wx/dcmemory.h"
10217444
VS
23#include "wx/dc.h"
24#include "wx/dcclient.h"
426195f9 25#include "wx/animate/animate.h"
4638d697
JS
26
27/*
28 * wxAnimationPlayer
29 */
30
31IMPLEMENT_CLASS(wxAnimationPlayer, wxObject)
32
33wxAnimationPlayer::wxAnimationPlayer(wxAnimationBase *animation, bool destroyAnimation)
34{
35 m_animation = animation;
36 m_destroyAnimation = destroyAnimation;
426195f9 37 m_customBackgroundColour = wxColour(0, 0, 0);
4638d697
JS
38 m_currentFrame = 0;
39 m_window = (wxWindow*) NULL;
40 m_position = wxPoint(0, 0);
426195f9
DS
41 m_looped = true;
42 m_isPlaying = false;
43 m_useBackgroundColour = false;
44 m_useCustomBackgroundColour = false;
45 m_useParentBackground = false;
4638d697
JS
46 m_timer.SetPlayer(this);
47}
48
49wxAnimationPlayer::~wxAnimationPlayer()
50{
51 Stop();
52 ClearCache();
53
54 if (m_destroyAnimation)
55 delete m_animation;
56}
57
58void wxAnimationPlayer::SetAnimation(wxAnimationBase* animation, bool destroyAnimation)
59{
60 ClearCache();
61 if (m_destroyAnimation)
62 delete m_animation;
63 m_animation = animation;
64 m_destroyAnimation = destroyAnimation;
65}
66
67// Play
87728739 68bool wxAnimationPlayer::Play(wxWindow& window, const wxPoint& pos, bool WXUNUSED(looped))
4638d697
JS
69{
70 m_window = & window;
71
72 if (!m_animation || !m_animation->IsValid())
426195f9 73 return false;
4638d697
JS
74
75 wxSize sz = GetLogicalScreenSize();
76 wxRect rect(pos, sz);
77 SaveBackground(rect);
78
10217444 79 if (m_frames.GetCount() == 0)
4638d697
JS
80 {
81 if (!Build())
82 {
10217444 83 wxLogWarning(_T("wxAnimationPlayer::Play: could not build the image cache."));
426195f9
DS
84
85 return false;
4638d697
JS
86 }
87 }
426195f9 88
4638d697
JS
89 m_currentFrame = 0;
90
91 // Create the backing store
92 m_backingStore.Create(sz.x, sz.y);
93
94 PlayFrame();
95
426195f9 96 return true;
4638d697
JS
97}
98
99// Build animation (list of wxImages). If not called before Play
100// is called, Play will call this automatically.
101bool wxAnimationPlayer::Build()
102{
103 ClearCache();
426195f9
DS
104 if (!m_animation)
105 return false;
106
107 int i, n;
108
109 n = GetFrameCount();
110 for (i = 0; i < n; i++)
4638d697 111 {
426195f9
DS
112 wxImage* image = GetFrame(i);
113 if (image == NULL)
114 return false;
115
116 // If the frame has transparency,
117 // set the colour so converting to a bitmap
118 // will create a mask
119 wxColour transparentColour;
120 if (GetTransparentColour(transparentColour))
121 image->SetMaskColour(transparentColour.Red(), transparentColour.Green(), transparentColour.Blue());
122
123 wxBitmap* bitmap = new wxBitmap(*image);
124 delete image;
125 if (bitmap == NULL)
126 return false;
127
128 m_frames.Append(bitmap);
4638d697 129 }
426195f9
DS
130
131 return true;
4638d697
JS
132}
133
134// Stop the animation
135void wxAnimationPlayer::Stop()
136{
137 m_timer.Stop();
426195f9 138 m_isPlaying = false;
4638d697
JS
139}
140
141// Draw the current view of the animation into this DC.
142// Call this from your OnPaint, for example.
143void wxAnimationPlayer::Draw(wxDC& dc)
144{
145 dc.DrawBitmap(m_backingStore, m_position.x, m_position.y);
146}
147
4638d697
JS
148int wxAnimationPlayer::GetFrameCount() const
149{
150 if (m_animation)
151 return m_animation->GetFrameCount();
152 else
153 return 0;
154}
155
156wxImage* wxAnimationPlayer::GetFrame(int i) const
157{
158 if (m_animation)
159 return m_animation->GetFrame(i);
160 else
161 return (wxImage*) NULL;
162}
163
164wxAnimationDisposal wxAnimationPlayer::GetDisposalMethod(int i) const
165{
166 if (m_animation)
167 return m_animation->GetDisposalMethod(i);
168 else
169 return wxANIM_UNSPECIFIED;
170}
171
172wxRect wxAnimationPlayer::GetFrameRect(int i) const
173{
174 if (m_animation)
175 return m_animation->GetFrameRect(i);
176 else
177 return wxRect(0, 0, 0, 0);
178}
179
180int wxAnimationPlayer::GetDelay(int i) const
181{
182 if (m_animation)
183 return m_animation->GetDelay(i);
184 else
185 return 0;
186}
187
188wxSize wxAnimationPlayer::GetLogicalScreenSize() const
189{
190 if (m_animation)
191 return m_animation->GetLogicalScreenSize();
192 else
193 return wxSize(0, 0);
194}
195
196bool wxAnimationPlayer::GetBackgroundColour(wxColour& col) const
197{
198 if (m_animation)
199 return m_animation->GetBackgroundColour(col);
200 else
426195f9 201 return false;
4638d697
JS
202}
203
204bool wxAnimationPlayer::GetTransparentColour(wxColour& col) const
205{
206 if (m_animation)
207 return m_animation->GetTransparentColour(col);
208 else
426195f9 209 return false;
4638d697
JS
210}
211
212// Play the frame
87728739 213bool wxAnimationPlayer::PlayFrame(int frame, wxWindow& window, const wxPoint& WXUNUSED(pos))
4638d697
JS
214{
215 wxMemoryDC dc;
216 dc.SelectObject(m_backingStore);
217
218 // Draw the background: colour or area beneath animation
219 wxColour col(255, 255, 255);
220
221 if (UsingBackgroundColour())
222 {
223 if (UsingCustomBackgroundColour())
224 col = GetCustomBackgroundColour();
225 else
4638d697 226 GetBackgroundColour(col);
4638d697
JS
227
228 // Draw the background colour loaded from the animation
229 // (or set by the user)
230 DrawBackground(dc, wxPoint(0, 0), col);
231 }
232 else
233 {
234 // Draw background we saved
235 dc.DrawBitmap(m_savedBackground, 0, 0);
236 }
237
426195f9 238 // Draw all intermediate frames that haven't been removed from the animation
4638d697 239 int i;
6b5403c8 240 for (i = 0; i < frame; i++)
4638d697
JS
241 {
242 if ((GetDisposalMethod(i) == wxANIM_DONOTREMOVE) || (GetDisposalMethod(i) == wxANIM_UNSPECIFIED))
4638d697 243 DrawFrame(i, dc, wxPoint(0, 0));
4638d697 244 }
426195f9 245
4638d697
JS
246 DrawFrame(frame, dc, wxPoint(0, 0));
247
248 dc.SelectObject(wxNullBitmap);
249
250 // Draw from backing bitmap onto window
251 wxClientDC clientDC(& window);
252 Draw(clientDC);
253
426195f9 254 return true;
4638d697
JS
255}
256
257bool wxAnimationPlayer::PlayFrame()
258{
426195f9 259 m_isPlaying = true;
4638d697
JS
260
261 PlayFrame(GetCurrentFrame(), * GetWindow(), GetPosition());
262
263 // Set the timer for the next frame
1c5f3f51
RD
264 int delay = GetDelay(GetCurrentFrame());
265 if (delay == 0)
266 delay = 1; // 0 is invalid timeout for wxTimer.
267
268 m_timer.Start(delay);
4638d697
JS
269
270 m_currentFrame ++;
271
272 if (m_currentFrame == GetFrameCount())
273 {
274 // Should a non-looped animation display the last frame?
275 if (!m_looped)
276 {
277 m_timer.Stop();
426195f9 278 m_isPlaying = false;
4638d697
JS
279 }
280 else
281 m_currentFrame = 0;
282 }
283
426195f9 284 return true;
4638d697
JS
285}
286
287// Clear the wxImage cache
288void wxAnimationPlayer::ClearCache()
289{
9c592b61 290 wxList::compatibility_iterator node = m_frames.GetFirst();
4638d697
JS
291 while (node)
292 {
9c592b61 293 wxList::compatibility_iterator next = node->GetNext();
10217444 294 wxBitmap* bitmap = (wxBitmap*) node->GetData();
4638d697 295 delete bitmap;
9c592b61 296 m_frames.Erase(node);
426195f9 297
4638d697
JS
298 node = next;
299 }
300}
301
302// Draw the background colour
303void wxAnimationPlayer::DrawBackground(wxDC& dc, const wxPoint& pos, const wxColour& colour)
304{
10217444 305 wxASSERT_MSG( (m_animation != NULL), _T("Animation not present in wxAnimationPlayer"));
426195f9 306 wxASSERT_MSG( (m_frames.GetCount() > 0), _T("Animation cache not present in wxAnimationPlayer"));
4638d697
JS
307
308 // Optimization: if the first frame fills the whole area, and is non-transparent,
309 // don't bother drawing the background
310
10217444 311 wxBitmap* firstBitmap = (wxBitmap*) m_frames.GetFirst()->GetData() ;
4638d697
JS
312 wxSize screenSize = GetLogicalScreenSize();
313 if (!firstBitmap->GetMask() && (firstBitmap->GetWidth() == screenSize.x) && (firstBitmap->GetHeight() == screenSize.y))
4638d697 314 return;
4638d697
JS
315
316 wxBrush brush(colour, wxSOLID);
317 wxPen pen(colour, 1, wxSOLID);
318 dc.SetBrush(brush);
319 dc.SetPen(pen);
320 dc.SetLogicalFunction(wxCOPY);
321
322 dc.DrawRectangle(pos.x, pos.y, screenSize.x, screenSize.y);
323}
324
325// Save the pertinent area of the window so we can restore
326// it if drawing transparently
327void wxAnimationPlayer::SaveBackground(const wxRect& rect)
328{
426195f9 329 wxASSERT( (GetWindow() != NULL) );
4638d697
JS
330
331 if (!GetWindow())
332 return;
333
334 m_savedBackground.Create(rect.width, rect.height);
335
336 wxMemoryDC memDC;
337 memDC.SelectObject(m_savedBackground);
338
339 if (m_useParentBackground && GetWindow()->GetParent())
340 {
341 wxWindow* parent = GetWindow()->GetParent();
342 wxClientDC dc(parent);
343
344 // Translate the point to coordinates in the
345 // parent's client area, going via screen coordinates
346 wxPoint pt(rect.x, rect.y);
347 wxPoint screenPt = GetWindow()->ClientToScreen(pt);
348 wxPoint parentPt = parent->ScreenToClient(screenPt);
349
350 memDC.Blit(0, 0, rect.width, rect.height, & dc, parentPt.x, parentPt.y);
351 }
352 else
353 {
354 wxClientDC dc(GetWindow());
355
356 memDC.Blit(0, 0, rect.width, rect.height, & dc, rect.x, rect.y);
357 }
426195f9 358
4638d697
JS
359 memDC.SelectObject(wxNullBitmap);
360}
361
362// Draw this frame
363void wxAnimationPlayer::DrawFrame(int frame, wxDC& dc, const wxPoint& pos)
364{
10217444
VS
365 wxASSERT_MSG( (m_animation != NULL), _T("Animation not present in wxAnimationPlayer"));
366 wxASSERT_MSG( (m_frames.GetCount() != 0), _T("Animation cache not present in wxAnimationPlayer"));
9c592b61 367 wxASSERT_MSG( !!m_frames.Item(frame), _T("Image not present in wxAnimationPlayer::DrawFrame"));
4638d697 368
10217444 369 wxBitmap* bitmap = (wxBitmap*) m_frames.Item(frame)->GetData() ;
4638d697
JS
370 wxRect rect = GetFrameRect(frame);
371
426195f9 372 dc.DrawBitmap(*bitmap, pos.x + rect.x, pos.y + rect.y, (bitmap->GetMask() != NULL));
4638d697
JS
373}
374
375void wxAnimationTimer::Notify()
376{
377 m_player->PlayFrame();
378}
379
380/*
381 * wxAnimationBase
382 */
383
384IMPLEMENT_ABSTRACT_CLASS(wxAnimationBase, wxObject)
385
386/*
387 * wxGIFAnimation
388 */
389
390IMPLEMENT_CLASS(wxGIFAnimation, wxAnimationBase)
391
392wxGIFAnimation::wxGIFAnimation()
393{
394 m_decoder = (wxGIFDecoder*) NULL;
395}
396
397wxGIFAnimation::~wxGIFAnimation()
398{
399 delete m_decoder;
400}
401
402int wxGIFAnimation::GetFrameCount() const
403{
10217444 404 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697
JS
405
406 return m_decoder->GetNumberOfFrames();
407}
408
409wxImage* wxGIFAnimation::GetFrame(int i) const
410{
10217444 411 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697 412
6b5403c8 413 m_decoder->GoFrame(i + 1);
4638d697
JS
414
415 wxImage* image = new wxImage;
416 m_decoder->ConvertToImage(image);
426195f9 417
4638d697
JS
418 return image;
419}
420
421wxAnimationDisposal wxGIFAnimation::GetDisposalMethod(int i) const
422{
10217444 423 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697 424
6b5403c8 425 m_decoder->GoFrame(i + 1);
4638d697
JS
426
427 int disposalMethod = m_decoder->GetDisposalMethod();
428 return (wxAnimationDisposal) disposalMethod;
429}
430
431wxRect wxGIFAnimation::GetFrameRect(int i) const
432{
10217444 433 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697 434
6b5403c8 435 m_decoder->GoFrame(i + 1);
4638d697
JS
436
437 wxRect rect(m_decoder->GetLeft(), m_decoder->GetTop(), m_decoder->GetWidth(), m_decoder->GetHeight());
426195f9 438
4638d697
JS
439 return rect;
440}
441
442int wxGIFAnimation::GetDelay(int i) const
443{
10217444 444 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697 445
6b5403c8 446 m_decoder->GoFrame(i + 1);
426195f9 447
4638d697
JS
448 return m_decoder->GetDelay();
449}
450
451wxSize wxGIFAnimation::GetLogicalScreenSize() const
452{
10217444 453 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697
JS
454
455 return wxSize(m_decoder->GetLogicalScreenWidth(), m_decoder->GetLogicalScreenHeight());
456}
457
458bool wxGIFAnimation::GetBackgroundColour(wxColour& col) const
459{
10217444 460 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697
JS
461
462 int i = m_decoder->GetBackgroundColour();
463 if (i == -1)
426195f9 464 return false;
4638d697 465
426195f9
DS
466 const unsigned char *pal = m_decoder->GetPalette();
467 bool result = (pal != NULL);
468
469 if (result)
470 col = wxColour(pal[3*i + 0], pal[3*i + 1], pal[3*i + 2]);
471
472 return result;
4638d697
JS
473}
474
475bool wxGIFAnimation::GetTransparentColour(wxColour& col) const
476{
10217444 477 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
4638d697
JS
478
479 int i = m_decoder->GetTransparentColour();
480 if (i == -1)
426195f9 481 return false;
4638d697 482
426195f9
DS
483 const unsigned char *pal = m_decoder->GetPalette();
484 bool result = (pal != NULL);
485
486 if (result)
487 col = wxColour(pal[3*i + 0], pal[3*i + 1], pal[3*i + 2]);
488
489 return result;
4638d697
JS
490}
491
492bool wxGIFAnimation::IsValid() const
493{
494 return ((m_decoder != NULL) && (m_decoder->IsAnimation()));
495}
496
497bool wxGIFAnimation::LoadFile(const wxString& filename)
498{
426195f9
DS
499 if (!wxFileExists(filename))
500 return false;
501
502 bool result = true;
503
4638d697 504 if (m_decoder)
426195f9 505 {
4638d697 506 delete m_decoder;
426195f9
DS
507 m_decoder = NULL;
508 }
4638d697 509
4638d697
JS
510 {
511 wxFileInputStream stream(filename);
4638d697 512
426195f9
DS
513 if (stream.GetLength() > 0)
514 m_decoder = new wxGIFDecoder(&stream, true);
4638d697 515
426195f9
DS
516 result = ((m_decoder != NULL) && (m_decoder->ReadGIF() == wxGIF_OK));
517 if (result)
518 result = m_decoder->IsAnimation();
519 }
4638d697 520
426195f9
DS
521 if (!result && (m_decoder != NULL))
522 {
523 delete m_decoder;
524 m_decoder = NULL;
4638d697 525 }
426195f9
DS
526
527 return result;
4638d697
JS
528}
529
530/*
531 * wxAnimationCtrlBase
532 * Abstract base class for format-specific animation controls.
533 * This class implements most of the functionality; all a derived
534 * class has to do is create the appropriate animation class on demand.
535 */
536
537IMPLEMENT_ABSTRACT_CLASS(wxAnimationCtrlBase, wxControl)
538
539BEGIN_EVENT_TABLE(wxAnimationCtrlBase, wxControl)
540 EVT_PAINT(wxAnimationCtrlBase::OnPaint)
541END_EVENT_TABLE()
542
543bool wxAnimationCtrlBase::Create(wxWindow *parent, wxWindowID id,
544 const wxString& filename, const wxPoint& pos,
545 const wxSize& size, long style, const wxString& name)
546{
547 m_animation = NULL;
548 m_filename = filename;
549
550 if (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name))
426195f9 551 return false;
4638d697
JS
552
553 SetBackgroundColour(parent->GetBackgroundColour());
554
555 m_animationPlayer.SetCustomBackgroundColour(GetBackgroundColour());
556
426195f9
DS
557 // give the impression of transparency by painting
558 // with the parent background
4638d697 559// if (parent)
426195f9
DS
560// m_animationPlayer.UseParentBackground(true);
561
4638d697
JS
562 m_animationPlayer.SetWindow(this);
563 m_animationPlayer.SetPosition(wxPoint(0, 0));
426195f9 564 m_animationPlayer.SetDestroyAnimation(false);
4638d697 565
770ddb3f
RD
566 LoadFile(filename);
567
426195f9 568 return true;
4638d697
JS
569}
570
571wxAnimationCtrlBase::~wxAnimationCtrlBase()
572{
573 if (m_animationPlayer.IsPlaying())
574 m_animationPlayer.Stop();
426195f9
DS
575 m_animationPlayer.SetAnimation(NULL, false);
576
4638d697
JS
577 delete m_animation;
578}
579
580bool wxAnimationCtrlBase::LoadFile(const wxString& filename)
581{
582 if (m_animationPlayer.IsPlaying())
583 m_animationPlayer.Stop();
584
585 wxString filename1(filename);
586
587 if (filename1.IsEmpty())
426195f9 588 {
4638d697 589 filename1 = m_filename;
426195f9
DS
590 if (filename1.IsEmpty())
591 return false;
592 }
4638d697
JS
593
594 if (m_animation)
595 {
596 delete m_animation;
597 m_animation = NULL;
598 }
599
600 m_animation = DoCreateAnimation(filename1);
601 if (!m_animation)
426195f9 602 return false;
4638d697
JS
603
604 if (!m_animation->LoadFile(filename) || !m_animation->IsValid())
605 {
606 delete m_animation;
607 m_animation = NULL;
426195f9
DS
608
609 return false;
4638d697 610 }
426195f9
DS
611
612 m_animationPlayer.SetAnimation(m_animation, false);
4638d697
JS
613
614 if (GetWindowStyle() & wxAN_FIT_ANIMATION)
615 FitToAnimation();
616
426195f9 617 return true;
4638d697
JS
618}
619
620bool wxAnimationCtrlBase::Play(bool looped)
621{
622 return m_animationPlayer.Play(*this, wxPoint(0, 0), looped);
623}
624
625wxSize wxAnimationCtrlBase::DoGetBestSize() const
626{
627 if (m_animationPlayer.HasAnimation() && (GetWindowStyle() & wxAN_FIT_ANIMATION))
4638d697 628 return m_animationPlayer.GetLogicalScreenSize();
4638d697 629 else
4638d697 630 return GetSize();
4638d697
JS
631}
632
633void wxAnimationCtrlBase::FitToAnimation()
634{
635 if (!m_animationPlayer.HasAnimation())
636 return;
637
638 wxSize sz = m_animationPlayer.GetLogicalScreenSize();
639 SetClientSize(sz);
640}
641
87728739 642void wxAnimationCtrlBase::OnPaint(wxPaintEvent& WXUNUSED(event))
4638d697
JS
643{
644 wxPaintDC dc(this);
645
646 if (GetPlayer().IsPlaying())
4638d697 647 GetPlayer().Draw(dc);
4638d697
JS
648}
649
650/*
651 * wxGIFAnimationCtrl
652 * Provides a GIF animation class when required.
653 */
654
655IMPLEMENT_ABSTRACT_CLASS(wxGIFAnimationCtrl, wxAnimationCtrlBase)
656
657wxAnimationBase* wxGIFAnimationCtrl::DoCreateAnimation(const wxString& WXUNUSED(filename))
658{
659 return new wxGIFAnimation;
660}
426195f9 661