C++绘画图板程序

 

近年来举行一些图像处理相关的事物 还是生那一点点体会分享下

第一片段:

做一个绘画图板程序类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值
明白赛。

别忘了丰富双缓冲代码

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消息了
于是咱无晓得原理C++了。

还有即使是开这种绘图或者即时性效果的代码里边
效率很关键,因为如果时时刻刻的重绘不断的重绘可能每秒钟那段代码都深受运行无数不行了。
代码用优化 包括各种标准判断 这样可以提高效率 一条
确保于真用的时刻代码才见面叫执行 学校里那些烂代码就绝不整到里面了
都是自从烂代码过来的嘛。
程序的规划啊是雅重点之 本人从不以winform里整些乱七八糟的代码
winfrom里只担负调用  哪个类继承哪个类  程序结构整整洁洁的 多好。

而且开口了这样多无因此底

终极是写图板最终程序源码
相撞此处
怕有的朋友运行不起是于.net2.0模式下编译的,这里就是是个学习之地方
别藏在掖着 整个东西下还未给源码给他人研究 多麻烦什么
倘发因此了我们的代码的乞求留下个名儿哈。

先后还有好多未全面的地方按照椭圆区域之判定 变形 等
望改进。椭圆的拖动功能暂未落实 其他的都OK。
再有拖动时图抓取的容差是5独如素 针对线段 
抓取拖动的经过没有其它颜色及光标的变化 。同鞋们注意咯
免得说咱功能尚未兑现哈。

实在写图板不麻烦 真谛不难 就算你如做成矢量的 可进展后处理的 可拖动的
变形的 多图层的 就终于完成photoshop那样 也未是啊坏艰难的事
大家可看在《图片裁剪效果》的章里alpha透明效果圆角矩形等等我还曾落实了。这个实际上不到底图像处理哈
,要召开特别的图象处理需要发出特别的答辩 算法  高数 平面多等等
本人承认在当下地方尚少缺 还需要努力学习 加油。

并且是经受夜写这个鬼东东 靠 五点多矣。