修改stb_image.c以讓Duilib直接支持Ico格式的圖標顯示


     duilib不支持ico格式的圖標資源, 但是我要想顯示ico格式的圖標...

     發現網上那些轉換ico為bmp或其它格式的都不是一個好辦法, 也還是不能讓duilib直接顯示ico...

     昨晚稍微研究了一下ico文件的格式, 發現其非常簡單, 其就是一個容器而已, ico文件是bmp/png文件的組合.

     於是我寫了幾句代碼修改了下duilib的圖片解碼核心stb_image.c, 讓她支持解碼ico.

      隨筆后面有文件下載, 可跳過接下來的內容, 直接下載stb_image.c並重新編譯duilib即可.

 

  代碼有BUG, 請不要再使用這種方法. 可以考慮使用評論中的那種方式.

 

      下面是對utils/stb_image.c添加的內容:

//女孩不哭 添加於 2014年4月22日 01:09:35
// QQ: 191035066
// 增加ico支持
// 

// ICO (file format)
// http://en.wikipedia.org/wiki/ICO_%28file_format%29
#pragma pack(push,1)
typedef struct{
    unsigned short reserved;    
    unsigned short type;        
    unsigned short nfiles;        
}ICONDIR;

typedef struct{
    unsigned char  width;        
    unsigned char  height;        
    unsigned char  ncolors;        
    unsigned char  reserved;    
    unsigned short color_planes;
    unsigned short bpp;
    unsigned long  cb;
    unsigned long  offset;
}ICONDIRENTRY;

//位圖文件頭數據
typedef struct _BITMAP_FILE_HEADER{
    unsigned char signature[2];            //00~01:文件頭簽名字節,僅檢測'BM'序
    unsigned long file_size;            //02~05:整個文件的大小
    unsigned long _reserved1;            //06~09:保留4字節,必須為0
    unsigned long data_offset;            //0A~0D:位圖數據距文件開始的偏移
}BITMAP_FILE_HEADER;

#pragma pack(pop)

static unsigned char png_sig[8] = {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A};

unsigned char* do_load_ico(unsigned char* buffer,unsigned int size,int* psize)
{
    //  我實在是討厭ms不支持完整的C語言
    ICONDIR* pIconDir;
    ICONDIRENTRY* pIconDirEntry;
    int nfiles,themax;
    int width,height;
    int i;

    unsigned char* data;

    if(size < sizeof(ICONDIR)) return NULL;
    pIconDir = (ICONDIR*)buffer;

    if(pIconDir->reserved != 0) return NULL;
    if(pIconDir->type != 1) return NULL;
    if(pIconDir->nfiles == 0) return NULL;

    nfiles = pIconDir->nfiles;

    if(sizeof(ICONDIR) + nfiles*sizeof(ICONDIRENTRY) > size) return NULL;

    //找最大的那張圖出來
    width=height = -1;
    themax = -1;
    for(i=0; i<nfiles; i++){
        pIconDirEntry = &((ICONDIRENTRY*)(buffer+sizeof(ICONDIR)))[i];
        if(pIconDirEntry->width==0 && pIconDirEntry->height==0){
            width=256;
            height=256;
            themax = i;
            break;
        }
        if(pIconDirEntry->width > width
            && pIconDirEntry->height > height)
        {
            width = pIconDirEntry->width;
            height = pIconDirEntry->height;
            themax = i;
        }
    }

    //定位到最大那張
    pIconDirEntry = (ICONDIRENTRY*)(buffer+sizeof(ICONDIR)) + themax;

    if(pIconDirEntry->offset + pIconDirEntry->cb > size) return NULL;

    if(memcmp(buffer+pIconDirEntry->offset, png_sig, 8) == 0){ // PNG
        data = (unsigned char*)malloc(pIconDirEntry->cb);
        if(!data) return NULL;
        memcpy(data, buffer+pIconDirEntry->offset,pIconDirEntry->cb);
        *psize = pIconDirEntry->cb;
        return data;
    }else{  //may BMP
        BITMAP_FILE_HEADER* pbfh;
        data = (unsigned char*)malloc(sizeof(BITMAP_FILE_HEADER) + pIconDirEntry->cb);
        if(!data) return NULL;
        pbfh = (BITMAP_FILE_HEADER*)data;
        pbfh->_reserved1 = 0;
        pbfh->signature[0]='B';
        pbfh->signature[1]='M';
        pbfh->file_size = sizeof(BITMAP_FILE_HEADER) + pIconDirEntry->cb;
        pbfh->data_offset = sizeof(BITMAP_FILE_HEADER) + 40; // 40: sizeof(BITMAP_INFO_HEADER), defined by MS.
        memcpy(data+sizeof(BITMAP_FILE_HEADER), buffer+pIconDirEntry->offset, pIconDirEntry->cb);
        *psize = pIconDirEntry->cb + sizeof(BITMAP_FILE_HEADER);
        return data;
    }
}

找到 stbi_load_main 函數, 並把上面的函數放到它前面即可.(其實放哪兒無所謂, 只要stbi_load_main能調用即可).

然后修改 stbi_load_main 為如下:

static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp)
{
    unsigned char* p; // 這個是我定義的
    int size;

   if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp);
   if (stbi_png_test(s))  return stbi_png_load(s,x,y,comp,req_comp);
   if (stbi_bmp_test(s))  return stbi_bmp_load(s,x,y,comp,req_comp);
   if (stbi_gif_test(s))  return stbi_gif_load(s,x,y,comp,req_comp);
   if (stbi_psd_test(s))  return stbi_psd_load(s,x,y,comp,req_comp);
   if (stbi_pic_test(s))  return stbi_pic_load(s,x,y,comp,req_comp);

   //////////////////////////////////////////////////////////////////////////
   //因為ico用得少, 所以放到最后
   p = do_load_ico(s->img_buffer,s->img_buffer_end-s->img_buffer_original,&size);
   if(p){
       unsigned char* q;
       stbi s;
       start_mem(&s,p,size);
       if (stbi_png_test(&s))  q = stbi_png_load(&s,x,y,comp,req_comp);
       else if (stbi_bmp_test(&s))  q = stbi_bmp_load(&s,x,y,comp,req_comp);
       else q = NULL;
       free(p);
       if(q) return q;
   }
   //////////////////////////////////////////////////////////////////////////

   #ifndef STBI_NO_HDR
   if (stbi_hdr_test(s)) {
      float *hdr = stbi_hdr_load(s, x,y,comp,req_comp);
      return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
   }
   #endif

   // test tga last because it's a crappy test!
   if (stbi_tga_test(s))
      return stbi_tga_load(s,x,y,comp,req_comp);
   return epuc("unknown image type", "Image not of any known type, or corrupt");
}

 

作了上面的修改后, duilib應該就能夠直接加載ico並顯示了.

另外, ico文件中的圖片大小信息無法直接知道, 還有另外一個幾行代碼拼成的小程序用來顯示將要加載的ico的大小信息, 在后面一並提供下載.

 

顯示效果:

dui_ico

 

stb_image.c和ico大小查看工具下載: http://share.weiyun.com/e4ac833f8c6d9315b7694e4007b8cf28

另外提供一個ico轉換png的工具:  http://www.cnblogs.com/memset/p/ico2png.html

 

女孩不哭 @ cnblogs.com/memset @ 2014-04-22

 

結束~~~~~~~~~~~~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM