65.9K
CodeProject 正在变化。 阅读更多。
Home

带加密功能的照片和视频查看器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (13投票s)

2009年2月12日

CPOL

3分钟阅读

viewsIcon

65562

downloadIcon

2939

一个使用 C# 和 Visual Studio 2008 查看照片和视频的个人媒体查看器。

引言

我尝试过一些图像查看器实用程序,但没有找到真正符合我偏好的一个,所以我决定自己写一个。我已经具备了我需要的所有功能,但想从专家那里获得关于一些问题的建议。

常见图像实用程序的问题

  • 以缩略图列表加载照片,没有切换到简单文件名列表的选项。当文件夹包含数百张照片时,这将花费很长时间。当卸载从具有 1GB+ SD 卡的数码相机拍摄的照片时,这非常常见。
  • 缩略图列表将驻留在宽视图窗格中,该窗格占用了主图像的宝贵视图空间,此外双击在主视图中打开照片,然后关闭并双击另一张照片也很烦人。

实用程序功能

功能太多无法一一列出,但总体思路是使照片列表尽可能窄,主视图尽可能大。选择照片将在主视图中使用默认的“适应屏幕”显示,以便用户无需向右/向下滚动即可看到整张图片。从 6 百万像素数码相机拍摄的照片通常具有 2576 x 1932 的分辨率。选择照片后,listview 将获得焦点,后续照片可以通过简单地按向上/向下键来选择下一个/上一个文件来查看。

有用的图像处理代码

//
       static Image ScaleByPercent(Image imgPhoto, int Percent)
        {
            float nPercent = ((float)Percent / 100);

            int sourceWidth = imgPhoto.Width;
            int sourceHeight = imgPhoto.Height;
            int destWidth = (int)(sourceWidth * nPercent);
            int destHeight = (int)(sourceHeight * nPercent);

            Bitmap bmPhoto = new Bitmap(destWidth, destHeight, 
					PixelFormat.Format24bppRgb);
            bmPhoto.SetResolution(imgPhoto.HorizontalResolution, 
					imgPhoto.VerticalResolution);

            Graphics grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            grPhoto.DrawImage(imgPhoto,
                new Rectangle(0, 0, destWidth, destHeight),
                new Rectangle(0, 0, sourceWidth, sourceHeight),
                GraphicsUnit.Pixel);

            grPhoto.Dispose();
            return bmPhoto;
        }

        static Image CreateThumbnail(Image imgPhoto)
        {
            Bitmap thumbBmp = new Bitmap(100, 100);
            Graphics g = Graphics.FromImage(thumbBmp);
            g.FillRectangle(Brushes.White, 0, 0, 100, 100);

            //Adjust settings to make this as high-quality as possible
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.InterpolationMode = 
		System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;

            int thumbWidth, thumbHeight;
            if (imgPhoto.Width >= imgPhoto.Height) 	// reduce width to 100 
						// then height proportionally
            {
                thumbWidth = 100;
                thumbHeight = (int)((double)imgPhoto.Height * 
					(100 / (double)imgPhoto.Width));
            }
            else // reduce height to 100 then height proportionally
            {
                thumbHeight = 100;
                thumbWidth = (int)((double)imgPhoto.Width * 
				(100 / (double)imgPhoto.Height));
            }

            // draw the original Image onto the empty Bitmap, 
	   // don't use GetThumbnailImage() from original
            // because it returns poor quality thumb
            int top = (100 - thumbHeight) / 2;
            int left = (100 - thumbWidth) / 2;

            g.DrawImage(imgPhoto, new Rectangle(left, top, thumbWidth, thumbHeight),
                new Rectangle(0, 0, imgPhoto.Width, imgPhoto.Height),
                GraphicsUnit.Pixel);

            g.Dispose();

            return thumbBmp;
        }

		  private static Image AlterBrightness(Image bmp, int level)
        {
            if (level == 50)
            {
                // do nothing
                return bmp;
            }

            Graphics graphics = Graphics.FromImage(bmp);
            if (level < 50)
            {
                // make it darker
                // Work out how much darker
                int conversion = 250 - (5 * level);
                Pen pDark = new Pen(Color.FromArgb(conversion, 0, 0, 0), bmp.Width * 2);
                graphics.DrawLine(pDark, 0, 0, bmp.Width, bmp.Height);
            }
            else if (level > 50)
            {
                // make it lighter
                // Work out how much lighter.
                int conversion = (5 * (level - 50));
                Pen pLight = new Pen(Color.FromArgb(conversion, 255, 255, 255), 
								bmp.Width * 2);
                graphics.DrawLine(pLight, 0, 0, bmp.Width, bmp.Height);
            }
            graphics.Save();
            graphics.Dispose();
            return bmp;
        }
//

加密/解密代码

//
	// encryption key (stored in global variable _CurrentPassword) must be 
	// an 8 character string and is case sensitive, ex: myKey123
         private bool EncryptFile(string inputFile, string outputFile)
        {
            bool bSuccess = false;

            if (_CurrentPassword == "") return bSuccess;

            try
            {
                UnicodeEncoding UE = new UnicodeEncoding();
                byte[] key = UE.GetBytes(@_CurrentPassword);

                string cryptFile = outputFile;
                FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);

                RijndaelManaged RMCrypto = new RijndaelManaged();

                CryptoStream cs = new CryptoStream(fsCrypt,
                    RMCrypto.CreateEncryptor(key, key),
                    CryptoStreamMode.Write);

                FileStream fsIn = new FileStream(inputFile, FileMode.Open);

                int data;
                while ((data = fsIn.ReadByte()) != -1)
                    cs.WriteByte((byte)data);


                fsIn.Close();
                cs.Close();
                fsCrypt.Close();

                bSuccess = true;
            }
            catch(Exception)
            {
                // nothing    
            }
            
            return bSuccess;
        }

	// decrypt file on the fly into MemoryStream without 
	// creating a physical file
	// useful for viewing an encrypted photo file directly 
	// ex: Bitmap orgImage = (Bitmap)Bitmap.FromStream
	// (DecryptFile(SomeFileName));
        private MemoryStream DecryptFile(string inputFile)
        {
            if (_CurrentPassword == "") return _MSErrorImage;

            FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
            try
            {
                MemoryStream msOut = new MemoryStream();
                UnicodeEncoding UE = new UnicodeEncoding();
                byte[] key = UE.GetBytes(@_CurrentPassword);

                RijndaelManaged RMCrypto = new RijndaelManaged();

                CryptoStream cs = new CryptoStream
		(fsCrypt, RMCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read);

                int data;
                while ((data = cs.ReadByte()) != -1)
                {
                    msOut.WriteByte((byte)data);
                }
                cs.Close();
                fsCrypt.Close();

                return msOut;
            }
            catch 
            {
                fsCrypt.Close(); // release file lock
                return _MSErrorImage;
            }
        }

		// decrypt into another physical file
        private bool DecryptFile(string inputFile, string outputFile)
        {
            bool bSuccess = false;
            if (_CurrentPassword == "") return bSuccess;

            FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
            try
            {
                FileStream fsOut = new FileStream(outputFile, FileMode.Create);
                UnicodeEncoding UE = new UnicodeEncoding();
                byte[] key = UE.GetBytes(@_CurrentPassword);

                RijndaelManaged RMCrypto = new RijndaelManaged();

                CryptoStream cs = new CryptoStream
		(fsCrypt, RMCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read);

                int data;
                while ((data = cs.ReadByte()) != -1)
                {
                    fsOut.WriteByte((byte)data);
                }
                cs.Close();
                fsCrypt.Close();
                fsOut.Close();
                bSuccess = true;
            }
            catch
            {
                fsCrypt.Close(); // release file lock
            }
            
            return bSuccess;
        }
//

部分缩略图创建

对于缩略图渲染,我使用类似于 Stack's Pop 的方法。

  • Listview 在设计视图中映射到 ImageList
  • 对于缩略图显示模式,将 ImageList 中的所有图像设置为默认的“正在处理”图像,并将待处理(要创建)的缩略图列表存储在自定义对象列表中。
  • 缩略图创建过程在单独的线程中运行。它循环遍历待处理的缩略图列表,每次处理 10 个(或者如果剩余少于 10 个则处理更少),然后退出并调用一个在父线程中执行的函数,以使用新创建的缩略图更新 ImageList 并刷新 ListView。然后,父线程函数从列表中弹出(删除)前 10 个项目,检查是否仍有待处理项目,并再次调用缩略图创建线程,直到 Pending 列表中没有更多项目。
//
        Thread _CreateThumbListThread = null;
        List<ThumbnailItem> _ThumbList = new List<ThumbnailItem>();
        int _MaxThumbPerThreadRun = 10;

        private void RunThumbGeneratorThread()
        {
            if (_ThumbList.Count > 0)
            {
                _CreateThumbListThread = new Thread(new ThreadStart(CreateThumbList));
                _CreateThumbListThread.Start();
            }
        }
        
        private void CreateThumbList()
        {
            
            int NumItemsToProcess = (_MaxThumbPerThreadRun 
		< _ThumbList.Count ? _MaxThumbPerThreadRun : _ThumbList.Count);
            for (int i = 0; i < NumItemsToProcess; i++)
            {
                if (IsPhoto(_ThumbList[i].FileExtension)) 
                {
                    Bitmap orgImage;

                    if (_ThumbList[i].FileFullName.IndexOf("_Enc@@") != -1) // encrypted 
							// file -> decrypt first
                    {
                        GetCurrentConfigEncryptionKey(false);
                        orgImage = (Bitmap)Bitmap.FromStream
				(DecryptFile(_ThumbList[i].FileFullName));
                    }
                    else
                    {
                        orgImage = (Bitmap)Bitmap.FromFile(_ThumbList[i].FileFullName);
                        
                    }
                    _ThumbList[i].ThumbnailImage = CreateThumbnail(orgImage);
                }
            }

            UpdateLargeIcons(NumItemsToProcess);
        }

        private delegate void UpdateLargeIconsDelegate(int ItemsProcessed);

        private void UpdateLargeIcons(int ItemsProcessed)
        {
            if (listView1.InvokeRequired)
            {
                UpdateLargeIconsDelegate d = new UpdateLargeIconsDelegate
							(UpdateLargeIcons);
                listView1.BeginInvoke(d, new object[] { ItemsProcessed });
            }
            else
            {
                for (int i = 0; i < ItemsProcessed; i++)
                {
                    thumbImageList.Images[_ThumbList[i].ThumbListIndex] = 
						_ThumbList[i].ThumbnailImage;
                }
                
                listView1.Refresh();
                
                // remove processed thumbs
                for (int j = 0; j < ItemsProcessed; j++)
                {
                    _ThumbList.RemoveAt(0);
                }

                // still has unprocessed thumbs => run generator again
                if (_ThumbList.Count > 0)
                {
                    RunThumbGeneratorThread();
                }
            }
        }

    public class ThumbnailItem
    {
        Image _ThumbImage = null;

        public ThumbnailItem(int ThumbListIndex, 
		string FileFullName, string FileExtension)
        {
            this.ThumbListIndex = ThumbListIndex;
            this.FileFullName = FileFullName;
            this.FileExtension = FileExtension;
        }
        public int ThumbListIndex
        {
            get;
            set;
        }
        public string FileFullName
        {
            get;
            set;
        }
        public string FileExtension
        {
            get;
            set;
        }
        public Image ThumbnailImage
        {
            get { return _ThumbImage; }
            set { _ThumbImage = value;  }
        }
//

使用的框架组件

  • TreeView, ListView, PictureBox, TabControl, 等等。

编译器要求/使用说明

改进需求

  • 当以缩略图列表查看照片时,ListView 最初仅加载文件名。调用一个单独的线程来生成所有缩略图,完成后,调用另一个在父线程中运行的函数,以使用生成的缩略图更新 listviewLargeIconList。这可以防止在后台创建数百个缩略图时 listview 卡死,并允许照片选择加载到主视图中。我正在寻找一种一次生成(例如)10 或 20 个缩略图并在 Listview 中显示它们,并重复相同的过程直到所有缩略图都显示出来的方法。我尝试了几种方法,例如线程回调,但显示效果太糟糕了。
    2009 年 2 月 18 日更新 - 成功修改为一次加载 10 个缩略图。
  • 缩略图创建使用 .NET 库提供的最佳设置,但某些照片的显示效果不如内置资源管理器的缩略图视图。
  • 效率 - 防止内存泄漏,更好地处理对象/初始化组件等。

历史

  • 2009 年 2 月 12 日:初始版本
  • 2009 年 2 月 18 日:修改版本
具有加密功能的照片和视频查看器 - CodeProject - 代码之家
© . All rights reserved.