WPF大位图加载和缓存辅助类

在使用WPF的Image时候,从硬盘加载大位图(1M以上)会使应用假死(卡顿)几秒钟,如果连续加载的话假死(卡)的时间更长。也很容易出现内存溢出。

初步分析,问题应该是出在文件的加载和渲染上。渲染上得耗时不多,重点是加载位图到内存的操作了。加载的操作是封装好的,kyler 没有去找内部实现去详细思考问题的原因,了解了几个实例位图的方式后。发现了可以通过流来实例并显示位图。

就想到,如果程序通过异步加载文件2进制流。然后在内存中实例化Image 就不会出现这个问题了。

理论是这样,至于是否真的能达到理想效果。写个实例出来自然就知道了。

首先是写一个测试的程序(个人习惯,会先用控件摆个界面。思考要使用的点,然后组织好界面后就开始实现逻辑)

主类代码:
--------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Threading;

namespace kyler.image
{
    /// <summary>
    /// 图片加载进度事件
    /// </summary>
    /// <param name="sender">加载者对象</param>
    /// <param name="byteLoaded">已经加载的byte数</param>
    /// <param name="byteTotile">总长度</param>
    public delegate void LoadProgress(ImageThreadLoad sender,long byteLoaded,long byteTotile);

    /// <summary>
    /// 图片加载完成
    /// </summary>
    /// <param name="sender">加载者对象</param>
    public delegate void LoadComplete(ImageThreadLoad sender);

    /// <summary>
    /// 加载状态枚举
    /// </summary>
    public enum ImageLoadStatus {
        /// <summary>
        /// 已准备好,可以加载图片
        /// </summary>
        Ready,
        /// <summary>
        /// 图片加载中
        /// </summary>
        Progress,
        /// <summary>
        /// 图片已经加载完成
        /// </summary>
        Complete
    }

    /// <summary>
    /// 异步图片加载类
    /// </summary>
    public class ImageThreadLoad : DependencyObject
    {
        /// <summary>
        /// 错误消息,在结束时间中如果没获取到对象。请查看该对象以了解错误原因
        /// </summary>
        public String ErrorMessage;

        /// <summary>
        /// 当前加载的图片原始地址
        /// </summary>
        public String ImagePath;

        /// <summary>
        /// 缩略图的限制大小
        /// </summary>
        public Size MaxSize = new Size(100,100);

        /// <summary>
        /// 加载完成事件
        /// </summary>
        public event LoadComplete Complete;

        /// <summary>
        /// 加载进度事件
        /// </summary>
        public event LoadProgress Progress;

        public static DependencyProperty StatusProperty = DependencyProperty.Register("Status", //属性名称
           typeof(ImageLoadStatus),
            //属性类型
           typeof(ImageThreadLoad),
            //该属性所有者,即将该属性注册到那个类上
           new PropertyMetadata(ImageLoadStatus.Ready)); //属性默认值
        public ImageLoadStatus Status { get { return (ImageLoadStatus)GetValue(StatusProperty); } set { SetValue(StatusProperty, value); } }
        //public ImageLoadStatus Status { get; set ; } ;

        public static DependencyProperty LengthProperty = DependencyProperty.Register("Length", //属性名称
           typeof(long),
            //属性类型
           typeof(ImageThreadLoad),
            //该属性所有者,即将该属性注册到那个类上
           new PropertyMetadata(0L)); //属性默认值
        public long Length { get { return (long)GetValue(LengthProperty); } set { SetValue(LengthProperty, value); } }

        public static DependencyProperty ByteLoadedProperty = DependencyProperty.Register("ByteLoaded", //属性名称
           typeof(long),
            //属性类型
           typeof(ImageThreadLoad),
            //该属性所有者,即将该属性注册到那个类上
           new PropertyMetadata(0L)); //属性默认值
        public long ByteLoaded { get { return (long)GetValue(ByteLoadedProperty); } set { SetValue(ByteLoadedProperty, value); } }

        public ImageThreadLoad() {
            thread = new Thread(new ThreadStart(runLoad));
            Status = ImageLoadStatus.Ready;
        }

        MemoryStream ms = null;//图像文件的内存流
        Thread thread = null;//异步进程

        /// <summary>
        /// 获取用于显示的 BitmapImage 对象,原始尺寸。
        /// </summary>
        public BitmapImage Image
        {
            get
            {

                if (ms == null ¦¦ Status != ImageLoadStatus.Complete)
                {
                    return null;
                }
                BitmapImage BI = null;
                lock (ms)
                {
                    BI = new BitmapImage();
                    ms.Seek(0, SeekOrigin.Begin);

                    BI.BeginInit();
                    BI.StreamSource = ms;
                    BI.EndInit();
                }
                return BI;
            }
        }

        /// <summary>
        /// 获取缩略图,默认不大于100,如要设置尺寸请在获取前设置MaxSize属性
        /// </summary>
        public BitmapImage ThumbnaiImage
        {
            get
            {

                if (ms == null ¦¦ Status != ImageLoadStatus.Complete)
                {
                    return null;
                }
                BitmapImage BI = null;
                lock (ms)
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    System.Drawing.Image fullSizeImg = System.Drawing.Image.FromStream(ms);

                    int filewidth = 0;
                    int fileheight = 0;

                    if (fullSizeImg.Height <= fullSizeImg.Width)
                    {
                        filewidth = (int)MaxSize.Width;
                        fileheight = fullSizeImg.Height /(fullSizeImg.Width / (int)MaxSize.Width) ;
                    }
                    else
                    {
                        fileheight = (int)MaxSize.Height;
                        filewidth = fullSizeImg.Width / (fullSizeImg.Height / (int)MaxSize.Height);
                    }

                    System.Drawing.Image nimag = fullSizeImg.GetThumbnailImage(filewidth, fileheight,null,System.IntPtr.Zero);
                    BI = new BitmapImage();

                    System.IO.MemoryStream tms = new System.IO.MemoryStream();
                    nimag.Save(tms, System.Drawing.Imaging.ImageFormat.Png);// 格式自处理,这里用 bitmap
                    // 下行,初始一个 ImageSource 作为 myImage 的Source
                    nimag.Dispose();
                    fullSizeImg.Dispose();

                    BI.BeginInit();
                    BI.StreamSource = tms;
                    BI.EndInit();
                }

                return BI;
            }
        }

        /// <summary>
        /// 清除所有资源占用并销毁线程内存流等对象,解除事件绑定。
        /// </summary>
        public void Destroy() {
            if (thread != null && thread.IsAlive)
            {
                thread.Abort();
            }
            if (ms != null) {
                ms.Close();
                ms = null;
            }

            if (Complete != null) {
                Delegate[] dlist = Complete.GetInvocationList();
                foreach (Delegate d in dlist) {
                    this.Complete -= (LoadComplete)d;
                }
            }

            if (Progress != null)
            {
                Delegate[] dlist = Progress.GetInvocationList();
                foreach (Delegate d in dlist)
                {
                    this.Progress -= (LoadProgress)d;
                }
            }
        }

        /// <summary>
        /// 加载指定本地图片。
        /// </summary>
        /// <param name="path">本地地址,相对或绝对地址</param>
        public void LoadImage(String path) {
            ImagePath = path;
            FileInfo fileinfo = new FileInfo(path);
            if (fileinfo.Exists)
            {
                Length = fileinfo.Length;

                if (Status == ImageLoadStatus.Ready)
                {
                    Status = ImageLoadStatus.Progress;
                    thread.Start();
                }
                else if (Status == ImageLoadStatus.Complete)
                {
                    //sendComplete();
                    Application.Current.Dispatcher.
                    BeginInvoke(DispatcherPriority.
                    Normal, (ThreadStart)delegate()
                    { sendComplete(); });
                }
            }
            else {
                ErrorMessage = "文件不存在!";
                Application.Current.Dispatcher.
                    BeginInvoke(DispatcherPriority.
                    Normal, (ThreadStart)delegate()
                    { sendComplete(); });
            }

        }

        /// <summary>
        /// 触发完成事件。完成不一定是加载成功,发生错误而失败也会触发完成事件。
        /// 具体查看ErrorMessage来获取错误信息
        /// </summary>
        private void sendComplete()
        {
            Status = ImageLoadStatus.Complete;
            if (Complete != null) {
                Complete.Invoke(this);
            }
        }

        void runLoad()
        {
            ms = new MemoryStream();
            FileStream fs = new FileStream(ImagePath, FileMode.Open);
            byte[] buff = new byte[2048*30];
            long size = 0;
            int readCount = 0;
            while ((readCount = fs.Read(buff, 0, buff.Length)) != 0)
            {
                size += readCount;

               // Console.WriteLine("Read:" + readCount + ",All Size:" + size);
                ms.Write(buff, 0, readCount);
                Thread.Sleep(10);
                try
                {
                    Application.Current.Dispatcher.
                        BeginInvoke(DispatcherPriority.
                        Normal, (ThreadStart)delegate()
                        { sendProgress(size); });
                }
                catch (Exception ex) {
                    //
                }

            }

            try
            {
                Application.Current.Dispatcher.
                    BeginInvoke(DispatcherPriority.
                    Normal, (ThreadStart)delegate()
                    { sendComplete(); });
            }
            catch (Exception ex)
            {
                //
            }
        }

        /// <summary>
        /// 发送当前的进度
        /// </summary>
        /// <param name="size"></param>
        private void sendProgress(long size)
        {
            ByteLoaded = size;
            if (Progress != null) {
                Progress.Invoke(this,ByteLoaded,Length);
            }
        }
    }
}

-------------------------

使用方法

---------------------------------

            ImageThreadLoad itl = new ImageThreadLoad();
            itl.Progress += new LoadProgress(itl_Progress);   //事件侦听-加载进度
            itl.Complete += new LoadComplete(itl_Complete);//事件侦听-完成事件

            itl.LoadImage(fileName);  //加载

----------------------------------

事件侦听处理函数

-------------------------------------

        void itl_Complete(ImageThreadLoad sender)
        {
            //image1.Source = sender.Image;              // 获取加载的原图
            //sender.Length                                     // 加载位图的文件大小
            //sender.MaxSize = new Size(150, 150);     // 缩略图最大尺寸
            //image.Source = sender.ThumbnaiImage;   // 获取缩略图
        }

        void itl_Progress(ImageThreadLoad sender, long byteLoaded, long byteTotile)
        {
            //progressBar1.Maximum = byteTotile;       // 总字节大小
            //progressBar1.Value = byteLoaded;          // 已加载大小
        }

--------------------------------------

至此,代码已经全部结束了。

希望能给予一定的帮助。谢谢。

转载于:https://www.cnblogs.com/kyler/archive/2011/08/17/2143091.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值