///////////////////////////////////////////////////////////////////////////////
-// Name: animateg.cpp
+// Name: src/generic/animateg.cpp
// Purpose: wxAnimation and wxAnimationCtrl
// Author: Julian Smart and Guillermo Rodriguez Garcia
// Modified by: Francesco Montorsi
// ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
-#define M_ANIMDATA wx_static_cast(wxAnimationDecoder*, m_refData)
+#define M_ANIMDATA static_cast<wxAnimationDecoder*>(m_refData)
wxSize wxAnimation::GetSize() const
{
return M_ANIMDATA->GetAnimationSize();
}
-size_t wxAnimation::GetFrameCount() const
+unsigned int wxAnimation::GetFrameCount() const
{
wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
return M_ANIMDATA->GetFrameCount();
}
-wxImage wxAnimation::GetFrame(size_t i) const
+wxImage wxAnimation::GetFrame(unsigned int i) const
{
wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid animation") );
return ret;
}
-int wxAnimation::GetDelay(size_t i) const
+int wxAnimation::GetDelay(unsigned int i) const
{
wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
return M_ANIMDATA->GetDelay(i);
}
-wxPoint wxAnimation::GetFramePosition(size_t frame) const
+wxPoint wxAnimation::GetFramePosition(unsigned int frame) const
{
wxCHECK_MSG( IsOk(), wxDefaultPosition, wxT("invalid animation") );
return M_ANIMDATA->GetFramePosition(frame);
}
-wxAnimationDisposal wxAnimation::GetDisposalMethod(size_t frame) const
+wxSize wxAnimation::GetFrameSize(unsigned int frame) const
+{
+ wxCHECK_MSG( IsOk(), wxDefaultSize, wxT("invalid animation") );
+
+ return M_ANIMDATA->GetFrameSize(frame);
+}
+
+wxAnimationDisposal wxAnimation::GetDisposalMethod(unsigned int frame) const
{
wxCHECK_MSG( IsOk(), wxANIM_UNSPECIFIED, wxT("invalid animation") );
return M_ANIMDATA->GetDisposalMethod(frame);
}
+wxColour wxAnimation::GetTransparentColour(unsigned int frame) const
+{
+ wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") );
+
+ return M_ANIMDATA->GetTransparentColour(frame);
+}
+
wxColour wxAnimation::GetBackgroundColour() const
{
wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") );
m_refData = handler->Clone();
return M_ANIMDATA->Load(stream);
}
-
}
wxLogWarning( _("No handler found for animation type.") );
handler = FindHandler(type);
- // do a copy of the handler from the static list which we will own
- // as our reference data
- m_refData = handler->Clone();
-
if (handler == NULL)
{
wxLogWarning( _("No animation handler for type %ld defined."), type );
return false;
}
+
+ // do a copy of the handler from the static list which we will own
+ // as our reference data
+ m_refData = handler->Clone();
+
if (stream.IsSeekable() && !M_ANIMDATA->CanRead(stream))
{
wxLogError(_("Animation file is not of type %ld."), type);
// for preventing duplicate additions. If someone ever has
// a good reason to add and remove duplicate handlers (and they
// may) we should probably refcount the duplicates.
- // also an issue in InsertHandler below.
- wxLogDebug( _T("Adding duplicate animation handler for '%d' type"),
+ wxLogDebug( wxT("Adding duplicate animation handler for '%d' type"),
handler->GetType() );
delete handler;
}
else
{
// see AddHandler for additional comments.
- wxLogDebug( _T("Inserting duplicate animation handler for '%d' type"),
+ wxLogDebug( wxT("Inserting duplicate animation handler for '%d' type"),
handler->GetType() );
delete handler;
}
void wxAnimation::InitStandardHandlers()
{
+#if wxUSE_GIF
AddHandler(new wxGIFDecoder);
+#endif // wxUSE_GIF
+#if wxUSE_ICO_CUR
AddHandler(new wxANIDecoder);
+#endif // wxUSE_ICO_CUR
}
void wxAnimation::CleanUpHandlers()
DECLARE_DYNAMIC_CLASS(wxAnimationModule)
public:
wxAnimationModule() {}
- bool OnInit() { wxAnimation::InitStandardHandlers(); return true; };
- void OnExit() { wxAnimation::CleanUpHandlers(); };
+ bool OnInit() { wxAnimation::InitStandardHandlers(); return true; }
+ void OnExit() { wxAnimation::CleanUpHandlers(); }
};
IMPLEMENT_DYNAMIC_CLASS(wxAnimationModule, wxModule)
EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
END_EVENT_TABLE()
-wxAnimationCtrl::wxAnimationCtrl()
+void wxAnimationCtrl::Init()
{
m_currentFrame = 0;
m_looped = false;
m_isPlaying = false;
- m_useWinBackgroundColour = false;
+
+ // use the window background colour by default to be consistent
+ // with the GTK+ native version
+ m_useWinBackgroundColour = true;
}
bool wxAnimationCtrl::Create(wxWindow *parent, wxWindowID id,
const wxAnimation& animation, const wxPoint& pos,
const wxSize& size, long style, const wxString& name)
{
- m_animation = animation;
- m_currentFrame = 0;
- m_looped = true;
- m_isPlaying = false;
- m_useWinBackgroundColour = false;
m_timer.SetOwner(this);
if (!base_type::Create(parent, id, pos, size, style, wxDefaultValidator, name))
// by default we get the same background colour of our parent
SetBackgroundColour(parent->GetBackgroundColour());
+
+ SetAnimation(animation);
+
return true;
}
}
bool wxAnimationCtrl::LoadFile(const wxString& filename, wxAnimationType type)
+{
+ wxFileInputStream fis(filename);
+ if (!fis.Ok())
+ return false;
+ return Load(fis, type);
+}
+
+bool wxAnimationCtrl::Load(wxInputStream& stream, wxAnimationType type)
{
wxAnimation anim;
- if (!anim.LoadFile(filename, type) ||
- !anim.IsOk())
+ if ( !anim.Load(stream, type) || !anim.IsOk() )
return false;
SetAnimation(anim);
if (IsPlaying())
Stop();
+ // set new animation even if it's wxNullAnimation
m_animation = animation;
+ if (!m_animation.IsOk())
+ {
+ DisplayStaticImage();
+ return;
+ }
if (m_animation.GetBackgroundColour() == wxNullColour)
SetUseWindowBackgroundColour();
if (!this->HasFlag(wxAC_NO_AUTORESIZE))
FitToAnimation();
- // display first frame
- m_currentFrame = 0;
- if (m_animation.IsOk())
- RebuildBackingStoreUpToFrame(0);
- else
- {
- // clear to
- wxMemoryDC dc;
- dc.SelectObject(m_backingStore);
+ DisplayStaticImage();
+}
- // Draw the background
- DisposeToBackground(dc);
- }
+void wxAnimationCtrl::SetInactiveBitmap(const wxBitmap &bmp)
+{
+ // if the bitmap has an associated mask, we need to set our background to
+ // the colour of our parent otherwise when calling DrawCurrentFrame()
+ // (which uses the bitmap's mask), our background colour would be used for
+ // transparent areas - and that's not what we want (at least for
+ // consistency with the GTK version)
+ if ( bmp.GetMask() != NULL && GetParent() != NULL )
+ SetBackgroundColour(GetParent()->GetBackgroundColour());
- Refresh();
+ wxAnimationCtrlBase::SetInactiveBitmap(bmp);
}
void wxAnimationCtrl::FitToAnimation()
SetSize(m_animation.GetSize());
}
+bool wxAnimationCtrl::SetBackgroundColour(const wxColour& colour)
+{
+ if ( !wxWindow::SetBackgroundColour(colour) )
+ return false;
+
+ // if not playing, then this change must be seen immediately (unless
+ // there's an inactive bitmap set which has higher priority than bg colour)
+ if ( !IsPlaying() )
+ DisplayStaticImage();
+
+ return true;
+}
+
// ----------------------------------------------------------------------------
// wxAnimationCtrl - stop/play methods
void wxAnimationCtrl::Stop()
{
- // leave current frame displayed until Play() is called again
m_timer.Stop();
m_isPlaying = false;
+
+ // reset frame counter
+ m_currentFrame = 0;
+
+ DisplayStaticImage();
}
bool wxAnimationCtrl::Play(bool looped)
if (!m_animation.IsOk())
return false;
- int oldframe = m_currentFrame;
m_looped = looped;
m_currentFrame = 0;
+
+ if (!RebuildBackingStoreUpToFrame(0))
+ return false;
+
m_isPlaying = true;
- // small optimization: if the back store was already updated to the
- // first frame, don't rebuild it
- if (oldframe != 0)
- RebuildBackingStoreUpToFrame(0);
+ // do a ClearBackground() to avoid that e.g. the custom static bitmap which
+ // was eventually shown previously remains partially drawn
+ ClearBackground();
// DrawCurrentFrame() will use our updated backing store
wxClientDC clientDC(this);
int delay = m_animation.GetDelay(0);
if (delay == 0)
delay = 1; // 0 is invalid timeout for wxTimer.
- m_timer.Start(delay);
+ m_timer.Start(delay, true);
return true;
}
// wxAnimationCtrl - rendering methods
// ----------------------------------------------------------------------------
-void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame)
+bool wxAnimationCtrl::RebuildBackingStoreUpToFrame(unsigned int frame)
{
// if we've not created the backing store yet or it's too
// small, then recreate it
if ( !m_backingStore.IsOk() ||
m_backingStore.GetWidth() < w || m_backingStore.GetHeight() < h )
{
- m_backingStore.Create(w, h);
+ if (!m_backingStore.Create(w, h))
+ return false;
}
wxMemoryDC dc;
DisposeToBackground(dc);
// Draw all intermediate frames that haven't been removed from the animation
- for (size_t i = 0; i < frame; i++)
+ for (unsigned int i = 0; i < frame; i++)
{
if (m_animation.GetDisposalMethod(i) == wxANIM_DONOTREMOVE ||
m_animation.GetDisposalMethod(i) == wxANIM_UNSPECIFIED)
{
DrawFrame(dc, i);
}
+ else if (m_animation.GetDisposalMethod(i) == wxANIM_TOBACKGROUND)
+ DisposeToBackground(dc, m_animation.GetFramePosition(i),
+ m_animation.GetFrameSize(i));
}
// finally draw this frame
DrawFrame(dc, frame);
dc.SelectObject(wxNullBitmap);
+
+ return true;
}
void wxAnimationCtrl::IncrementalUpdateBackingStore()
switch (m_animation.GetDisposalMethod(m_currentFrame-1))
{
case wxANIM_TOBACKGROUND:
- DisposeToBackground(dc);
+ DisposeToBackground(dc, m_animation.GetFramePosition(m_currentFrame-1),
+ m_animation.GetFrameSize(m_currentFrame-1));
break;
case wxANIM_TOPREVIOUS:
// this disposal should never be used too often.
- // E.g. GIF specification explicitely say to keep the usage of this
+ // E.g. GIF specification explicitly say to keep the usage of this
// disposal limited to the minimum.
// In fact it may require a lot of time to restore
if (m_currentFrame == 1)
DisposeToBackground(dc);
}
else
- RebuildBackingStoreUpToFrame(m_currentFrame-2);
+ if (!RebuildBackingStoreUpToFrame(m_currentFrame-2))
+ Stop();
break;
case wxANIM_DONOTREMOVE:
dc.SelectObject(wxNullBitmap);
}
-void wxAnimationCtrl::DrawFrame(wxDC &dc, size_t frame)
+void wxAnimationCtrl::DisplayStaticImage()
+{
+ wxASSERT(!IsPlaying());
+
+ // m_bmpStaticReal will be updated only if necessary...
+ UpdateStaticImage();
+
+ if (m_bmpStaticReal.IsOk())
+ {
+ // copy the inactive bitmap in the backing store
+ // eventually using the mask if the static bitmap has one
+ if ( m_bmpStaticReal.GetMask() )
+ {
+ wxMemoryDC temp;
+ temp.SelectObject(m_backingStore);
+ DisposeToBackground(temp);
+ temp.DrawBitmap(m_bmpStaticReal, 0, 0, true /* use mask */);
+ }
+ else
+ m_backingStore = m_bmpStaticReal;
+ }
+ else
+ {
+ // put in the backing store the first frame of the animation
+ if (!m_animation.IsOk() ||
+ !RebuildBackingStoreUpToFrame(0))
+ {
+ m_animation = wxNullAnimation;
+ DisposeToBackground();
+ }
+ }
+
+ Refresh();
+}
+
+void wxAnimationCtrl::DrawFrame(wxDC &dc, unsigned int frame)
{
// PERFORMANCE NOTE:
// this draw stuff is not as fast as possible: the wxAnimationDecoder
wxASSERT( m_backingStore.IsOk() );
// m_backingStore always contains the current frame
- dc.DrawBitmap(m_backingStore, 0, 0);
+ dc.DrawBitmap(m_backingStore, 0, 0, true /* use mask in case it's present */);
+}
+
+void wxAnimationCtrl::DisposeToBackground()
+{
+ // clear the backing store
+ wxMemoryDC dc;
+ dc.SelectObject(m_backingStore);
+ if ( dc.IsOk() )
+ DisposeToBackground(dc);
}
void wxAnimationCtrl::DisposeToBackground(wxDC& dc)
{
- wxBrush brush(IsUsingWindowBackgroundColour()
+ wxColour col = IsUsingWindowBackgroundColour()
? GetBackgroundColour()
- : m_animation.GetBackgroundColour());
+ : m_animation.GetBackgroundColour();
+
+ wxBrush brush(col);
dc.SetBackground(brush);
dc.Clear();
}
+void wxAnimationCtrl::DisposeToBackground(wxDC& dc, const wxPoint &pos, const wxSize &sz)
+{
+ wxColour col = IsUsingWindowBackgroundColour()
+ ? GetBackgroundColour()
+ : m_animation.GetBackgroundColour();
+ wxBrush brush(col);
+ dc.SetBrush(brush); // SetBrush and not SetBackground !!
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ dc.DrawRectangle(pos, sz);
+}
+
// ----------------------------------------------------------------------------
// wxAnimationCtrl - event handlers
// ----------------------------------------------------------------------------
// VERY IMPORTANT: the wxPaintDC *must* be created in any case
wxPaintDC dc(this);
- // both if we are playing or not, we need to refresh the current frame
if ( m_backingStore.IsOk() )
- DrawCurrentFrame(dc);
- //else: m_animation is not valid and thus we don't have a valid backing store...
+ {
+ // NOTE: we draw the bitmap explicitly ignoring the mask (if any);
+ // i.e. we don't want to combine the backing store with the
+ // possibly wrong preexisting contents of the window!
+ dc.DrawBitmap(m_backingStore, 0, 0, false /* no mask */);
+ }
+ else
+ {
+ // m_animation is not valid and thus we don't have a valid backing store...
+ // clear then our area to the background colour
+ DisposeToBackground(dc);
+ }
}
void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event))
// Should a non-looped animation display the last frame?
if (!m_looped)
{
- m_timer.Stop();
- m_isPlaying = false;
+ Stop();
return;
}
else
int delay = m_animation.GetDelay(m_currentFrame);
if (delay == 0)
delay = 1; // 0 is invalid timeout for wxTimer.
- m_timer.Start(delay);
+ m_timer.Start(delay, true);
}
void wxAnimationCtrl::OnSize(wxSizeEvent &WXUNUSED(event))
{
// NB: resizing an animation control may take a lot of time
// for big animations as the backing store must be
- // extended and rebuilt. Try to avoid it!!
+ // extended and rebuilt. Try to avoid it e.g. using
+ // a null proportion value for your wxAnimationCtrls
+ // when using them inside sizers.
if (m_animation.IsOk())
- RebuildBackingStoreUpToFrame(m_currentFrame);
+ {
+ // be careful to change the backing store *only* if we are
+ // playing the animation as otherwise we may be displaying
+ // the inactive bitmap and overwriting the backing store
+ // with the last played frame is wrong in this case
+ if (IsPlaying())
+ {
+ if (!RebuildBackingStoreUpToFrame(m_currentFrame))
+ Stop(); // in case we are playing
+ }
+ }
}
-#endif // wxUSE_ANIMATIONCTRL
+#endif // wxUSE_ANIMATIONCTRL