开心乐园精美句子

No Comments

      1.虽说强扭瓜不甜,可强扭有瓜吃阿!
      2.我这人从不记仇,一般有仇当场我就报了。
      3.80后男生,官都被50后当走了,钱都被60后赚走了,看上的80后女生也被70后抢走了,连栽赃给我们的那些名号也被90后抢走了,眼瞅着00后就要冲上来了,最后被10后(孩子)折腾死
      4.你永远看不到我最寂寞的时候,因为在看不到你的时候才是我最寂寞的时候!
      5.师太和老衲恋爱了,刚开始会说:师太,你就从了老衲吧,过几天会说:师太,你就放过老衲吧;
      6.都说人和人在一起久了生活习惯就会相同;几个女人住在一起时间长了生理周期都一样了……
      7.世界上最遥远的距离不是生与死 而是你站在我面前说英语 而我也懂英语…… 可是……我却不知道你在说什么
      8.一百块:我不是这样的人。。
            二百块:我今晚就是你的人。。
            三百块:今晚别把我当人。。
            四百块:随便你带多少人。。
            五百块:随便你带的是不是人。。
      9.有事秘书干,没事干秘书
      10.上放着一瓶水,积极的人会说哇塞,还有半瓶水,消极的人会说,我靠,就剩半瓶水了,积极的人像太阳,走到哪里那里亮,消极的人就像月亮,初一十五都不一样
      11.世界上最遥远的距离不是生与死 而是你站在我面前说英语 而我也懂英语…… 可是……我却不知道你在说什么
      12.做梦梦到自己吃意大利面,醒来发现自己鞋带不见了!
      13.干掉熊猫,我就是国宝
      14.不吃饱哪有力气减肥啊?
      15.我那么喜欢你,你喜欢我一下会死啊?
      16.孔子曰:打架用砖乎,不亦乱乎,使劲乎,往死里乎,乎不死在乎,不乎白不乎!乎死白乎
      17.你长的很象古代的一个大将军阿!真的,真的象啊!就是天棚大元帅啊!
      18.“你们还有我累么?我这个月那个都来两次了……”
      19.就算我是一只公癞蛤蟆,我也坚决不找母癞蛤蟆!
      20.人的本性就是越容易得到的东西越容易放弃!别人追求你的时候,有时候也要多一点矜持!多给他增加一点难度!即使你很喜欢他!
      21心中有敌,天下皆敌!心中无敌,那自然就天下无敌了!
      22.下辈子做女人,嫁我这样的男人!
      23.我始终相信生活不会有困境,不管多少困难,挫折,失败。我都会鼓励自己:奋勇向前!
      25.天上掉馅饼,地上就有馅饼
      26.所有的理想只要没有涉及到金钱,都是光环闪耀的,只要涉及到金钱,理想全部变成梦想了!
      27.中国有风险,投胎需谨慎!
      28.你还真是,给你个棒槌,你还当针了!
      29.我看你是猪鼻子插大葱装大象
      30.人生就是茶几,上面摆放了无数的杯具和餐具。我的人参泡在杯具里
      31.文字的作用就是:当你不想说话时,你可以用手去呐喊
      32.明明自己是一个p,却喜欢玩倒立,把自己装成一个b
      33.这可是我压箱底的绝技!不传男,不传女,就传你!
      34.夫妻二人生活在一起,既没性生活,也不离婚。我把这种现象总结为“一不做,二不休
      35.老师常说:不要把宝贵的时间浪费在早恋上,现在才明白,不把宝贵的时间用来早恋,那才叫浪费
      36.只要女人肯出手,没有男人能逃走
      37.大家都知道白领的定义就是他们每个月拿来的工资都去还房屋按揭了,所以他们领了工资也白领。那何为蓝领呢?他们的工作很累,每个月工资又少,所以发工资的时候他们都懒的去领
      38.已经将整个青春都用来检讨青春,还要把整个生命都用来怀疑生命。
      39.是这样的张总,你在家里的电脑上按了CTRL+C,然后在公司的电脑上再按CTRL+V是肯定不行的。即使同一篇文章也不行。不不,多贵的电脑都不行。
      40.自己选择45°仰视别人,就休怪他人135°俯视着看你。
      41.过往的人啊 不要为我的死悲伤 如果我活着 你们谁也活不了────-罗伯斯庇尔墓志铭
      42.现在你骂我,是因为你还不了解我,等你以后了解了我,你一定会动手打我的。
      43.“你喜欢我天使的脸孔,还是魔鬼的身材?”“我就喜欢你这种幽默感。”
      44.到一寺庙,一得道高僧讲:施主捐些款吧,三百五百的都行。回答说:实在没带这么多钱,下次吧。高僧回答:可以刷卡。
      45.说假话总会被人揭穿,戴假发总会被风揭穿。
      46.人干点好事总想让神鬼知道,干点坏事总以为神鬼不知道,我们太难为神鬼了
      47.雷锋做了好事不留名,但是每一件事情都记到日记里面。
      48.骗子太多,傻子明显不够用了
      49.史上最神秘的部门:有关部门;史上最神秘的人:知情人士;史上最权威的人:砖家叫兽。
      50.就算是一坨屎,也有遇见屎壳郎的那天。所以你大可不必为今天的自己有太多担忧。
      51.如果我有1000万,那我会买30套房子,然后每天去收一次房租,哇塞,充实!
      52.你可别小看1元钱,用来买老鼠药,可以毒翻你好几十回了
      53.首先我们之间有代沟,其次你又没有乳沟,你说我们怎么交流?
      54.谁说天下乌鸦一般黑?其实一个比一个黑
      55.工作好有意思,尤其是看着别人工作
      56.生米都给你煮成爆米花了
      57.你个老秃驴,敢和贫道抢师太
      58.一口吃不成胖子,但胖子确是一口一口吃出来的
      59.没有拆不散的夫妻,只有不努力的小三
      60.又不聪明,还学别人秃顶

Agg学习笔记之渲染

No Comments

        Agg渲染层(Render)分为三层,最底层和RenderBuffer通信,主要封装各种颜色模式和基本的操作,如Copy/Blend。中层主要是BaseRender,作为上下两种的连接层,主要实现基本的裁剪。渲染层最上是应用层,用来和Scanline Rasterizer 层通信。他们的交互通过scanline 来完成。

      Scanline Rasterizer 主要用于光栅化上层提供的矢量图形,并且以Y轴将其分解成y条水平线,这些水平线最终存储在scanline中。

      关于scanline,这里摘抄一段http://blog.csdn.net/liyiwen007/archive/2010/06/09/5659411.aspx

      AGG 中主要的光栅化技术是基于 scanline 的,也就是说,一个多边形会先转换成很多的水平扫描线,然后再逐个描画这些扫描线。同样的, scanline rasterizer 并不是唯一可以用于生成水平扫描线(scanline)的类,你也可以使用一些容器,甚至是你自己的  super-ultra-mega rasterizer

      在从 rasterizer(光栅化器)到 scanline renderer 之间的信息转换时,用到了 scanline 容器。一个 scanline 由很多的水平的、不相交的 span 组成。这些 span x 轴排序。这就意味着并没有提供排序这个操作,必须在将 span 添加到 scanline 时就把顺序处理好。如果顺序不是按要求排列的,那么可能会出现未定义的行为。

      AGG 中有以下三种 scanline 容器。

  1. scanline_u – 未包装的 scanline 容器。
  2. scanline_p – 包装过的 scanline 容器。
  3. scanline_bin – 原始的,“带锯齿”的 scanline 容器。

      前面两种容器可以包含去锯齿信息,第三种不行。

      包装的(packedscanline 与未包装的(unpackedscanline 的区别在于, unpacked scanline 总是会为所有像素保存覆盖值(coverage value),包括那些全部覆盖在多边形内部的像素。而 packed scanlines 则会把具有相同 coverage value 的像素合并成 solid span

      看起来好像使用 packed 版的 scanline 总是会好一点,但在实践中并非如此。但你渲染一些大型的实心多边型时,使用 scanline_p 会更快,那是因为多边型的面积比周长要大很多(从像素的角度来衡量)。但是如果是渲染字符的时候绝对应该使用 scanline_u ,这样就能处理更少的 span 了,(scanline_u 会比 scanline_p)大约少产生三倍左右的 span,而 span 数本身就是一项重要的开销。同样的,在大多数 span 产生器中(比如 gradients, Gouraud shader, 以及 image 产生器等),span 的数量更加是(效能的)关键,所以一般也不会使用 scanline_p

      站在巨人的肩膀上,我也废话几句,Agg抗锯齿信息通过存储在scanline_u和scanline_p中的cover值来体现,虽然cover值不等同alpha值,不过它的大小直接影响到alpha值的大小,这种影响在Agg渲染层顶层中得以体现。

      应用层包含几个类和若干工具函数,实际上这些类也就对工具函数进行简单的封装agg_renderer_scanline.h。这些类包括:

http://www.cppprog.com/2009/0821/150_5.html

template<class BaseRenderer>
	class renderer_scanline_aa_solid; //实色AA渲染
template<class BaseRenderer>
	class renderer_scanline_bin_solid; //实色原始渲染
template<class BaseRenderer, class SpanAllocator, class SpanGenerator>
 	class renderer_scanline_aa; // 自定义AA渲染
template<class BaseRenderer, class SpanAllocator, class SpanGenerator>
 	class renderer_scanline_bin; // 自定义原始渲染

      关于这四个类,有两种分类方法:

      aa/bin(一三/二四),包含aa的使用scanline的反锯齿信息(通过alpha值实现),包含bin的则仅仅根据原始的顶点渲染。这两者的区别可以通过下面的图来说明:

aa

 

      左边使用 renderer_scanline_bin_solid渲染,每一次Span渲染操作都是同一颜色(因为忽略了反锯齿信息),右边使用 renderer_scanline_aa_solid,因为会使用scanline的抗锯齿信息,所以每一次Span都可能包含若干种颜色(通过scanlinecover值改变alpha值,然后alpha blend改变颜色),从而导致锯齿不明显(这也导致了抗锯齿的线比没有抗锯齿的线稍窄)

      另一种分类方法是solid/none(一二/三四),solid(如其意)在渲染Span是只能使用单种颜色,如上图。而后者则可以对一次Span渲染中的每一个像素指定一种颜色。这些颜色从哪里来呢?通过其模板参数 SpanGenerator提供,那SpanGenerator是什么,它的颜色信息从哪里来呢?看看这里 http://www.cppprog.com/2009/0829/153.html 。SpanGenerator用于从一些复杂的颜色源来获取Span颜色信息,例如位图,渐变色等等。

      这些工具函数包括render_scanlines、render_scanlines_aa_solid等。其实现也相当之简单,通过遍历Rasterizer来生成Scanline,然后对每一个Scanline调用底层渲染层来完成多边形的渲染。

现代广告搞笑新传

No Comments

1.金鹿杀虫气雾剂
襄阳城内。一只蚊子飞来,叮在郭破虏的脸上,郭破虏惊叫一声:“爹,有刺客。
郭靖:“亢龙有悔”,一掌拍向郭破虏的脸上。郭破虏捂着被拍肿的脸:“爹,刺客跑了。”“想跑?没那么容易。”,黄蓉掏出一个杀虫气雾剂,朝空中一喷,蚊子纷纷落下。黄蓉:“金鹿杀虫气雾剂,高效无毒,害虫的死对头,家庭的好帮手。”
2.盖中盖
武当山。几个平时欺负张无忌的师兄弟又在欺负他。只见张无忌跳起几个飞腿把他们全都踢倒。 周芷若:“耶!无忌,你好棒,我喜欢你,你现在怎么这么历害啊?”
张无忌:“我看了大夫,大夫说我得的不是什么伤寒,而是骨质疏松症,得补钙。自从我服了哈尔滨制药六厂的盖中盖,腰不酸了,背不疼了,病也好了,走路也有劲了,还跟外公学了功夫”。(众师兄弟吓的撒腿没命的逃。)
3.红牛饮料
药王谷。程灵素正在种草药。胡斐看见了,走过去接过她的锄头,干起了活。程灵素帮胡斐擦汗,又递过了一杯茶,唱道:“同志哥,请你喝杯茶啊,请你喝杯茶……” 胡斐摇摇头,从包袱里拿出了一罐饮料,仰口喝了一口,美美的闭上眼睛道: “渴了,累了,困了,就要喝红牛。

4.佳美护肤霜
星宿派正在后面追杀。虚竹只得背着天山童姥在山间狂奔。突然,从旁边跳出了三位美人,原来是阿紫、王语焉、梦姑。阿紫:“把童姥放下。”虚竹:“姑娘们,不要胡闹了,形势危急,快让开。”王语焉:“好说,好说,只要童姥把驻颜术的密笈说出来,我们就让路。”只见童姥手一抖,三件暗器已破空向三位美女的膻中穴飞去。三位美女也是不简单,顺手一抽,各自把暗器接在了纤手中。原来都是一盒佳美护肤霜。童姥:“我都是因为用了佳美护肤霜,佳美护肤霜,今年二十,明年十八。”“耶”,三位美女跳起,让开了路
5.奇圣胶囊
桃谷六仙正在争论到底谁才是老大,这时,桃爸桃妈出现了。桃妈:“傻孩子, 不要争了,谁大谁小都没有关系,重要的是你们要记住,男儿< 裆>自强。”桃爸:“对,男儿〈裆〉自强,自从服了奇圣胶囊,我的自强成了持久。”
6.脑白金
春节将至,到处爆竹乱响。郭靖、黄蓉乘船往桃花岛。黄蓉:“爹,靖哥哥给你送礼物来了。” 黄药师:“这蠢物,难道不知道我黄药师最恨这些繁文褥节吗”
郭靖:“爹,这可是脑白金啊!” 黄药师:“哦!” 正要伸手拿,突然被困在桃花岛的老顽童跳了出来,一把抢过礼物,叫道:“今年春节不送礼,送礼只送脑白金,黄老邪,你不要,那就给我吧!”
7.诺基亚手机
黑木涯。
任盈盈难产,众英雄束手无策。大家想到了平一指(平一指正在摆弄草药)。少林寺长老方正用“狮子吼”叫平一指(平一指没有反应)。令狐冲用“千里传音” 叫平一指(平一指没有反应)。大家急得象热窝上的蚂蚁,计无施急匆匆的跑进来,掏出手机拨打。“嘀铃……”,平一指的手机响了,他打开手机:“好,我就来。”大家围住了计无施看他的手机,计无施:“诺基亚手机,一拔就通,诺基亚手机,科技以人为本。”
8.排毒养颜胶囊
绝情谷。
杨过:“姑姑,这颗情花毒的解药给你用。” 小龙女:“不,应该是过儿用。”
杨过:“姑姑即然不用,我就把它丢了,我要和姑姑同生共死。”说完把解药丢下了山涯。 小龙女:“过儿,你……,”依到了杨过的怀里。
杨过狡诘的一笑:“姑姑,你真傻,你知道我为什么要把解药给丢了吗?”
小龙女:“过儿,那是为什么?” 杨过:“因为我有了排毒养颜胶囊,排毒养颜胶囊,我们健康的共同选择,排出毒素,一身轻松。”
9.婷美内衣
黑木涯。
任我行正领着一帮江湖豪杰攻打黑森涯,任我行、令狐冲、任盈盈正使出浑身绝学围攻东方不败,东方不败渐渐体力不支,令狐冲寻了个破绽,使出了一招独孤刀九剑,刺向东方不败的胸前,东方不败大惊,慌忙中使出了一招“铁板桥”,可惜已晚了半步,剑锋正从他胸前的外衣划过,只听“嘶”的一声,整件外衣被撕了下来。任盈盈突然一声惊叫:“婷美内衣”。整个黑木涯的英雄都停住了手,全都呆呆盯着东方不败穿得内衣,只见那里峰恋突兀,俊秀挺拨。东方不败突然也变得千娇百媚,轻启朱唇道:“婷美内衣,美体修形,内外皆修,一穿就变。做女人挺好的。”
“扑通,……”黑木涯众英雄全部昏倒。
10.蓝天六必治牙膏
黄蓉、郭靖正在烧“叫化鸡”。
鸡熟了,黄蓉拿起递给郭靖:“靖哥哥,快吃啊。”
郭靖:“蓉儿,你真好。”正要伸手拿,突然一只更快的手把鸡给抢了过去。“嘿嘿,黄丫头,有了靖哥哥,就把老叫化给忘了?”“没有啊七公,我是怕你老了牙齿咬不动嘛!” “讲什么话,我现在牙好,胃口就好,吃嘛,嘛香,您看好了,蓝天六必治。” “嗖”的一声洪七公没影了,就留黄蓉在那儿直跺脚。

2010年8月最新搞笑短句100句

No Comments

来源:猫扑
1.你个不要脸的是不是以为四海之内皆你妈啊,谁都得惯着你!?

2.脏话对俺们这种俗人就是个语气助词而已,其实并不是故意就想说它,譬如要是搁古时候。《史记》里头那句“呜呼哀哉,逢时不祥!”,要是让俺说那就是“妈了个逼,逢时不祥!”,表达的意思都是一样一样一样的。
3.这么不要脸,这么没心没肺,你的体重应该会很轻吧?
4.别老问别人为什么不愿意理你,不愿意跟你说话,因为太稀罕你而不愿意搭理你现实吗?你信吗?
5.我没认识你之前,我真没发现原来我有以貌取人这毛病。
6.你这个贱人那么爱占便宜,假如拿人家的真手短的话,你他妈早就高位截瘫了!
7.牛逼 总是比衣服还潮流,旧款还没过时呢,新款就出来了。
8.能做到“三思而后行”的人不是因为他又聪明又理智,而是他怕轻而易举的被别人骂他妈和他大爷。
9.好男人就是反复睡一个姑娘,一睡就睡一辈子。
10.打是亲骂是爱,总骂你妈,都快跟你妈产生感情了。
11.人生就像曾轶可,要是一开始就跑偏就再也跑不回来了。
12.本人口儿重,拟禁绝可乐,改喝急支糖浆。
13.打算理发了,甩刘海甩的我脖子都崴了。
14.得知你过得不好我也就安心了。
15.两个B血型的人合作生产出的小孩是不是2B血型?
16.多年以后。我还是记得那个冬夜她满身大汉的样子。
17.我臊眉耷眼的走了,正如我挤眉弄眼地来。
18.我为你大爷感到悲哀。
19.“如果你老婆和你情人同时掉进水里,请问你是再找一个丰满型的还是娇小型的?”“还找不会游泳的。”
20.让一部分人先富起来,再消灭富不起来的,最终实现共同富裕。
21.任何一个消息在经过官方否认之前都不能相信。
22.我觉得全世界的熊全都一个熊样。
23.世界上有10种人,懂二进制和不懂的……
24.不用怀疑,我就是你梦中的穷人。
25.你没事儿老梦我干嘛,我忙你不知道么?
26.有个成语叫“一波三折”,谁给解释下这是怎么长的?
27.说吧,你是想死呢还是不想活了?
28.您是不是杂种操的,只有杂种和您本人清楚。
29.一失足成千古风流人物。
30.昨天中午跟同事在食堂吃饭的时候讨论我们几个的身高:我被冠以夜用加长型,她自己是日用的,旁边一个1米5多的同事说那我呢我是啥?我俩异口同声的说,你是护垫。
31.你何止是见钱眼开啊,你见钱屁眼儿都开了,不能叫开,那简直就是怒放。
32.学什么英语啊学英语,你看看九大常委哪个是英语专业毕业的,学英语没前途的。
33,那些总说别人装逼的人,你们连逼都不是。

34,曾经相信能把日子过成段子,如今只盼别把日子变成案子。
35,良家妇女也是妇女。
36,要说《阿凡达》中出场的人物里我最喜欢的还得说是巴依老爷。
37,我老觉着张铁林演戏缺一件道具——狗咬胶。
38,小李要移民到美国,领导问他:“你对你的工资不满意吗?” 小李说:“满意。” “对你的住房不满意?” “满意” “那是上网环境不满意?” “也满意” “对医疗,孩子上学都不满意?” “都满意!” “既然你都满意为什么还要移民?” “因为那里允许有不满意!”
39,听君一席话,自挂东南枝。
40,我最大的本事就是把便宜的东西用出昂贵的效果来。比如相机,话筒,自己。
41,什么?你们是纯洁的男女关系?什么意思?戴套儿了?
42,忙得我都不知道要干什么了。
43,鸡的反抗是让自己的肉变得不好吃。

44,这种烈女型女流氓,真是世所罕见。

45,我不是个仇go-vern-ment主义者,但很多下作的行为让我很看不起。
46,我对一切伪流氓行径一贯嗤之以鼻你不是不知道。

47,就算是人工智能,也不敌~天~然~傻~逼。

48,提醒各位mopper慎用网络语言。我认识一个小朋友,总说“我晕”、“我倒”,后来就得了脑缺血。专家建议,相比之下,还是改为传统说法“我操”更为安全,但专家同时也说:仍不宜过于频繁。
49,路上见一车,车后贴着六个字:着急你飞过去。
50,当今中国已是一个自由程度很高的国家,对于大多数普通公民来说,其实只有两件事是不允许的——就是这也不许,那也不许。

51,不管是李宇春还是曾轶可都是我的哥我的哥。
52,脸不见血身不见伤四下无人。
53,您硬要活生生把一奥黛丽赫本给打造成了站街妹。
54,实际上,到最后我们都会在别人的MSN上处于脱机状态。
55,善良就是别人挨饿的时候我吃肉不bia ji嘴。
56,倒霉是一种永远也不会错过的运气。
57,杜蕾斯破产不是悲剧,杜蕾斯破了才是悲剧。
58,好久没有人把牛皮吹的这么清新脱俗了!
59,领导们和我们的区别就在于,他们走红地毯,而我们走斑马线。
60,每个人都有不得不吃屎的时候,只是不要细嚼。
61,你跟他讲道理,他就和你耍流氓;你跟耍流氓,他就和你讲道理。请问,他是谁?
62,钱乃一味良药,有明目张胆之功效。
63,世界是我们的,也是儿子们的,可最终是那帮孙子们的。
64,虽然你身上喷了古龙水,但我还是能隐约闻到一股人渣味儿。
65,昨天接到一短信,让我速把钱汇入农行一账号。我回复说:别急,我马上烧给你!
66, 再逼我,再逼我我就装死给你们看。

68,有人说药品的说明书和它的作用是没有任何联系的,名不符实,这纯粹是污蔑。因为有一次我肚子疼去看医生,那位医生竟然给我开了感冒药,结果我马上就感冒了。
69,等我有钱了, 咱买棒棒糖 , 买二根 …… 一根你看着我吃 ,另一根 我吃给你看。
70,煮饭时,一只螃蟹顶出锅盖,对你说:”我 热!”,答曰: 想红就忍着 ……

71,昨日,李宇春在湖南长沙发布记者会,会上首次正面回应了网络上广为流传的“信春哥,得永生”等热门词句。李宇春说:“你们信我也好,不信我也好,你们总归都要死的。”在场的许多记者都流下了感动的眼泪!
72,我们要向前看,不错过些歪瓜劣枣怎么知道什么是好的。
73,伯母你好,我是你儿子的男朋友。
74,当初惊艳,完完全全,只为世面见得少。
75,人人都说我丑,其实我只是美得不明显。
76,明骚易躲,暗贱难防。
77,人类朝着~傻~逼~的方向一路狂奔!
78,能用钱解决的问题都不是问题,可问题是我是穷人。
79,士为知己者装死,女为悦己者整容。
80,不想当厨子的裁缝,不是好司机。

81,如果心情不好, 就去超市捏捏方便面 。
82,你给我滚,马不停蹄的滚……
83,老板,先来两斤真爱,拿回去喂狗!
84,要么忍,要么残忍。
85,八戒,别以为你站在路灯下就是夜明猪了.
86,孔子不能解决的问题,老子帮你解决。
87,没有不透风的墙,没有不能上吊的梁。
88,还没来得及去沾花惹草,就被人拔光了。
89,无理取闹,必有所图!
90,今天你醒来,枕边躺着一只蚊子,旁边有一封遗嘱:我奋斗了一晚,你的脸皮厚的让我无颜活在这个世上。主啊!宽恕他吧,我是自杀的!
91,我终究没能飙得过那辆宝马,只能眼看着它在夕阳中绝尘而去,不是我的引擎不好,而是我的车链子掉了
92,我的兴趣爱好可分为静态和动态两种,静态就是睡觉,动态就是翻身…
93,我诅咒你一辈子买方便面没调料包!
94,别谢!谢完还怎么好意思向你收钱啊?

95,我都不好意思抓你了,你怎么还好意思偷呢?
96,我这心碎得,捧出来跟饺子馅似的。
97,英雄不问出处,流氓不看岁数。
98,上帝肯定会原谅我的,因为那是他的职业。
99,脸先着地,无力回天。
100,难道你回个贴会死么 ^*^为了维护宇宙和平,回吧!

WTL学习总结

No Comments

         在windows平台,相比MFC,我更喜欢WTL,因其简洁漂亮。所以陆续花了一年的时间学习之,这里总结一下(在学习Wtl/Atl之前,最好是对WinApi编程有一定的了解)。

安装

        Wtl主页 http://sourceforge.net/projects/wtl/ ,整个库就是一堆.h文件,官方没有提供Installer,下载后解压到某个目录即可。

        如果需要在VS中使用“工作导向”,可以点击Appwiz目录下的对应js文件来安装之。虽然没有直接对VS2010的支持,不过拿VS2008的改改即可,详情Google下。

        为了在VS中使用Wtl,可以将include目录添加到全局Include Path。不过如果仅仅希望对单个项目有效,则在添加C++ Include Path的同时,可能还需要添加资源的Include Path。如果不希望以来第三方的库,可以直接将源码放在项目内。

 

Atl

        学习Wtl,不可能跳过Atl,Wtl底层就是使用Atl。关于Atl这里就几个点讲一下:

  1. ATL-style 模板
  2. 窗口创建和初始化
  3. Thunk技术
  4. 回调和消息绑定

 

ATL-style 模板

 
template <class T>
class B1
{
public:
    void SayHi()
    {
        T* pT = static_cast<T*>(this);   

        pT->PrintClassName();
    }
protected:
    void PrintClassName() { cout << "This is B1"; }
};

class D1 : public B1<D1>
{
    // No overridden functions at all
};

 

使用这个模板形式有几个好处:

  1. 不需要使用指向对象的指针。
  2. 节省内存,因为不需要虚函数表。
  3. 因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。
  4. 所有的函数调用在编译时确定(译者加:区别于C++的虚函数机制使用的动态编连),有利于编译程序对代码的优化。

    http://www.winmsg.com/wtl/Part1.htm

     

    窗口创建和初始化

            有个哥们就这个流程分析了一下,地址在这里 http://blog.csdn.net/jznsmail/archive/2004/12/01/200947.aspx

            在Windows下,任何窗口创建都是通过CreateWindowEx或者CreateWindow函数来实现之,Wtl/Atl也不例外,(不过WtlAtl的流程略有不同)。详细的流程见上面链接,这里就几个重点说一下(不仅仅是Atl,也包括Wtl的内容)

            对于Windows窗口,需要派生自CFrameWindowImpl类,创建和初始化窗口就在该类的CreateEx函数实现。通过前面讲到的“ATL-style 模板”,CreateEx会调用Create函数,CFrameWindowImpl提供了默认的Create实现,不过也可以在派生类中定制。在CFrameWindowImpl类成员函数Create中会首先注册窗口,然后才创建之。

            在CFrameWindowImpl的Create函数中会通过CFrameWindowImplBase的成员函数Create创建实际的窗口,使用API函数CreateWindowEx。

            在CFrameWindowImpl的Create函数中会调用CFrameWndClassInfo类成员函数Register注册窗口,注册用到的信息通过DECLARE_FRAME_WND_CLASS或DECLARE_FRAME_WND_CLASS_EX宏来指定。在这两个宏中会指定静态成员函数StartWindowProc 作为窗口回调。这个静态成员在CWindowImplBaseT类中定义。在这个静态成员中使用了下面提到的Thunk技术动态地修改回调参数,同时将回调重置为该类的另一个静态成员WindowProc(见API函数SetWindowLongPtr和属性GWLP_WNDPROC

            以上提到的是Wtl流程的一个简单概述,对于普通窗口(控件),我们一般使用Atl的那一套而非Wtl。

            对于控件,一般派生自CWindowImpl窗口,窗口工作在该类的成员函数Create中完成。注册窗口的信息使用DECLARE_WND_CLASS或DECLARE_WND_CLASS_EX宏。具体的注册操作在ATL::CWndClassInfo类的成员函数Register中完成,窗口的创建在CWindowImplBaseT类的成员函数Create中完成。

     

    Thunk技术

            对于Windows API,从系统的消息回调出来的消息唯一的标识符就是HWND句柄,而当前随便一个UI程序都有相当之多的控件,所以必须要有一种行之有效的方法来通过这个句柄定位控件。比较傻B的方法就是建立一个HWND到控件类实例的映射,然后在消息收到后查询之,不过这种方法有很大的局限性。

            Atl用Thunk技术来处理这个问题,这个所谓的“Thunk技术”干的事挺简单,不过要理解代码还真不容易(至少我是花了不少功夫)。有兴趣可以研究下代码:

    (Microsoft Visual Studio 9.0\VC\atlmfc\include\atlstdthunk.h)

    #pragma pack(push,1)
    struct _stdcallthunk
    {
    	DWORD   m_mov;          // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
    	DWORD   m_this;         //
    	BYTE    m_jmp;          // jmp WndProc
    	DWORD   m_relproc;      // relative jmp
    	BOOL Init(DWORD_PTR proc, void* pThis)
    	{
    		m_mov = 0x042444C7;  //C7 44 24 0C
    		m_this = PtrToUlong(pThis);
    		m_jmp = 0xe9;
    		m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
    		// write block from data cache and
    		//  flush from instruction cache
    		FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
    		return TRUE;
    	}
    	//some thunks will dynamically allocate the memory for the code
    	void* GetCodeAddress()
    	{
    		return this;
    	}
    	void* operator new(size_t)
    	{
            return __AllocStdCallThunk();
        }
        void operator delete(void* pThunk)
        {
            __FreeStdCallThunk(pThunk);
        }
    };
    #pragma pack(pop)

     

            这里我就不说什么原理了,我这水平也说不清:-)。

            Thunk技术实际上就是用一段汇编代码将函数的参数动态的替换,具体来说是将回调函数参数HWND动态替换成窗口类实例的指针。关于它推荐阅读 http://www.cngr.cn/article/54/395/2006/2006071928301.shtml

            这个过程在CWindowImplBaseT类的静态成员StartWindowProc中实现。首先在注册窗口时会指定它为回调,当第一次调用时,通过Thunk技术动态地修改回调参数,同时将回调重置为该类的另一个静态成员WindowProc。具体的实现代码如下:

    pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);
    WNDPROC pProc = pThis->m_thunk.GetWNDPROC();
    WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);

     

            那这个类实例在哪获取呢?其实Wtl/Atl实现也并不美观,通过CAtlWinModule类存储这些指针,很显然整个程序只应该包含一个CAtlWinModule类实例,这也是为什么我们经常看到这个实例前带一个extern关键字。

            在新的回调WindowProc中,Atl将参数HWND转型为CWindowImplBaseT类指针,然后调用该类的成员函数ProcessWindowMessage来进一步派发消息。这个函数怎么定义看下一节内容。

     

    回调和消息绑定

            前面提到一个ProcessWindowMessage函数,它最初在CMessageMap类中以纯虚函数的形式定义。在Atl中不管什么类,只要派生自CMessageMap就可以处理消息,这就提供了很大的灵活性。

            ATL定义了一堆预处理宏来实现这个函数和分发逻辑,典型的如下:

    	BEGIN_MSG_MAP(CMyWindow)
    		MESSAGE_HANDLER(WM_CLOSE, OnClose)
    		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    		COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
    	END_MSG_MAP()

            前后两个宏用于定义ProcessWindowMessage函数,中间则通过一个Switch结构来定义分发逻辑和回调绑定。

            ATL这一套结构有几个好处:

    1. 避免过多的纯虚函数导致额外的开销
    2. 容易将消息处理重定向,例如将消息处理重定向到一个非窗口类,实现UI和逻辑分离。也可以让同一个消息处理类同时处理多个窗口的消息。具体见ATL定义的那堆宏。

            Wtl在Atl基础上针对特定消息定义了很多更加精确的宏,不过个人不是很喜欢,因为懒得去记。

     

    Wtl入门

            最基本的Wtl程序可以通过Appwiz来生成,很有必要理解下Wtl程序的流程。简单地讲,控件使用CButton等封装类(atlctrls.h),窗口使用CFrameWindowImpl派生。如果希望自定义控件,可以派生自CWindowImpl(自绘还需要继承其他自绘支持类,见后面说明),例如

    class CBeepButton: public CWindowImpl< CBeepButton,CButton >
    {
    public:
    	DECLARE_WND_CLASS( _T("CBeepButton"))
    	BEGIN_MSG_MAP( CBeepButton )
    	END_MSG_MAP()
    };

            不过上面的类封装除了装逼没有任何用处!

            发现几个比较恶心的地方提一下。

            如果在自定义控件有绘制字体时,绘出来的字很奇怪,和默认的字体相差较大,后来发现可以手动设置字体将其设置为UI默认字体。

    	m_widget_->SetFont(AtlGetStockFont(DEFAULT_GUI_FONT));

            对于动态创建的Edit或者RichEdit,边框都很奇怪,为了设置默认的边框,需要

    	m_widget_->ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_DRAWFRAME);

     

    超类化

            超类化(superclass )是一种生成新的窗口类的方法。它的中心思想是依靠现有的窗口类,克隆出另一个窗口类。被克隆的类可以是Windows预定义的窗口类,这些预定义的窗口类有按钮或下拉框控制等等。也可以是一般的类。克隆的窗口类使用被克隆的类(基类)的窗口消息处理函数。

            克隆类可以有自己的窗口消息处理函数,也可以使用基类的窗口处理函数。

    http://www.builder.com.cn/2007/1116/637833.shtml

            超类化是以类型为单位来设置,也就是被超类的类必须是已经存在的窗口(很显然,如果想“超”自定义窗口类,必须保证该类至少被用过一次或者手动注册过,如果没注册则超类会初始化失败)。在消息处理时,Wtl首先会使用超类的处理函数,如果没处理,则会调用“被超类”的消息处理函数来处理。

            那这个“被超类”的消息处理函数在哪里呢?毕竟每次只能注册一个回调函数。实际上在CWindowImplBaseT类中包含一个成员m_pfnSuperWindowProc用于在超类化时存储“被超类”的原始回调。这个成员在CFrameWndClassInfo类或_ATL_WNDCLASSINFOW结构的成员函数Register调用时作为参数传入实现初始化。

            通过宏DECLARE_FRAME_WND_SUPERCLASS或DECLARE_WND_SUPERCLASS来声明一个超类,两者原理都是一致的,即定义一个OrigWndClassName。在注册窗口类是首先获得OrigWndClassName类的注册信息,然后替换回调,并且将原来的回调保存在参数中,这个参数即前面提到的m_pfnSuperWindowProc。

            关于这两回调的配合,可以参考CWindowImplBaseT类静态成员函数WindowProc(这个函数即通过Thunk技术重置后的回调函数,见前面说明)。Wtl首先会调用当前的回调,如果返回FALSE,则继续调用原来的回调。

            为了测试超类化自定义窗口类,我写了如下代码:

    class CBeepButton1: public CWindowImpl< CBeepButton1,CButton >
    {
    public:
    	DECLARE_WND_CLASS( _T("CBeepButton1"))
    	BEGIN_MSG_MAP( CBeepButton1 )
    		MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
    		MESSAGE_HANDLER( WM_LBUTTONUP, OnLButtonUp )
    	END_MSG_MAP()
    
    	LRESULT OnLButtonDown( UINT, WPARAM, LPARAM, BOOL& bHandled )
    	{
    		this->SetWindowText("Press");
    		bHandled = FALSE; // alternatively: DefWindowProc()
    		return 0;
    	}
    
    	LRESULT OnLButtonUp( UINT, WPARAM, LPARAM, BOOL& bHandled )
    	{
    		this->SetWindowText("Release");
    		bHandled = FALSE; // alternatively: DefWindowProc()
    		return 0;
    	}
    };
    
    class CBeepButton: public CWindowImpl< CBeepButton >
    {
    public:
    	DECLARE_WND_SUPERCLASS( _T("BeepButton"), _T("CBeepButton1") )
    	BEGIN_MSG_MAP( CBeepButton )
    		MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
    	END_MSG_MAP()
    	LRESULT OnLButtonDown( UINT, WPARAM, LPARAM, BOOL& bHandled )
    	{
    		bHandled = FALSE; // alternatively: DefWindowProc()
    		return 0;
    	}
    };

            经测试发现几个问题,为了使用 CBeepButton,必须先实现一个 CBeepButton1(目的是为了注册窗口类,主要是手动注册不方便)。否则 CBeepButton1会注册失败。即便如此 CbeepButton还是无法运行,究其原因是因为Thunk技术未能重置回调。

            前面说过,Atl在初始化一个窗口类时,会使用Thunk技术动态重置回调。这个两个回调分别是CWindowImplBaseT类静态成员函数StartWindowProc和WindowPro,Thunk技术在前者中被使用,是一个临时的回调,具体的消息分发在后者进行。

            当超类化一个自定义窗口类时,首先会获得原窗口的回调,而这个回调很显然是StartWindowProc,而新的窗口类并非处理了所有消息,所以总是有部分消息会发送到原窗口类的回调,即StartWindowProc。杯具就在这里,StartWindowProc是一个临时的回调。实际中当消息派发到这里时会碰到断点,因为无法从CAtlWinModule中获得类实例的指针(这都不是该窗口类初始化过程,当然找不到了)。这不得不说是Wtl的一个Bug,不过就目前这种框架,要改好还真不容易。

     

    子类化

            子类化(subclass)是普遍采用的一种扩展窗口功能的方法。它的大致原理如下。

            在一个窗口创建完了之后,将该窗口的窗口函数替换成新的窗口消息处理函数。这个新的窗口函数可以对某些需要处理的特定的消息进行处理,然后再将处理传给原来的窗口函数。

            注意它与superclass的区别。

            Superclass是以一个类为原版,进行克隆。既在注册新的窗口类时,使用的是基类窗口的窗口函数。

            而subclass是在某一个窗口注册并创建后,通过修改该窗口的窗口消息函数的地址而实现的。它是针对窗口实例。

    http://www.builder.com.cn/2007/1116/637833.shtml

            子类化的核心就是前面提到的Thunk技术,这里就不废话了。

     

    自绘

            说的自绘,我真想吐血,很不明白微软为什么要为了自绘搞出那么一套鸟毛东西,虽然所有的自绘都可以通过Paint来完成,不过既然微软推出那么一套鸟毛,总是会要犹豫一下,”我用Paint来做会不会有什么不妥“。

            并不是所有的内置控件都支持那一套鸟毛的自绘,Wtl通过COwnerDraw类和CCustomDraw 类来简化这一套东西的开发,其实就是定义一堆宏来处理回调,提供一个相对简单的接口给程序员。为了能够让控件收到这些自定义消息,需要在父窗口加入反射宏DEFAULT_REFLECTION_HANDLER()。

            不过个人很少用这东西,即便要自绘Button,我也是通过WM_PAINT来做的(这种做法有什么不妥忘各位指出,不过我没发现效率有多低,至少结构一致和美观,我想谁都不想去理解那么多规则,简单就是美)。

     

    浏览器控件

            如今越来越多的UI程序选择浏览器控件来显示网页内容,这有很多好处,至少简化了开发并且可以实现很复杂的样式,更重要的是无需升级可保持最新。

            Wtl官方并没有内置浏览器控件,不过可以参考这里http://devel.openocr.org/svn/openocr/trunk/cuneiform/interface/icrashreport/wtl/samples/tabbrowser/browserview.h ,               

    Older Entries