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