上午空着肚子研究了半天css和php,把blog的模板重新规划了一下:
- 页面自适应各种分辨率,这样才对得起我的2k显示器。
- Archives显示最近50年的文章链接,估计是够用了。
- 时间显示精确到分钟,大幅缓解了我的强迫症。
最终感想:好饿。
Hello World
上午空着肚子研究了半天css和php,把blog的模板重新规划了一下:
最终感想:好饿。
之前并没有很在乎这方面的事情,直到最近需要在某个bcb编译的程序中集成steam sdk。
故事是这样的,steam sdk提供的接口是c++代码,官方号称支持visual studio和gcc两种编译器。但我不想先做一层包装转换成c的接口再使用,所以打算强行在bcb中链接这些lib。
现在问题来了,由于c++的abi很混乱,微软又喜欢玩花活,一旦接口是c++的格式(class, virtual table)就很微妙。这就牵扯到了calling convention的事情,也就是函数调用约定。这件事情本来很简单,在c的时代只要解决两个问题就好:1、参数个数是不是固定的。2、参数是如何被放到堆栈中的。第二个问题无关痛痒,所以c规定参数一律是从右到左压入栈中,这样用的时候就可以从左到右依次弹出。
所以calling convention只和第一个问题有关,这样就诞生了两个关键字:
__cdecl:支持不固定个数的参数,调用方caller负责清理堆栈。好处是printf之类的东西可以实现,坏处是函数内部不能对堆栈玩花活,因为管理权在外面。
__stdcall:参数个数固定,被调用方callee负责清理堆栈。好处就是函数结尾直接用ret xx解决所有问题,坏处是参数个数固定,因为编译期就得决定ret之后的字节数。顺道说,COM中所有调用都是__stdcall。
目前为止一切还是有序的,直到link的时候,出现了name mangling的问题。M$和Borland使用了截然相反的方式来修饰函数名,具体可以参考《Using Visual C++ DLLs with C++Builder》中的Table A。还好双方都提供了解决方法,任选一种就可以生成合适的符号名,可以愉快的link。
即使link没有问题,问题也还远未解决,一旦开始使用c++的接口,run-time的问题就浮现出来。这就要提到M$的发明:
__thiscall:用于non-static member function,会把this放入ecx,再调用函数。好处就是this指针非常容易找到,调试方便。坏处就是……这是M$的发明,而非标准。还好时至今日很多编译器都支持了__thiscall,巨硬不再孤家寡人。
实际上bcb对this的解决方法非常简单,就是把它当作第一个函数的参数,照样传递进去即可。调试时候也不见得有多麻烦,而且如今编译器对代码的优化非常风骚,ecx在入口可能还有用,进入函数体之后几乎就完全无用了。
__fastcall:时代的眼泪,从名字看很拉风,但定义特别复杂。具体就是说把前几个较小的参数从寄存器里传入,而不是压入堆栈中。这样调用双方省去了很多进出栈的操作,故名fast。时至今日,这些开销似乎没什么人在乎了。
其他的调用约定我没怎么看就不说了,手头的情况只和后两种有关。这个bcb的程序诞生于近20年前,作者大概觉得函数调用开销是一件很重要的事情,所以默认全部函数都采用__fastcall。而steam sdk的c++接口全部用默认的__thiscall,运行的时候caller把参数往寄存器一丢了事,而callee还苦苦在ecx和堆栈中寻找,代码就跑飞了。
解决方案非常悲剧,对于每个class的每个non-static member function,都提供一个静态函数作为wrapper。内部帮他们把参数位置转换一下,然后再调用sdk中的实际代码。
用macro封装的效果如下所示(1个参数)
#define STEAM_API_CALL_WRAPPER1(ret, type, interface, index, var1) \ ret STEAM_API_CALL type##_##interface(type * thisptr, var1) { \ asm { \ MOV eax, [ebp + 4 * 3]; \ push eax; \ MOV ecx, thisptr; \ MOV edx, [ecx]; \ CALL [edx + 4 * index]; \ } \ } //
使用之前必须逐个定义,还得注意虚函数在虚表中的位置。不幸中的万幸是bcb和vs的虚表排列都是一样的。万幸中的不幸是,steam sdk中居然还有头文件和编译好的lib中虚表里函数顺序不一致的情况。大概是某个程序员手贱没事就调整函数的位置,又不肯重新编译sdk吧……
STEAM_API_CALL_WRAPPER1(bool, ISteamUserStats, SetAchievement, 7, const char *pchName);
经过这么多折腾之后,终于成功在bcb中链接运行了steam sdk。接下来发现还有callback的问题,steam要调用从bcb程序里传入的函数指针!解决方法和上面类似,callback函数也需要有一层wrapper……
只要对所有使用到的接口做上述处理,最终一切都和谐美好的运行起来,完全没有使用额外的dll,直接在bcb中使用了steam_api.dll。我研究了一下网上已有的实现,都是采用包装一个wrapper dll的方式。我这边的方法相对安全一些,否则wrapper可以被轻松的替换掉。当然咯,steam有提供额外的drm措施,简单换掉wrapper并不能移除steam绑定就是了。所以如果不是像我这样有强迫症的话,写个wrapper dll是最简单直接的方式。
结论是如果以后需要写什么对外发布的sdk,还是以c接口的形式提供最具有兼容性。
鉴于没有多少时间可以被杀掉,我决定弃用各种SNS。
前段时间因为搬家折腾了五六周,现在终于安顿下来,又要准备开始过年。
先把自己的工作环境整理好,接下来就是工作时间。
坑已经挖了很多,必须赶紧填起来……
现在问题来了:哪个坑的优先级比较高呢?
本来想1号贴一篇的,可惜新年搬了一次家,错过了……
老样子,先写去年的关键字:HoS,Titanfall,Torchlight2,FF14,Call of Juarez Gunslinger……当然还有:炉石传说,Risk of Rain,以及我用150+小时几乎完美通关的《Dragon Age Inquisition》。
DAI不枉我等了好多年,B社这次十分给力。下一个期待点是《Witcher 3》,看这跳票的节奏应该不会是个雷。
14年后半年逐渐退出了所有的投机活动,感觉浪费自己的时间,还会扭曲价值观(严肃脸)。没想到年底遇上倒霉的事情,浪费了不少钱,真是瞎折腾的一年。
不过14年算是混到了一个credit,可惜上头不给力,把口碑弄坏掉了。接下来又是自研项目,先看看会搞成这么样子吧。反正已经对AAA厌倦了,一切都是浮云,搬砖不分高低贵贱……
恩,就这样吧。
Multiply是PS中常用的一种混合方式,中文版称为“正片叠底”模式。
说起来非常简单,就是颜色分量相乘。但实际上简单的相乘并没有考虑到alpha channel。
如果两个图层都是RGBA四个通道,Multiply的算法还得加入alpha。
为了搞清楚这个问题,我琢磨了好几个小时。最后从颜色含义入手,配合一些测试结果,终于推出了PS的混合公式:
OutAlpha = A1 + A2 - A1*A2 OutColor = (1.0 - (1.0 - C1)*A1) * (1.0 - (1.0 - C2)*A2)
可以看出这个公式对于A1和A2是对称的,所以被混合的两个图层没有前后之分。
推理过程如下:Multiply混合可以看作是两张幻灯片叠在一起,因为变得更厚所以透过的颜色变少,从而达到减色效果。在这里颜色分量Color是光可以透过的程度,1.0即表示该颜色分量全部可以透过。1.0-Color表示被吸收掉的部分。在PS的混合算法中,Alpha用来调节吸收程度。因为Alpha经常用于表示透明度,1.0表示完全不透明,即吸收程度为100%。
结合以上两个定义:
(1.0 - Color) * Alpha // 表示被吸收的颜色 1.0 - (1.0 - Color) * Alpha // 表示最终透过的颜色
把两个最终透过的颜色相乘,即得到我们需要的最终减色结果。
在忽略Alpha只有RGB混合的情况下,Alpha都取1.0,则公式可以化简为:
OutAlpha = 1.0 OutColor = C1 * C2
结果为颜色分量直接相乘,也就是到处都可以找到的对Multiply的算法的描述。
以下是c#版的实现……
static byte Clamp(int result) { if (result < 0) return (byte)0; return result > 255 ? (byte)255 : (byte)result; } static byte MultiplyColor(int lhs, int rhs, int lhsAlpha, int rhsAlpha) { if (lhsAlpha == 0) return Clamp(rhs); else if (rhsAlpha == 0) return Clamp(lhs); int lhsMultiply = (255 - (255 - lhs) * lhsAlpha / 255); int rhsMultiply = (255 - (255 - rhs) * rhsAlpha / 255); int result = rhsMultiply * lhsMultiply / 255; return Clamp(result); } // same as Photoshop multiply blend mode static public void Multiply(Bitmap lhs, Bitmap rhs, Rectangle roi) { BitmapData lhsData = SetImageToProcess(lhs, roi); BitmapData rhsData = SetImageToProcess(rhs, roi); int width = lhsData.Width; int height = lhsData.Height; int offset = lhsData.Stride; unsafe { byte* lhsPtr = (byte*)lhsData.Scan0; byte* rhsPtr = (byte*)rhsData.Scan0; for (int y = 0; y < height; ++y) { for (int x = 0; x < width * 4; x+=4) { int lhsAlpha = lhsPtr[x + 3]; int rhsAlpha = rhsPtr[x + 3]; // multiply color with alpha factor lhsPtr[x + 0] = MultiplyColor(lhsPtr[x + 0], rhsPtr[x + 0], lhsAlpha, rhsAlpha); lhsPtr[x + 1] = MultiplyColor(lhsPtr[x + 1], rhsPtr[x + 1], lhsAlpha, rhsAlpha); lhsPtr[x + 2] = MultiplyColor(lhsPtr[x + 2], rhsPtr[x + 2], lhsAlpha, rhsAlpha); // also blend the alpha channel int retAlpha = (lhsAlpha + rhsAlpha - lhsAlpha * rhsAlpha / 255); lhsPtr[x + 3] = Clamp(retAlpha); } lhsPtr += offset; rhsPtr += offset; } } lhs.UnlockBits(lhsData); rhs.UnlockBits(rhsData); }
这里确实已经变成年签了,好久不登录连密码都忘记了。
2013很丰富,通了很多游戏,更新了战斗装备,所以关键字一大堆。
有ROSE,树莓派,DQ,DAO,巫师,星际2,炉石传说……
还有悲剧的丢包,棒极了的圣诞游,难忘的歌剧,以及超萌的小猫……
年中开始做AAA,却很快产生了厌倦,无论去留都不完美,十分鸡肋。
作为互联网难民,翻墙的代价还真不小啊……
对未来就不期待什么突破了,2014能完坑我就很满意啦。
因为要改sdl的代码,又不想用hg,所以想找个类似remote svn一样的玩意儿。
放狗一搜结果发现还真有,作者felipec写了很详细的介绍:
http://felipec.wordpress.com/2012/11/13/git-remote-hg-bzr-2/
看起来是linux下的玩意儿,但作者没说在windows下怎么用。既然是个脚本,没理由不能在windows下跑。继续搜,找到了一个人在stack overflow上的发问,关于这玩意儿怎么安装的问题:
http://stackoverflow.com/questions/883452/git-interoperability-with-a-mercurial-repository
felipec很乐于推销自己的作品,此问题就是他回答的。可惜还是没说清楚怎么安装,所以评论里也有人喷他,’Simplicity. Ah… Magic!’
我只好先去他的github上看看代码:
https://github.com/felipec/git/blob/fc/master/contrib/remote-helpers/git-remote-hg.py
代码里似乎有windows的字样,也许最近更新过。好吧就假设windows可以直接用了,我下载了一份,把python27也装上,设置好PATH。
根据作者一贯的口气,只要把脚本也放到PATH中的路径下即可。我照做,然后执行:
git clone hg::http://<any-hg-url>
却报了个很奇怪的错:
fatal: git was built without support for C:\Program (NO_PYTHON=YesPlease)
这个YesPlease的提示看起来很怨念的样子,我继续搜,果然也有人在问:
https://groups.google.com/forum/?fromgroups#!topic/msysgit/LpyViW97g0A
一看内容我就笑了,貌似是msysgit的人在喷这个felipec,说他不肯和msysgit合作。结果felipec跳出来反喷,说msysgit有意忽略他的作品。
总之他俩很欢乐的喷了半天,还是没说怎么安装……
我只好自力更生…… 先搜了一下文件名,在msysgit的安装目录git\libexec\git-core中居然发现了名为git-remote-hg的文件,内容就几行:
#!/bin/sh echo >&2 "fatal: git was built without support for `basename $0` (NO_PYTHON=YesPlease)." exit 128
我擦咧这不就是报错的地方么。我想了想,把git-remote-hg.py去掉扩展名,然后替换了这个文件。试了下git clone,这下有点进步了,脚本开始运行,报错说找不到mercurial模块。
好吧我确实忘记给python安装mercurial模块了。于是到hg的官网下载mercurial-2.6.tar.gz,然后尝试执行里面的setup.py:
python setup.py build
build居然报错:
error: Unable to find vcvarsall.bat
我一直以为mercurial是纯python的,原来还有c代码!可是我有装vs2012,vcvarsall.bat是用来配置vc编译环境的,按道理我是有的。
只好继续搜,翻过各种不靠谱的页面之后,找到了一篇详细的解释:
http://stackoverflow.com/questions/3047542/building-lxml-for-python-2-7-on-windows/5122521#5122521
简单的说就是python的windows版都是用vs2008编译的,所以它非要找vs2008的环境变量。如果换编译器来编译模块,可能工作不正常。
这个帖子建议是,去装个免费的vs2008 express吧!
看到这里我直接晕倒,开源社区的人对待windows用户真是不友好啊。何以解忧?唯有google!绕了一大圈后,我在mercurial官网看到了解决方案:
http://mercurial.selenic.com/wiki/WindowsInstall
原来只需要强制换用其他编译环境就好,譬如官网说可以用mingw来代替:
python setup.py build --force -c mingw32 python setup.py install --force --skip-build
嗯嗯,还是官网靠谱,于是乎mercurial的python package终于可以编译安装了。
接下来回到我的最初目的:
git clone hg::http://<any-hg-url>
顺利通过了!嗯嗯,终于可以开始干正事儿了……
树莓派到手之后,参考网上的资料折腾了几天,都是很基本的东西。
1、SD卡
为了读写速度,专程购买了Kingston的32G Class10高速SD卡,标号SD10V/32G。
虽然官方列表http://elinux.org/RPi_SD_cards 信誓旦旦表示it works,但装好系统镜像之后boot报错。似乎是部分数据不可读,虽然能看到树莓派logo,也似乎开始加载内核,但很快就停滞,一直提示timeout。在网上搜了下,很多人抱怨这个卡没法用,推荐Sandisk的牌子,我手头没有不评价。更换成一张Kingston 8G Class4的卡之后终于可以启动系统镜像,还好新买的卡可以用在相机上,否则就浪费了……
后来找到个很老的Kingston 512m TF卡,加了套子之后也可以正常的使用在树莓派上。因为容量问题,只能加载其他小号系统镜像,因为官方镜像要求2G空间。
2、系统镜像
我安装的是2013-02-09-wheezy-raspbian.zip,Raspbian “wheezy”,应该是个Debian的RPi移植版。
关于如何安装镜像,如何设置,参考了这篇文章:《树莓派Raspberry Pi上手报告》
不过我的显示器无法直接全屏显示1080p的画面,手动修改了config.txt中的显示模式才得以全屏。树莓派的官方系统镜像为2G,刷写之后,多于2G的部分没有分区,最好在第一次设置的时候扩展到全盘。
系统设置界面的选项,选定之后项目会立即生效,这有点出乎我的意料。实际上设置界面并不是简单的修改配置文件,而是要运行某些配置程序。
3、系统更新
注意更新之前需要连接网线。刚开始玩的时候我也不知道为啥需要更新,但很多文章都建议首先做如下步骤,我就照做了:
sudo apt-get update sudo apt-get upgrade
后来才知道这是更新Raspbian的软件包和系统程序。最新的镜像里可能不如网上当前的版本新,更新一下聊胜于无。
4、固件更新
树莓派的固件是放在SD卡的第一个分区上的,这个分区是FAT分区,直接在windows也可以查看。
此分区上保存的几个文件,是在启动时被GPU/CPU自动加载的。因为树莓派没有bios,它的GPU首先会读取SD卡文件,靠文件名或config.txt中的配置,以固定顺序加载几个文件,然后再唤醒CPU。
具体可以参考这里的描述:
http://elinux.org/RPi_Software
http://kariddi.blogspot.sg/2012/08/raspberry-pi-bare-metal-part-1-boot.html
总之,更新固件很简单,拷贝文件到SD卡即可。最新的固件托管在github上,而且有人制作了脚本来自动更新固件。
https://github.com/Hexxeh/rpi-update
参考网站说明即可,相当于自动下载文件然后拷贝到第一个分区/boot/
5、软件安装
最方便的方法是接网线,然后通过网络安装各种软件。基本上两种方法:
a) 用sudu apt-get install命令安装软件包。很方便,不过需要有网络。
b) 用git来安装,很多软件并没有放到系统的软件包列表中,需要用git直接拿到编译好的版本或源码。
已经记不太清楚最初安装了些什么,基本上属于用到啥装啥。但gcc编译器,git,以及chrome都是一定要装的,系统自带的浏览器比较弱……
安装chrome,仍然可以参考雷锋网那篇文章。装好编译器,源码就可以直接在树莓派上编了。在pc上编比较麻烦,得配置交叉编译环境,因为树莓派是arm芯片。
6、总结
到此为止,一个可用的系统差不多就弄好了。不过这样只是换了个地方玩linux,如果想研究linux可以继续折腾。因为树莓派比我想象的慢,在上面玩linux不如在虚拟机上玩。
但我对树莓派本身比较感兴趣,打算抛开linux来折腾它。但接下来先记录一下安装ruby,SDL,web服务,以及我那坑爹的Linksys AE3000无线网卡的安装过程。
To be continued…
终于从家里把Raspberry Pi带来了,红色PCB果然好看很多。显然装个linux是不能满足我的好奇心的,接下来就好好折腾一下吧~
不过当初是因为缺货才从家里买,结果现在也不需要了,于是又搞了个绿色的版本。一红一绿,试试看pi to pi的开发模式……
以前一直对操作系统没有兴趣,最近兴趣大增,是该研究研究了。
过了这么多个除夕之后,已经木有当年那种兴奋的感觉了……不过今年例外,头一次这样过年,还挺新鲜的。
值得一提的是,从去年11.3开始沉迷Dota2,至今已在它身上消耗了近250个小时。难道要打破我在MH3中创下的300小时的记录么?
说起来Farcry3确实是个好游戏,是唯一能让我停下Dota2还能将之穿掉的玩意儿,MGS4都不能做到这一点。还有可怜的Borderlands也终于被搞定了,遗憾的是二代很贵且担心机器带不动,从长计议吧……
蛇年要到了,嘶嘶嘶~~