Monthly Archives: May 2007

ICON合并(3)

合并ICON的另外一种方法,在win32api中要合并两个HICON是很方便的,程序如下: HICON CombineIcon(            HICON hBaseIcon,  //背景图标            HICON hTopIcon,   //前景图标            int iWidth,       //宽度            int iHeight,      //高度            COLORREF colorkey //透明色,如RGB(0,0,0)            ){    HDC hdc = GetDC(NULL);    //建立一个兼容DC,用于作图    HDC memdc = CreateCompatibleDC(hdc);    HBITMAP membmp=(HBITMAP)CreateCompatibleBitmap                        (hdc, iWidth, iHeight);    HBITMAP oldbmp = (HBITMAP)SelectObject(memdc,                                        membmp);    HBRUSH br = (HBRUSH)GetStockObject(colorkey);    RECT rect;    rect.left = 0;    rect.right = iWidth;    rect.top = 0;    rect.bottom = iHeight;    SelectObject(memdc, br);    //填充透明色    FillRect(hdc, &rect, br);    DrawIconEx(memdc, 0, 0, hBaseIcon, iWidth,                    iHeight, 0, NULL, DI_NORMAL);     DrawIconEx(memdc, 0, 0, hTopIcon , iWidth,                    iHeight, 0, NULL, DI_NORMAL);    SelectObject(memdc, oldbmp);    ICONINFO info;    info.fIcon = true;    info.hbmMask = CreateBitmap(iWidth, iHeight,                                    1, 1, NULL);    info.hbmColor = membmp;    //返回合并后的图标句柄    return CreateIconIndirect(&info);} 用LoadIcon函数即可从icon文件得到 HICON句柄,有了上面的代码,可以很方便的合并两个HICON并显示出来。但是,怎么把HICON保存为ico文件呢?用CImageList应该是可以实现,但是我做这道题时被要求只能用win32api,不能用MFC的东西。就必须用到GetIconInfo,然后一步一步获取ico文件中必须的相应信息。这其中有个问题,就是在获取图像数据时用到了GetDIBits,而这个函数中颜色表必须是系统预设的,不能自己指定颜色表,这样,就会导致颜色失真,因为系统自带的颜色表中颜色很少,而要合并的图标中的颜色很有可能就没有出现在这个颜色表中。没有试过CImageList,不知道它能否解决这个问题。

ICON合并(2)

上次写了一个完整的icon文件结构了,那么,读取一个icon文件的代码也就容易写了,有个小地方要注意,打开文件的选项是rb,因为icon文件是2进制文件,针对32×32的256色icon文件的 代码如下: LPTSTR              filename,ICONDIR*            pIconDir,ICONDIRENTRY*       pIconDirEntry,ICONIMAGE*          pIconImage,BITMAPINFOHEADER *  pBIH,RGBQUAD *           pColorTable,BYTE*               xorMask,BYTE*               andMaskFILE *f=fopen(filename,"rb");// 创建ICONDIR来存放数据pIconDir =(ICONDIR*)malloc(sizeof(ICONDIR));// 保留字fread(&(pIconDir->idReserved),sizeof(WORD),1,f);// 类型字fread(&(pIconDir->idType),sizeof(WORD),1,f);// 文件中包含的图标个数fread(&(pIconDir->idCount),sizeof(WORD),1,f);// 根据图像个数重新分配ICONDIR空间pIconDir = (ICONDIR*)realloc( pIconDir,    (sizeof(WORD)* 3 )+    (sizeof(ICONDIRENTRY)*pIconDir->idCount));// 读取ICONDIRENTRYfread(pIconDir->idEntries,      pIconDir->idCount*sizeof(ICONDIRENTRY),1,f);// 对于每个图标,循环for(i=0;i<pIconDir->idCount;i++){    pIconDirEntry=&(pIconDir->idEntries[i]);    // 分配保存图像信息的空间    pIconImage = (ICONIMAGE* )malloc(                    pIconDirEntry->dwBytesInRes );    // seek至图像所在位置    fseek(f,pIconDir->idEntries[i].dwImageOffset,            SEEK_SET);    // 读取图像数据    fread(pIconImage,        pIconDir->idEntries[i].dwBytesInRes,1,f);    pBIH=&(pIconImage->icHeader);    // 获得色彩表    pColorTable=(RGBQUAD*)(        (LPSTR)pIconImage+        (WORD) (pIconImage->icHeader.biSize));    xorMask=(BYTE*)((LPSTR)pColorTable+256*4);    andMask=xorMask+32*32;    /*    do something    */} 至于icon的合并,图片分为前景图片和背景图片,对前景图片每个像素遍历,如果像素不是透明的,就把该像素的颜色复制到背景图片上去。这里颜色是不能直接复制的,当前像素的掩码就是当前像素颜色在颜色表中的索引,查找颜色表得到像素的RGB编码,然后在背景图片的颜色表中找到该RGB编码对应的索引,再把该索引写到背景图片的掩码区。我说得可能啰嗦了点,简单点表示就是:前景xorMask -> 前景颜色表index -> RGB -> 背景颜色表index -> 背景xorMask。然后还有一点不能忽略,该像素如果在背景颜色的andMask中表明是透明(值为1)的话,现在要改为非透明(值为0)。 思路大体如此,但并不完善,因为前景图片中的颜色也许在背景图片里面就没有,要真正解决好这个问题,可能还有些麻烦,要重新构建icon的颜色表。

icon合并(1) – 文件结构

要合并icon,就要明白ICON文件的格式。 首先是文件头。由于一个ico文件可以包含多个icon,为了表示的方便,就在文件头部用了一个目录结构 ICONDIR: typedef struct{    WORD   idReserved;   // Reserved (must be 0)    WORD   idType;       // Resource Type (1 for icons)    WORD   idCount;      // How many images?    ICONDIRENTRY   idEntries[1]; // An entry for each image} ICONDIR, *LPICONDIR 前三项分别是保留区,类型标识和icon个数。第四项开始就是一个ICONDIRENTRY的数组,每个数组元素将对应一个icon。当然,icon数据不是存储在这个ICONDIRENTRY里面的。下面就是ICONDIRENTRY的结构: typedef struct{    BYTE     bWidth;          // Width, in pixels, of the image    BYTE     bHeight;         // Height, in pixels, of the image    BYTE     bColorCount;   // Number of colors in image (0 if >=8bpp)    BYTE     bReserved;     // Reserved ( must be 0)    WORD    wPlanes;        // Color Planes    WORD    wBitCount;     // Bits per pixel    DWORD  dwBytesInRes;    // How many bytes in this resource?    DWORD  dwImageOffset;   // Where in the file is this image?} ICONDIRENTRY, *LPICONDIRENTRY; 可见,在这个ICONDIRENTRY中定义了icon的一些基本信息,dwImageOffset指向真正的icon数据,它标记了icon数据在此图标文件中的偏移位置。icon数据区的大小记录在dwBytesInRes中。 那么,icon图像数据又是怎么存储的呢?它由下面的结构表示,ICONIMAGE : typdef struct{    BITMAPINFOHEADER   icHeader;      // DIB header    RGBQUAD         icColors[1];   // Color table    BYTE            icXOR[1];      // DIB bits for XOR mask    BYTE            icAND[1];      // DIB bits for AND mask} ICONIMAGE, *LPICONIMAGE; 每个icon图像有一个和bmp文件一样的DIB头,有一张颜色表,然后就是图像数据的掩码。再来一个个分析这些结构,下面是 BITMAPINFOHEADER: typedef struct tagBITMAPINFOHEADER{    DWORD  biSize;    LONG      biWidth;    LONG      biHeight;    WORD     biPlanes;    WORD     biBitCount;    DWORD   biCompression;    DWORD   biSizeImage;    LONG      biXPelsPerMeter;    LONG      biYPelsPerMeter;    DWORD  biClrUsed;    DWORD  biClrImportant;} BITMAPINFOHEADER, *PBITMAPINFOHEADER;  在icon数据中的BITMAPINFOHEADER只用到了其中的 biSize,biWidth,biHeight,biPlanes,biBitCount,biSizeImage,其它的成员必须赋值为0。其中 biSize表示了该结构体需要的字节数,一般就是40(但也有例外,可能采用BITMAPV4HEADER或者BITMAPV5HEADER)。 biHeight在这里是像素高度的两倍,因为是XOR掩码区和AND掩码区高度之和。biBitCount取值范围是 0,1,4,8,16,24,32。我处理256色的icon,这里取值就是8。biSizeImage表示掩码数据区的大小(XOR和AND)。有了BITMAPINFOHEADER信息,就可以定位颜色表,地址为 icColors= ((LPSTR)pIconImage + (WORD)(pIconImage ->icHeader.biSize)) 然后再来看颜色表的格式,RGBQUAD: typedef struct tagRGBQUAD {    BYTE rgbBlue;    BYTE rgbGreen;    BYTE rgbRed;    BYTE rgbReserved;} RGBQUAD;  颜色表的大小取决于icon文件中所用到的颜色数(icHeader.biBitCount),对于24位或者32位色的图像就不需要用到颜色表了。 定位了颜色表并且知道了颜色表的大小就可以算出XOR掩码和AND掩码的地址。掩码的应用规则如下: AND掩码 XOR掩码 规则 0 n 显示icColors[n]指定的颜色 1 0 透明 1 非0 屏幕反色 一个完整的icon文件结构如下: ——————–Begin Of FileIconDir    idReserved    idType    idCount    idEntries[0] //ICONDIRENTRY        bWidth        bHeight        bColorCount        bReserved        wPlanes        wBitCount        dwBytesInRes        dwImageOffset    …    …    idEntries[idCount-1] //ICONDIRENTRY        bWidth        bHeight        bColorCount        bReserved        wPlanes        wBitCount        dwBytesInRes        dwImageOffset——————–IconDir结束,图片数据开头IconImage 1    icHeader        biSize        biWidth        biHeight        biPlanes        biBitCount        biCompression        biSizeImage        biXPelsPerMeter        biYPelsPerMeter        biClrUsed        biClrImportant    icColors[256]   //假设是32×32的256色的icon    icXOR[1024] //32×32    icAND[128]  //32×32/8每个像素之要1bit……IconImage idCount-1    icHeader        biSize        biWidth        biHeight        biPlanes        biBitCount        biCompression        biSizeImage        biXPelsPerMeter        biYPelsPerMeter        biClrUsed        biClrImportant    icColors[256]   //假设是32×32的256色的icon    icXOR[1024] //32×32    icAND[128]  //32×32/8每个像素之要1bit——————-End Of File (to be continued)

微软面试(3)

       周三有老板的课,正好可以问一下老板是否同意。不过我知道希望不大,一般进实验室的底线就是9月初,要到11月进实验室,我是老板的话我也不会答应。不过就是希望渺茫,我也还是争取一下,也许老板就真的同意了。我不是个会沟通的人,心里构思着见了老板要怎么说。他会怎么反对我,然后我又要怎么说……。但是,事实上,我忽略了一个前提:老板根本不同意我们出来找实习!等我向老板提起实习的事情的时候,他直接就说不同意我去找实习,他说现在最重要的是自己学点东西,工作是一辈子的事情,以后还有更好的机会。……我郁闷了,老板都不同意实习,更别说要实习6个月了。我只好在周四上午给yang打了电话,告诉他我只能做到8月底。我心里还是抱着一丝希望的,也许微软能同意我做4个月呢。yang说稍后再给我答复。当天晚上,yang就给了我电话,结果是显然的。时间太少,刚刚开始熟悉项目就要走人,他们不能聘用我。        这次的微软面试就这样结束了?很显然,我是这样认为的,有些失望,多好的机会啊,就这么错过了。        而实际上,还没有完全结束。        周五的下午,再次接到微软的笔试通知,不过这次应该是换了一个部门了。周六下午去笔试,同行的还有班上两个同学。笔试一个半小时,两道题目:        1.写一个类,能够注册一个人的生日,输入为字符串 yyyy/mm/dd,能够查询在 mm/dd 那天是多少人的生日。要求就是性能要好,然后要处理好所有的错误情况。        2.写一个函数求两个矩形区域的交集。自己定义矩形的数据结构。        很遗憾,对于C++,我真的不能说精通,熟悉都不算,我完全不知道该怎么去定义好这个类,最后干脆直接定义结构体,然后写了几个函数。第二个题就是定义矩形,上下左右4条边的坐标(也即两个顶点的坐标)。然后求交集的操作就是对于两个矩形顶点位置的判断过程。        由于老板不同意实习,有些失落,对于这次的笔试也就不太在意,后来也一直没有消息。不过意外的是在30号的时候,yang给我打电话了,他说打算让我先过去做着,4个月也行,但是要导师开证明。天哪,老板都不同意实习,怎么可能开证明?无奈我只好再次打电话找老板,但是老板的口气依旧坚决,没有商量的余地。微软都做出了让步,但是我还是错过了这次机会。安心自己学点东西吧,工作,挣钱的机会以后多的是。        微软面试:我把微软给拒了。

微软面试(2)

       给yang发完邮件,就和小户去参加晚上在清华的google笔试。        预定的笔试开始时间是7:30pm,但是由于宣讲会时间延长,笔试最终在8:30pm开始,我当时在考场等得真是郁闷啊,本来还打算早点笔试完回去写程序的,结果在那里白白浪费了一个小时。回忆一下google的笔试题目吧。印象比较深的题目:第一个选择题,关于进制的问题,456×567=150216,问这里的数字都是几进制的,选项是9,10,12,18。这题的思路应该是化成多项式预算,可以算出9和12的时候首位肯定是2,就可以知道选项是18。另一个选择题,给了一段小程序,问程序的输出,其中有一个判断条件 if ( x&1 == 0 ) ,这个条件可能很多人都会认识等同于 if ( x/2 == 0 ) 其实错了。这个判断条件是永远不会为真的,因为==的优先级比&高,所以先做 1==0 的运算,结果总是假,所以这道选择题实际上是没有答案的。另外,还有两道有问题的选择题,一个是给了树的先序序列和中序序列,问后序序列,一个是问一个产生式对应的文法,这两个题目也是没有正确选项的。很遗憾,我笔试的时候只看出了其中的一个,另外两个题目我都选了。然后就是两道程序题,第一个是写判断在图中是否有v到w的路径的程序,第二个是给定一棵树,现在要伸长树的某些边,使得根到所有叶子节点的距离相同,求伸长后树边长度之和的最小值。这两题都还好做,不过第二个要求算最小值,具体写的时候我只写了做法和时间复杂度,没有证明这种做法能够得到最小值。至于这次笔试中的错题,我觉得google很有可能就是故意搞的。        笔试完回到宿舍,已经是11:00pm,我是不可能在12:00pm之前做好icon的合并程序了。但是这是个难得的机会,我不能再次放弃,我决定通宵。        首先,我调整了思路,可以说是彻底了换了思路,既然是合并两个文件成一个文件,我就着力分析icon文件的存储结构,然后做合并处理。思路就是根据icon的格式,获得前景图标和背景图标的数据,根据andMask找出前景图标不透明的像素,然后获得这个像素的RGB值,在背景图标的颜色表中查找改RGB值对应的索引,把该索引赋值给背景图标上的对应像素,同时如果背景图标的对应像素是透明的话,要修改其andMask。具体的做法,可以再写一篇文章来专门说明了。到第二天7:00am的时候,程序完成,可以合并两个256色的含一个32×32图标的ico文件。这次的程序比周一的要好了太多了。我赶紧加了注释,打包,然后发邮件给yang,7:40am的时候搞定,赶紧上床躺一会儿,因为在9:30还有火箭的比赛啊,呵呵。        上午发了第二次的版本,下午yang给我打了电话,他说我这个程序搞得太复杂了。其实没有必要从这么底层的做的,win32api里面应该有封装好的东西可以帮我完成这些事情的。我明白他的意思,就是通过LoadIcon,把ico文件读为HICON对象,然后可以很方便的合并,但是如何把HICON保存为ico文件,我昨天一直没有找到解决办法。但看来yang的意思还是要我用HICON合并的办法来做,我只好继续改我的程序,并且他要求我在当天晚上12点之前要发给他。我再次上网搜了一晚上,还是没有找到可以直接把HICON保存为ico文件的api。我后来总算搜到把HICON保存为ico文件的代码,不过还是基于ico文件结构的一些操作。通过一些函数调用获取保存ico的必要数据信息,然后写入ico文件。没有直接的api,我只好用上这份代码,稍加修改,然后在凌晨1点的时候搞定。不过还是有些问题,保存的时候ico文件的颜色表是用的系统默认提供的颜色表,所以会导致颜色失真。当时没有找到这个问题的原因,但是第二天有英语课,还要考听力,还有英语作业,我就赶紧打包,把程序发给了yang,并告诉他我会尽快修正颜色失真的错误。        1点睡的,为了来得及做英语作业,我定了6:30的闹钟,一大早爬起来做完了的题目去上课。第一次感受到时间可以用得这么紧凑。上完英语,在移动通信的课上就开始看代码,最后搞明白了颜色失真的原因就是使用了系统默认的颜色表。至于怎么解决,还没有想到,准备下午做。然而,就在上移动通信的时候,yang给我打了电话,听他的口气应该是可以录用我了,问我能不能做6个月。OMG,6个月意味着我要到11月才进实验室,impossible! 我没有办法马上决定,我说我得找老板商量。yang让我第二天(周四)给他答复。 (待续。。。)