Cocos2d-x之CCImage深入分析

      本节所用Cocos2d-x版本:cocos2d-2.0-x-2.0.2

            CCImage类:支持从JPG,PNG,TIFF以及数据流,字符串中创建供Cocos2d-x进行访问的图片数据对象。

        这是一个非常重要的类,因为它是你使用cocos2d-x来加载图片的基础。目前此类只支持JPG,PNG,TIFF三种图像文件,对于这三种图像文件,CCImage分别使用libjpg,libpng,libtiff进行读取访问和写文件的操作。有兴趣的同学可以上网查询相关图像库的分析和使用,同时建议有能力的同学对此类进行扩展,令它支持更多的图像格式,是为“举一反三”。

        学之前最好先了解一下libjpg,libpng,libtiff等图片功能库的使用。可以参见文章底部的参考资料。

               现在,先来看CCImage图像头文件:

     
    #ifndef __CC_IMAGE_H__  
    #define __CC_IMAGE_H__  
    //派生于CCObject  
    #include "cocoa/CCObject.h"  
    //Cocos2d命名空间  
    NS_CC_BEGIN  
      
    class CC_DLL CCImage : public CCObject  
    {  
    public:  
        //构造函数  
        CCImage();  
        //析构函数  
        ~CCImage();  
        //支持的图片类型  
        typedef enum  
        {     
            kFmtJpg = 0,    //JPG  
            kFmtPng,        //PNG  
            kFmtTiff,       //TIFF  
            kFmtRawData,    //数据流,要求为RGBA8888  
            kFmtUnKnown //无效  
        }EImageFormat;  
        //对齐方式  
        typedef enum  
        {  
            kAlignCenter        = 0x33, //横向纵向都居中.  
            kAlignTop           = 0x13, //横向居中,纵向居上.  
            kAlignTopRight      = 0x12, //横向居右,纵向居上.  
            kAlignRight         = 0x32, //横向居中,纵向居中.  
            kAlignBottomRight   = 0x22, //横向居右,纵向居下.  
            kAlignBottom        = 0x23, //横向居中,纵向居下.  
            kAlignBottomLeft    = 0x21, //横向居左,纵向居下.  
            kAlignLeft          = 0x31, //横向居左,纵向居中.  
            kAlignTopLeft       = 0x11, //横向居左,纵向居上.  
        }ETextAlign;  
      
        //从指定的路径载入一个所支持的格式的图片文件。  
        bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng);  
      
        //从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。  
        bool initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType = kFmtPng);  
      
        //从内存中加载图片数据。  
        //参1:指向图片数据所处内存地址的指针。  
        //参2:图片数据长度  
        //参3:数据对应图片的格式,  
        //参4:数据对应图片的宽度  
        //参5:数据对应图片的高度  
        //参6:每像素的字节位数,即色深。  
        bool initWithImageData(void * pData,   
                               int nDataLen,   
                               EImageFormat eFmt = kFmtUnKnown,  
                               int nWidth = 0,  
                               int nHeight = 0,  
                               int nBitsPerComponent = 8);  
      
        //从字符串创建图片数据。  
        //参1:字符串  
        //参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。  
        //参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。  
        //参4:文字的对齐方式。  
        //参5:字体名称  
        //参6:字体大小  
        bool initWithString(  
            const char *    pText,   
            int             nWidth = 0,   
            int             nHeight = 0,  
            ETextAlign      eAlignMask = kAlignCenter,  
            const char *    pFontName = 0,  
            int             nSize = 0);  
      
        //取得图像数据地址  
        unsigned char *   getData()               { return m_pData; }  
        //取得图像数据的长度  
        int         getDataLen()            { return m_nWidth * m_nHeight; }  
        //是否有Alpha通道。  
        bool hasAlpha()                     { return m_bHasAlpha; }  
        //是否有Alpha渐变  
        bool isPremultipliedAlpha()         { return m_bPreMulti; }  
      
        //将当前图片数据保存成指定的文件格式。  
        //参1:绝对路径名  
        //参2:是否保存成RGB格式  
        bool saveToFile(const char *pszFilePath, bool bIsToRGB = true);  
        //定义变量m_nWidth和get接口  
        CC_SYNTHESIZE_READONLY(unsigned short,   m_nWidth,       Width);  
        //定义变量m_nHeight和get接口  
        CC_SYNTHESIZE_READONLY(unsigned short,   m_nHeight,      Height);  
        //定义变量m_nBitsPerComponent和get接口  
        CC_SYNTHESIZE_READONLY(int,     m_nBitsPerComponent,   BitsPerComponent);  
      
    protected:  
        //读取JPG图片数据  
        //参1:数据地址  
        //参2:数据长度  
        bool _initWithJpgData(void *pData, int nDatalen);  
        //读取PNG图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中  
        bool _initWithPngData(void *pData, int nDatalen);  
        //读取TIFF图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中  
             bool _initWithTiffData(void* pData, int nDataLen);  
        //读取RGBA8888格式的图片数据。  
        //参1:数据地址  
        //参2:数据长度  
        //参3:图片宽度  
        //参4:图片高度  
        //参5:图片色深  
        bool _initWithRawData(void *pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent);  
        //将图像数据保存为PNG图片  
        bool _saveImageToPNG(const char *pszFilePath, bool bIsToRGB = true);  
        //将图像数据保存为JPG图片  
        bool _saveImageToJPG(const char *pszFilePath);  
        //图像数据地址  
        unsigned char *m_pData;  
        //是否有Alpha  
        bool m_bHasAlpha;  
        //是否有Alpha渐变    bool m_bPreMulti;  
      
    private:  
        // 拷贝构造与重载等号拷贝  
        CCImage(const CCImage&    rImg);  
        CCImage & operator=(const CCImage&);  
    };  
      
    NS_CC_END  
      
    #endif    // __CC_IMAGE_H__  

    继续分析CPP文件,其CPP文件由两个文件构成:

    CCImageCommon_cpp.h和CCImage.cpp。在CCImage.cpp的起始代码处:

     //定义 基于平台的CCImage的CPP标记宏

     #define __CC_PLATFORM_IMAGE_CPP__

     //这里引用CCImageCommon_cpp.h

     #include "platform/CCImageCommon_cpp.h"

    可以看到这里引用了头文件CCImageCommon_cpp.h。那我们就打开CCImageCommon_cpp.h:

   
    #ifndef __CC_PLATFORM_IMAGE_CPP__  
        //如果没有定义基于平台的CCImage的CPP标记宏,编译时打印出错。  
    #error "CCFileUtilsCommon_cpp.h can only be included for CCFileUtils.cpp in platform/win32(android,...)"  
    #endif /* __CC_PLATFORM_IMAGE_CPP__ */  
      
    #include "CCImage.h"  
    #include "CCCommon.h"  
    #include "CCStdC.h"  
    #include "CCFileUtils.h"  
    //libpng库的头文件  
    #include "png.h"  
    //libjpg库的头文件  
    #include "jpeglib.h"  
    //libtiff库的头文件  
    #include "tiffio.h"  
    #include <string>  
    #include <ctype.h>  
    //使用Cocos2d命名空间  
    NS_CC_BEGIN  
      
    //定义宏从RGB888或RGB5A1像素格式数据中返回一个RGBA8888的像素格式数据。  
    #define CC_RGB_PREMULTIPLY_APLHA(vr, vg, vb, va) \  
        (unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \  
        ((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \  
        ((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \  
        ((unsigned)(unsigned char)(va) << 24))  
      
    //图片文件数据的信息结构  
    typedef struct   
    {  
        unsigned char* data;  
        int size;  
        int offset;  
    }tImageSource;  
    //读取PNG文件数据的回调函数  
    //参1:PNG文件数据指针  
    //参2:返回的图片数据地址  
    //参3:要从PNG文件中读取的图片数据的长度,其值 = 每像素字节数X图片的宽X图片的高。  
    static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)  
    {  
        //从一个PNG文件数据指针指针中返回图片文件数据的信息结构  
        tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);  
        //如果要读取的长度有效。则将相应长度的图像数据拷贝到返回的图片数据地址中。  
        if((int)(isource->offset + length) <= isource->size)  
        {  
            memcpy(data, isource->data+isource->offset, length);  
            isource->offset += length;  
        }  
        else  
        {  
            png_error(png_ptr, "pngReaderCallback failed");  
        }  
    }  
      
    //  
    //构造函数  
    CCImage::CCImage()  
    : m_nWidth(0)  
    , m_nHeight(0)  
    , m_nBitsPerComponent(0)  
    , m_pData(0)  
    , m_bHasAlpha(false)  
    , m_bPreMulti(false)  
    {  
      
    }  
    //析构函数  
    CCImage::~CCImage()  
    {     
        //释放图像数据占用的内存  
        CC_SAFE_DELETE_ARRAY(m_pData);  
    }  
    //从指定的路径载入一个所支持的格式的图片文件。  
    bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/)  
    {  
        bool bRet = false;  
        unsigned long nSize = 0;  
        //调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。  
        unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(strPath), "rb", &nSize);  
        if (pBuffer != NULL && nSize > 0)  
        {     
                //如果读取成功,则将内存地址做为参数调用initWithImageData函数来加载图片数据。  
            bRet = initWithImageData(pBuffer, nSize, eImgFmt);  
        }  
        //释放读取文件所创建的内存。  
        CC_SAFE_DELETE_ARRAY(pBuffer);  
        return bRet;  
    }  
      
    //从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。  
    bool CCImage::initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType)  
    {  
        bool bRet = false;  
        unsigned long nSize = 0;  
        //调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。  
        unsigned char *pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath, "rb", &nSize);  
        if (pBuffer != NULL && nSize > 0)  
        {  
        //如果读取成功,则将内存地址做为参数调用initWithImageData函数来加载图片数据。  
            bRet = initWithImageData(pBuffer, nSize, imageType);  
        }  
        //释放读取文件所创建的内存。  
        CC_SAFE_DELETE_ARRAY(pBuffer);  
        return bRet;  
    }  
      
    //从内存中加载图片数据。  
    //参1:指向图片数据所处内存地址的指针。  
    //参2:图片数据长度  
    //参3:数据对应图片的格式,  
    //参4:数据对应图片的宽度  
    //参5:数据对应图片的高度  
    //参6:每像素的字节位数,即色深。  
    bool CCImage::initWithImageData(void * pData,   
                                    int nDataLen,   
                                    EImageFormat eFmt/* = eSrcFmtPng*/,   
                                    int nWidth/* = 0*/,  
                                    int nHeight/* = 0*/,  
                                    int nBitsPerComponent/* = 8*/)  
    {  
        bool bRet = false;  
        do   
        {  
                //参数有效性判断  
            CC_BREAK_IF(! pData || nDataLen <= 0);  
            //根据不同的图片数据格式调用不同的函数创建相应的图片数据。  
            if (kFmtPng == eFmt)  
            {     
               //读取PNG格式的图片数据。  
                bRet = _initWithPngData(pData, nDataLen);  
                break;  
            }  
            else if (kFmtJpg == eFmt)  
            {  
                //读取JPG格式的图片数据。  
                bRet = _initWithJpgData(pData, nDataLen);  
                break;  
            }  
            else if (kFmtTiff == eFmt)  
            {  
                //读取TIFF格式的图片数据。  
                bRet = _initWithTiffData(pData, nDataLen);  
                break;  
            }  
            else if (kFmtRawData == eFmt)  
            {  
                //读取RGBA8888格式的图片数据。  
                bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent);  
                break;  
            }  
            else  
            {  
                // 如果未指定数据的格式.则通过对比相应格式的文件头信息判断格式。  
               //判断是否是PNG  
                if (nDataLen > 8)  
                {  
                    unsigned char* pHead = (unsigned char*)pData;  
                    if (   pHead[0] == 0x89  
                        && pHead[1] == 0x50  
                        && pHead[2] == 0x4E  
                        && pHead[3] == 0x47  
                        && pHead[4] == 0x0D  
                        && pHead[5] == 0x0A  
                        && pHead[6] == 0x1A  
                        && pHead[7] == 0x0A)  
                    {  
                       //通过对比如果是属于PNG格式则读取PNG格式的图片数据  
                        bRet = _initWithPngData(pData, nDataLen);  
                        break;  
                    }  
                }  
                //判断是否是TIFF  
                if (nDataLen > 2)  
                {  
                    unsigned char* pHead = (unsigned char*)pData;  
                    if (  (pHead[0] == 0x49 && pHead[1] == 0x49)  
                        || (pHead[0] == 0x4d && pHead[1] == 0x4d)  
                        )  
                    {   //通过对比如果是属于TIFF格式则读取TIFF格式的图片数据  
                        bRet = _initWithTiffData(pData, nDataLen);  
                        break;  
                    }  
                }  
                //判断是否是JPG  
                if (nDataLen > 2)  
                {  
                    unsigned char* pHead = (unsigned char*)pData;  
                    if (   pHead[0] == 0xff  
                        && pHead[1] == 0xd8)  
                    {  
                        bRet = _initWithJpgData(pData, nDataLen);  
                        break;  
                    }  
                }  
            }  
        } while (0);  
        return bRet;  
    }  
    //读取JPG格式的图片数据。  
    bool CCImage::_initWithJpgData(void * data, int nSize)  
    {  
        //此处使用libjpeg库来读取JPG,这里需要建立相应的结构变量。  
        struct jpeg_decompress_struct cinfo;  
        struct jpeg_error_mgr jerr;  
        JSAMPROW row_pointer[1] = {0};  
        unsigned long location = 0;  
        unsigned int i = 0;  
      
        bool bRet = false;  
        do   
        {  
            //下面是使用libjpeg来进行JPG格式数据的读取。  
      
            cinfo.err = jpeg_std_error( &jerr );  
            jpeg_create_decompress( &cinfo );  
            jpeg_mem_src( &cinfo, (unsigned char *) data, nSize );  
            jpeg_read_header( &cinfo, true );  
      
            // JPG只能支持RGB的像素格式  
            if (cinfo.jpeg_color_space != JCS_RGB)  
            {  
                if (cinfo.jpeg_color_space == JCS_GRAYSCALE || cinfo.jpeg_color_space == JCS_YCbCr)  
                {  
                    cinfo.out_color_space = JCS_RGB;  
                }  
            }  
            else  
            {  
                break;  
            }  
            //开始解压JPG。  
            jpeg_start_decompress( &cinfo );  
      
            //设置相应成员变量。  
            m_nWidth  = (short)(cinfo.image_width);  
            m_nHeight = (short)(cinfo.image_height);  
            m_bHasAlpha = false;  
            m_bPreMulti = false;  
            m_nBitsPerComponent = 8;  
            row_pointer[0] = new unsigned char[cinfo.output_width*cinfo.output_components];  
            CC_BREAK_IF(! row_pointer[0]);  
           //为图片数据指针申请相应大小的内存。  
            m_pData = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components];  
            CC_BREAK_IF(! m_pData);  
      
          //将像素信息读取到图片数据指针指向的内存中。  
            while( cinfo.output_scanline < cinfo.image_height )  
            {  
               //每次读取一个扫描行的像素信息  
                jpeg_read_scanlines( &cinfo, row_pointer, 1 );  
                for( i=0; i<cinfo.image_width*cinfo.output_components;i++)   
                {  
                   //将读取到的像素信息存入相应的内存中。  
                    m_pData[location++] = row_pointer[0][i];  
                }  
            }  
            //完成解压  
            jpeg_finish_decompress( &cinfo );  
            //释放所用的结构  
            jpeg_destroy_decompress( &cinfo );        
            bRet = true;  
        } while (0);  
        //释放申请的内存  
        CC_SAFE_DELETE_ARRAY(row_pointer[0]);  
        return bRet;  
    }  
    //读取PNG格式的图片数据。  
    bool CCImage::_initWithPngData(void * pData, int nDatalen)  
    {  
    // PNG文件头信息长度值  
    #define PNGSIGSIZE  8  
        bool bRet = false;  
        //定义存储PNG文件头信息的BYTE数组  
        png_byte        header[PNGSIGSIZE]   = {0};       
        //PNG的读取说明结构,这里是libpng用到的结构体。  
        png_structp     png_ptr     =   0;  
        //PNG的信息结构  
        png_infop       info_ptr    = 0;  
          
        do   
        {  
            // PNG文件头有效性判断  
            CC_BREAK_IF(nDatalen < PNGSIGSIZE);  
      
            // 存储文件头信息  
            memcpy(header, pData, PNGSIGSIZE);  
            CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));  
      
            //初始化PNG的读取说明结构  
            png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);  
            CC_BREAK_IF(! png_ptr);  
      
            // 初始化 PNG信息结构  
            info_ptr = png_create_info_struct(png_ptr);  
            CC_BREAK_IF(!info_ptr);  
      
    #if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)  
            CC_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));  
    #endif  
      
            //图片文件数据的信息结构  
            tImageSource imageSource;  
            imageSource.data    = (unsigned char*)pData;  
            imageSource.size    = nDatalen;  
            imageSource.offset  = 0;  
           //设置读取PNG的回调函数  
            png_set_read_fn(png_ptr, &imageSource, pngReadCallback);  
      
            //读取PNG信息结构  
            png_read_info(png_ptr, info_ptr);  
            //取得图片数据的相关属性。  
            m_nWidth = png_get_image_width(png_ptr, info_ptr);  
            m_nHeight = png_get_image_height(png_ptr, info_ptr);  
            m_nBitsPerComponent = png_get_bit_depth(png_ptr, info_ptr);  
            png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);  
            //打印像素格式  
            //CCLOG("color type %u", color_type);  
              
            // 如果是调色板格式的PNG,将其转为RGB888的像素格式。  
            if (color_type == PNG_COLOR_TYPE_PALETTE)  
            {  
                png_set_palette_to_rgb(png_ptr);  
            }  
            // 如果是像素格式少于1字节长度的灰度图,将其转为每像素占1字节的像素格式。  
            if (color_type == PNG_COLOR_TYPE_GRAY && m_nBitsPerComponent < 8)  
            {  
                png_set_expand_gray_1_2_4_to_8(png_ptr);  
            }  
            // 将RNS块数据信息扩展为完整的ALPHA通道信息  
            if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))  
            {  
                png_set_tRNS_to_alpha(png_ptr);  
            }    
            // reduce images with 16-bit samples to 8 bits  
            if (m_nBitsPerComponent == 16)  
            {  
                png_set_strip_16(png_ptr);              
            }   
            // 将灰度图格式扩展成RGB  
            if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)  
            {  
                png_set_gray_to_rgb(png_ptr);  
            }  
      
            // 读取PNG数据  
            // m_nBitsPerComponent will always be 8  
            m_nBitsPerComponent = 8;  
            png_uint_32 rowbytes;  
           //后面读取PNG信息是按行读取,将每一行的像素数据读取到相应内存块中,下面这个BYTE指针数组就是为了存储每行图片像素信息读取到的相应内存块位置。  
            png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * m_nHeight );  
              
            png_read_update_info(png_ptr, info_ptr);  
            //取得图片每一行像素的字节数量  
            rowbytes = png_get_rowbytes(png_ptr, info_ptr);  
            //为图片数据申请内存。  
            m_pData = new unsigned char[rowbytes * m_nHeight];  
            CC_BREAK_IF(!m_pData);  
            //将申请到的内存地址按行放入相应的读取结构中  
            for (unsigned short i = 0; i < m_nHeight; ++i)  
            {  
                row_pointers[i] = m_pData + i*rowbytes;  
            }  
            //将图片文件数据读取到相应的内存地址。  
            png_read_image(png_ptr, row_pointers);  
            //结束读取。  
            png_read_end(png_ptr, NULL);  
            //计算每像素占字节数。不管是RGB888还是RGBA8888的像素格式,其实际每像素占用的字节数均是4,只不过RGB888中多余的1字节不被用到。  
            png_uint_32 channel = rowbytes/m_nWidth;  
            if (channel == 4)  
            {  
               //设置为带ALPHA通道  
                m_bHasAlpha = true;  
               //定义指针变量tmp指向图像数据地址。用于在后面存放图像数据。  
                unsigned int *tmp = (unsigned int *)m_pData;  
               //双循环遍历像素数据。  
                for(unsigned short i = 0; i < m_nHeight; i++)  
                {  
                    for(unsigned int j = 0; j < rowbytes; j += 4)  
                    {  
                       //将R,G,B,A四个BYTE值组成一个DWORD值。  
                        *tmp++ = CC_RGB_PREMULTIPLY_APLHA( row_pointers[i][j], row_pointers[i][j + 1],   
                                                          row_pointers[i][j + 2], row_pointers[i][j + 3] );  
                    }  
                }  
                //设置使用渐变ALPHA  
                m_bPreMulti = true;  
            }  
           //释放row_pointers  
            CC_SAFE_FREE(row_pointers);  
      
            bRet = true;  
        } while (0);  
      
        if (png_ptr)  
        {  
                //释放png_ptr  
            png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);  
        }  
        return bRet;  
    }  
    //读取TIFF图片数据时的回调函数。  
    //参1:文件数据内存。  
    //参2:输出参数,读取到的图像数据复制到对应的内存地址中。  
    //参3:图片数据长度。  
    static tmsize_t _tiffReadProc(thandle_t fd, void* buf, tmsize_t size)  
    {  
        //将fd转化为图片文件数据的信息结构指针。  
        tImageSource* isource = (tImageSource*)fd;  
        uint8* ma;  
        uint64 mb;  
        //定义一次可以读取的数据长度。  
        unsigned long n;  
        //定义变量o统计每次循环读取的数据长度。  
        unsigned long o;  
        //定义变量统计读取完的数据长度。  
        tmsize_t p;  
        //让前面定义的uint8类型指针变量ma指向buf。用于在后面存放图像数据。  
        ma=(uint8*)buf;  
        //让前面定义的变量mb来统计剩余未读取的数据长度。  
        mb=size;  
        p=0;  
        //使用while循环进行读取,判断条件为剩余未读的数据长度是否大于0。  
        while (mb>0)  
        {  
            n=0x80000000UL;  
            if ((uint64)n>mb)  
                n=(unsigned long)mb;  
      
            //如果尚未读完所有数据,则继续读取,否则出错返回0  
            if((int)(isource->offset + n) <= isource->size)  
            {  
                memcpy(ma, isource->data+isource->offset, n);  
                isource->offset += n;  
                o = n;  
            }  
            else  
            {  
                return 0;  
            }  
            //读取完长度为o的数据,则对指针进行相应的偏移操作供下次进行读取操作。  
            ma+=o;  
           //更新未读取的剩余长度  
            mb-=o;  
           //更新读取完的数量长度  
            p+=o;  
            //下面这个if比较奇怪,因为是不可能为true的。在上一个if判断中已经设置了o=n。  
            if (o!=n)  
            {  
                break;  
            }  
        }  
        return p;  
    }  
    //将数据保存为tiff图像文件所调用的回调函数。这里未用。  
    static tmsize_t _tiffWriteProc(thandle_t fd, void* buf, tmsize_t size)  
    {  
        CC_UNUSED_PARAM(fd);  
        CC_UNUSED_PARAM(buf);  
        CC_UNUSED_PARAM(size);  
        return 0;  
    }  
      
    //在对TIFF图像文件进行解析时进行重定位时调用的回调函数。  
    static uint64 _tiffSeekProc(thandle_t fd, uint64 off, int whence)  
    {  
        //将fd转化为图片文件数据的信息结构指针。  
        tImageSource* isource = (tImageSource*)fd;  
        uint64 ret = -1;  
        do   
        {  
               //如果定位方式为从头开始计算  
            if (whence == SEEK_SET)  
            {  
                CC_BREAK_IF(off > isource->size-1);  
                ret = isource->offset = (uint32)off;  
            }  
            else if (whence == SEEK_CUR)  
            {  //如果定位方式为从当前位置开始计算  
                CC_BREAK_IF(isource->offset + off > isource->size-1);  
                ret = isource->offset += (uint32)off;  
            }  
            else if (whence == SEEK_END)  
            {   //如果定位方工业从文件尾部开始计算  
                CC_BREAK_IF(off > isource->size-1);  
                ret = isource->offset = (uint32)(isource->size-1 - off);  
            }  
            else  
            {//其它方式也按照从头开始计算  
                CC_BREAK_IF(off > isource->size-1);  
                ret = isource->offset = (uint32)off;  
            }  
        } while (0);  
      
        return ret;  
    }  
    //取得tiff图片文件大小的回调函数。  
    static uint64 _tiffSizeProc(thandle_t fd)  
    {  
        tImageSource* pImageSrc = (tImageSource*)fd;  
        return pImageSrc->size;  
    }  
    //关闭tiff图片文件读取的回调函数。  
    static int _tiffCloseProc(thandle_t fd)  
    {  
        CC_UNUSED_PARAM(fd);  
        return 0;  
    }  
    //将tiff图片文件映射到内存时调用的回调函数。  
    static int _tiffMapProc(thandle_t fd, void** pbase, toff_t* psize)  
    {  
        CC_UNUSED_PARAM(fd);  
        CC_UNUSED_PARAM(pbase);  
        CC_UNUSED_PARAM(psize);  
        return 0;  
    }  
    //解除tiff图片映射到内存的回调函数。  
    static void _tiffUnmapProc(thandle_t fd, void* base, toff_t size)  
    {  
        CC_UNUSED_PARAM(fd);  
        CC_UNUSED_PARAM(base);  
        CC_UNUSED_PARAM(size);  
    }  
    //使用LibTiff读取TIFF格式的图片数据。  
    bool CCImage::_initWithTiffData(void* pData, int nDataLen)  
    {  
        bool bRet = false;  
        do   
        {  
            //设置图片文件数据的信息结构  
            tImageSource imageSource;  
            imageSource.data    = (unsigned char*)pData;  
            imageSource.size    = nDataLen;  
            imageSource.offset  = 0;  
            //使用libtiff打开一个tif文件,设置对其进行操作的各行为的回调函数。如果成功打开文件返回一个TIFF结构指针。  
            TIFF* tif = TIFFClientOpen("file.tif", "r", (thandle_t)&imageSource,   
                _tiffReadProc, _tiffWriteProc,  
                _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,  
                _tiffMapProc,  
                _tiffUnmapProc);  
            //有效性判断。  
            CC_BREAK_IF(NULL == tif);  
              
            uint32 w = 0, h = 0;  
            uint16 bitsPerSample = 0, samplePerPixel = 0, planarConfig = 0;  
           //定义变量nPixels存储图像数据像素数量。  
            size_t npixels = 0;  
            //读取相应的图片属性信息。  
            //图片宽度。  
            TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);  
            //图片高度。  
            TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);  
            //图片色深。  
            TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);  
            //每像素数据占的字节数  
            TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplePerPixel);  
            //图像的平面配置  
            TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);  
            //取得像素数量  
            npixels = w * h;  
            //设置带ALPHA通道。  
            m_bHasAlpha = true;  
            m_nWidth = w;  
            m_nHeight = h;  
            m_nBitsPerComponent = 8;  
           //申请相应的内存用来存储像素数据。  
            m_pData = new unsigned char[npixels * sizeof (uint32)];  
           //申请临时内存进行TIFF数据读取。  
            uint32* raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));  
            if (raster != NULL)   
            {  
              //读取TIFF数据  
               if (TIFFReadRGBAImageOriented(tif, w, h, raster, ORIENTATION_TOPLEFT, 0))  
               {  
                   //下面是从TIFF数据中解析生成我们Cocos2d-x所用的图像数据。  
                  //这里定义指针变量指向TIFF数据  
                    unsigned char* src = (unsigned char*)raster;  
                  //这里定义指针变量指向我们Cocos2d-x所用的图像数据  
                    unsigned int* tmp = (unsigned int*)m_pData;  
      
        /* 
                    //遍历每像素进行,对像素的RGBA值进行组合,将组合成的DWORD值写入到图像数据中。 
                    for(int j = 0; j < m_nWidth * m_nHeight * 4; j += 4) 
                    { 
                        *tmp++ = CC_RGB_PREMULTIPLY_APLHA( src[j], src[j + 1],  
                            src[j + 2], src[j + 3] ); 
                    } 
        */  
        //ALPHA通道有效。  
                    m_bPreMulti = true;  
                  //上面循环将组合后的DWORD值写入图像数据太慢,这里直接进行内存拷贝可以达到同样目的。  
                   memcpy(m_pData, raster, npixels*sizeof (uint32));  
               }  
              //释放临时申请的内存。  
              _TIFFfree(raster);  
            }  
              
      
            //关闭TIFF文件读取。  
            TIFFClose(tif);  
      
            bRet = true;  
        } while (0);  
        return bRet;  
    }  
    //读取RGBA8888格式的图片数据。  
    //参1:数据地址  
    //参2:数据长度  
    //参3:图片宽度  
    //参4:图片高度  
    //参5:图片色深  
    bool CCImage::_initWithRawData(void * pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent)  
    {  
        bool bRet = false;  
        do   
        {  
           //宽高有效性判断  
            CC_BREAK_IF(0 == nWidth || 0 == nHeight);  
           //保存相关属性  
            m_nBitsPerComponent = nBitsPerComponent;  
            m_nHeight   = (short)nHeight;  
            m_nWidth    = (short)nWidth;  
            m_bHasAlpha = true;  
      
            // 只支持 RGBA8888 格式  
            int nBytesPerComponent = 4;  
            int nSize = nHeight * nWidth * nBytesPerComponent;  
            //为图像数据申请相应内存,将地址返回给m_pData。   
            m_pData = new unsigned char[nSize];  
            //内存申请成败判断  
            CC_BREAK_IF(! m_pData);  
           //将参数数据拷贝到m_pData指向的内存地址中。  
            memcpy(m_pData, pData, nSize);  
      
            bRet = true;  
        } while (0);  
        return bRet;  
    }  
    //将图像数据保存为图片文件,目前只支持PNG和JPG  
    //参1:文件路径  
    //参2:是否是RGB的像素格式  
    bool CCImage::saveToFile(const char *pszFilePath, bool bIsToRGB)  
    {  
        bool bRet = false;  
      
        do   
        {  
           //参数有效性判断  
            CC_BREAK_IF(NULL == pszFilePath);  
           //通过是否有扩展名判断参数有效性。  
            std::string strFilePath(pszFilePath);  
            CC_BREAK_IF(strFilePath.size() <= 4);  
           //将路径名转为小写字符串  
            std::string strLowerCasePath(strFilePath);  
            for (unsigned int i = 0; i < strLowerCasePath.length(); ++i)  
            {  
                strLowerCasePath[i] = tolower(strFilePath[i]);  
            }  
            //通过扩展名转成相应的图片文件  
            //PNG  
            if (std::string::npos != strLowerCasePath.find(".png"))  
            {  
                CC_BREAK_IF(!_saveImageToPNG(pszFilePath, bIsToRGB));  
            }  
            //JPG  
            else if (std::string::npos != strLowerCasePath.find(".jpg"))  
            {  
                CC_BREAK_IF(!_saveImageToJPG(pszFilePath));  
            }  
            else  
            {  
               //不支持其它格式  
                break;  
            }  
      
            bRet = true;  
        } while (0);  
      
        return bRet;  
    }  
    //将图像数据保存为PNG图片  
    bool CCImage::_saveImageToPNG(const char * pszFilePath, bool bIsToRGB)  
    {  
        bool bRet = false;  
        do   
        {  
            //参数有效性判断  
            CC_BREAK_IF(NULL == pszFilePath);  
           //使用libpng来写PNG文件。  
           //定义文件指针变量用于写文件  
            FILE *fp;  
           //定义libpng所用的一些信息结构  
            png_structp png_ptr;  
            png_infop info_ptr;  
            png_colorp palette;  
            png_bytep *row_pointers;  
           //打开文件开始写入  
            fp = fopen(pszFilePath, "wb");  
            CC_BREAK_IF(NULL == fp);  
           //创建写PNG的文件结构体,将其结构指针返回给png_ptr  
            png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);  
           //指针有效性判断  
            if (NULL == png_ptr)  
            {  
                fclose(fp);  
                break;  
            }  
           //创建PNG的信息结构体,将其结构指针返回给info_ptr。  
            info_ptr = png_create_info_struct(png_ptr);  
            if (NULL == info_ptr)  
            {  
                fclose(fp);  
                png_destroy_write_struct(&png_ptr, NULL);  
                break;  
            }  
    #if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)  
            if (setjmp(png_jmpbuf(png_ptr)))  
            {  
                fclose(fp);  
                png_destroy_write_struct(&png_ptr, &info_ptr);  
                break;  
            }  
    #endif  
            //初始化png_ptr  
            png_init_io(png_ptr, fp);  
            //根据是否有ALPHA来写入相应的头信息  
            if (!bIsToRGB && m_bHasAlpha)  
            {  
                png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA,  
                    PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);  
            }   
            else  
            {  
                png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB,  
                    PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);  
            }  
            //创建调色板  
            palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color));  
           //设置调色板  
            png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);  
            //写入info_ptr  
            png_write_info(png_ptr, info_ptr);  
            //  
            png_set_packing(png_ptr);  
            //申请临时存储m_pData中每一行像素数据地址的内存空间,将申请到的内存地址返回给row_pointers。  
            row_pointers = (png_bytep *)malloc(m_nHeight * sizeof(png_bytep));  
            if(row_pointers == NULL)  
            {  
                fclose(fp);  
                png_destroy_write_struct(&png_ptr, &info_ptr);  
                break;  
            }  
            //根据是否有ALPHA分别处理写入像素数据到文件中。  
            if (!m_bHasAlpha)  
            {  
               //如果没有ALPHA,只是RGB,这里将m_pData中数据,遍历每一行,将每一行的起始内存地址放入row_pointers指针数组中。  
                for (int i = 0; i < (int)m_nHeight; i++)  
                {  
                    row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 3;  
                }  
               //将row_pointers中指向的每一行数据写入文件。  
                png_write_image(png_ptr, row_pointers);  
      
                //释放内存  
        free(row_pointers);  
                row_pointers = NULL;  
            }  
            else  
            {  
               //如果带ALPHA通道。对是否是RGB格式又进行分别处理。  
               //如果是RGB888格式  
                if (bIsToRGB)  
                {  
                   //创建临时的内存存放像素数据。每个像素3字节,分别存R,G,B值。  
                    unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3];  
                    if (NULL == pTempData)  
                    {  
                        fclose(fp);  
                        png_destroy_write_struct(&png_ptr, &info_ptr);  
                        break;  
                    }  
                  //双循环遍历每个像素,将R,G,B值保存到数组中。  
                    for (int i = 0; i < m_nHeight; ++i)  
                    {  
                        for (int j = 0; j < m_nWidth; ++j)  
                        {  
                            pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];  
                            pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];  
                            pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];  
                        }  
                    }  
                  //将数组中保存的每行像素的内存地址存入row_pointers数组中。  
                    for (int i = 0; i < (int)m_nHeight; i++)  
                    {  
                        row_pointers[i] = (png_bytep)pTempData + i * m_nWidth * 3;  
                    }  
                  //将row_pointers中指向的每一行数据写入文件。  
                    png_write_image(png_ptr, row_pointers);  
                      //释放内存  
                    free(row_pointers);  
                    row_pointers = NULL;  
                     
                    CC_SAFE_DELETE_ARRAY(pTempData);  
                }   
                else  
                {  
                   //如果是RGBA8888格式  
                   //将数组中保存的每行像素的内存地址存入row_pointers数组中。  
                    for (int i = 0; i < (int)m_nHeight; i++)  
                    {  
                        row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 4;  
                    }  
                    //将row_pointers中指向的每一行数据写入文件。  
                    png_write_image(png_ptr, row_pointers);  
                   //释放内存  
      
                    free(row_pointers);  
                    row_pointers = NULL;  
                }  
            }  
            //结束写PNG文件  
            png_write_end(png_ptr, info_ptr);  
            //释放相应的信息结构  
            png_free(png_ptr, palette);  
            palette = NULL;  
            png_destroy_write_struct(&png_ptr, &info_ptr);  
      
            fclose(fp);  
      
            bRet = true;  
        } while (0);  
        return bRet;  
    }  
      
    //将图像数据保存为JPG文件  
    bool CCImage::_saveImageToJPG(const char * pszFilePath)  
    {  
        bool bRet = false;  
        do   
        {  
            //参数有效性判断  
            CC_BREAK_IF(NULL == pszFilePath);  
            //使用libjpg库要用到的相关结构。  
            struct jpeg_compress_struct cinfo;  
            struct jpeg_error_mgr jerr;  
            FILE * outfile;                 /* target file */  
            JSAMPROW row_pointer[1];        /* pointer to JSAMPLE row[s] */  
            int     row_stride;          /* physical row width in image buffer */  
            //初始化相关结构  
            cinfo.err = jpeg_std_error(&jerr);  
            jpeg_create_compress(&cinfo);  
            //开始写入文件  
            CC_BREAK_IF((outfile = fopen(pszFilePath, "wb")) == NULL);  
                //写入JPG头文件基本信息  
            jpeg_stdio_dest(&cinfo, outfile);  
            //填充JPG图像的属性信息结构  
            cinfo.image_width = m_nWidth;         
            cinfo.image_height = m_nHeight;  
            cinfo.input_components = 3;           
            cinfo.in_color_space = JCS_RGB;       
            //将信息结构来设置JPG图像  
            jpeg_set_defaults(&cinfo);  
            //开始进行数据压缩输出  
            jpeg_start_compress(&cinfo, TRUE);  
            //设置每行的字节长度  
            row_stride = m_nWidth * 3;  
            //跟据图像数据是否有ALPHA通道来进行分别处理  
            if (m_bHasAlpha)  
            {  
               //创建内存来存放图像的像素数据。  
                unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3];  
                if (NULL == pTempData)  
                {  
                    jpeg_finish_compress(&cinfo);  
                    jpeg_destroy_compress(&cinfo);  
                    fclose(outfile);  
                    break;  
                }  
                //双循环遍历每一个图像像素的数据,取R,G,B值存放到临时申请的内存地址中,A值弃之不取。  
                for (int i = 0; i < m_nHeight; ++i)  
                {  
                    for (int j = 0; j < m_nWidth; ++j)  
      
                    {  
                      //因图像数据有A通道,所以找相应的像素地址时会由像素索引x4。  
                        pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];  
                        pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];  
                        pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];  
                    }  
                }  
               //将扫描行的数据写入JPG文件  
                while (cinfo.next_scanline < cinfo.image_height) {  
                    row_pointer[0] = & pTempData[cinfo.next_scanline * row_stride];  
                    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);  
                }  
      
                CC_SAFE_DELETE_ARRAY(pTempData);  
            }   
            else  
            { //将扫描行的数据写入JPG文件  
                while (cinfo.next_scanline < cinfo.image_height) {  
                    row_pointer[0] = & m_pData[cinfo.next_scanline * row_stride];  
                    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);  
                }  
            }  
            //结束数据压缩,关闭文件并释放相应信息结构。  
            jpeg_finish_compress(&cinfo);  
            fclose(outfile);  
            jpeg_destroy_compress(&cinfo);  
              
            bRet = true;  
        } while (0);  
        return bRet;  
    }  
      
    NS_CC_END  

现在我们回到CCImage.cpp:

    
    #define __CC_PLATFORM_IMAGE_CPP__  
    #include "platform/CCImageCommon_cpp.h"  
      
    NS_CC_BEGIN  
      
    //此处定义一个BitmapDC类在位图上进行文字绘制。  
    class BitmapDC  
    {  
    public:  
        //构造函数  
        BitmapDC(HWND hWnd = NULL)  
        : m_hDC(NULL)  
        , m_hBmp(NULL)  
        , m_hFont((HFONT)GetStockObject(DEFAULT_GUI_FONT))  
        , m_hWnd(NULL)  
        {  
        //保存窗口句柄  
            m_hWnd = hWnd;  
            //取得窗口的hdc  
            HDC hdc = GetDC(hWnd);  
            //创建兼容的hdc  
            m_hDC   = CreateCompatibleDC(hdc);  
            //释放hdc  
            ReleaseDC(hWnd, hdc);  
        }  
        //析构函数  
        ~BitmapDC()  
        {  
            prepareBitmap(0, 0);  
            if (m_hDC)  
            {  
                DeleteDC(m_hDC);  
            }  
            //创建字体  
            HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);  
            if (hDefFont != m_hFont)  
            {  
                DeleteObject(m_hFont);  
                m_hFont = hDefFont;  
            }  
            // release temp font resource     
            if (m_curFontPath.size() > 0)  
            {  
                wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);  
                if (pwszBuffer)  
                {  
                    RemoveFontResource(pwszBuffer);  
                    SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);  
                    delete [] pwszBuffer;  
                    pwszBuffer = NULL;  
                }  
            }  
        }  
        //多字节转宽字符  
        wchar_t * utf8ToUtf16(std::string nString)  
        {  
            wchar_t * pwszBuffer = NULL;  
            do   
            {  
                if (nString.size() < 0)  
                {  
                    break;  
                }  
                // utf-8 to utf-16  
                int nLen = nString.size();  
                int nBufLen  = nLen + 1;              
                pwszBuffer = new wchar_t[nBufLen];  
                CC_BREAK_IF(! pwszBuffer);  
                memset(pwszBuffer,0,nBufLen);  
                nLen = MultiByteToWideChar(CP_UTF8, 0, nString.c_str(), nLen, pwszBuffer, nBufLen);       
                pwszBuffer[nLen] = '\0';  
            } while (0);      
            return pwszBuffer;  
      
        }  
        //设置使用的字体和大小  
        bool setFont(const char * pFontName = NULL, int nSize = 0)  
        {  
            bool bRet = false;  
            do   
            {  
                //创建字体  
                std::string fontName = pFontName;  
                std::string fontPath;  
                HFONT       hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);  
                LOGFONTA    tNewFont = {0};  
                LOGFONTA    tOldFont = {0};  
                GetObjectA(hDefFont, sizeof(tNewFont), &tNewFont);  
                if (fontName.c_str())  
                {      
                    // 如果字体名称是ttf文件,取得其全路径名  
                    int nFindttf = fontName.find(".ttf");  
                    int nFindTTF = fontName.find(".TTF");  
                    if (nFindttf >= 0 || nFindTTF >= 0)  
                    {  
                        fontPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(fontName.c_str());  
                        int nFindPos = fontName.rfind("/");  
                        fontName = &fontName[nFindPos+1];  
                        nFindPos = fontName.rfind(".");  
                        fontName = fontName.substr(0,nFindPos);                  
                    }  
                    tNewFont.lfCharSet = DEFAULT_CHARSET;  
                    strcpy_s(tNewFont.lfFaceName, LF_FACESIZE, fontName.c_str());  
                }  
                if (nSize)  
                {  
                    tNewFont.lfHeight = -nSize;  
                }  
                //  
                GetObjectA(m_hFont,  sizeof(tOldFont), &tOldFont);  
                  
                if (tOldFont.lfHeight == tNewFont.lfHeight  
                    && ! strcpy(tOldFont.lfFaceName, tNewFont.lfFaceName))  
                {  
                    // already has the font   
                    bRet = true;  
                    break;  
                }  
      
                // 删除旧的字体  
                if (m_hFont != hDefFont)  
                {  
                    DeleteObject(m_hFont);  
                    // release old font register  
                    if (m_curFontPath.size() > 0)  
                    {  
                        wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);  
                        if (pwszBuffer)  
                        {  
                            if(RemoveFontResource(pwszBuffer))  
                            {  
                                SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);  
                            }                         
                            delete [] pwszBuffer;  
                            pwszBuffer = NULL;  
                        }  
                    }  
                    fontPath.size()>0?(m_curFontPath = fontPath):(m_curFontPath.clear());  
      
                    if (m_curFontPath.size() > 0)  
                    {  
                        wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);  
                        if (pwszBuffer)  
                        {  
                            if(AddFontResource(pwszBuffer))  
                            {  
                                SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);  
                            }                         
                            delete [] pwszBuffer;  
                            pwszBuffer = NULL;  
                        }  
                    }  
                }  
                m_hFont = NULL;  
      
                // 字体不使用锐利字体  
                tNewFont.lfQuality = ANTIALIASED_QUALITY;  
      
                // 创建新字体  
                m_hFont = CreateFontIndirectA(&tNewFont);  
                if (! m_hFont)  
                {  
                    // create failed, use default font  
                    m_hFont = hDefFont;  
                    break;  
                }  
                  
                bRet = true;  
            } while (0);  
            return bRet;  
        }  
        //取得使用当前字体写出的字符串所占据的图像大小。  
        SIZE sizeWithText(const wchar_t * pszText, int nLen, DWORD dwFmt, LONG nWidthLimit)  
        {  
            SIZE tRet = {0};  
            do   
            {  
                CC_BREAK_IF(! pszText || nLen <= 0);  
      
                RECT rc = {0, 0, 0, 0};  
                DWORD dwCalcFmt = DT_CALCRECT;  
      
                if (nWidthLimit > 0)  
                {  
                    rc.right = nWidthLimit;  
                    dwCalcFmt |= DT_WORDBREAK  
                        | (dwFmt & DT_CENTER)  
                        | (dwFmt & DT_RIGHT);  
                }  
                // 使用当前字体。  
                HGDIOBJ hOld = SelectObject(m_hDC, m_hFont);  
      
                // 写字。  
                DrawTextW(m_hDC, pszText, nLen, &rc, dwCalcFmt);  
                SelectObject(m_hDC, hOld);  
      
                tRet.cx = rc.right;  
                tRet.cy = rc.bottom;  
            } while (0);  
      
            return tRet;  
        }  
        //创建一个指定大小的位图。  
        bool prepareBitmap(int nWidth, int nHeight)  
        {  
            // 释放原来的位图  
            if (m_hBmp)  
            {  
                DeleteObject(m_hBmp);  
                m_hBmp = NULL;  
            }       //如果大小有效则创建位图。  
            if (nWidth > 0 && nHeight > 0)  
            {  
                m_hBmp = CreateBitmap(nWidth, nHeight, 1, 32, NULL);  
                if (! m_hBmp)  
                {  
                    return false;  
                }  
            }  
            return true;  
        }  
         //在指定位置按照指定对齐方式写字。  
        //参1:字符串  
        //参2:指定拉置及大小  
        //参3:文字对齐方式  
        int drawText(const char * pszText, SIZE& tSize, CCImage::ETextAlign eAlign)  
        {  
            int nRet = 0;  
            wchar_t * pwszBuffer = 0;  
            do   
            {  
                CC_BREAK_IF(! pszText);  
      
                DWORD dwFmt = DT_WORDBREAK;  
                DWORD dwHoriFlag = eAlign & 0x0f;  
                DWORD dwVertFlag = (eAlign & 0xf0) >> 4;  
                //设置横向对齐方式。  
                switch (dwHoriFlag)  
                {  
                case 1: // 左对齐  
                    dwFmt |= DT_LEFT;  
                    break;  
                case 2: // 右对齐  
                    dwFmt |= DT_RIGHT;  
                    break;  
                case 3: // 居中  
                    dwFmt |= DT_CENTER;  
                    break;  
                }  
                //取得字符串的字节长度。  
                int nLen = strlen(pszText);  
                int nBufLen  = nLen + 1;  
                // 为转换后的宽字符串申请内存地址。  
                pwszBuffer = new wchar_t[nBufLen];  
                CC_BREAK_IF(! pwszBuffer);  
                //内存数据置零  
                memset(pwszBuffer, 0, sizeof(wchar_t)*nBufLen);  
                // 将多字节转宽字符串。  
                nLen = MultiByteToWideChar(CP_UTF8, 0, pszText, nLen, pwszBuffer, nBufLen);  
                //取得写字符串占据的图像区域大小  
                SIZE newSize = sizeWithText(pwszBuffer, nLen, dwFmt, tSize.cx);  
                //建立RECT变量做为实际绘制占据的图像区域大小  
                RECT rcText = {0};  
                // 如果宽度为0,则使用显示字符串所需的图像大小。  
                if (tSize.cx <= 0)  
                {  
                    tSize = newSize;  
                    rcText.right  = newSize.cx;  
                    rcText.bottom = newSize.cy;  
                }  
                else  
                {  
                    LONG offsetX = 0;  
                    LONG offsetY = 0;  
                    rcText.right = newSize.cx;   
                    //根据对齐方式计算横向偏移。  
                    if (1 != dwHoriFlag               
                        && newSize.cx < tSize.cx)      
                    {                                     
                        offsetX = (2 == dwHoriFlag) ? tSize.cx - newSize.cx                                 : (tSize.cx - newSize.cx) / 2;                                          }  
                    //如果指定矩形高度为0,则使用显示字符串所需的图像高度。  
                    if (tSize.cy <= 0)  
                    {  
                        tSize.cy = newSize.cy;  
                        dwFmt   |= DT_NOCLIP;  
                        rcText.bottom = newSize.cy; // store the text height to rectangle  
                    }  
                    else if (tSize.cy < newSize.cy)  
                    {  
                        // content height larger than text height need, clip text to rect  
                        rcText.bottom = tSize.cy;  
                    }  
                    else  
                    {  
                        rcText.bottom = newSize.cy;   
      
                        // content larger than text, need adjust vertical position  
                        dwFmt |= DT_NOCLIP;  
      
                        //根据对齐方式计算纵向偏移。  
                        offsetY = (2 == dwVertFlag) ? tSize.cy - newSize.cy     // 居下                        : (3 == dwVertFlag) ? (tSize.cy - newSize.cy) / 2   // 居中                        : 0;                                                // 居上                }  
                    //如果需要,调整偏移。  
                    if (offsetX || offsetY)  
                    {  
                        OffsetRect(&rcText, offsetX, offsetY);  
                    }  
                }  
                //创建相应大小的位图。  
                CC_BREAK_IF(! prepareBitmap(tSize.cx, tSize.cy));  
                // 使用当前字体和位图  
                HGDIOBJ hOldFont = SelectObject(m_hDC, m_hFont);  
                HGDIOBJ hOldBmp  = SelectObject(m_hDC, m_hBmp);  
                //设置背景透明模式和写字的颜色  
                SetBkMode(m_hDC, TRANSPARENT);  
                SetTextColor(m_hDC, RGB(255, 255, 255)); // white color  
                //写字  
                nRet = DrawTextW(m_hDC, pwszBuffer, nLen, &rcText, dwFmt);  
                //DrawTextA(m_hDC, pszText, nLen, &rcText, dwFmt);  
                //还原为之前使用的字体和位图  
                SelectObject(m_hDC, hOldBmp);  
                SelectObject(m_hDC, hOldFont);  
            } while (0);  
            //释放内存  
            CC_SAFE_DELETE_ARRAY(pwszBuffer);  
            return nRet;  
        }  
        //成员变量m_hDC及get接口  
        CC_SYNTHESIZE_READONLY(HDC, m_hDC, DC);  
        //成员变量m_hBmp及get接口  
        CC_SYNTHESIZE_READONLY(HBITMAP, m_hBmp, Bitmap);  
    private:  
        //友元类CCImage  
        friend class CCImage;  
        //成员变量m_hFont代表字体  
        HFONT   m_hFont;  
        //成员变量m_hWnd代表当前窗口句柄。  
        HWND    m_hWnd;  
        //成员m_curFontPath代表当前字体ttf文件全路径。  
        std::string m_curFontPath;  
    };  
    //取得单例BitmapDC  
    static BitmapDC& sharedBitmapDC()  
    {  
        static BitmapDC s_BmpDC;  
        return s_BmpDC;  
    }  
    //CCImage的成员函数,使用相应的字体写字符串生成图像数据。  
    //参1:字符串  
    //参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。  
    //参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。  
    //参4:文字的对齐方式。  
    //参5:字体名称  
    //参6:字体大小  
    bool CCImage::initWithString(  
                                   const char *    pText,   
                                   int             nWidth/* = 0*/,   
                                   int             nHeight/* = 0*/,  
                                   ETextAlign      eAlignMask/* = kAlignCenter*/,  
                                   const char *    pFontName/* = nil*/,  
                                   int             nSize/* = 0*/)  
    {  
        bool bRet = false;  
        unsigned char * pImageData = 0;  
        do   
        {  
            CC_BREAK_IF(! pText);         
        //取得单例BitmapDC  
            BitmapDC& dc = sharedBitmapDC();  
        //设置使用的字体和大小  
            if (! dc.setFont(pFontName, nSize))  
            {  
                CCLog("Can't found font(%s), use system default", pFontName);  
            }  
      
            // 写字  
            SIZE size = {nWidth, nHeight};  
            CC_BREAK_IF(! dc.drawText(pText, size, eAlignMask));  
      
            //申请图像大小的内存,成功申请后将其地址返回给指针pImageData。  
            pImageData = new unsigned char[size.cx * size.cy * 4];  
            CC_BREAK_IF(! pImageData);  
            //创建位图头信息结构  
            struct  
            {  
                BITMAPINFOHEADER bmiHeader;  
                int mask[4];  
            } bi = {0};  
            bi.bmiHeader.biSize = sizeof(bi.bmiHeader);  
            //取得写字符串的位图像素  
            CC_BREAK_IF(! GetDIBits(dc.getDC(), dc.getBitmap(), 0, 0,   
                NULL, (LPBITMAPINFO)&bi, DIB_RGB_COLORS));  
      
            m_nWidth    = (short)size.cx;  
            m_nHeight   = (short)size.cy;  
            m_bHasAlpha = true;  
            m_bPreMulti = false;  
            m_pData     = pImageData;  
            pImageData  = 0;  
            m_nBitsPerComponent = 8;  
            // 位图像素拷到pImageData中。   
            bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0)  
               ? - bi.bmiHeader.biHeight : bi.bmiHeader.biHeight;  
            GetDIBits(dc.getDC(), dc.getBitmap(), 0, m_nHeight, m_pData,   
                (LPBITMAPINFO)&bi, DIB_RGB_COLORS);  
      
            // 双循环遍历每个像素。取得R,G,B值组成4字节的RGBA值保存。  
            COLORREF * pPixel = NULL;  
            for (int y = 0; y < m_nHeight; ++y)  
            {  
                pPixel = (COLORREF *)m_pData + y * m_nWidth;  
                for (int x = 0; x < m_nWidth; ++x)  
                {  
                    COLORREF& clr = *pPixel;  
                    if (GetRValue(clr) || GetGValue(clr) || GetBValue(clr))  
                    {  
                        clr |= 0xff000000;  
                    }  
                    ++pPixel;  
                }  
            }  
      
            bRet = true;  
        } while (0);  
      
        return bRet;  
    }  
      
    NS_CC_END  
       CCImage类的代码都解析完了,其内部并没有对png,jpg,tiff做真正的解析,只是利用第三方的库进行相应的处理。经过处理后,CCImage会被图片文件的数据读取转换为Cocos2d-x所用的图像数据,存储在m_pData中。如果我们不是使用这些图片文件,那么我们就需要自已增加接口函数进行相应的处理。比如有时候做游戏不希望使用标准的图像格式以防止别人能够很容易打开图片进行查看,或者有时候将多个图片组成一个数据包。

例如:

          我们想读取一个自定义二进制图片数据(假设为Pic格式)

          (1)我们可以先定义一个新的图片类型kFmtPic,

          (2)然后定义函数

                    bool _initWithPicData(void *pData, int nDatalen);

          在其中将自定义图片文件数据pData解析到m_pData中,并设置一些属性变量。

         (3) 然后在initWithImageData函数中加入:    

    else if (kFmtPic == eFmt)  
       //读取自定义的图片数据。  
       bRet = _initWithPicData(pData, nDataLen);  
       break;  
        这样我们就可以让CCImage支持将我们自已定义的图像数据包加载为Cocos2d-x支持的图像类型了。

        最后,祝大家双节快乐!下课

       本节用到的图片功能库参考资料:

        图像解码之一——使用libjpeg解码jpeg图片 :http://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349763.html

       图像解码之二——使用libpng解码png图片

       http://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349765.html

      图像解码之三——giflib解码gif图片

      http://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349770.html

      VC下使用LibTiff处理TIFF文件:

     http://wenku.baidu.com/view/63b08afaaef8941ea76e05bc.html

      原文地址:http://blog.csdn.net/honghaier/article/details/8032434
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: cocos2d-x 4. 学习之路 cocos2d-x是一款开源的跨平台游戏引擎,支持多种平台,包括iOS、Android、Windows、Mac OS X等。cocos2d-x 4.是最新版本,相比之前版本,它有很多新特性和改进,如增强的渲染性能、更好的3D支持、更好的物理引擎等。 如果你想学习cocos2d-x 4.,可以从以下几个方面入手: 1. 学习基础知识:了解cocos2d-x的基本概念、架构和工作原理,掌握cocos2d-x的编程语言和开发环境。 2. 学习API:熟悉cocos2d-x的API,包括场景管理、精灵、动画、音频、物理引擎等。 3. 学习示例代码:通过阅读和分析cocos2d-x的示例代码,了解如何使用cocos2d-x开发游戏。 4. 学习实践:通过实践开发小游戏,掌握cocos2d-x的开发流程和技巧,提高自己的编程能力。 总之,学习cocos2d-x 4.需要一定的时间和耐心,但只要你有兴趣和热情,相信你一定能够掌握它。 ### 回答2: cocos2d-x是一个强大的游戏引擎,可用于开发移动和桌面游戏。随着cocos2d-x更新至4.0版本,它的功能得到了大幅升级。如果你想学习cocos2d-x 4.0,以下是一些重要的步骤和建议。 1. 更改代码结构 cocos2d-x 4.0中启用了新的代码结构,旨在更好地实现模块化和解耦。新代码结构包括Core、Renderer、2d等模块,使代码更易于维护和升级。要理解新代码结构,请先阅读cocos2d-x官方文档,并针对各个模块学习和熟悉其API。 2. 学习新功能 cocos2d-x 4.0中引入了许多新功能,例如Shader、Render Queue等。学习新功能是非常必要的,因为它们将改变以前的游戏开发模式。了解这些新功能的实现原理,并在自己的项目中应用它们,有助于提高游戏性能和质量。 3. 学习C++11 cocos2d-x 4.0开始支持C++11标准,这意味着你可以使用C++11的新特性来编写更好的代码。要理解C++11的特性,建议通读一遍C++11的官方标准,并尝试在cocos2d-x项目中使用这些新特性。 4. 实战练习 最后,实战练习是学习任何技能的关键。为了更好地学习cocos2d-x 4.0,建议你尝试开发自己的游戏项目。通过尝试解决实际问题,你能更好地理解cocos2d-x的API,并在实践中掌握游戏开发的技术。 总而言之,学习cocos2d-x 4.0需要掌握新的代码结构、新的功能和C++11标准,并通过实际项目实战练习来加深理解。这需要一定的时间和耐心,但只要你认真学习、实践和不断尝试,必定能够取得成功。 ### 回答3: cocos2d-x 4.0是目前市面上非常流行的开源游戏开发引擎,在游戏开发领域有着较为广泛的应用。然而,学习cocos2d-x 4.0需要付出一定的努力和时间。以下是我对cocos2d-x 4.0学习之路的一些经验和建议。 1. 学习基础知识 在开始学习cocos2d-x 4.0之前,我们需要了解一些基础知识,比如C++语言、OpenGL ES等,这些都是cocos2d-x 4.0的底层实现技术。掌握这些基础知识会让我们从事游戏开发时更加得心应手。 2. 学习文档 学习cocos2d-x 4.0需要阅读官方文档,这些文档详细介绍了引擎的各个方面,而且是学习的最佳资料。文档里包括了引擎的安装、使用、开发以及调试等。建议大家先从官网下载文档,并且仔细阅读。 3. 实践和开发 掌握了基础知识以及学习了文档之后,最好的方式就是通过实践和开发来加深对cocos2d-x 4.0的理解。通过实际开发游戏来体验引擎的使用,这样能够更深刻的理解引擎的机制以及遇到的各种问题该如何解决。同时,通过找到一些相近的问题,并通过查阅文档、代码实现等方式来解决问题,可以增强自己的技术水平。 4. 参与社区 cocos2d-x 4.0的官方论坛以及社区非常活跃,里面的开发者也有着丰富的经验和技术,在学习中可以多向论坛、社区里的大牛请教,获得更多的技术指导,同时也可以参与讨论,提出自己的问题和思考来获得反馈,这样可以更快地提高自己的技术。 总之,学习cocos2d-x 4.0需要耐心和对游戏开发的热情。只有通过不断的学习与实践,我们才能最终掌握这个优秀的游戏开发引擎,从而创建属于自己的游戏作品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值