画图板程序

 

近来做一些图像处理有关的东西 依然有那么一小点体验分享下

首先有的:

做四个画图板程序类windows里的mspaint
率先是想到怎么把基本作用完结 铅笔阿 画线阿 画圆 画框阿
啥的,为了卓绝咱的例外 咱做3个对图纸进行拖动 调整大小的效力 。
要对图纸重定义 那么要对图像保存元数据以便以往调整
说得忽悠点就是类别化啥的 砖家们平日这么讲
画线 铅笔 画圆 画框 这几个在.net的graphic里调用都一句话的事
然后在onpaint里实行重绘就可以了。
精通原理自个儿去贯彻也不是不能 之前作者写过一篇在winfrom里画直线的稿子
只是我们尚无要求再去造轮子。

咱俩先弄一个工具栏 整个工具切换效果 先拖个menustrip控件
然后加七个菜单项:

图片 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;
}

 然后再声雅培(Abbott)个枚举类型 枚举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> 就能够 铅笔用graphic帕特h 矩形就用
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();
}

 并且矩形那里也只对从左上源点到右下停止的那种景况作了拍卖
其余的动静没惦念,在1体化代码里效能是兑现了的。
再有正是怎么未有“即时效果”
就是画线断跟画框的拖拽效果,在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变量  在鼠标移动时给他赋值。
如上全数源码下载相撞此处
当当当当 现行反革命3个画图板出来了呢基本上能够用了吧 挖哈哈 (¯▽¯;)

图片 2
对了拖动功效吗?那一个嘛其实相当的粗略, 借使当前是指标选用工具(最终一个按钮)
在鼠标按下时把格外坐标得到全部目的里去检测 如若有契合的 选定那多个图形。
检验的艺术嘛 自个儿去搞咯, 看有些点是或不是在一条线断上本身格外 画直线
的稿子里有 第三部分的源码里也有。检查测试矩形就更简便啦
鼠标移动的时候看近日是还是不是检查测试并采纳了目的 假设有 拖动正是啊,拖动的进度那还不不难 更改当前目的的坐标就是啦 point.offset 依据当下鼠标地方改
不要告诉自个儿你不会哈。
那一个具有的在其次有的的源码里都有。

第叁片段:

先后不是完了吧,为啥还要有第一局地,看完就知晓了 并且前面的更优异嘿嘿,并且第3部分全体代码都以再次写的。
地点那样还是否好的结构 为了更系统些包涵现在功用的壮大 程序结构的清晰
,大家得用面向对象的主意去规划它 把 数据模型 代码逻辑分离, 作用的归类
定义好
想像一下上中学几何课的时候在黑板上画图,比如那是三个系统。它有黑板(Board类)那些指标对不
它是兼具图形的承接体 包含背景观 粉笔 当前正在画的图形 等,
图表是预订好的 有三角形 矩形 各样图片(Circle line pencil square)
可是她们都有相同的风味比如在黑板上的职位 线条的水彩 填充颜色
他们有三头的基类(Shape) 那一点大家能够透过三番五次来兑现:

图片 3

图片 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++的也平素没得那样多乱柒八糟的面向对象设计的驳斥,只是有那样二个概念而已。

那是显示图形及情势达成部分代码,这么做的指标正是为着对数据模型进行定义
便于扩张,比如作者从此从未画矩形的急需了 可以把矩形那多少个类删掉
事后想画5角星了 能够增添3个五角星的类
继承Shape。比如每个图形都有1致的背景观。能够在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)));
}

 图片 5
其一看上去有点像橡皮擦哈
百度上搜的东西都以废的。始终找不到原因
到底是鼠标未有捕捉到那三个点么,事实上鼠标的每2个平移的坐标种类底层都以会捕捉到的
只是画的进程跟不上,
鼠标移动慢点幸而 你说移动快了刷刷的 不断的drawRectangle多费系统资源啊
怎么大概跟得上 或许它也是基于CPU时间片来的呗 所以某个就漏掉了嘛。
只怕有点原理性的东西思索大约也就知道了  不过你得去讨论啊
不然做什么顺序开发,今后做程序设计的早未有从前C语言时候那一个人的认真跟谨慎了
一天费力心境去斟酌这几个数学算法 谭大师的《C程序设计》是本好书
有难度不过不是太难 只要认真看都依然看得懂的。
后来通晓了有graphics帕特h这些东西做什么用的听名字就理解啊
然后在鼠标移动的时候不断的graphicsPath.addLine
把鼠标移动的门路用graphicsPath连起来
果然一试就打响 就跟Windows里的画图板一模1样
小编想他那里面也是选用这种方法吧。以后铅笔工具代码 注意未有用到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>();

 

图片 6

图片 7

设若循环里换来 i+=二 则改为虚线,并且值越大虚得越凶 有意思吧 (¯▽¯;) 
。实际上多边形也得以用那种艺术来做 数组里五个一组三个1组的point值
领悟赛。

别忘了加上双缓冲代码

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

对图像钦点部分进行破除 达到橡皮擦的效果想起来好像很复杂
其实你扭曲想不就得了啊,橡皮擦是一种画笔 1种特殊的画笔 背景象的画笔。

还后onPaint的定义自MFC里就部分并不是winfrom里才有
那是Windows窗体的七个重中之重概念 必须弄精通。
自然参数e里就有e.graphics  从前看到网上在鼠标移动时graphics
g=graphics.fromhandle(this.handle)  就径直这么用
结果正是老是一闪1闪的,
此次在onpaint直接用e.graphics就不闪了。可知onpaint函数是直接接管底层的windwos重绘音信有专门意义的 是分化等的。有.net了 有winform了 方便了
不无的都以遵照托管平台编制程序了 我们不用去处理底层windows音信了
于是我们不明白原理了。

还有就是做那种绘图大概即时性效果的代码里边
功效很重大,因为要不断的重绘不断的重绘可能每分钟那段代码都被周转无数10回了。
代码必要优化 包蕴种种规格判断 那样能够提升功能 一条
确定保障在真的须要的时候代码才会被执行 高校里这几个烂代码就不用整到里面了
都以从烂代码过来的嘛。
程序的安插也是很重要的 自己从不在winform里整些乱七8糟的代码
winfrom里只担负调用  哪个类继承哪个类  程序结构整整洁洁的 多好。

又讲了如此多没用的

末段是画图板最终程序源码
冲击此处
怕有的朋友运转不起是在.net二.0情势下编写翻译的,那里正是个学习的地点别藏着掖着 整个东西出来还不给源码给人家斟酌 多烦啊
倘诺有用了咱的代码的请留个名儿哈。

程序还有众多未周全的地点比如椭圆区域的判断 变形 等
望革新。椭圆的拖动功效暂未兑现 别的的都OK。
再有拖动时图形抓取的容差是陆个像素 针对线段 
抓取拖动的经过并未其它颜色跟光标的变化 。同鞋们注意咯
免得说咱功效没落成哈。

其实画图板不难 真谛简单 就算你要做成矢量的 可开始展览后甩卖的 可拖动的
变形的 多图层的 固然完结photoshop那样 也不是怎么很拮据的事
我们能够看来在《图片裁剪效果》的小说里阿尔法透明效果圆角矩形等等作者都早已达成了。那么些实际不算图像处理哈
,要做越发的图象处理供给有专门的辩解 算法  高数 平面几何等等
自个儿认同在那方面还欠缺 还需努力学习 加油。

又是熬夜写这几个鬼东东 靠 伍点多了。