C#作业总结(2)
先来说一下MiniPhoto部分。
一。核心 滤镜算法(当然网上也有,我自己不过是修改其中的错误和优化)
二。UI界面 这是仿写的,不过写的一般
先给出程序的运行图片
开始一步步分析
1.在C++里面图片的访问都是使用指针,C#里面也是可以用指针的,不过不建议使用,这里我也是上网补了一下C#里面图片的访问,代码如下
private int width; //图片的宽度 private int height; private int step; //步长 private BitmapData srcBitData; //图像数据 private BitmapData dstBitData; private Bitmap srcBit; //具体的图片 private Bitmap dstBit; private byte[] srcValues; //C#里面就是将所有的图片数据放到数组里面然后访问 private byte[] dstValues; private System.IntPtr srcPtr; //指针 private System.IntPtr dstPtr; public BasicFilter(Bitmap src) { srcBit = src; width = src.Width; height = src.Height; Init(); } private void Init() { Rectangle rect = new Rectangle(0, 0, width, height); dstBit = new Bitmap(width, height); //实例化 srcBitData = srcBit.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //锁定内存 获取数据(c++里面会获得内存的头指针) step = srcBitData.Stride; srcValues = new byte[step * height]; //分配数组内存 dstValues = new byte[step * height]; srcBit.UnlockBits(srcBitData); //解锁 否则其他程序无法访问 } private void InitFilter() { Rectangle rect = new Rectangle(0, 0, width, height); srcBitData = srcBit.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); dstBitData = dstBit.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); srcPtr = srcBitData.Scan0; //获取相关指针 dstPtr = dstBitData.Scan0; System.Runtime.InteropServices.Marshal.Copy(srcPtr, srcValues, 0, srcValues.Length); //内存的复制 将数据复制到数组里面 System.Runtime.InteropServices.Marshal.Copy(dstPtr, dstValues, 0, dstValues.Length); }
然后所有的操作就如同操作数组一样,不过最后必须加上下面的代码
System.Runtime.InteropServices.Marshal.Copy(dstValues, 0, dstPtr, dstValues.Length); //数组复制到指针内存区 srcBit.UnlockBits(srcBitData); //解锁 dstBit.UnlockBits(dstBitData);
2.好了,下面就是改代码,从C++到C#,这里我就不放代码了,直接给出下载的链接(C#版本的,当然C++我也有,需要的在下面留言)
http://download.csdn.net/detail/zhoupeng39/8255507
3.下面就是UI部分了
刚开始做的时候遇到了一个很简单的问题,就是透明图片的问题,当时我以为需要自己写一个透明Panel,然后就是搜代码,改写,后来发现C#里面会自动对有透明通道的图片进行合成,这可比MFC方便多了,省去我自己重绘控件,其实透明的原理也相当简单,我就不说了。
(1)选项卡的切换
其实是不存在选项卡的,下面的选项都是Label控件,我增加了鼠标移入移出和点击的效果,然后对应显示图片,就OK了
(2)图片的切换
这里由于滤镜写的不多,没有用到滚动条,这块也就简单了,直接上十个PictureBox控件,对应的加载图片,切换的时候去掉不需要显示的控件就可以了
(3)参数调节窗口
本来这块是准备写在主界面上的,后来觉得里面的控件太丑,自己去重绘控件时间不够,于是加了额外的窗口,而且使用原始的控件。
这里提一下子窗口和父窗口的交互问题,网上很多,我搜了一个比较简单的(MFC里面直接传递消息就OK了)
父窗口
ColorTone Base1 = new ColorTone(255, 0, 0, 20); Base1.ShowDialog(this); //显示 制定当前窗口是父窗口 //子窗口里面调用 public void ColorToneChange(byte r,byte g,byte b,int satu) { Bitmap dst = m_BaseFilter.ColorToneFilter(r, g, b, satu); picture_Photo.Image = dst; }
子窗口
m_mainForm = (Form1)this.Owner; //获取父窗口 m_mainForm.ColorToneChange(m_R, m_G, m_B, m_satu); //交互
可以看出是通过一个Public方法来交互的,是不是很简单,当然还有很多其他方法。
(4)菜单部分
菜单的移入和移出不是很难,几个定时器足以完成,这部分我在历史图片处犯了很多错误,就是撤销按钮和还原按钮
给出示例代码
private void InsertBitmap(Bitmap src) { if (m_CurrentIndex<10) { m_HistoryBitmap[m_CurrentIndex++] = src.Clone(new Rectangle(0, 0, src.Width, src.Height), src.PixelFormat); //注意 直接 = 是引用操作 历史图片不会保存 必须使用复制的方法 } else { for (int i = 0; i < 9; i++) m_HistoryBitmap[i] = m_HistoryBitmap[i + 1]; //引用操作 m_HistoryBitmap[9] = src.Clone(new Rectangle(0, 0, src.Width, src.Height), src.PixelFormat); //复制 } }
给引用操作搞了很长时间,必须复制
(5)图片的放大缩小
为了保证滤镜的速度,我先是缩小图片,保存的时候再放大图片,使用GDI插值缩小放大
代码如下
Bitmap src = new Bitmap(path); m_SrcWidth = src.Width; m_SrcHeight = src.Height; //记住原始的宽度高度 double scale; int w, h; if (m_SrcWidth >= m_SrcHeight) { scale = m_SrcHeight / (double)m_SrcWidth; //记住缩放的比例 w = 300; h = (int)(300 * scale); } else { scale = m_SrcWidth / (double)m_SrcHeight; w = (int)(300 * scale); h = 300; } m_ProcessImage = new Bitmap(w, h); Graphics g = Graphics.FromImage(m_ProcessImage); //使用GDI+ 绘制 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; //高质量插值 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.SmoothingMode |= System.Drawing.Drawing2D.SmoothingMode.AntiAlias; //抗锯齿 g.DrawImage(src, new Rectangle(0, 0, w, h), new Rectangle(0, 0, m_SrcWidth, m_SrcHeight), GraphicsUnit.Pixel); //缩小 picture_Photo.Size = new Size(w, h); picture_Photo.Image = m_ProcessImage; g.Dispose(); //释放资源 src.Dispose();
好了基本的东西就这些,不是很多,毕竟花的时间很短,得去复习了,蛋疼的考试..