WPF и GIF. Класс анимации
-
3 мая 2012 г. 8:23
Здравствуйте. У меня вопрос по классу анимации. Предыдущий класс шустро забивал озу. На 1 анимацию приходилось 4-8 мб. Нашел другой, который должен быть в этом плане лучше, ковырял ковырял и ничего не получилось. Исходник ниже. При указании Source и проверки, должна пойти анимация. Но как не пытался, ничего не происходит. Помогите пожалуйста "запустить" класс.
Imports System.Windows.Media.Animation Public Class AnimatedImage Inherits Image Public Property FrameIndex() As Integer Get Return DirectCast(GetValue(FrameIndexProperty), Integer) End Get Set(ByVal value As Integer) SetValue(FrameIndexProperty, value) End Set End Property Public Shadows Property Source() As ImageSource Get Return DirectCast(GetValue(SourceProperty), ImageSource) End Get Set(ByVal value As ImageSource) SetValue(SourceProperty, value) End Set End Property Protected Overridable Sub OnSourceChanged(ByVal aEventArgs As DependencyPropertyChangedEventArgs) ClearAnimation() Dim lBitmapImage As BitmapImage = TryCast(aEventArgs.NewValue, BitmapImage) If lBitmapImage IsNot Nothing Then Dim lImageSource As ImageSource = TryCast(aEventArgs.NewValue, ImageSource) MyBase.Source = lImageSource Return End If If Not IsAnimatedGifImage(lBitmapImage) Then MyBase.Source = lBitmapImage Return End If PrepareAnimation(lBitmapImage) End Sub Private Property Animation() As Int32Animation Get End Get Set(ByVal value As Int32Animation) End Set End Property Private Property Decoder() As GifBitmapDecoder Get End Get Set(ByVal value As GifBitmapDecoder) End Set End Property Private Property IsAnimationWorking() As Boolean Get End Get Set(ByVal value As Boolean) End Set End Property Private Sub ClearAnimation() If Animation IsNot Nothing Then BeginAnimation(FrameIndexProperty, Nothing) End If IsAnimationWorking = False Animation = Nothing Decoder = Nothing End Sub Private Sub PrepareAnimation(ByVal aBitmapImage As BitmapImage) Debug.Assert(aBitmapImage IsNot Nothing) If aBitmapImage.UriSource <> Nothing Then Decoder = New GifBitmapDecoder(aBitmapImage.UriSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.[Default]) Else aBitmapImage.StreamSource.Position = 0 Decoder = New GifBitmapDecoder(aBitmapImage.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.[Default]) End If Animation = New Int32Animation(0, Decoder.Frames.Count - 1, New Duration(New TimeSpan(0, 0, 0, Decoder.Frames.Count / 10, Int(Decoder.Frames.Count / 10.0 - Decoder.Frames.Count / 10) * 1000))) With { _ .RepeatBehavior = RepeatBehavior.Forever _ } MyBase.Source = Decoder.Frames(0) BeginAnimation(FrameIndexProperty, Animation) IsAnimationWorking = True End Sub Private Function IsAnimatedGifImage(ByVal aBitmapImage As BitmapImage) As Boolean Debug.Assert(aBitmapImage IsNot Nothing) Dim lResult As Boolean = False If aBitmapImage.UriSource <> Nothing Then Dim lBitmapDecoder As BitmapDecoder = BitmapDecoder.Create(aBitmapImage.UriSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.[Default]) lResult = TypeOf lBitmapDecoder Is GifBitmapDecoder ElseIf aBitmapImage.StreamSource IsNot Nothing Then Try Dim lStreamPosition As Long = aBitmapImage.StreamSource.Position aBitmapImage.StreamSource.Position = 0 Dim lBitmapDecoder As New GifBitmapDecoder(aBitmapImage.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.[Default]) lResult = lBitmapDecoder.Frames.Count > 1 aBitmapImage.StreamSource.Position = lStreamPosition Catch lResult = False End Try End If Return lResult End Function Private Shared Sub ChangingFrameIndex(ByVal aObject As DependencyObject, ByVal aEventArgs As DependencyPropertyChangedEventArgs) Dim lAnimatedImage As AnimatedImage = TryCast(aObject, AnimatedImage) If lAnimatedImage Is Nothing OrElse Not lAnimatedImage.IsAnimationWorking Then Return End If Dim lFrameIndex As Integer = DirectCast(aEventArgs.NewValue, Integer) DirectCast(lAnimatedImage, Image).Source = lAnimatedImage.Decoder.Frames(lFrameIndex) lAnimatedImage.InvalidateVisual() End Sub Private Shared Sub OnSourceChanged(ByVal aObject As DependencyObject, ByVal aEventArgs As DependencyPropertyChangedEventArgs) DirectCast(aObject, AnimatedImage).OnSourceChanged(aEventArgs) End Sub Public Shared ReadOnly FrameIndexProperty As DependencyProperty = DependencyProperty.Register("FrameIndex", GetType(Integer), GetType(AnimatedImage), New UIPropertyMetadata(0, AddressOf ChangingFrameIndex)) Public Shared Shadows ReadOnly SourceProperty As DependencyProperty = DependencyProperty.Register("Source", GetType(ImageSource), GetType(AnimatedImage), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender Or FrameworkPropertyMetadataOptions.AffectsMeasure, AddressOf OnSourceChanged)) End Class
Все ответы
-
3 мая 2012 г. 12:46Отвечающий
С этим страшно ужасным классом разобраться не смог но нашел другой использующий те же классы, а значит работающий с той же производительностью. Вообще тот класс что мы с вами наваяли давным давно на этом форуме работал через Drawing а значит через GDI, то есть не имел аппаратного ускорения, ел память и процессор.
Нам же нужно показывать гиф средствами WPF и мощью DX.
Итакк - создаете новый проект WPF. В окшке только добавляете имя к Grid, называете ее "MyGrid", потом переходите к коду и полностью заменяете содержимое на
Imports System.Windows Imports System.Windows.Controls Imports System.Windows.Media Imports System.Windows.Media.Animation Imports System.Windows.Media.Imaging Class MainWindow Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded Dim x As New GifImage MyGrid.Children.Add(x) ' Путь к картинке x.GifSource = New Uri("http://www.livegif.ru/Gallery/BEST/35M2.GIF", UriKind.Absolute) ' Скорость отображения (чем выше цифра, тем быстрее скорость) x.FramesPerSecond = 4 End Sub End Class ''' <summary> ''' Класс для отображения анимированных GIF-ов. ''' </summary> Public Class GifImage Inherits Image Private _gf As GifBitmapDecoder Private _anim As Int32Animation Private _animationIsWorking As Boolean = False ''' <summary> ''' Индес отображаемого кадра GIF-а. ''' </summary> Public Property FrameIndex() As Integer Get Return CInt(GetValue(FrameIndexProperty)) End Get Set(value As Integer) SetValue(FrameIndexProperty, value) End Set End Property ''' <summary> ''' Свойство зависимоти - индес отображаемого кадра GIF-а. ''' </summary> Public Shared ReadOnly FrameIndexProperty As DependencyProperty = DependencyProperty.Register("FrameIndex", GetType(Integer), GetType(GifImage), New UIPropertyMetadata(0, AddressOf ChangingFrameIndex)) Private _gifSource As Uri Private _framesPerSecond As Double = 10.0 Private Shared Sub ChangingFrameIndex(obj As DependencyObject, ev As DependencyPropertyChangedEventArgs) Dim ob = DirectCast(obj, GifImage) ob.Source = ob._gf.Frames(CInt(ev.NewValue)) ob.InvalidateVisual() End Sub ''' <summary> ''' Адрес отображаемого GIF-а. ''' </summary> Public Property GifSource() As Uri Get Return _gifSource End Get Set(value As Uri) _gifSource = value RefreshGif() End Set End Property ''' <summary> ''' Скорость анимации GIF-а (кадров в секунду) ''' </summary> Public Property FramesPerSecond() As Double Get Return _framesPerSecond End Get Set(value As Double) _framesPerSecond = value RefreshGif() End Set End Property Private Sub RefreshGif() _gf = New GifBitmapDecoder(_gifSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.[Default]) If _gf.Frames IsNot Nothing Then _anim = New Int32Animation(0, _gf.Frames.Count - 1, New Duration(TimeSpan.FromSeconds(_gf.Frames.Count / FramesPerSecond))) _anim.RepeatBehavior = RepeatBehavior.Forever Source = _gf.Frames(0) End If End Sub Protected Overrides Sub OnRender(dc As DrawingContext) MyBase.OnRender(dc) If Not _animationIsWorking AndAlso _anim IsNot Nothing Then BeginAnimation(FrameIndexProperty, _anim) _animationIsWorking = True End If End Sub End ClassИсходя из этого кода разбираетесь дальше...
Влюблен в WPF Не пишу на C#
-
4 мая 2012 г. 13:01А в этом классе анимация страшная :) Рябит ужасно. Все изображение как из песка и частично пропадает. А можно ли сделать анимацию со скоростью кадров заданных в самом Gif? Тут просто вся практически анимация в определенном месте быстрее, в другом наоборот останавливается.
-
4 мая 2012 г. 14:01Попробуйте посмотреть проекты, в которых представлена работа с gif анимацией - WPF GIF Animation и GIF animation in WPF. Если они вас устроют - поможем переписать на vb.net.
Для связи [mail]
-
4 мая 2012 г. 19:49Первый хороший пример, работает отлично, но опять же при одном нажатии загрузки Gif выделяемая память взлетает с 13 до 60... А во втором случае видно как меняются кадры. Кадр-чисто-кадр.
-
5 мая 2012 г. 5:19Отвечающий
А в этом классе анимация страшная :) Рябит ужасно. Все изображение как из песка и частично пропадает. А можно ли сделать анимацию со скоростью кадров заданных в самом Gif? Тут просто вся практически анимация в определенном месте быстрее, в другом наоборот останавливается.
На самом деле качество картинки зависит от размера элемента GifImage, поэтому вам показалось что картинка как из песка. Сделайте оригинальный размер, то есть размер самого gif и будет нормальное качество.
Со сменой кадров согласен, тут с одной стороны плюс, можно управлять скоростью смены, а с другой стороны минус, так как часто нужны оригинальные настройки заданные в самом gif. Сечас помочь в решении проблемы не могу, но на досуге поковыряю и может решу эту проблему.
UPD. Хотя если вы все же найдете идеальное решение, то многие вам будут признательны.
Влюблен в WPF Не пишу на C#
- Изменено LXGDARKMicrosoft Community Contributor, Editor 5 мая 2012 г. 5:33
-
5 мая 2012 г. 9:13Ищу и пробую )) Насчет песка - размер оригинальный стоит. Разницы нет какой, все равно так получается. Вот gif для примера
-
13 мая 2012 г. 10:23Отвечающий
Попробуйте вот этот вариант. Входил в библиотеку семплов VS2011.
Imports System.Drawing Imports System.IO Imports System.Runtime.InteropServices Imports System.Windows Imports System.Windows.Interop Imports System.Windows.Media.Imaging Imports System.Windows.Threading Public Class AnimatedGIFControl Inherits System.Windows.Controls.Image Private _bitmap As Bitmap ' Local bitmap member to cache image resource Private _bitmapSource As BitmapSource Public Delegate Sub FrameUpdatedEventHandler() ''' <summary> ''' Delete local bitmap resource ''' Reference: http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx ''' </summary> <DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _ Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean End Function ''' <summary> ''' Override the OnInitialized method ''' </summary> Protected Overrides Sub OnInitialized(ByVal e As EventArgs) MyBase.OnInitialized(e) End Sub ''' <summary> ''' Load the embedded image for the Image.Source ''' </summary> Private Sub AnimatedGIFControl_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Me.Loaded ' Get GIF image from Resources If (My.Resources.ProgressIndicator IsNot Nothing) Then _bitmap = My.Resources.ProgressIndicator Width = _bitmap.Width Height = _bitmap.Height _bitmapSource = GetBitmapSource() Source = _bitmapSource End If End Sub ''' <summary> ''' Close the FileStream to unlock the GIF file ''' </summary> Private Sub AnimatedGIFControl_Unloaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Me.Unloaded StopAnimate() End Sub ''' <summary> ''' Start animation ''' </summary> Public Sub StartAnimate() ImageAnimator.Animate(_bitmap, AddressOf OnFrameChanged) End Sub ''' <summary> ''' Stop animation ''' </summary> Public Sub StopAnimate() ImageAnimator.StopAnimate(_bitmap, AddressOf OnFrameChanged) End Sub ''' <summary> ''' Event handler for the frame changed ''' </summary> Private Sub OnFrameChanged(ByVal sender As Object, ByVal e As EventArgs) Dispatcher.BeginInvoke(DispatcherPriority.Normal, New FrameUpdatedEventHandler(AddressOf FrameUpdatedCallback)) End Sub Private Sub FrameUpdatedCallback() ImageAnimator.UpdateFrames() If _bitmapSource IsNot Nothing Then _bitmapSource.Freeze() End If ' Convert the bitmap to BitmapSource that can be display in WPF Visual Tree _bitmapSource = GetBitmapSource() Source = _bitmapSource InvalidateVisual() End Sub Private Function GetBitmapSource() As BitmapSource Dim handle As IntPtr = IntPtr.Zero Try handle = _bitmap.GetHbitmap() _bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()) Finally If handle <> IntPtr.Zero Then DeleteObject(handle) End If End Try Return _bitmapSource End Function End ClassЧто бы его размещать в xaml смотрите старую тему, там описано как это делать. Проверил на примере той анимации что вы дали выше - работает. Только нужно gif помещать в ресурсы. Можно конечно переделать, но я думаю вы и сами справитесь. Главное основа хорошая наконец то найдена.
Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!
- Изменено LXGDARKMicrosoft Community Contributor, Editor 13 мая 2012 г. 10:28
- Помечено в качестве ответа Abolmasov DmitryMicrosoft Community Contributor 25 мая 2012 г. 11:13
-
13 мая 2012 г. 10:58ОтвечающийИ кстати не зацикливайтесь так на выделении памяти. Не сталкивались с сообщением "Out of memory" при работе наприме с фотошопом? Такое бывает когда на программу выделено меньше памяти чем ей в определенный момент понадобилось. В .Net эта проблема решается выделением памяти с запасом. Вот если каждая новая gif картинка будет прибавлять по 10 мб к памяти и после размещения 40-50 картинок програ будет есть память как добрая 3D игра, то это проблема... Если же с 40-50 картинками прога возьмет 70-80 мб, ничего страшного в этом нет.
Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

