相信每一个在windows下编过程序的人都或多或少地用过位图,大多数人是从网上下载一些成熟完善的dib类库来使用(例如cximage、cdib),少数人有一套自己封装好的dib类库,方便以后的扩充和使用。(近几年gdi+异军突起,在某些处理方面,如:缩放、旋转、渐变填充等它提供无与伦比的速度和质量,但,如果你想做一个完善的图像处理程序,直接使用它会给架构设计带来困难,你可以用adapter模式封装它后再使用)。
这时候,如果你需要一些图像处理操作你会怎么办呢?很多没有oo经验的c++程序员(例如一年前的我)可能会这样做:在类中直接添加方法。
//================================================================ int fclamp0255 (int nvalue) {return max (0, min (0xff, nvalue));} // 饱和到0–255 class fcobjimage { public : invert () ; adjustrgb (int r, int g, int b) ; } ; //================================================================ void fcobjimage::invert () { if ((gethandle() == null) (colorbits() < 24)) return ; int nspan = colorbits() / 8 ; // 每象素字节数3, 4 for (int y=0 ; y < height() ; y++) { byte * ppixel = getbits (y) ; for (int x=0 ; x < width() ; x++, ppixel += nspan) { ppixel[0] = ~ppixel[0] ; ppixel[1] = ~ppixel[1] ; ppixel[2] = ~ppixel[2] ; } } } //================================================================ void fcobjimage::adjustrgb (int r, int g, int b) { if ((gethandle() == null) (colorbits() < 24)) return ; int nspan = colorbits() / 8 ; // 每象素字节数3, 4 for (int y=0 ; y < height() ; y++) { byte * ppixel = getbits (y) ; for (int x=0 ; x < width() ; x++, ppixel += nspan) { ppixel[0] = fclamp0255 (ppixel[0] + b) ; ppixel[1] = fclamp0255 (ppixel[1] + g) ; ppixel[2] = fclamp0255 (ppixel[2] + r) ; } } } //================================================================ |
这里举了两个例子(分别实现反色,调节rgb值功能),现实中会有大量的此类操作:亮度、对比度、饱和度……现在回想一下,你添加这些方法的步骤是什么,ooooooooo,rcp(我同事的发明,全称:rapid copy paste^-^),第一步一定是从上面复制一块代码下来,然后改掉其中的接口和处理部分。虽然这里的示范代码很短小,不会连同bug一起复制,但,定时炸弹却又多了一个。有天,你的boss告诉你:我不能忍受长时间的等待,请给我加个进度条…..。你也许会加个全局变量,也许会给每个函数加个参数,但不变的是:你必须修改所有这些处理函数的代码,内心的咒骂并不会使你少改其中的任何一个。而此时,bug已经在旁边伺机而动了…然而苦日子远没熬到头,一个月后,你心血来潮的老板会让你在其中加上区域处理的功能,再一个月后……
回头重新看看代码?没错,除了红色的代码外,其他地方一摸一样,那能不能把这些算法分离抽出来呢?可能我们马上会想到标准库中qsort和windows中常用的回调方法。好,让我们实作一下:
//================================================================void pixel_invert (byte * ppixel) { ppixel[0] = ~ppixel[0] ; ppixel[1] = ~ppixel[1] ; ppixel[2] = ~ppixel[2] ; } //================================================================ void fcobjimage::pixelprocess (void(__cdecl*pixelproc)(byte * ppixel)) { if ((gethandle() == null) (colorbits() < 24)) return ; int nspan = colorbits() / 8 ; // 每象素字节数3, 4 for (int y=0 ; y < height() ; y++) { byte * ppixel = getbits (y) ; for (int x=0 ; x < width() ; x++, ppixel += nspan) { pixelproc (ppixel) ; } } } //================================================================ void fcobjimage::invert () { pixelprocess (pixel_invert) ; } //================================================================ |
嗯,看样子不错,算法被剥离到一个单一函数中,我们似乎已经解决问题了。处理invert它完成的非常好,但处理adjustrgb时遇到了麻烦,rgb那三个调节参数怎么传进去呢?我们的接口参数只有一个,通过添加全局变量/成员变量?这是一个办法,但随着类方法的增加,程序的可读性和维护性会急剧的下降,反而倒不如改之前的效果好。
>> 本文固定链接: http://www.vcgood.com/archives/1381