]> git.saurik.com Git - wxWidgets.git/blob - 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
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
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif //__BORLANDC__
17
18 #include "wx/log.h"
19 #include "wx/wfstream.h"
20 #include "wx/image.h"
21 #include "wx/gifdecod.h"
22 #include "wx/dcmemory.h"
23 #include "wx/dc.h"
24 #include "wx/dcclient.h"
25 #include "wx/animate/animate.h"
26
27 /*
28 * wxAnimationPlayer
29 */
30
31 IMPLEMENT_CLASS(wxAnimationPlayer, wxObject)
32
33 wxAnimationPlayer::wxAnimationPlayer(wxAnimationBase *animation, bool destroyAnimation)
34 {
35 m_animation = animation;
36 m_destroyAnimation = destroyAnimation;
37 m_customBackgroundColour = wxColour(0, 0, 0);
38 m_currentFrame = 0;
39 m_window = (wxWindow*) NULL;
40 m_position = wxPoint(0, 0);
41 m_looped = true;
42 m_isPlaying = false;
43 m_useBackgroundColour = false;
44 m_useCustomBackgroundColour = false;
45 m_useParentBackground = false;
46 m_timer.SetPlayer(this);
47 }
48
49 wxAnimationPlayer::~wxAnimationPlayer()
50 {
51 Stop();
52 ClearCache();
53
54 if (m_destroyAnimation)
55 delete m_animation;
56 }
57
58 void 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
68 bool wxAnimationPlayer::Play(wxWindow& window, const wxPoint& pos, bool WXUNUSED(looped))
69 {
70 m_window = & window;
71
72 if (!m_animation || !m_animation->IsValid())
73 return false;
74
75 wxSize sz = GetLogicalScreenSize();
76 wxRect rect(pos, sz);
77 SaveBackground(rect);
78
79 if (m_frames.GetCount() == 0)
80 {
81 if (!Build())
82 {
83 wxLogWarning(_T("wxAnimationPlayer::Play: could not build the image cache."));
84
85 return false;
86 }
87 }
88
89 m_currentFrame = 0;
90
91 // Create the backing store
92 m_backingStore.Create(sz.x, sz.y);
93
94 PlayFrame();
95
96 return true;
97 }
98
99 // Build animation (list of wxImages). If not called before Play
100 // is called, Play will call this automatically.
101 bool wxAnimationPlayer::Build()
102 {
103 ClearCache();
104 if (!m_animation)
105 return false;
106
107 int i, n;
108
109 n = GetFrameCount();
110 for (i = 0; i < n; i++)
111 {
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);
129 }
130
131 return true;
132 }
133
134 // Stop the animation
135 void wxAnimationPlayer::Stop()
136 {
137 m_timer.Stop();
138 m_isPlaying = false;
139 }
140
141 // Draw the current view of the animation into this DC.
142 // Call this from your OnPaint, for example.
143 void wxAnimationPlayer::Draw(wxDC& dc)
144 {
145 dc.DrawBitmap(m_backingStore, m_position.x, m_position.y);
146 }
147
148 int wxAnimationPlayer::GetFrameCount() const
149 {
150 if (m_animation)
151 return m_animation->GetFrameCount();
152 else
153 return 0;
154 }
155
156 wxImage* wxAnimationPlayer::GetFrame(int i) const
157 {
158 if (m_animation)
159 return m_animation->GetFrame(i);
160 else
161 return (wxImage*) NULL;
162 }
163
164 wxAnimationDisposal wxAnimationPlayer::GetDisposalMethod(int i) const
165 {
166 if (m_animation)
167 return m_animation->GetDisposalMethod(i);
168 else
169 return wxANIM_UNSPECIFIED;
170 }
171
172 wxRect 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
180 int wxAnimationPlayer::GetDelay(int i) const
181 {
182 if (m_animation)
183 return m_animation->GetDelay(i);
184 else
185 return 0;
186 }
187
188 wxSize wxAnimationPlayer::GetLogicalScreenSize() const
189 {
190 if (m_animation)
191 return m_animation->GetLogicalScreenSize();
192 else
193 return wxSize(0, 0);
194 }
195
196 bool wxAnimationPlayer::GetBackgroundColour(wxColour& col) const
197 {
198 if (m_animation)
199 return m_animation->GetBackgroundColour(col);
200 else
201 return false;
202 }
203
204 bool wxAnimationPlayer::GetTransparentColour(wxColour& col) const
205 {
206 if (m_animation)
207 return m_animation->GetTransparentColour(col);
208 else
209 return false;
210 }
211
212 // Play the frame
213 bool wxAnimationPlayer::PlayFrame(int frame, wxWindow& window, const wxPoint& WXUNUSED(pos))
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
226 GetBackgroundColour(col);
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
238 // Draw all intermediate frames that haven't been removed from the animation
239 int i;
240 for (i = 0; i < frame; i++)
241 {
242 if ((GetDisposalMethod(i) == wxANIM_DONOTREMOVE) || (GetDisposalMethod(i) == wxANIM_UNSPECIFIED))
243 DrawFrame(i, dc, wxPoint(0, 0));
244 }
245
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
254 return true;
255 }
256
257 bool wxAnimationPlayer::PlayFrame()
258 {
259 m_isPlaying = true;
260
261 PlayFrame(GetCurrentFrame(), * GetWindow(), GetPosition());
262
263 // Set the timer for the next frame
264 int delay = GetDelay(GetCurrentFrame());
265 if (delay == 0)
266 delay = 1; // 0 is invalid timeout for wxTimer.
267
268 m_timer.Start(delay);
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();
278 m_isPlaying = false;
279 }
280 else
281 m_currentFrame = 0;
282 }
283
284 return true;
285 }
286
287 // Clear the wxImage cache
288 void wxAnimationPlayer::ClearCache()
289 {
290 wxList::compatibility_iterator node = m_frames.GetFirst();
291 while (node)
292 {
293 wxList::compatibility_iterator next = node->GetNext();
294 wxBitmap* bitmap = (wxBitmap*) node->GetData();
295 delete bitmap;
296 m_frames.Erase(node);
297
298 node = next;
299 }
300 }
301
302 // Draw the background colour
303 void wxAnimationPlayer::DrawBackground(wxDC& dc, const wxPoint& pos, const wxColour& colour)
304 {
305 wxASSERT_MSG( (m_animation != NULL), _T("Animation not present in wxAnimationPlayer"));
306 wxASSERT_MSG( (m_frames.GetCount() > 0), _T("Animation cache not present in wxAnimationPlayer"));
307
308 // Optimization: if the first frame fills the whole area, and is non-transparent,
309 // don't bother drawing the background
310
311 wxBitmap* firstBitmap = (wxBitmap*) m_frames.GetFirst()->GetData() ;
312 wxSize screenSize = GetLogicalScreenSize();
313 if (!firstBitmap->GetMask() && (firstBitmap->GetWidth() == screenSize.x) && (firstBitmap->GetHeight() == screenSize.y))
314 return;
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
327 void wxAnimationPlayer::SaveBackground(const wxRect& rect)
328 {
329 wxASSERT( (GetWindow() != NULL) );
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 }
358
359 memDC.SelectObject(wxNullBitmap);
360 }
361
362 // Draw this frame
363 void wxAnimationPlayer::DrawFrame(int frame, wxDC& dc, const wxPoint& pos)
364 {
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"));
367 wxASSERT_MSG( !!m_frames.Item(frame), _T("Image not present in wxAnimationPlayer::DrawFrame"));
368
369 wxBitmap* bitmap = (wxBitmap*) m_frames.Item(frame)->GetData() ;
370 wxRect rect = GetFrameRect(frame);
371
372 dc.DrawBitmap(*bitmap, pos.x + rect.x, pos.y + rect.y, (bitmap->GetMask() != NULL));
373 }
374
375 void wxAnimationTimer::Notify()
376 {
377 m_player->PlayFrame();
378 }
379
380 /*
381 * wxAnimationBase
382 */
383
384 IMPLEMENT_ABSTRACT_CLASS(wxAnimationBase, wxObject)
385
386 /*
387 * wxGIFAnimation
388 */
389
390 IMPLEMENT_CLASS(wxGIFAnimation, wxAnimationBase)
391
392 wxGIFAnimation::wxGIFAnimation()
393 {
394 m_decoder = (wxGIFDecoder*) NULL;
395 }
396
397 wxGIFAnimation::~wxGIFAnimation()
398 {
399 delete m_decoder;
400 }
401
402 int wxGIFAnimation::GetFrameCount() const
403 {
404 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
405
406 return m_decoder->GetNumberOfFrames();
407 }
408
409 wxImage* wxGIFAnimation::GetFrame(int i) const
410 {
411 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
412
413 m_decoder->GoFrame(i + 1);
414
415 wxImage* image = new wxImage;
416 m_decoder->ConvertToImage(image);
417
418 return image;
419 }
420
421 wxAnimationDisposal wxGIFAnimation::GetDisposalMethod(int i) const
422 {
423 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
424
425 m_decoder->GoFrame(i + 1);
426
427 int disposalMethod = m_decoder->GetDisposalMethod();
428 return (wxAnimationDisposal) disposalMethod;
429 }
430
431 wxRect wxGIFAnimation::GetFrameRect(int i) const
432 {
433 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
434
435 m_decoder->GoFrame(i + 1);
436
437 wxRect rect(m_decoder->GetLeft(), m_decoder->GetTop(), m_decoder->GetWidth(), m_decoder->GetHeight());
438
439 return rect;
440 }
441
442 int wxGIFAnimation::GetDelay(int i) const
443 {
444 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
445
446 m_decoder->GoFrame(i + 1);
447
448 return m_decoder->GetDelay();
449 }
450
451 wxSize wxGIFAnimation::GetLogicalScreenSize() const
452 {
453 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
454
455 return wxSize(m_decoder->GetLogicalScreenWidth(), m_decoder->GetLogicalScreenHeight());
456 }
457
458 bool wxGIFAnimation::GetBackgroundColour(wxColour& col) const
459 {
460 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
461
462 int i = m_decoder->GetBackgroundColour();
463 if (i == -1)
464 return false;
465
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;
473 }
474
475 bool wxGIFAnimation::GetTransparentColour(wxColour& col) const
476 {
477 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), _T("m_decoder must be non-NULL"));
478
479 int i = m_decoder->GetTransparentColour();
480 if (i == -1)
481 return false;
482
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;
490 }
491
492 bool wxGIFAnimation::IsValid() const
493 {
494 return ((m_decoder != NULL) && (m_decoder->IsAnimation()));
495 }
496
497 bool wxGIFAnimation::LoadFile(const wxString& filename)
498 {
499 if (!wxFileExists(filename))
500 return false;
501
502 bool result = true;
503
504 if (m_decoder)
505 {
506 delete m_decoder;
507 m_decoder = NULL;
508 }
509
510 {
511 wxFileInputStream stream(filename);
512
513 if (stream.GetLength() > 0)
514 m_decoder = new wxGIFDecoder(&stream, true);
515
516 result = ((m_decoder != NULL) && (m_decoder->ReadGIF() == wxGIF_OK));
517 if (result)
518 result = m_decoder->IsAnimation();
519 }
520
521 if (!result && (m_decoder != NULL))
522 {
523 delete m_decoder;
524 m_decoder = NULL;
525 }
526
527 return result;
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
537 IMPLEMENT_ABSTRACT_CLASS(wxAnimationCtrlBase, wxControl)
538
539 BEGIN_EVENT_TABLE(wxAnimationCtrlBase, wxControl)
540 EVT_PAINT(wxAnimationCtrlBase::OnPaint)
541 END_EVENT_TABLE()
542
543 bool 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))
551 return false;
552
553 SetBackgroundColour(parent->GetBackgroundColour());
554
555 m_animationPlayer.SetCustomBackgroundColour(GetBackgroundColour());
556
557 // give the impression of transparency by painting
558 // with the parent background
559 // if (parent)
560 // m_animationPlayer.UseParentBackground(true);
561
562 m_animationPlayer.SetWindow(this);
563 m_animationPlayer.SetPosition(wxPoint(0, 0));
564 m_animationPlayer.SetDestroyAnimation(false);
565
566 LoadFile(filename);
567
568 return true;
569 }
570
571 wxAnimationCtrlBase::~wxAnimationCtrlBase()
572 {
573 if (m_animationPlayer.IsPlaying())
574 m_animationPlayer.Stop();
575 m_animationPlayer.SetAnimation(NULL, false);
576
577 delete m_animation;
578 }
579
580 bool 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())
588 {
589 filename1 = m_filename;
590 if (filename1.IsEmpty())
591 return false;
592 }
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)
602 return false;
603
604 if (!m_animation->LoadFile(filename) || !m_animation->IsValid())
605 {
606 delete m_animation;
607 m_animation = NULL;
608
609 return false;
610 }
611
612 m_animationPlayer.SetAnimation(m_animation, false);
613
614 if (GetWindowStyle() & wxAN_FIT_ANIMATION)
615 FitToAnimation();
616
617 return true;
618 }
619
620 bool wxAnimationCtrlBase::Play(bool looped)
621 {
622 return m_animationPlayer.Play(*this, wxPoint(0, 0), looped);
623 }
624
625 wxSize wxAnimationCtrlBase::DoGetBestSize() const
626 {
627 if (m_animationPlayer.HasAnimation() && (GetWindowStyle() & wxAN_FIT_ANIMATION))
628 return m_animationPlayer.GetLogicalScreenSize();
629 else
630 return GetSize();
631 }
632
633 void wxAnimationCtrlBase::FitToAnimation()
634 {
635 if (!m_animationPlayer.HasAnimation())
636 return;
637
638 wxSize sz = m_animationPlayer.GetLogicalScreenSize();
639 SetClientSize(sz);
640 }
641
642 void wxAnimationCtrlBase::OnPaint(wxPaintEvent& WXUNUSED(event))
643 {
644 wxPaintDC dc(this);
645
646 if (GetPlayer().IsPlaying())
647 GetPlayer().Draw(dc);
648 }
649
650 /*
651 * wxGIFAnimationCtrl
652 * Provides a GIF animation class when required.
653 */
654
655 IMPLEMENT_ABSTRACT_CLASS(wxGIFAnimationCtrl, wxAnimationCtrlBase)
656
657 wxAnimationBase* wxGIFAnimationCtrl::DoCreateAnimation(const wxString& WXUNUSED(filename))
658 {
659 return new wxGIFAnimation;
660 }
661