• 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__
    #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
  • 相关阅读:
    A Tour of Go Switch
    A Tour of Go Exercise: Fibonacci closure
    curl的简单使用
    thinkphp自动验证方法的使用
    Forbidden You don't have permission to access / on this server. You don't have permission to access /phpmyadmin/ on this server. 解决办法
    创建、删除文件夹和文件夹里的文件
    图片的copy,从一个目录复制到另一个目录
    如何把内容写入到文件
    读取文件操作
    文件打开操作
  • 原文地址:https://www.cnblogs.com/newlist/p/3418484.html
Copyright © 2020-2023  润新知