#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