画图程序简单实现

《计算机图形学》系统设计10月进展报告

151220129 计科 吴政亿

(南京大学 计算机科学与技术系,南京 210093)

摘要

用C#向windows的画图看齐,实现了文件的打开与保存,直线/圆/曲线/点 等的形状绘制,可以自主选择粗细与颜色,并且应用派生的ArrayList类Step保存了每一步的操作,可以进行撤销与恢复操作。

效果

实验环境

报告采用markdown编写,并另存为了html与pdf格式,置于压缩包内report文件夹,code文件夹中包含了本次阶段性代码,Paint文件则是project的打包发布版本,便于助教测试。

基于Visual Studio 2015下的 C# 编写。

代码结构浅析

代码主要分为三个部分:

NameInformation
Program.cs应用程序的主入口点。
Form1.cs画图程序的主窗口与实现的部分代码。
StepPaint.csArrayList的派生类,用于存储每一步的操作。
Form1.cs
Variable NameInformation
CASE now_case定义了当前的操作形状,例如画线与画圆
BREATH bh定义了画笔的粗细
Color color当前画笔颜色
int x1,y1定义了直线,圆等的起点坐标,在鼠标摁下时更新
bool mouse_down用于判断鼠标时候松开
bool is_back用于判断是否处于撤销状态
StepPaint Step存储每一步的操作动作

Function NameInformation
private void InitForm1()初始化成员变量
private void drawPixel(x,y)在(x,y)处画点(粗细取决于bh)
private void DDALine(x1,y1,x2,y2)画线函数DDA算法
private void BresenhamLine(x1, y1, x2, y2)画线函数Bresenham算法
private void MidpointLine(x0, y0, x1, y1)画线函数中心点生成算法
private void BresenhamCircle(R, xc, yc)画圆函数Bresenham算法
private void button_*Color*_Click(sender, e)不同颜色对应了不同的按钮来改变当前画笔颜色
private void openfile_Click(sender, e)打开文件响应函数
private void savefile_Click(sender, e)保存文件响应函数
private void button_*Function*_Click(sender, e)相应功能的相应函数,其中有撤销、恢复以及各种形状选择
private void pictureBox1_MouseUp(sender, e)鼠标左键松开时响应函数
private void pictureBox1_MouseMove(sender, e)鼠标移动时响应函数
private void pictureBox1_MouseDown(sender, e)鼠标左键摁下时响应函数
private void *粗细*ToolStripMenuItem_Click(sender, e)设置画笔粗细的响应函数
StepPoint.cs
Function NameInformation
ArrayList存储所有步骤的Image
int StepImage_now记录当前显示的图像在ArrayList的下标
void ClearStep()清空ArrayList与StepImage_now
void InitStep(Image)用一张Image初始化并作为保卫者
void AddStep(obj)添加一个Image Step
bool StepIsNull()判断StepImage_now是否位于最底部
bool StepIsFull()判断StepImage_now是否位于最顶部
Image PopStep()撤销当前步骤
Image PushStep()恢复上一个步骤
Image RefreshStep()将最后一个步骤永久撤销
void RemoveNullStep()当撤销状态下执行一个步骤,将清除后面的步骤

核心代码实现

这部分简述略过,因为都是按照上课讲的实现的算法

DDA直线算法
private void DDALine(int x1, int y1, int x2, int y2){
    double dx, dy, e, x, y;
    dx = x2 - x1;
    dy = y2 - y1;
    e = (Math.Abs(dx) > Math.Abs(dy)) ? Math.Abs(dx) : Math.Abs(dy);
    dx /= e; dy /= e;
    x = x1;
    y = y1;
    for (int i = 1; i <= e; i++)
    {
        drawPixel((int)(x + 0.5), (int)(y + 0.5));
        x += dx;
        y += dy;
    }
}
Bresenham画圆算法
private void BresenhamCircle(int R, int xc, int yc){
    int x, y, p;
    x = 0; y = R;
    p = 3 - 2 * R;
    for (; x <= y; x++)
    {
        /* 下面八个分别对称 八个部分*/
        drawPixel(x + xc, y + yc);
        drawPixel(x + xc, -y + yc);
        drawPixel(y + xc, x + yc);
        drawPixel(y + xc, -x + yc);
        drawPixel(-x + xc, y + yc);
        drawPixel(-x + xc, -y + yc);
        drawPixel(-y + xc, x + yc);
        drawPixel(-y + xc, -x + yc);
        if (p >= 0)
        {
            p += 4 * (x - y) + 10;
            y--;
        }
        else
        {
            p += 4 * x + 6;
        }
    }
}

实验感悟

对直线,圆等的生成算法有了更深刻的了解,并将ppt中的所有算法都简要测试了一遍并选择了最为简洁的方法,另外对像素与图像生成也有了实践的经验,下一次的目标是对于线的粗细有着更好的优化方案,现在的线粗细采用的是画圆的方案,当选项为粗时略有卡顿。

下附代码

Form1.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Painting
{
    enum CASE { no_operation, dot, line, curve, square, roundness, rectangle, pencil };
    enum BREATH { ss, s, b, bb};

    public partial class Form1 : Form
    {
        private Color color;
        private CASE now_case;
        private BREATH bh;
        private int x1, y1;
        private bool mouse_down, is_back;
        private StepPaint Step = new StepPaint();

        //private Bitmap bmp;
        public Form1()
        {
            InitializeComponent();
        }

        private void InitForm1()
        {
            color = Color.Black;
            now_case = CASE.no_operation;
            x1 = y1 = 0;
            button_color.BackColor = color;
            mouse_down = is_back = false;
            bh = BREATH.ss;
            pictureBox.Image = new Bitmap(pictureBox.Width, pictureBox.Height);
            Step.InitStep((Image)pictureBox.Image.Clone());
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            InitForm1();
        }

        private void drawPixel(int x, int y)//画点
        {
            Brush br = new SolidBrush(color);
            switch (bh)
            {
                case BREATH.ss:
                    Graphics.FromImage(pictureBox.Image).FillRectangle(br, x, y, 1, 1);
                    break;
                case BREATH.s:
                    bh = BREATH.ss;
                    Graphics.FromImage(pictureBox.Image).FillRectangle(br, x, y, 1, 1);
                    BresenhamCircle(1, x, y);
                    bh = BREATH.s;
                    break;
                case BREATH.b:
                    bh = BREATH.ss;
                    Graphics.FromImage(pictureBox.Image).FillRectangle(br, x, y, 1, 1);
                    for (int i = 1; i < 3; i++)
                        BresenhamCircle(1 + i, x, y);
                    bh = BREATH.b;
                    break;
                case BREATH.bb:
                    bh = BREATH.ss;
                    Graphics.FromImage(pictureBox.Image).FillRectangle(br, x, y, 1, 1);
                    for (int i = 1; i < 5; i++)
                        BresenhamCircle(1 + i, x, y);
                    bh = BREATH.bb;
                    break;
                default:
                    Graphics.FromImage(pictureBox.Image).FillRectangle(br, x, y, 1, 1);
                    break;
            }

            pictureBox.Invalidate();
            br.Dispose();
        }

        //private void drawPixel(int x, int y,Color cr)//画点
        //{
        //    Brush br = new SolidBrush(cr);
        //    //Graphics g = pictureBox.CreateGraphics();
        //    //Graphics g = Graphics.FromImage(bmp);
        //    //g.FillRectangle(br, x, y, 1, 1);
        //    Graphics.FromImage(pictureBox.Image).FillRectangle(br, x, y, 1, 1);
        //    pictureBox.Invalidate();
        //    br.Dispose();
        //}

        private void DDALine(int x1, int y1, int x2, int y2)
        {
            double dx, dy, e, x, y;
            dx = x2 - x1;
            dy = y2 - y1;
            e = (Math.Abs(dx) > Math.Abs(dy)) ? Math.Abs(dx) : Math.Abs(dy);
            dx /= e; dy /= e;
            x = x1;
            y = y1;
            for (int i = 1; i <= e; i++)
            {
                drawPixel((int)(x + 0.5), (int)(y + 0.5));
                x += dx;
                y += dy;
            }
        }

        private void BresenhamLine(int x1, int y1, int x2, int y2)
        {
            int x, y, dx, dy, p;
            x = x1;
            y = y1;
            dx = x2 - x1;
            dy = y2 - y1;
            p = 2 * dy - dx;
            for (; x <= x2; x++)
            {
                drawPixel(x, y);
                if (p >= 0)
                {
                    y++;
                    p += 2 * (dy - dx);
                }
                else
                {
                    p += 2 * dy;
                }
            }
        }

        private void MidpointLine(int x0, int y0, int x1, int y1)
        {
            int a, b, delta1, delta2, d, x, y;
            a = y0 - y1;
            b = x1 - x0;
            d = 2 * a + b;
            delta1 = 2 * a;
            delta2 = 2 * (a + b);
            x = x0;
            y = y0;
            drawPixel(x, y);
            while (x < x1)
            {
                if (d < 0)
                {
                    x++;
                    y++;
                    d += delta2;
                }
                else
                {
                    x++;
                    d += delta1;
                }
                drawPixel(x, y);
            } /* while */
        }/* MidpointLine */


        //private void DDALine(int x1, int y1, int x2, int y2, Color cr)
        //{
        //    double dx, dy, e, x, y;
        //    dx = x2 - x1;
        //    dy = y2 - y1;
        //    e = (Math.Abs(dx) > Math.Abs(dy)) ? Math.Abs(dx) : Math.Abs(dy);
        //    dx /= e; dy /= e;
        //    x = x1;
        //    y = y1;
        //    for (int i = 1; i <= e; i++)
        //    {
        //        drawPixel((int)(x + 0.5), (int)(y + 0.5), cr);
        //        x += dx;
        //        y += dy;
        //    }
        //}

        //private void MidpointCircle2(int R, int xc, int yc)
        //{
        //    int x, y, deltax, deltay, d;
        //    x = 0; y = R; d = 1 - R;
        //    deltax = 3;
        //    deltay = 5 - R - R;
        //    drawPixel(x + xc, y + yc);
        //    while (x < y)
        //    {
        //        if (d < 0)
        //        {
        //            d += deltax;
        //            deltax += 2;
        //            deltay += 2;
        //            x++;
        //        }
        //        else
        //        {
        //            d += deltay;
        //            deltax += 2;
        //            deltay += 4;
        //            x++;
        //            y--;
        //        }
        //        drawPixel(x + xc, y + yc);
        //    }
        //}

        private void BresenhamCircle(int R, int xc, int yc)
        {
            int x, y, p;
            x = 0; y = R;
            p = 3 - 2 * R;
            for (; x <= y; x++)
            {
                drawPixel(x + xc, y + yc);
                drawPixel(x + xc, -y + yc);
                drawPixel(y + xc, x + yc);
                drawPixel(y + xc, -x + yc);
                drawPixel(-x + xc, y + yc);
                drawPixel(-x + xc, -y + yc);
                drawPixel(-y + xc, x + yc);
                drawPixel(-y + xc, -x + yc);
                if (p >= 0)
                {
                    p += 4 * (x - y) + 10;
                    y--;
                }
                else
                {
                    p += 4 * x + 6;
                }
            }
        }

        //private void BresenhamCircle(int R, int xc, int yc, Color cr)
        //{
        //    int x, y, p;
        //    x = 0; y = R;
        //    p = 3 - 2 * R;
        //    for (; x <= y; x++)
        //    {
        //        drawPixel(x + xc, y + yc, cr);
        //        drawPixel(x + xc, -y + yc, cr);
        //        drawPixel(y + xc, x + yc, cr);
        //        drawPixel(y + xc, -x + yc, cr);
        //        drawPixel(-x + xc, y + yc, cr);
        //        drawPixel(-x + xc, -y + yc, cr);
        //        drawPixel(-y + xc, x + yc, cr);
        //        drawPixel(-y + xc, -x + yc, cr);

        //        if (p >= 0)
        //        {
        //            p += 4 * (x - y) + 10;
        //            y--;
        //        }
        //        else
        //        {
        //            p += 4 * x + 6;
        //        }
        //    }
        //}
        private void button_white_Click(object sender, EventArgs e)
        {
            color = Color.White;
            button_color.BackColor = color;
        }

        private void button_black_Click(object sender, EventArgs e)
        {
            color = Color.Black;
            button_color.BackColor = color;
        }

        private void button_red_Click(object sender, EventArgs e)
        {
            color = Color.Red;
            button_color.BackColor = color;
        }

        private void button_orange_Click(object sender, EventArgs e)
        {
            color = Color.Orange;
            button_color.BackColor = color;
        }

        private void button_yellow_Click(object sender, EventArgs e)
        {
            color = Color.Yellow;
            button_color.BackColor = color;
        }

        private void button_lime_Click(object sender, EventArgs e)
        {
            color = Color.Lime;
            button_color.BackColor = color;
        }

        private void button_aqua_Click(object sender, EventArgs e)
        {
            color = Color.Aqua;
            button_color.BackColor = color;
        }

        private void button_blue_Click(object sender, EventArgs e)
        {
            color = Color.Blue;
            button_color.BackColor = color;
        }

        private void button_fuchsia_Click(object sender, EventArgs e)
        {
            color = Color.Fuchsia;
            button_color.BackColor = color;
        }

        private void button_silver_Click(object sender, EventArgs e)
        {
            color = Color.Silver;
            button_color.BackColor = color;
        }

        private void openfile_Click(object sender, EventArgs e)
        {
            openFileDialog1.InitialDirectory = "D:\\";            // 这里是初始的路径名
            openFileDialog1.Filter = "png文件|*.png|jpg文件|*.jpg|所有文件|*.*";  //设置打开文件的类型
            openFileDialog1.RestoreDirectory = true;              //设置是否还原当前目录
            openFileDialog1.FilterIndex = 0;                      //设置打开文件类型的索引
            string path = "";                                     //用于保存打开文件的路径
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                path = openFileDialog1.FileName;
                //MessageBox.Show(path);                            //显示该路径名
            }

            pictureBox.ImageLocation = path;
            Step.InitStep(Image.FromFile(path));
            //Step.ShowImageStep(0);
        }

        private void savefile_Click(object sender, EventArgs e)
        {
            saveFileDialog1.InitialDirectory = "D:\\";            // 这里是初始的路径名
            saveFileDialog1.Filter = "png文件|*.png|jpg文件|*.jpg|所有文件|*.*";  //设置打开文件的类型
            saveFileDialog1.RestoreDirectory = true;              //设置是否还原当前目录
            saveFileDialog1.FilterIndex = 0;                      //设置打开文件类型的索引
            string path = "";                                     //用于保存打开文件的路径
            if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                path = saveFileDialog1.FileName;
                //MessageBox.Show(path);                            //显示该路径名
            }
            pictureBox.Image.Save(path);
        }

        private void button_back_Click(object sender, EventArgs e)
        {
            is_back = true;
            if(!Step.StepIsNull())
                pictureBox.Image = Step.PopStep();
        }

        private void button_front_Click(object sender, EventArgs e)
        {
            if (!Step.StepIsFull())
                pictureBox.Image = Step.PushStep();
        }

        private void button_line_Click_1(object sender, EventArgs e)//直线
        {
            now_case = CASE.line;
        }

        private void button_dot_Click(object sender, EventArgs e)//点
        {
            now_case = CASE.dot;
        }

        private void button_pencil_Click(object sender, EventArgs e)
        {
            now_case = CASE.pencil;
        }

        private void button_roundness_Click(object sender, EventArgs e)
        {
            now_case = CASE.roundness;
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)//鼠标左键松开
        {
            if (mouse_down)
                mouse_down = false;
            if (is_back)
            {
                Step.RemoveNullStep();
                is_back = false;
            }
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)//鼠标移动
        {
            if (mouse_down)
            {
                if(now_case == CASE.dot)
                {
                    drawPixel(e.X, e.Y);
                }
                else if (now_case == CASE.pencil)
                {
                    DDALine(x1, y1, e.X, e.Y);
                    x1 = e.X;
                    y1 = e.Y;
                }
                else
                {
                    pictureBox.Image = Step.RefreshStep();
                    switch (now_case)
                    {
                        case CASE.no_operation:
                            break;
                        case CASE.dot:
                            //drawPixel(e.X, e.Y);
                            break;
                        case CASE.line:
                            DDALine(x1, y1, e.X, e.Y);
                            //BresenhamLine(x1, y1, e.X, e.Y);
                            //MidpointLine(x1, y1, e.X, e.Y);
                            break;
                        case CASE.curve:
                            break;
                        case CASE.square:
                            break;
                        case CASE.roundness:
                            BresenhamCircle((int)Math.Sqrt((x1 - e.X) * (x1 - e.X) + (y1 - e.Y) * (y1 - e.Y)) / 2, (x1 + e.X) / 2, (y1 + e.Y) / 2);
                            break;
                        case CASE.rectangle:
                            break;
                        case CASE.pencil:
                            break;
                        default:
                            break;
                    }
                }

            }

        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {

            //设置起点
            x1 = e.X;
            y1 = e.Y;

            //标记鼠标摁下
            mouse_down = true;

            Image temp = (Image)pictureBox.Image.Clone();
            //Form fm = new Form();
            //fm.BackgroundImage=temp;
            //fm.Show();

            Step.AddStep(temp);
            pictureBox.Image = temp;
        }

        private void 好细ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            bh = BREATH.ss;
        }

        private voidToolStripMenuItem_Click(object sender, EventArgs e)
        {
            bh = BREATH.s;
        }

        private voidToolStripMenuItem_Click(object sender, EventArgs e)
        {
            bh = BREATH.b;
        }

        private void 好粗ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            bh = BREATH.bb;
        }
    }
}
StepPaint.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Painting
{
    class StepPaint: ArrayList
    {
        private int StepImage_now;
        public void ClearStep()
        {
            this.Clear();
            StepImage_now = -1;
        }
        public void AddStep(Object obj)
        {
            this.Add(obj);
            StepImage_now++;
        }

        public void InitStep(Image obj)
        {
            ClearStep();
            AddStep(obj);
        }

        public bool StepIsNull()
        {
            return StepImage_now == 0;
        }
        public Image PopStep()
        {
            if (StepIsNull())
                MessageBox.Show("StepImage_now == 0!");
            StepImage_now--; 
            return (Image)(((Image)this[StepImage_now]).Clone());
        }

        public bool StepIsFull()
        {
            return StepImage_now == this.Count - 1 ;
        }

        public Image PushStep()
        {
            if (StepIsFull())
                MessageBox.Show("StepImage_now is full");
            StepImage_now++;
            return (Image)(((Image)this[StepImage_now]).Clone());
        }

        public Image RefreshStep()
        {
            Image temp = (Image)((Image)this[StepImage_now - 1]).Clone();
            this[StepImage_now] = temp;
            return temp;
        }

        public void RemoveNullStep()
        {
            //MessageBox.Show("set count to now!");
            this.RemoveRange(StepImage_now + 1, this.Count - StepImage_now - 1);
        }

        //public void ShowImageStep(int i)
        //{
        //    Form fm = new Form();
        //    fm.BackgroundImage = (Image)this[i];
        //    fm.Show();
        //}
    }
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值