X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/72045d5768a126191a907bc640c28e81a11afdd6..f239a20092359e3c914adb79bd39f3f5d2b2e06f:/src/generic/animateg.cpp?ds=sidebyside diff --git a/src/generic/animateg.cpp b/src/generic/animateg.cpp index 2ab49659dd..d6e44ba9a2 100644 --- a/src/generic/animateg.cpp +++ b/src/generic/animateg.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: animateg.cpp +// Name: src/generic/animateg.cpp // Purpose: wxAnimation and wxAnimationCtrl // Author: Julian Smart and Guillermo Rodriguez Garcia // Modified by: Francesco Montorsi @@ -15,34 +15,34 @@ #pragma hdrstop #endif //__BORLANDC__ - #if wxUSE_ANIMATIONCTRL -#include "wx/log.h" +#include "wx/animate.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/image.h" + #include "wx/dcmemory.h" + #include "wx/dcclient.h" + #include "wx/module.h" +#endif + #include "wx/wfstream.h" -#include "wx/image.h" #include "wx/gifdecod.h" #include "wx/anidecod.h" -#include "wx/dcmemory.h" -#include "wx/dc.h" -#include "wx/dcclient.h" -#include "wx/animate.h" -#include "wx/animdecod.h" - -#include -WX_DEFINE_LIST(wxAnimationDecoderList); +#include "wx/listimpl.cpp" +WX_DEFINE_LIST(wxAnimationDecoderList) wxAnimationDecoderList wxAnimation::sm_handlers; - // ---------------------------------------------------------------------------- // wxAnimation // ---------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase) -#define M_ANIMDATA wx_static_cast(wxAnimationDecoder*, m_refData) +#define M_ANIMDATA static_cast(m_refData) wxSize wxAnimation::GetSize() const { @@ -51,14 +51,14 @@ 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") ); @@ -68,27 +68,41 @@ wxImage wxAnimation::GetFrame(size_t i) const 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") ); @@ -99,7 +113,7 @@ wxColour wxAnimation::GetBackgroundColour() const bool wxAnimation::LoadFile(const wxString& filename, wxAnimationType type) { wxFileInputStream stream(filename); - if (!stream.Ok()) + if ( !stream.IsOk() ) return false; return Load(stream, type); @@ -112,7 +126,7 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type) const wxAnimationDecoder *handler; if ( type == wxANIMATION_TYPE_ANY ) { - for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst(); + for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst(); node; node = node->GetNext() ) { handler=(const wxAnimationDecoder*)node->GetData(); @@ -124,7 +138,6 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type) m_refData = handler->Clone(); return M_ANIMDATA->Load(stream); } - } wxLogWarning( _("No handler found for animation type.") ); @@ -133,10 +146,6 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType 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 ); @@ -144,6 +153,11 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType 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); @@ -171,9 +185,8 @@ void wxAnimation::AddHandler( wxAnimationDecoder *handler ) // 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; } @@ -189,7 +202,7 @@ void wxAnimation::InsertHandler( wxAnimationDecoder *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; } @@ -209,8 +222,12 @@ const wxAnimationDecoder *wxAnimation::FindHandler( wxAnimationType animType ) 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() @@ -237,15 +254,13 @@ class wxAnimationModule: public wxModule 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) - - // ---------------------------------------------------------------------------- // wxAnimationCtrl // ---------------------------------------------------------------------------- @@ -257,22 +272,31 @@ BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase) EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer) END_EVENT_TABLE() +void wxAnimationCtrl::Init() +{ + m_currentFrame = 0; + m_looped = false; + m_isPlaying = 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 (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name)) + if (!base_type::Create(parent, id, pos, size, style, wxDefaultValidator, name)) return false; // by default we get the same background colour of our parent SetBackgroundColour(parent->GetBackgroundColour()); + + SetAnimation(animation); + return true; } @@ -282,10 +306,17 @@ wxAnimationCtrl::~wxAnimationCtrl() } bool wxAnimationCtrl::LoadFile(const wxString& filename, wxAnimationType type) +{ + wxFileInputStream fis(filename); + if (!fis.IsOk()) + 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); @@ -305,28 +336,33 @@ void wxAnimationCtrl::SetAnimation(const wxAnimation& animation) 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); - - // Draw the background - DisposeToBackground(dc); - } + DisplayStaticImage(); +} - Refresh(); +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()); + + wxAnimationCtrlBase::SetInactiveBitmap(bmp); } void wxAnimationCtrl::FitToAnimation() @@ -334,6 +370,19 @@ 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 @@ -341,9 +390,13 @@ void wxAnimationCtrl::FitToAnimation() 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) @@ -351,15 +404,17 @@ 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); @@ -369,7 +424,7 @@ bool wxAnimationCtrl::Play(bool looped) 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; } @@ -380,7 +435,7 @@ bool wxAnimationCtrl::Play(bool looped) // 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 @@ -389,9 +444,12 @@ void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame) int w = wxMin(sz.GetWidth(), winsz.GetWidth()); int h = wxMin(sz.GetHeight(), winsz.GetHeight()); - if (m_backingStore.GetWidth() < w || - m_backingStore.GetHeight() < h) - m_backingStore.Create(w, h); + if ( !m_backingStore.IsOk() || + m_backingStore.GetWidth() < w || m_backingStore.GetHeight() < h ) + { + if (!m_backingStore.Create(w, h)) + return false; + } wxMemoryDC dc; dc.SelectObject(m_backingStore); @@ -400,18 +458,23 @@ void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame) 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() @@ -435,14 +498,15 @@ 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 - // disposal limited to the minimum. - // In fact it may require a lot of time to restore + // 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) { // if 0-th frame disposal is to restore to previous frame, @@ -450,7 +514,8 @@ void wxAnimationCtrl::IncrementalUpdateBackingStore() DisposeToBackground(dc); } else - RebuildBackingStoreUpToFrame(m_currentFrame-2); + if (!RebuildBackingStoreUpToFrame(m_currentFrame-2)) + Stop(); break; case wxANIM_DONOTREMOVE: @@ -464,7 +529,42 @@ void wxAnimationCtrl::IncrementalUpdateBackingStore() 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 @@ -479,20 +579,43 @@ void wxAnimationCtrl::DrawFrame(wxDC &dc, size_t frame) void wxAnimationCtrl::DrawCurrentFrame(wxDC& dc) { - wxASSERT(m_backingStore.Ok()); + 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() ? - this->GetBackgroundColour() : m_animation.GetBackgroundColour(), wxSOLID); + wxColour col = IsUsingWindowBackgroundColour() + ? 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 // ---------------------------------------------------------------------------- @@ -502,10 +625,19 @@ void wxAnimationCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) // 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.Ok()) - DrawCurrentFrame(dc); - //else: m_animation is not valid and thus we don't have a valid backing store... + if ( m_backingStore.IsOk() ) + { + // 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)) @@ -516,8 +648,7 @@ 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 @@ -529,21 +660,38 @@ void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event)) wxClientDC dc(this); DrawCurrentFrame(dc); +#ifdef __WXMAC__ + // without this, the animation currently doesn't redraw under Mac + Refresh(); +#endif // __WXMAC__ + // Set the timer for the next frame 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