描绘图板程序

 

新近开有图像处理有关的物 还是起那一点点体验分享下

率先片段:

开一个描绘图板程序类windows里的mspaint
第一是想开怎么管基本功能实现 铅笔阿 画线阿 画圆 画框阿
啥的,为了突出咱的不等 咱开一个针对性图纸进行拖动 调整大小的效应 。
倘若对准图纸重定义 那么一旦对图像保存元数据以便以后调整
说得忽悠点就是序列化啥的 砖家们隔三差五如此说
画线 铅笔 画圆 画框 这些以.net的graphic里调用都一致句话的从业
然后当onpaint里展开重绘就可了。
略知一二原理自己去实现呢非是无可以 以前我勾勒了千篇一律首在winfrom里写直线的章
只是咱们无必要更夺之轮子。

我们事先打一个家伙栏 整个工具切换效果 先拖个menustrip控件
然后加5单菜单项:

C语言 1
描绘menustrip的itemclicked事件的代码, 注意勿是小菜单项的click事件:

//工具切换效果
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
    for (int i = 0; i < menuStrip1.Items.Count; i++)
    {
        menuStrip1.Items[i].BackColor = Color.FromName("Control");
    }
    e.ClickedItem.BackColor = Color.Green;
}

 然后再次声称一个枚举类型 枚举5种植工具,单击菜单项的时进行切换:

private void 画线ToolStripMenuItem_Click(object sender, EventArgs e)
{
    type = toolType.line;
}

private void 铅笔ToolStripMenuItem_Click(object sender, EventArgs e)
{
    type = toolType.pencil;
}
toolType type;
enum toolType
{
    line, pencil,rec
}

 然后是画画的过程,咱不能够一直在窗体上画 咱得保存元数据阿,线段吗暂时用
Ilist<Point> 就可以 铅笔用graphicPath 矩形就就此
Ilist<Rectangle>
写的下因鼠标 按下->拖动->释放
把图片添加到后台。onpaint事件的时刻把图片重现。
下面是一些代码
我说过画线画框那些当.net里都是相同词话调用的行,铅笔工具稍微复杂点
在末面有说明:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    //for (int i = 0; i < line.Count; i += 1)
    //{
    //    if (i >= line.Count - 1)
    //        break;
    //    e.Graphics.DrawLine(Pens.Red, line[i], line[i + 1]);
    //}
    for (int i = 0; i < lines.Count; i += 2)
    {
        if (i >= lines.Count - 1)
            break;
        e.Graphics.DrawLine(Pens.Red, lines[i], lines[i + 1]);
    }
    foreach (Rectangle rec in recs)
    {
        e.Graphics.DrawRectangle(Pens.Green, rec);
    }
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    //line.Add(e.Location);
    Invalidate();
}

IList<Point> lines = new List<Point>();
IList<Rectangle> recs = new List<Rectangle>();
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
    startMouse = e.Location;
}
Point startMouse;

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
    switch (type)
    {
        case toolType.line: {
            lines.Add(startMouse);
            lines.Add(e.Location);
            break;
        }
        case toolType.rec:
            {
                recs.Add(new Rectangle(startMouse, new Size
                    (e.Location.X - startMouse.X, e.Location.Y - startMouse.Y)));
                break;
            }
    }
    Invalidate();
}

 并且矩形这里呢仅仅针对由左上起点至右手下了的这种状况作了处理
其他的场面没有考虑,在整机代码里功能是实现了底。
还有就是是怎么没有“即经常效力”
就是画画线断跟画框的拖拽效果,在onpaint最终加上这简单词就好了:

if (!mouseDown)
    return;
if (type == toolType.line)
    e.Graphics.DrawLine(Pens.Red, startMouse, tmpMouse);
if (type == toolType.rec)
    e.Graphics.DrawRectangle(Pens.Green, new Rectangle(startMouse, new Size
                    (tmpMouse.X - startMouse.X, tmpMouse.Y - startMouse.Y)));

 当然得加一个mouseDown的bool变量 让他当鼠标按下时也true
弹起时为false,再加一个tmpMouse的 Point变量  在鼠标移动时给他赋值。
如上所有源码下载撞倒此处
当当当当 现一个绘画图板出来了吧基本上可以用了吧 挖哈哈 (¯▽¯;)

C语言 2
针对了拖动功能为?这个嘛其实特别粗略, 如果当前凡是目标选择工具(最后一个按钮)
在鼠标按下时把特别坐标拿到所有目标里去检测 如果发生符合的 选定那个图形。
检测的不二法门嘛 自己失去打了, 看有点是否在平长线断上本人非常 画直线
的章里产生 第二有些之源码里为时有发生。检测矩形就再度简单啦
鼠标移动的时段看时是不是检测并择了靶 如果产生 拖动就是哪,拖动的历程
这尚免略 更改当前目标的坐标就是啦 point.offset 根据当前鼠标位置改
不要告诉我若免见面哈。
这些拥有的当亚有的底源码里都生。

第二局部:

次不是得了了吗,为什么还要起第二有些,看了便掌握了 并且后面的重不错
嘿嘿,并且第二局部有代码都是又勾的。
点那样依然未是好之结构 为了更系统来包括后功能的恢宏 程序结构的清晰
,我们得用面向对象的道去规划它 把 数据模型 代码逻辑分离, 功能的分类
定义好
想像一下齐中学几哪里课的时在黑板上画图,比如就是一个网。它起黑板(Board类)这个目标对不
它是装有图形的承载体 包括背景色 粉笔 当前正值打的图形 等,
图形是预约好的 有三角形 矩形 各种图片(Circle line pencil square)
而是她们还来一致的特点比如当黑板上的职 线条的水彩 填充颜色
他们有伙同的基类(Shape) 这点我们可以经过连续来兑现:

C语言 3

C语言 4

来复习生最基本的存续与virtual方法实现:

class Program
{
    static void Main(string[] args)
    {
        IList<father> fathers = new List< father>();
        fathers.Add(new son());
        fathers.Add(new son());
        fathers[0].method();
        fathers[0].method2();
    }
}

class father
{
    public virtual void method()
    {
        Console.WriteLine("father's method");
    }
    public virtual void method2()
    {
        Console.WriteLine("father's method2");
    }
}
class son : father {
    public override void method()
    {
        Console.WriteLine("son's method");
        //base.method();
    }
}

输出:
son’s method
father’s method2
归根结底virtual跟override就是面向对象的花所在, 子类实现了调用子类的
子类非实现调用父类的。 包括VC++的MFC到处都为此到了这种模式。
原本从C转到C++的吗向来无得这样多乱七八糟的面向对象设计之辩论,只是来这般一个概念而已。

当即是表现图形和艺术实现有代码,这么做的目的就是是以对数据模型进行定义
便于扩展,比如自己后从未画矩形的求了 可以把矩形那个看似删掉
从此想写五角星了 可以加一个五角星的类
继承Shape。比如每种图形都产生雷同之背景色。可以以Shape
定义showBackground()供每个继承的目标调用。
假使休用每个都重新实现这艺术。

#region 图形基类
public  class Shape
{
    public Rectangle region;//图形的大小及位置
    public int lineAlpha=100;//透明度
    public int layer=1;//层叠位置
    public Pen line = new Pen(Color.Red, 1);//线段属性
    public Color bgColor = Color.Lavender;//背景色
    public int bgColorAlpha = 50;
    public Point mouseStart = Point.Empty;//鼠标起点
    public Point mouseEnd = Point.Empty;//鼠标终点
    public bool selected = false;

    public Shape() { }


    public Shape(Color _lineColor, Point _mouseStart)
    {
        line.Color = _lineColor; this.region = new Rectangle(); region.Location = _mouseStart; region.Width = 0; region.Height = 0; 
    }
    //绘制(其实就是数据更新)
    public virtual void draw(Point p){}

    public virtual void move(int x, int y)//根据偏移量移动
    {
        region.Location.Offset(x, y);
    }

    public virtual bool select(Point p)
    {
        return false;
    }

    public virtual void show(Graphics handle)//显示
    {
        handle.DrawRectangle(line, this.region);
    }

}
#endregion

#region 线段
public class Line : Shape
{

    //通过两点初始化一条线段
    public Line(Point _start, Point _end)
    {
        this.mouseStart = _start; this.mouseEnd = _end;

        region.X = mouseEnd.X > mouseStart.X ? mouseStart.X : mouseStart.X - (mouseStart.X - mouseEnd.X);
        region.Y = mouseEnd.Y > mouseStart.Y ? mouseStart.Y : mouseStart.Y - (mouseStart.Y - mouseEnd.Y);
        region.Width = Math.Abs(mouseStart.X - mouseEnd.X);
        region.Height = Math.Abs(mouseStart.Y - mouseEnd.Y);
    }
    public Line() { }
    //通过一点初始化一条线段(起点跟终点同一个坐标不能称之为线段 准确的说应该是点)
    public Line(Point _start)
    {

        line.Color = Color.Red;
        this.mouseStart = _start; this.mouseEnd = Point.Empty;

        region.X = mouseStart.X;
        region.Y = mouseStart.Y;
        region.Width = 0;
        region.Height = 0;
    }
    //绘制(对两个点进行数据更新)
    public override void draw(Point p)
    {
        if (mouseStart == Point.Empty)
        { this.region = new Rectangle(); region.Location = p; region.Width = 0; region.Height = 0; }
        else //if (mouseEnd == Point.Empty)
        {
            mouseEnd = p;

            region.X = mouseEnd.X > mouseStart.X ? mouseStart.X : mouseStart.X - (mouseStart.X - mouseEnd.X);
            region.Y = mouseEnd.Y > mouseStart.Y ? mouseStart.Y : mouseStart.Y - (mouseStart.Y - mouseEnd.Y);
            region.Width = Math.Abs(mouseStart.X - mouseEnd.X);
            region.Height = Math.Abs(mouseStart.Y - mouseEnd.Y);
        }
    }

    //对当前实例进行显示(根据起始坐标画一条直线)
    public override void show(Graphics handle)
    {
        handle.DrawLine(line, mouseStart, mouseEnd);
    }
    //移动
    public override void move(int x, int y)
    {
        mouseStart.Offset(x, y);
        mouseEnd.Offset(x, y);

        region.Location.Offset(x, y);
    }

    public override bool select(Point mouse)
    {
        Util u = new Util();
        if (u.linedetector(mouse, mouseStart, mouseEnd))
        {
            selected = true;
            return true;
        }

        selected = false;
        return false;
    }
}
#endregion

 
和计算机有关的东西 特别是先后开发,有些东西是要将明白原理的 原理弄明白了
什么还吓办 ,不要只是浮于表面的有些底工具啊技术之
发现培训机构出来的都产生接触这种。

不过菜之时刻下手铅笔工具 我怀念就此鼠标跟随的主意不断drawRectangle
这样就是可以拿鼠标笔迹画出了,最后发现鼠标是跟不上的
画出来断断续续的,
自打这个之前为从来不看罢其他人的代码,想想呢真的该这么实现啊。
原本的铅笔工具代码。。。

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    Graphics g = Graphics.FromHwnd(this.Handle);
    g.DrawRectangle(Pens.Red, new Rectangle(e.Location, new Size(2, 2)));
}

 C语言 5
这看起来有些像橡皮擦哈
百度上搜的事物都是废的。始终找不交由
到底是鼠标没有捕捉到异常点么,事实及鼠标的诸一个移动的坐标体系底层都是会捕捉到之
只是画画的速度跟不上,
鼠标移动缓慢点还好 你说走快了刷刷的 不断的drawRectangle多费系统资源啊
怎么可能与得上 可能她吧是因CPU时间片来的呗 所以有些就是脱了呗。
也许稍原理性的物想大概为便明白了  但是若得错过雕饰啊
否则举行啊顺序开发,现在召开程序设计的早没有先C语言时候那些人的认真和谨慎了
无异于天费劲心思去研究那些数学算法 谭大师之《C程序设计》是遵循好写
有难度但是非是最最难 只要认真看都要看得懂的。
后来理解了发出graphicsPath这个东西做啊用之任名字便清楚呀
然后在鼠标移动的上不断的graphicsPath.addLine
把鼠标移动的不二法门用graphicsPath连起来
果真一试就成 就与Windows里之作画图板一模型一样
我怀念他那里面为是使这种措施吧。现在铅笔工具代码 注意没有用到graphicpath

private void Form1_Paint(object sender, PaintEventArgs e)
{
    for (int i = 0; i < line.Count; i += 1)
    {
        if (i >= line.Count - 1)
            break;
        e.Graphics.DrawLine(Pens.Red, line[i], line[i + 1]);
    }
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    line.Add(e.Location);
    Invalidate();
}
IList<Point> line = new List<Point>();

 

C语言 6

C语言 7

如若循环里换成 i+=2 则变为虚线,并且值更充分虚得进一步凶 有意思吧 (¯▽¯;) 
。实际上多边形也得据此这种措施来开 数组里片独一律组简单个相同组的point值
明白赛。

转移忘了长C语言双缓冲代码

this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
this.SetStyle(ControlStyles.DoubleBuffer, true);

对图像指定部分开展破除 达到橡皮擦的机能想起来好像特别复杂
其实你扭曲想不就是得矣咔嚓,橡皮擦是同等栽画笔 一种植特殊的画笔 背景色的画笔。

尚后onPaint的定义自MFC里就是有些连无是winfrom里才生
这是Windows窗体的一个生死攸关概念 必须将明白。
本来参数e里就时有发生e.graphics  以前看到网上以鼠标移动时graphics
g=graphics.fromhandle(this.handle)  就径直如此用
结果虽总是平闪一扭的,
这次以onpaint直接用e.graphics就不闪了。可见onpaint函数是直接接管底层的windwos重绘消息
有专门作用的 是勿等同的。有.net了 有winform了 方便了
不无的都是根据托管平台编程了 我们不用失去处理底层windows消息了
于是咱们不知晓原理了。

再有就是是做这种绘图或者即时性效果的代码里边
效率很关键,因为若不断的重绘不断的重绘可能每秒钟那段代码都让周转无数涂鸦了。
代码用优化 包括各种规格判断 这样好提高效率 一长条
确保以真正需要的时节代码才会于实践 学校里那些烂代码就毫无整到里面了
都是起烂代码过来的嘛。
先后的计划啊是非常重点之 本人从不以winform里整些乱七八糟的代码
winfrom里只是当调用  哪个类继承哪个类  程序结构整整洁洁的 多好。

而讲了这般多没有因此之

终极是写图板最终程序源码
撞倒此处
怕有的朋友运行不起是当.net2.0模式下编译的,这里虽是单学习的地方
别藏着掖着 整个东西下还无深受源码给别人研究 多辛苦啊
一旦生因此了俺们的代码的伸手留下个名儿哈。

次还有为数不少请勿健全之地方按照椭圆区域的判断 变形 等
望改进。椭圆的拖动功能暂未实现 其他的都OK。
再有拖动时图抓取的容差是5个像素 针对线段 
抓取拖动的长河没另外颜色与光标的变化 。同鞋们注意咯
免得说人家功能没有实现哈。

实则打图板不麻烦 真谛不难 就算你如做成矢量的 可开展后甩卖的 可拖动的
变形的 多图层的 就到底完photoshop那样 也未是什么异常不便的事
大家好视在《图片裁剪效果》的稿子里alpha透明效果圆角矩形等等我还曾落实了。这个实际不到底图像处理哈
,要做特别的图象处理需要出专门的辩解 算法  高数 平面多等等
本人承认于马上点尚缺乏缺 还得努力学习 加油。

再就是是熬夜写是鬼东东 靠 五点多矣。