当前位置:编程学习 > C#/ASP.NET >>

c#绘图程序 内存占用巨大,求解决

小弟正在上的一门课,做一个类似MS 画图的程序,老师讲的代码中,利用双缓冲绘图时会闪烁,拼接了网上的一些代码,解决了闪烁的问题,但是内存的占用却直线飙升,最高达到300M,一个28k的小程序占用这么多内存,汗。。。
求高手指点一二(初学,水平有限,高手勿喷)
namespace pencil
{
    public partial class FormMain : Form
    {
        //声明canmove以判定是否绘制
        public bool CanMove = false;
        //当前画笔的线宽
        public int DrawPenWidth = 5;
        //画笔的颜色
        public Color DrawPenColor = Color.Red;
        public Image FinishImg;
        //定义窗口绘图对象
        public Graphics WndGraphics = null;
        //画笔
        private Pen _pen = null;
        //双缓冲技术的位图
        public Bitmap Bitmap = null;
        //与位图相关的绘图对象
        public Graphics BitmapGraphics = null;
        public List<Shape> _listShape = new List<Shape>();
        //保存绘图类型,默认为直线
        public OperationType _OperationType = OperationType.Line;

        public FormMain()
        {
            InitializeComponent();
        }
        //菜单内各个选项的响应事件
        //直线
        private void MenuItemLine_Click(object sender, EventArgs e)
        {
            _OperationType = OperationType.Line;
        }
        //矩形
        private void MenuItemRectangle_Click(object sender, EventArgs e)
        {
            _OperationType = OperationType.Rectangle;
        }
        //圆
        private void MenuItemCircle_Click(object sender, EventArgs e)
        {
            _OperationType = OperationType.Circle;
        }
        //停止
        private void MenuItemStop_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        //曲线
        private void MenuItemCurve_Click(object sender, EventArgs e)
        {
            _OperationType = OperationType.Curve;
        }
        //鼠标下压的响应事件
        private void FormMain_MouseDown(object sender, MouseEventArgs e)
        {
            if (_OperationType == OperationType.Line)
            {
                //保存直线的起点
                Line templine = new Line();
                templine._Point1 = new Point(e.X, e.Y);
                //将起点存入链表
                _listShape.Add(templine);
            }
            else if (_OperationType == OperationType.Rectangle)
            {
                //保存矩形的起点
                Rectangle tempRec = new Rectangle();
                tempRec._Point1 = new Point(e.X, e.Y);
                _listShape.Add(tempRec);
            }
            else if (_OperationType == OperationType.Circle)
            {
                //保存圆心
                Circle circle = new Circle();
                circle._Center = new Point(e.X, e.Y);
                _listShape.Add(circle);
            }
            else if (_OperationType == OperationType.Curve)
            {
                //FinishImg = (Image)Bitmap.Clone();
                //将move设置为true,以检测move值确定是否终止
                CanMove = true;
                //创建曲线对象
                Line curve = new Line();
                //保存曲线的第一个点
                curve._Point1 = new Point(e.X, e.Y);
                _listShape.Add(curve);
            }
        }
        //绘制曲线时鼠标移动的响应事件
        private void FormMain_MouseMove(object sender, MouseEventArgs e)
        {
            this.Text = string.Format("X:{0},Y:{1}", e.X, e.Y);
            if (e.Button == MouseButtons.Left)
            {
                if (_OperationType != OperationType.Curve)
                {
                    FinishImg =new Bitmap(Bitmap);
                    WndGraphics = Graphics.FromImage(FinishImg);
                    WndGraphics.SmoothingMode = SmoothingMode.AntiAlias;
                }
                if (_OperationType == OperationType.Curve && CanMove == true)
                {
                    Graphics g1 = Graphics.FromImage(FinishImg);
                    g1.SmoothingMode = SmoothingMode.AntiAlias;
                    //确定第二个点并绘制直线
                    g1.DrawLine(_pen, ((Line)_listShape[_listShape.Count - 1])._Point1, new Point(e.X, e.Y));
                    //将point2赋给point1,继续移动鼠标,绘制第二个点与第三个点之间的直线,以此类推
                    ((Line)(_listShape[_listShape.Count - 1]))._Point1 = new Point(e.X, e.Y);
                    g1.Dispose();
                }
                else if (_OperationType == OperationType.Line)
                {
                    WndGraphics.DrawLine(_pen, ((Line)_listShape[_listShape.Count - 1])._Point1, new Point(e.X, e.Y));
                }
                else if (_OperationType == OperationType.Rectangle)
                {
                    //将第一个点存入临时点中
                    Point tempPoint = ((Rectangle)_listShape[_listShape.Count - 1])._Point1;
                    Point leftTop = new Point();
                    //确定左上角的点横纵坐标
                    leftTop.X = (tempPoint.X < e.X) ? tempPoint.X : e.X;
                    leftTop.Y = (tempPoint.Y < e.Y) ? tempPoint.Y : e.Y;
                    //绘制矩形
                    WndGraphics.DrawRectangle(_pen, leftTop.X, leftTop.Y, Math.Abs(tempPoint.X - e.X), Math.Abs(tempPoint.Y - e.Y));
                }
                else if (_OperationType == OperationType.Circle)
                {
                    //取出圆心
                    Point center = ((Circle)_listShape[_listShape.Count - 1])._Center;
                    //以像素填充法突出显示圆心,二三个参数为圆心坐标,四五个参数为填充像素的大小
                    WndGraphics.FillRectangle(new SolidBrush(Color.Red), center.X, center.Y, 3, 3);
                    //圆的半径
                    float R = (float)Math.Sqrt(Math.Pow((e.X - center.X), 2) + Math.Pow((e.Y - center.Y), 2));
                    //绘制
                    WndGraphics.DrawEllipse(_pen, center.X - R, center.Y - R, 2 * R, 2 * R);
                }
            }
            WndGraphics.Dispose();
            reDraw(FinishImg);
        }
        private void reDraw(Image img)
        {
            Graphics graphics = this.CreateGraphics();
            //graphics = this.CreateGraphics();
            //重绘
            graphics.DrawImage(img, 0, 0);
            graphics.Dispose();
        }
        //鼠标松开的响应事件
        private void FormMain_MouseUp(object sender, MouseEventArgs e)
        {
            //直线
            if (_OperationType == OperationType.Line)
            {
                //保存终点
                ((Line)(_listShape[_listShape.Count - 1]))._Point2 = new Point(e.X, e.Y);
                _listShape[_listShape.Count - 1].Draw(BitmapGraphics);
            }
            //矩形
            else if (_OperationType == OperationType.Rectangle)
            {
                ((Rectangle)(_listShape[_listShape.Count - 1]))._Point2 = new Point(e.X, e.Y);
                _listShape[_listShape.Count - 1].Draw(BitmapGraphics);
            }
            //圆
            else if (_OperationType == OperationType.Circle)
            {
                //圆心
                Point center = ((Circle)_listShape[_listShape.Count - 1])._Center;
                //圆的半径
                ((Circle)(_listShape[_listShape.Count - 1]))._R =
                    (float)Math.Sqrt(Math.Pow((e.X - center.X), 2) + Math.Pow((e.Y - center.Y), 2));
                //绘制
                _listShape[_listShape.Count - 1].Draw(BitmapGraphics);
            }
            //曲线
            if (_OperationType == OperationType.Curve)
            {
                //将move设置为false,鼠标移动是不再响应
                CanMove = false;
            }
            //WndGraphics.DrawImage(Bitmap, 0, 0);
            Bitmap = (Bitmap)FinishImg;
        }

        private void FormMain_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawImage(Bitmap, 0, 0);
        }
        //清空画板
        private void MenuItemClear_Click(object sender, EventArgs e)
        {
            WndGraphics.Clear(Color.White);
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            //创建窗体画笔对象和绘图对象
            WndGraphics = this.CreateGraphics();
            _pen = new Pen(DrawPenColor, DrawPenWidth);
            //创建双缓冲技术的位图,其大小与屏幕大小相同
            Bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
            //创建与缓冲位图相关的绘图对象
            BitmapGraphics = Graphics.FromImage(Bitmap);
            BitmapGraphics.Clear(Color.White);
            //防止出现锯齿
            BitmapGraphics.SmoothingMode = SmoothingMode.AntiAlias;
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            this.UpdateStyles();
            FinishImg = (Image)Bitmap;
            this.BackgroundImage = Bitmap;
        }
        //关闭窗体,释放资源
        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            //释放资源,防止内存泄露
            WndGraphics.Dispose();
            Bitmap.Dispose();
            BitmapGraphics.Dispose();
        }

        private void FormMain_Resize(object sender, EventArgs e)
        {
            //resize保证画图区随窗体的大小变化而变化
            WndGraphics = this.CreateGraphics();
        }
        //设置画笔颜色
        private void MenuItemColor_Click(object sender, EventArgs e)
        {
            //默认选中当前颜色
            colorDialogPen.Color = DrawPenColor;
            //点击确定,存储用户选择的颜色
            if (colorDialogPen.ShowDialog() == DialogResult.OK)
            {
                DrawPenColor = colorDialogPen.Color;
                //设置画笔颜色
                _pen.Color = DrawPenColor;
            }
        }
        //设置画笔线宽
        private void MenuItemWidth_Click(object sender, EventArgs e)
        {
            //创建对话框
            DialogPenWidth dialogPenWidth = new DialogPenWidth();
            //显示上一次的线宽
            dialogPenWidth.textBoxWidth.Text = DrawPenWidth.ToString();
            //获取设置的线宽
            if (dialogPenWidth.ShowDialog() == DialogResult.OK)
            {
                DrawPenWidth = Convert.ToInt32(dialogPenWidth.textBoxWidth.Text);
                _pen.Width = DrawPenWidth;
            }
        }
    }
}
--------------------编程问答-------------------- 内存飙升可能是因为内存泄露,代码太长,很难看,最好用工具来分析, --------------------编程问答-------------------- 代码好多啊,,,, --------------------编程问答-------------------- 有些资源要及时释放掉
--------------------编程问答-------------------- 每次绘制完毕都释放一次旧图。或者定义一个全局的画布,每次重新绘制前先释放掉旧的画布。 --------------------编程问答-------------------- 只看到_listShape.Add,存这么多点干嘛用啊 --------------------编程问答-------------------- 用工具测一下,代码这么长~ --------------------编程问答-------------------- 请将Graphic对象放到方法中创建,然后销毁,不要有全局的Graphic对象,试试。 --------------------编程问答-------------------- 没用的Bitmap对象使用完后要调用dispose,释放掉! --------------------编程问答-------------------- 确认了一下,问题就出在bitmap转成image这里了(第102行),但是不会改, --------------------编程问答--------------------
引用 3 楼 gxingmin 的回复:
有些资源要及时释放掉
确认了一下,问题就出在bitmap转成image这里了(第102行),但是不会改,  --------------------编程问答-------------------- FinishImg 和 Bitmap 这两个变量是什么关系,直接等于不行吗?

//FinishImg =new Bitmap(Bitmap);
FinishImg = Bitmap; --------------------编程问答--------------------
引用 11 楼 gxingmin 的回复:
FinishImg 和 Bitmap 这两个变量是什么关系,直接等于不行吗?

//FinishImg =new Bitmap(Bitmap);
FinishImg = Bitmap;
直接等于的话,鼠标松开之前绘制的轨迹就不会被擦除了,每移动一次鼠标的绘制都会画出来 --------------------编程问答-------------------- 你在reDraw(FinishImg);后面,没有将FinishImg对象释放,这个FinishImg不手动释放,内存就不会被释放的。 --------------------编程问答-------------------- 你只在窗口关闭事件里面用了释放资源,一直一直画图,不关闭窗口的,你想想你的内存会被占用多少资源 --------------------编程问答--------------------
引用 12 楼 chenh_w 的回复:
引用 11 楼 gxingmin 的回复:FinishImg 和 Bitmap 这两个变量是什么关系,直接等于不行吗?

//FinishImg =new Bitmap(Bitmap);
FinishImg = Bitmap;直接等于的话,鼠标松开之前绘制的轨迹就不会被擦除了,每移动一次鼠标的绘制都会画出来


是的,那你FinishImg要先释放
if(FinishImg!=null)
{
   FinishImg.Dispose();
   FinishImg =null;
}
FinishImg =new Bitmap(Bitmap); --------------------编程问答--------------------
引用 15 楼 gxingmin 的回复:
引用 12 楼 chenh_w 的回复:引用 11 楼 gxingmin 的回复:FinishImg 和 Bitmap 这两个变量是什么关系,直接等于不行吗?

//FinishImg =new Bitmap(Bitmap);
FinishImg = Bitmap;直接等于的话,鼠标松开之前绘制的轨迹就不会被擦除了,每移动一次鼠标的绘制都会画出来

是的,那你F……
运行直接报错。。。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 是什么时候内存井喷了,是页面加载的时候吗,先定位好问题 --------------------编程问答-------------------- 除
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,