wpf GifBitmapDecoder 解析 gif 格式

本人看见了天涯论坛首页登录背景以及普通的地人描绘的博客,发现了只能看的意义。

每当网上有诸多图纸都是gif,那么咋样以 wpf 解析 gif?

图片 1

本通知诉我们怎么样用 GifBitmapDecoder
把gif分开为同一摆同摆,拿到他的信息。

那么我来报大家如何做那多少个效果。

设若欲把一个 gif 分开,使用的代码很简短

率先步是于 Canvas 画点,第二步是吃点运动,第三步是画线

            var file = "E:\\林德熙\\测试文件\\2017年9月1日 10.gif";
            var stream = new FileStream(file, FileMode.Open);
            var decoder = new GifBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

以 Canvas 画一个接触

咱画点可以接纳 Ellipse 我们为他从容和强,Fill,就可写出。需要加以于
Canvas ,可以用canvas.Children.Add(ellipse)

Canvas 一开端之分寸是 0 ,需要同上马为他一个足大的价值

<Canvas x:Name="P" Width="1000" Height="1000"/>

遂为他一个于坏之价,超过外面的Grid就非显。

补给加 一个 Ellipse 就会面显,可以没有点名在哪展现,也即便是突显的 X 和 Y。

于 decoder 就足以获得每个图片,例如写一个按钮,按一下虽切换一个图纸。

点名添加到 Canvas 的 Element 的岗位

咱得以经过多少个措施改变控件的岗位,在我后边写的拖动控件博客有说及。

现在动 Canvas,可以利用 Canvas 有的一个主意。

设有一个 X 和 Y 要安装在控件,那么可用

                Canvas.SetLeft(control, X);
                Canvas.SetTop(control, Y);

注意,Canvas 是类。

斯主意好活动控件。

自己就是之所以外移动点。

      private int n = 0;

        private void Button_OnClick(object sender, RoutedEventArgs e)
        {
            var t = decoder.Frames[n];

            Image.Source = t;
            n++;
            if (n >= decoder.Frames.Count)
            {
                n = 0;
            }
        }

随便移动点

我首先写一个看似,Staf。包含呈现的 Point 和外的
X,Y,两单样子走速度。还有移动多长时间,领先了可走的日子,就自由给新活动速度。

    public class Staf
    {
        public UIElement Point { set; get; }

        public double X { set; get; }

        public double Y { set; get; }

        public double Vx { set; get; }

        public double Vy { set; get; }

        public void RandomStaf(Random ran)
        {
            var staf = this;
            _ran = ran;
            staf.Vx = (double)ran.Next(-1000, 1000) / 1000;
            staf.Vy = (double)ran.Next(-1000, 1000) / 1000;
            staf.Time = ran.Next(100);
        }
        private Random _ran;
        public int Time
        {
            set
            {
                _time = value;
                if (value == 0)
                {
                    RandomStaf(_ran);
                }
            }
            get
            {
                return _time;
            }
        }

        private int _time;
    }

按钮点击如下边代码,可以看看 decoder 把 gif
分开很简单,不过怎么赢得一帧之岁月。假使当 wpf 拿到 gif
图片间隔,就需有些特别模式。

画线

动用有限再次 foreach
,拿到三只点期间距离,如若离开小于我叫的一个价,那么就是得连线

这距离长的虽将连线的宽度变短。

其一做法特别简短,可以运用 StrokeThickness 设置线宽度。

line.StrokeThickness=最大宽度 * (最大距离-距离)/最大距离

线需要有些只点可以确定?这一个自家便非说啊,确定了点儿独点是可连线,于是下就可设置线的点。需要精晓,点的X和Y是左上角,需要加上画的图形的值才是连当触发,不然看起不是并在点。

先创制一个类 用于获取 gif
的音信,需要领悟,每个gif的内部的图纸都来信息。

自行移动

好应用 Dispatcher提姆er ,过 0.1 s就移动点和画线。

        public MainPage()
        {
            this.InitializeComponent();
            _time = new DispatcherTimer();
            _time.Interval = TimeSpan.FromTicks(500);
            _time.Tick += Time_Tick;            
            _time.Start();
        }

private DispatcherTimer _time

Time_Tick就描写移动点和线之代码

        class FrameInfo
        {
            public TimeSpan Delay { get; set; }
            public FrameDisposalMethod DisposalMethod { get; set; }
            public double Width { get; set; }
            public double Height { get; set; }
            public double Left { get; set; }
            public double Top { get; set; }

            public Rect Rect
            {
                get { return new Rect(Left, Top, Width, Height); }
            }
        }

一体代码

<Page
    x:Class="Bsgame.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Bsgame"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Canvas x:Name="P" Width="1000" Height="1000">

        </Canvas>
        <Canvas x:Name="Pw" Width="1000" Height="1000"></Canvas>
    </Grid>
</Page>


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;

namespace Bsgame
{
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            _time = new DispatcherTimer();
            _time.Interval = TimeSpan.FromTicks(500);
            _time.Tick += Time_Tick;
            RandomStaf();
            _time.Start();
            _width = Window.Current.Bounds.Width;
            _height = Window.Current.Bounds.Height;//lindexi
        }

        private void RandomStaf()
        {
            const int count = 20;

            for (int i = 0; i < count; i++)
            {
                Staf staf = new Staf();
                staf.X = ran.Next((int)_width);
                staf.Y = ran.Next((int)_height);
                staf.Point = new Ellipse()
                {
                    Height = 10,
                    Width = 10,
                    Fill = new SolidColorBrush(Colors.Gray),
                };
                staf.RandomStaf(ran);
                // CSDN
                _staf.Add(staf);
            }

            foreach (var temp in _staf)
            {
                P.Children.Add(temp.Point);
                //lindexi
            }
        }

        private List<Staf> _staf = new List<Staf>();

        private double _width;
        private double _height;

        private void Time_Tick(object sender, object e)
        {
            foreach (var temp in _staf)
            {
                if (temp.X > _width || temp.Y > _height
                    || temp.X < 0 || temp.Y < 0)
                {
                    temp.X = ran.Next((int)_width);
                    temp.Y = ran.Next((int)_height);
                }//lindexi.oschina.io

                temp.X -= temp.Vx;
                temp.Y -= temp.Vy;

                Canvas.SetLeft(temp.Point, temp.X);
                Canvas.SetTop(temp.Point, temp.Y);

                temp.Time--;
            }
            const double distan = 200;
            Pw.Children.Clear();
            Line line = new Line();
            foreach (var temp in _staf)
            {
                foreach (var p in _staf)
                {
                    line.X1 = temp.X + 5;
                    line.Y1 = temp.Y + 5;
                    line.X2 = p.X + 5;
                    line.Y2 = p.Y + 5;
                    double sqrt = Math.Sqrt(Math.Pow((line.X1 - line.X2), 2) +
                      Math.Pow((line.Y1 - line.Y2), 2));
                    if (sqrt < distan)
                    {
                        line.Stroke = new SolidColorBrush(Colors.Gray);
                        line.StrokeThickness = 5* (distan- sqrt) /distan;
                        Pw.Children.Add(line);
                        line = new Line();
                    }
                }
            }
        }

        private Random ran = new Random();

        private DispatcherTimer _time;
    }

    public class Staf
    {
        public UIElement Point { set; get; }

        public double X { set; get; }

        public double Y { set; get; }

        public double Vx { set; get; }

        public double Vy { set; get; }

        public void RandomStaf(Random ran)
        {
            var staf = this;
            _ran = ran;
            staf.Vx = (double)ran.Next(-1000, 1000) / 1000;
            staf.Vy = (double)ran.Next(-1000, 1000) / 1000;
            staf.Time = ran.Next(100);
        }
        private Random _ran;
        public int Time
        {
            set
            {
                _time = value;
                if (value == 0)
                {
                    RandomStaf(_ran);
                }
            }
            get
            {
                return _time;
            }
        }

        private int _time;
    }
}

可看看性能好不同,于是将连线去丢,呈现点不展现连续

        private void RandomStaf(object sender, object e)
        {
            Storyboard board = new Storyboard();
            board.Duration = new Duration(TimeSpan.FromSeconds(1));
            board.Completed += RandomStaf;
            DoubleAnimationUsingKeyFrames animation;
            foreach (var temp in _staf)
            {
                double f = temp.X;

                temp.X += temp.Vx * 10;
                if (temp.X > _width - 100)
                {
                    temp.X = _width - 100;
                }
                else if (temp.X < 0)
                {
                    temp.X = 0;
                }



                animation = EllPoile(f, temp.X);
                Storyboard.SetTarget(animation, temp.Point);
                Storyboard.SetTargetProperty(animation, "(Canvas.Left)");
                board.Children.Add(animation);

                f = temp.Y;
                temp.Y += temp.Vy * 10;

                if (temp.Y > _height - 100)
                {
                    temp.Y = _height - 100;
                }
                else if (temp.Y < 0)
                {
                    temp.Y = 0;
                }

                animation = EllPoile(f, temp.Y);
                Storyboard.SetTarget(animation, temp.Point);
                Storyboard.SetTargetProperty(animation, "(Canvas.Top)");

                if (temp.X >= _width - 100 || temp.Y >= _height - 100
                      || temp.X <= 0 || temp.Y <= 0)
                {
                    temp.X = ran.Next((int)_width);
                    temp.Y = ran.Next((int)_height);
                }
                board.Children.Add(animation);
                temp.Time -= 10;

                animation = EllPoile(10, 15);
                Storyboard.SetTarget(animation, temp.Point);
                Storyboard.SetTargetProperty(animation, "Height");
                board.Children.Add(animation);

                animation = EllPoile(10, 15);
                Storyboard.SetTarget(animation, temp.Point);
                Storyboard.SetTargetProperty(animation, "Width");
                board.Children.Add(animation);

                animation = new DoubleAnimationUsingKeyFrames();
                EasingDoubleKeyFrame frame = new EasingDoubleKeyFrame();
                frame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0));
                frame.Value = 0;
                animation.KeyFrames.Add(frame);

                frame = new EasingDoubleKeyFrame();
                frame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.5));
                frame.Value = 180;
                animation.KeyFrames.Add(frame);

                frame = new EasingDoubleKeyFrame();
                frame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1));
                frame.Value = 0;
                animation.KeyFrames.Add(frame);
                Storyboard.SetTarget(animation, temp.Point.RenderTransform);
                Storyboard.SetTargetProperty(animation, "(CompositeTransform.Rotation)");
                board.Children.Add(animation);

            }
            board.Begin();

        }

图片 2
按随笔使用文化共享署名-非商业性使用-相同情势共同享 4.0
国际许可协议
举行许可。欢迎转载、使用、重新宣布,但不能够不保留小说署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd
),不得用于生意目标,基于本文修改后的著作必须以同一之认可发表。如有另问题,请和己联系

其中 Delay
就是少数只图片播放的年华,FrameDisposalMethod表示两布置图片是何等播放,完全替换前同一摆设或于头里同一摆基础继续显示。

获 gif 的信息需要使用 GetQuery
,这一个艺术不佳用,于是下下代码把他转类型

        private static T? GetQueryOrNull<T>(this BitmapMetadata metadata, string query)
            where T : struct
        {
            if (metadata.ContainsQuery(query))
            {
                object value = metadata.GetQuery(query);
                if (value != null)
                    return (T) value;
            }
            return null;
        }

可看看 decoder 的每个图片的 MetadataImageMetadata
,而且wr也从不说它们里面有安数据。

实则得行使BitmapMetadata得每个图片音讯,因为Metadata实际是BitmapMetadata,通过/grctlext/Delay得取有限单图片的时,/grctlext/Disposal可抱有限独图片是什么样显示,/imgdesc/Width
可以拿走宽度。于是用下函数可以取得图片信息

        public static FrameInfo GetFrameInfo(BitmapFrame frame)
        {
            var frameInfo = new FrameInfo
            {
                Delay = TimeSpan.FromMilliseconds(100),
                DisposalMethod = FrameDisposalMethod.Replace,
                Width = frame.PixelWidth,
                Height = frame.PixelHeight,
                Left = 0,
                Top = 0
            };

            BitmapMetadata metadata;
            try
            {
                metadata = frame.Metadata as BitmapMetadata;
                if (metadata != null)
                {
                    const string delayQuery = "/grctlext/Delay";
                    const string disposalQuery = "/grctlext/Disposal";
                    const string widthQuery = "/imgdesc/Width";
                    const string heightQuery = "/imgdesc/Height";
                    const string leftQuery = "/imgdesc/Left";
                    const string topQuery = "/imgdesc/Top";

                    var delay = metadata.GetQueryOrNull<ushort>(delayQuery);
                    if (delay.HasValue)
                        frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value);

                    var disposal = metadata.GetQueryOrNull<byte>(disposalQuery);
                    if (disposal.HasValue)
                        frameInfo.DisposalMethod = (FrameDisposalMethod) disposal.Value;

                    var width = metadata.GetQueryOrNull<ushort>(widthQuery);
                    if (width.HasValue)
                        frameInfo.Width = width.Value;

                    var height = metadata.GetQueryOrNull<ushort>(heightQuery);
                    if (height.HasValue)
                        frameInfo.Height = height.Value;

                    var left = metadata.GetQueryOrNull<ushort>(leftQuery);
                    if (left.HasValue)
                        frameInfo.Left = left.Value;

                    var top = metadata.GetQueryOrNull<ushort>(topQuery);
                    if (top.HasValue)
                        frameInfo.Top = top.Value;
                }
            }
            catch (NotSupportedException)
            {
            }

            return frameInfo;
        }

参见:
http://www.thomaslevesque.com/2011/03/27/wpf-display-an-animated-gif-image/

http://stackoverflow.com/questions/210922/how-do-i-get-an-animated-gif-to-work-in-wpf

图片 3
依据作使用文化共享署名-非商业性使用-相同方法共同享 4.0
国际许可协议
展开许可。欢迎转载、使用、重新发布,但要保留著作署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd
),不得用于生意目标,基于本文修改后底著述要以同一之认同宣布。如暴发另问题,请和自家联系

Leave a Comment.