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