1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implementation of wxAnimation classes
4 // Author: Julian Smart and Guillermo Rodriguez Garcia
8 // Copyright: (c) Julian Smart and Guillermo Rodriguez Garcia
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
19 #include "wx/wfstream.h"
21 #include "wx/gifdecod.h"
22 #include "wx/dcmemory.h"
24 #include "wx/dcclient.h"
25 #include "wx/animate/animate.h"
31 IMPLEMENT_CLASS(wxAnimationPlayer
, wxObject
)
33 wxAnimationPlayer::wxAnimationPlayer(wxAnimationBase
*animation
, bool destroyAnimation
)
35 m_animation
= animation
;
36 m_destroyAnimation
= destroyAnimation
;
37 m_customBackgroundColour
= wxColour(0, 0, 0);
39 m_window
= (wxWindow
*) NULL
;
40 m_position
= wxPoint(0, 0);
43 m_useBackgroundColour
= false;
44 m_useCustomBackgroundColour
= false;
45 m_useParentBackground
= false;
46 m_timer
.SetPlayer(this);
49 wxAnimationPlayer::~wxAnimationPlayer()
54 if (m_destroyAnimation
)
58 void wxAnimationPlayer::SetAnimation(wxAnimationBase
* animation
, bool destroyAnimation
)
61 if (m_destroyAnimation
)
63 m_animation
= animation
;
64 m_destroyAnimation
= destroyAnimation
;
68 bool wxAnimationPlayer::Play(wxWindow
& window
, const wxPoint
& pos
, bool WXUNUSED(looped
))
72 if (!m_animation
|| !m_animation
->IsValid())
75 wxSize sz
= GetLogicalScreenSize();
79 if (m_frames
.GetCount() == 0)
83 wxLogWarning(_T("wxAnimationPlayer::Play: could not build the image cache."));
91 // Create the backing store
92 m_backingStore
.Create(sz
.x
, sz
.y
);
99 // Build animation (list of wxImages). If not called before Play
100 // is called, Play will call this automatically.
101 bool wxAnimationPlayer::Build()
110 for (i
= 0; i
< n
; i
++)
112 wxImage
* image
= GetFrame(i
);
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());
123 wxBitmap
* bitmap
= new wxBitmap(*image
);
128 m_frames
.Append(bitmap
);
134 // Stop the animation
135 void wxAnimationPlayer::Stop()
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
)
145 dc
.DrawBitmap(m_backingStore
, m_position
.x
, m_position
.y
);
148 int wxAnimationPlayer::GetFrameCount() const
151 return m_animation
->GetFrameCount();
156 wxImage
* wxAnimationPlayer::GetFrame(int i
) const
159 return m_animation
->GetFrame(i
);
161 return (wxImage
*) NULL
;
164 wxAnimationDisposal
wxAnimationPlayer::GetDisposalMethod(int i
) const
167 return m_animation
->GetDisposalMethod(i
);
169 return wxANIM_UNSPECIFIED
;
172 wxRect
wxAnimationPlayer::GetFrameRect(int i
) const
175 return m_animation
->GetFrameRect(i
);
177 return wxRect(0, 0, 0, 0);
180 int wxAnimationPlayer::GetDelay(int i
) const
183 return m_animation
->GetDelay(i
);
188 wxSize
wxAnimationPlayer::GetLogicalScreenSize() const
191 return m_animation
->GetLogicalScreenSize();
196 bool wxAnimationPlayer::GetBackgroundColour(wxColour
& col
) const
199 return m_animation
->GetBackgroundColour(col
);
204 bool wxAnimationPlayer::GetTransparentColour(wxColour
& col
) const
207 return m_animation
->GetTransparentColour(col
);
213 bool wxAnimationPlayer::PlayFrame(int frame
, wxWindow
& window
, const wxPoint
& WXUNUSED(pos
))
216 dc
.SelectObject(m_backingStore
);
218 // Draw the background: colour or area beneath animation
219 wxColour
col(255, 255, 255);
221 if (UsingBackgroundColour())
223 if (UsingCustomBackgroundColour())
224 col
= GetCustomBackgroundColour();
226 GetBackgroundColour(col
);
228 // Draw the background colour loaded from the animation
229 // (or set by the user)
230 DrawBackground(dc
, wxPoint(0, 0), col
);
234 // Draw background we saved
235 dc
.DrawBitmap(m_savedBackground
, 0, 0);
238 // Draw all intermediate frames that haven't been removed from the animation
240 for (i
= 0; i
< frame
; i
++)
242 if ((GetDisposalMethod(i
) == wxANIM_DONOTREMOVE
) || (GetDisposalMethod(i
) == wxANIM_UNSPECIFIED
))
243 DrawFrame(i
, dc
, wxPoint(0, 0));
246 DrawFrame(frame
, dc
, wxPoint(0, 0));
248 dc
.SelectObject(wxNullBitmap
);
250 // Draw from backing bitmap onto window
251 wxClientDC
clientDC(& window
);
257 bool wxAnimationPlayer::PlayFrame()
261 PlayFrame(GetCurrentFrame(), * GetWindow(), GetPosition());
263 // Set the timer for the next frame
264 int delay
= GetDelay(GetCurrentFrame());
266 delay
= 1; // 0 is invalid timeout for wxTimer.
268 m_timer
.Start(delay
);
272 if (m_currentFrame
== GetFrameCount())
274 // Should a non-looped animation display the last frame?
287 // Clear the wxImage cache
288 void wxAnimationPlayer::ClearCache()
290 wxList::compatibility_iterator node
= m_frames
.GetFirst();
293 wxList::compatibility_iterator next
= node
->GetNext();
294 wxBitmap
* bitmap
= (wxBitmap
*) node
->GetData();
296 m_frames
.Erase(node
);
302 // Draw the background colour
303 void wxAnimationPlayer::DrawBackground(wxDC
& dc
, const wxPoint
& pos
, const wxColour
& colour
)
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"));
308 // Optimization: if the first frame fills the whole area, and is non-transparent,
309 // don't bother drawing the background
311 wxBitmap
* firstBitmap
= (wxBitmap
*) m_frames
.GetFirst()->GetData() ;
312 wxSize screenSize
= GetLogicalScreenSize();
313 if (!firstBitmap
->GetMask() && (firstBitmap
->GetWidth() == screenSize
.x
) && (firstBitmap
->GetHeight() == screenSize
.y
))
316 wxBrush
brush(colour
, wxSOLID
);
317 wxPen
pen(colour
, 1, wxSOLID
);
320 dc
.SetLogicalFunction(wxCOPY
);
322 dc
.DrawRectangle(pos
.x
, pos
.y
, screenSize
.x
, screenSize
.y
);
325 // Save the pertinent area of the window so we can restore
326 // it if drawing transparently
327 void wxAnimationPlayer::SaveBackground(const wxRect
& rect
)
329 wxASSERT( (GetWindow() != NULL
) );
334 m_savedBackground
.Create(rect
.width
, rect
.height
);
337 memDC
.SelectObject(m_savedBackground
);
339 if (m_useParentBackground
&& GetWindow()->GetParent())
341 wxWindow
* parent
= GetWindow()->GetParent();
342 wxClientDC
dc(parent
);
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
);
350 memDC
.Blit(0, 0, rect
.width
, rect
.height
, & dc
, parentPt
.x
, parentPt
.y
);
354 wxClientDC
dc(GetWindow());
356 memDC
.Blit(0, 0, rect
.width
, rect
.height
, & dc
, rect
.x
, rect
.y
);
359 memDC
.SelectObject(wxNullBitmap
);
363 void wxAnimationPlayer::DrawFrame(int frame
, wxDC
& dc
, const wxPoint
& pos
)
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"));
369 wxBitmap
* bitmap
= (wxBitmap
*) m_frames
.Item(frame
)->GetData() ;
370 wxRect rect
= GetFrameRect(frame
);
372 dc
.DrawBitmap(*bitmap
, pos
.x
+ rect
.x
, pos
.y
+ rect
.y
, (bitmap
->GetMask() != NULL
));
375 void wxAnimationTimer::Notify()
377 m_player
->PlayFrame();
384 IMPLEMENT_ABSTRACT_CLASS(wxAnimationBase
, wxObject
)
390 IMPLEMENT_CLASS(wxGIFAnimation
, wxAnimationBase
)
392 wxGIFAnimation::wxGIFAnimation()
394 m_decoder
= (wxGIFDecoder
*) NULL
;
397 wxGIFAnimation::~wxGIFAnimation()
402 int wxGIFAnimation::GetFrameCount() const
404 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
406 return m_decoder
->GetNumberOfFrames();
409 wxImage
* wxGIFAnimation::GetFrame(int i
) const
411 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
413 m_decoder
->GoFrame(i
+ 1);
415 wxImage
* image
= new wxImage
;
416 m_decoder
->ConvertToImage(image
);
421 wxAnimationDisposal
wxGIFAnimation::GetDisposalMethod(int i
) const
423 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
425 m_decoder
->GoFrame(i
+ 1);
427 int disposalMethod
= m_decoder
->GetDisposalMethod();
428 return (wxAnimationDisposal
) disposalMethod
;
431 wxRect
wxGIFAnimation::GetFrameRect(int i
) const
433 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
435 m_decoder
->GoFrame(i
+ 1);
437 wxRect
rect(m_decoder
->GetLeft(), m_decoder
->GetTop(), m_decoder
->GetWidth(), m_decoder
->GetHeight());
442 int wxGIFAnimation::GetDelay(int i
) const
444 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
446 m_decoder
->GoFrame(i
+ 1);
448 return m_decoder
->GetDelay();
451 wxSize
wxGIFAnimation::GetLogicalScreenSize() const
453 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
455 return wxSize(m_decoder
->GetLogicalScreenWidth(), m_decoder
->GetLogicalScreenHeight());
458 bool wxGIFAnimation::GetBackgroundColour(wxColour
& col
) const
460 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
462 int i
= m_decoder
->GetBackgroundColour();
466 const unsigned char *pal
= m_decoder
->GetPalette();
467 bool result
= (pal
!= NULL
);
470 col
= wxColour(pal
[3*i
+ 0], pal
[3*i
+ 1], pal
[3*i
+ 2]);
475 bool wxGIFAnimation::GetTransparentColour(wxColour
& col
) const
477 wxASSERT_MSG( (m_decoder
!= (wxGIFDecoder
*) NULL
), _T("m_decoder must be non-NULL"));
479 int i
= m_decoder
->GetTransparentColour();
483 const unsigned char *pal
= m_decoder
->GetPalette();
484 bool result
= (pal
!= NULL
);
487 col
= wxColour(pal
[3*i
+ 0], pal
[3*i
+ 1], pal
[3*i
+ 2]);
492 bool wxGIFAnimation::IsValid() const
494 return ((m_decoder
!= NULL
) && (m_decoder
->IsAnimation()));
497 bool wxGIFAnimation::LoadFile(const wxString
& filename
)
499 if (!wxFileExists(filename
))
511 wxFileInputStream
stream(filename
);
513 if (stream
.GetLength() > 0)
514 m_decoder
= new wxGIFDecoder(&stream
, true);
516 result
= ((m_decoder
!= NULL
) && (m_decoder
->ReadGIF() == wxGIF_OK
));
518 result
= m_decoder
->IsAnimation();
521 if (!result
&& (m_decoder
!= NULL
))
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.
537 IMPLEMENT_ABSTRACT_CLASS(wxAnimationCtrlBase
, wxControl
)
539 BEGIN_EVENT_TABLE(wxAnimationCtrlBase
, wxControl
)
540 EVT_PAINT(wxAnimationCtrlBase::OnPaint
)
543 bool wxAnimationCtrlBase::Create(wxWindow
*parent
, wxWindowID id
,
544 const wxString
& filename
, const wxPoint
& pos
,
545 const wxSize
& size
, long style
, const wxString
& name
)
548 m_filename
= filename
;
550 if (!wxControl::Create(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
553 SetBackgroundColour(parent
->GetBackgroundColour());
555 m_animationPlayer
.SetCustomBackgroundColour(GetBackgroundColour());
557 // give the impression of transparency by painting
558 // with the parent background
560 // m_animationPlayer.UseParentBackground(true);
562 m_animationPlayer
.SetWindow(this);
563 m_animationPlayer
.SetPosition(wxPoint(0, 0));
564 m_animationPlayer
.SetDestroyAnimation(false);
571 wxAnimationCtrlBase::~wxAnimationCtrlBase()
573 if (m_animationPlayer
.IsPlaying())
574 m_animationPlayer
.Stop();
575 m_animationPlayer
.SetAnimation(NULL
, false);
580 bool wxAnimationCtrlBase::LoadFile(const wxString
& filename
)
582 if (m_animationPlayer
.IsPlaying())
583 m_animationPlayer
.Stop();
585 wxString
filename1(filename
);
587 if (filename1
.IsEmpty())
589 filename1
= m_filename
;
590 if (filename1
.IsEmpty())
600 m_animation
= DoCreateAnimation(filename1
);
604 if (!m_animation
->LoadFile(filename
) || !m_animation
->IsValid())
612 m_animationPlayer
.SetAnimation(m_animation
, false);
614 if (GetWindowStyle() & wxAN_FIT_ANIMATION
)
620 bool wxAnimationCtrlBase::Play(bool looped
)
622 return m_animationPlayer
.Play(*this, wxPoint(0, 0), looped
);
625 wxSize
wxAnimationCtrlBase::DoGetBestSize() const
627 if (m_animationPlayer
.HasAnimation() && (GetWindowStyle() & wxAN_FIT_ANIMATION
))
628 return m_animationPlayer
.GetLogicalScreenSize();
633 void wxAnimationCtrlBase::FitToAnimation()
635 if (!m_animationPlayer
.HasAnimation())
638 wxSize sz
= m_animationPlayer
.GetLogicalScreenSize();
642 void wxAnimationCtrlBase::OnPaint(wxPaintEvent
& WXUNUSED(event
))
646 if (GetPlayer().IsPlaying())
647 GetPlayer().Draw(dc
);
652 * Provides a GIF animation class when required.
655 IMPLEMENT_ABSTRACT_CLASS(wxGIFAnimationCtrl
, wxAnimationCtrlBase
)
657 wxAnimationBase
* wxGIFAnimationCtrl::DoCreateAnimation(const wxString
& WXUNUSED(filename
))
659 return new wxGIFAnimation
;