Cocos2d-x之CCImage深入分析

Cocos2d-x之CCImage深入分析

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ群:249941957 加群写:Cocos2d-x

本节所用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图像头文件:

[cpp] view plaincopy

  1. #ifndef __CC_IMAGE_H__
  2. #define __CC_IMAGE_H__
  3. //派生于CCObject
  4. #include "cocoa/CCObject.h"
  5. //Cocos2d命名空间
  6. NS_CC_BEGIN
  7. class CC_DLL CCImage : public CCObject
  8. {
  9. public:
  10. //构造函数
  11. CCImage();
  12. //析构函数
  13. ~CCImage();
  14. //支持的图片类型
  15. typedef enum
  16. {
  17. kFmtJpg = 0,    //JPG
  18. kFmtPng,        //PNG
  19. kFmtTiff,       //TIFF
  20. kFmtRawData,    //数据流,要求为RGBA8888
  21. kFmtUnKnown //无效
  22. }EImageFormat;
  23. //对齐方式
  24. typedef enum
  25. {
  26. kAlignCenter        = 0x33, //横向纵向都居中.
  27. kAlignTop           = 0x13, //横向居中,纵向居上.
  28. kAlignTopRight      = 0x12, //横向居右,纵向居上.
  29. kAlignRight         = 0x32, //横向居中,纵向居中.
  30. kAlignBottomRight   = 0x22, //横向居右,纵向居下.
  31. kAlignBottom        = 0x23, //横向居中,纵向居下.
  32. kAlignBottomLeft    = 0x21, //横向居左,纵向居下.
  33. kAlignLeft          = 0x31, //横向居左,纵向居中.
  34. kAlignTopLeft       = 0x11, //横向居左,纵向居上.
  35. }ETextAlign;
  36. //从指定的路径载入一个所支持的格式的图片文件。
  37. bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng);
  38. //从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。
  39. bool initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType = kFmtPng);
  40. //从内存中加载图片数据。
  41. //参1:指向图片数据所处内存地址的指针。
  42. //参2:图片数据长度
  43. //参3:数据对应图片的格式,
  44. //参4:数据对应图片的宽度
  45. //参5:数据对应图片的高度
  46. //参6:每像素的字节位数,即色深。
  47. bool initWithImageData(void * pData,
  48. int nDataLen,
  49. EImageFormat eFmt = kFmtUnKnown,
  50. int nWidth = 0,
  51. int nHeight = 0,
  52. int nBitsPerComponent = 8);
  53. //从字符串创建图片数据。
  54. //参1:字符串
  55. //参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。
  56. //参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。
  57. //参4:文字的对齐方式。
  58. //参5:字体名称
  59. //参6:字体大小
  60. bool initWithString(
  61. const char *    pText,
  62. int             nWidth = 0,
  63. int             nHeight = 0,
  64. ETextAlign      eAlignMask = kAlignCenter,
  65. const char *    pFontName = 0,
  66. int             nSize = 0);
  67. //取得图像数据地址
  68. unsigned char *   getData()               { return m_pData; }
  69. //取得图像数据的长度
  70. int         getDataLen()            { return m_nWidth * m_nHeight; }
  71. //是否有Alpha通道。
  72. bool hasAlpha()                     { return m_bHasAlpha; }
  73. //是否有Alpha渐变
  74. bool isPremultipliedAlpha()         { return m_bPreMulti; }
  75. //将当前图片数据保存成指定的文件格式。
  76. //参1:绝对路径名
  77. //参2:是否保存成RGB格式
  78. bool saveToFile(const char *pszFilePath, bool bIsToRGB = true);
  79. //定义变量m_nWidth和get接口
  80. CC_SYNTHESIZE_READONLY(unsigned short,   m_nWidth,       Width);
  81. //定义变量m_nHeight和get接口
  82. CC_SYNTHESIZE_READONLY(unsigned short,   m_nHeight,      Height);
  83. //定义变量m_nBitsPerComponent和get接口
  84. CC_SYNTHESIZE_READONLY(int,     m_nBitsPerComponent,   BitsPerComponent);
  85. protected:
  86. //读取JPG图片数据
  87. //参1:数据地址
  88. //参2:数据长度
  89. bool _initWithJpgData(void *pData, int nDatalen);
  90. //读取PNG图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中
  91. bool _initWithPngData(void *pData, int nDatalen);
  92. //读取TIFF图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中
  93. bool _initWithTiffData(void* pData, int nDataLen);
  94. //读取RGBA8888格式的图片数据。
  95. //参1:数据地址
  96. //参2:数据长度
  97. //参3:图片宽度
  98. //参4:图片高度
  99. //参5:图片色深
  100. bool _initWithRawData(void *pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent);
  101. //将图像数据保存为PNG图片
  102. bool _saveImageToPNG(const char *pszFilePath, bool bIsToRGB = true);
  103. //将图像数据保存为JPG图片
  104. bool _saveImageToJPG(const char *pszFilePath);
  105. //图像数据地址
  106. unsigned char *m_pData;
  107. //是否有Alpha
  108. bool m_bHasAlpha;
  109. //是否有Alpha渐变    bool m_bPreMulti;
  110. private:
  111. // 拷贝构造与重载等号拷贝
  112. CCImage(const CCImage&    rImg);
  113. CCImage & operator=(const CCImage&);
  114. };
  115. NS_CC_END
  116. #endif    // __CC_IMAGE_H__

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

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

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

#define __CC_PLATFORM_IMAGE_CPP__

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

[cpp] view plaincopy

  1. #ifndef __CC_PLATFORM_IMAGE_CPP__
  2. //如果没有定义基于平台的CCImage的CPP标记宏,编译时打印出错。
  3. #error "CCFileUtilsCommon_cpp.h can only be included for CCFileUtils.cpp in platform/win32(android,...)"
  4. #endif /* __CC_PLATFORM_IMAGE_CPP__ */
  5. #include "CCImage.h"
  6. #include "CCCommon.h"
  7. #include "CCStdC.h"
  8. #include "CCFileUtils.h"
  9. //libpng库的头文件
  10. #include "png.h"
  11. //libjpg库的头文件
  12. #include "jpeglib.h"
  13. //libtiff库的头文件
  14. #include "tiffio.h"
  15. #include <string>
  16. #include <ctype.h>
  17. //使用Cocos2d命名空间
  18. NS_CC_BEGIN
  19. //定义宏从RGB888或RGB5A1像素格式数据中返回一个RGBA8888的像素格式数据。
  20. #define CC_RGB_PREMULTIPLY_APLHA(vr, vg, vb, va) \
  21. (unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \
  22. ((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \
  23. ((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \
  24. ((unsigned)(unsigned char)(va) << 24))
  25. //图片文件数据的信息结构
  26. typedef struct
  27. {
  28. unsigned char* data;
  29. int size;
  30. int offset;
  31. }tImageSource;
  32. //读取PNG文件数据的回调函数
  33. //参1:PNG文件数据指针
  34. //参2:返回的图片数据地址
  35. //参3:要从PNG文件中读取的图片数据的长度,其值 = 每像素字节数X图片的宽X图片的高。
  36. static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
  37. {
  38. //从一个PNG文件数据指针指针中返回图片文件数据的信息结构
  39. tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);
  40. //如果要读取的长度有效。则将相应长度的图像数据拷贝到返回的图片数据地址中。
  41. if((int)(isource->offset + length) <= isource->size)
  42. {
  43. memcpy(data, isource->data+isource->offset, length);
  44. isource->offset += length;
  45. }
  46. else
  47. {
  48. png_error(png_ptr, "pngReaderCallback failed");
  49. }
  50. }
  51. //////////////////////////////////////////////////////////////////////////
  52. //构造函数
  53. CCImage::CCImage()
  54. : m_nWidth(0)
  55. , m_nHeight(0)
  56. , m_nBitsPerComponent(0)
  57. , m_pData(0)
  58. , m_bHasAlpha(false)
  59. , m_bPreMulti(false)
  60. {
  61. }
  62. //析构函数
  63. CCImage::~CCImage()
  64. {
  65. //释放图像数据占用的内存
  66. CC_SAFE_DELETE_ARRAY(m_pData);
  67. }
  68. //从指定的路径载入一个所支持的格式的图片文件。
  69. bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/)
  70. {
  71. bool bRet = false;
  72. unsigned long nSize = 0;
  73. //调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。
  74. unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(strPath), "rb", &nSize);
  75. if (pBuffer != NULL && nSize > 0)
  76. {
  77. //如果读取成功,则将内存地址做为参数调用initWithImageData函数来加载图片数据。
  78. bRet = initWithImageData(pBuffer, nSize, eImgFmt);
  79. }
  80. //释放读取文件所创建的内存。
  81. CC_SAFE_DELETE_ARRAY(pBuffer);
  82. return bRet;
  83. }
  84. //从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。
  85. bool CCImage::initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType)
  86. {
  87. bool bRet = false;
  88. unsigned long nSize = 0;
  89. //调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。
  90. unsigned char *pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath, "rb", &nSize);
  91. if (pBuffer != NULL && nSize > 0)
  92. {
  93. //如果读取成功,则将内存地址做为参数调用initWithImageData函数来加载图片数据。
  94. bRet = initWithImageData(pBuffer, nSize, imageType);
  95. }
  96. //释放读取文件所创建的内存。
  97. CC_SAFE_DELETE_ARRAY(pBuffer);
  98. return bRet;
  99. }
  100. //从内存中加载图片数据。
  101. //参1:指向图片数据所处内存地址的指针。
  102. //参2:图片数据长度
  103. //参3:数据对应图片的格式,
  104. //参4:数据对应图片的宽度
  105. //参5:数据对应图片的高度
  106. //参6:每像素的字节位数,即色深。
  107. bool CCImage::initWithImageData(void * pData,
  108. int nDataLen,
  109. EImageFormat eFmt/* = eSrcFmtPng*/,
  110. int nWidth/* = 0*/,
  111. int nHeight/* = 0*/,
  112. int nBitsPerComponent/* = 8*/)
  113. {
  114. bool bRet = false;
  115. do
  116. {
  117. //参数有效性判断
  118. CC_BREAK_IF(! pData || nDataLen <= 0);
  119. //根据不同的图片数据格式调用不同的函数创建相应的图片数据。
  120. if (kFmtPng == eFmt)
  121. {
  122. //读取PNG格式的图片数据。
  123. bRet = _initWithPngData(pData, nDataLen);
  124. break;
  125. }
  126. else if (kFmtJpg == eFmt)
  127. {
  128. //读取JPG格式的图片数据。
  129. bRet = _initWithJpgData(pData, nDataLen);
  130. break;
  131. }
  132. else if (kFmtTiff == eFmt)
  133. {
  134. //读取TIFF格式的图片数据。
  135. bRet = _initWithTiffData(pData, nDataLen);
  136. break;
  137. }
  138. else if (kFmtRawData == eFmt)
  139. {
  140. //读取RGBA8888格式的图片数据。
  141. bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent);
  142. break;
  143. }
  144. else
  145. {
  146. // 如果未指定数据的格式.则通过对比相应格式的文件头信息判断格式。
  147. //判断是否是PNG
  148. if (nDataLen > 8)
  149. {
  150. unsigned char* pHead = (unsigned char*)pData;
  151. if (   pHead[0] == 0x89
  152. && pHead[1] == 0x50
  153. && pHead[2] == 0x4E
  154. && pHead[3] == 0x47
  155. && pHead[4] == 0x0D
  156. && pHead[5] == 0x0A
  157. && pHead[6] == 0x1A
  158. && pHead[7] == 0x0A)
  159. {
  160. //通过对比如果是属于PNG格式则读取PNG格式的图片数据
  161. bRet = _initWithPngData(pData, nDataLen);
  162. break;
  163. }
  164. }
  165. //判断是否是TIFF
  166. if (nDataLen > 2)
  167. {
  168. unsigned char* pHead = (unsigned char*)pData;
  169. if (  (pHead[0] == 0x49 && pHead[1] == 0x49)
  170. || (pHead[0] == 0x4d && pHead[1] == 0x4d)
  171. )
  172. {   //通过对比如果是属于TIFF格式则读取TIFF格式的图片数据
  173. bRet = _initWithTiffData(pData, nDataLen);
  174. break;
  175. }
  176. }
  177. //判断是否是JPG
  178. if (nDataLen > 2)
  179. {
  180. unsigned char* pHead = (unsigned char*)pData;
  181. if (   pHead[0] == 0xff
  182. && pHead[1] == 0xd8)
  183. {
  184. bRet = _initWithJpgData(pData, nDataLen);
  185. break;
  186. }
  187. }
  188. }
  189. } while (0);
  190. return bRet;
  191. }
  192. //读取JPG格式的图片数据。
  193. bool CCImage::_initWithJpgData(void * data, int nSize)
  194. {
  195. //此处使用libjpeg库来读取JPG,这里需要建立相应的结构变量。
  196. struct jpeg_decompress_struct cinfo;
  197. struct jpeg_error_mgr jerr;
  198. JSAMPROW row_pointer[1] = {0};
  199. unsigned long location = 0;
  200. unsigned int i = 0;
  201. bool bRet = false;
  202. do
  203. {
  204. //下面是使用libjpeg来进行JPG格式数据的读取。
  205. cinfo.err = jpeg_std_error( &jerr );
  206. jpeg_create_decompress( &cinfo );
  207. jpeg_mem_src( &cinfo, (unsigned char *) data, nSize );
  208. jpeg_read_header( &cinfo, true );
  209. // JPG只能支持RGB的像素格式
  210. if (cinfo.jpeg_color_space != JCS_RGB)
  211. {
  212. if (cinfo.jpeg_color_space == JCS_GRAYSCALE || cinfo.jpeg_color_space == JCS_YCbCr)
  213. {
  214. cinfo.out_color_space = JCS_RGB;
  215. }
  216. }
  217. else
  218. {
  219. break;
  220. }
  221. //开始解压JPG。
  222. jpeg_start_decompress( &cinfo );
  223. //设置相应成员变量。
  224. m_nWidth  = (short)(cinfo.image_width);
  225. m_nHeight = (short)(cinfo.image_height);
  226. m_bHasAlpha = false;
  227. m_bPreMulti = false;
  228. m_nBitsPerComponent = 8;
  229. row_pointer[0] = new unsigned char[cinfo.output_width*cinfo.output_components];
  230. CC_BREAK_IF(! row_pointer[0]);
  231. //为图片数据指针申请相应大小的内存。
  232. m_pData = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components];
  233. CC_BREAK_IF(! m_pData);
  234. //将像素信息读取到图片数据指针指向的内存中。
  235. while( cinfo.output_scanline < cinfo.image_height )
  236. {
  237. //每次读取一个扫描行的像素信息
  238. jpeg_read_scanlines( &cinfo, row_pointer, 1 );
  239. for( i=0; i<cinfo.image_width*cinfo.output_components;i++)
  240. {
  241. //将读取到的像素信息存入相应的内存中。
  242. m_pData[location++] = row_pointer[0][i];
  243. }
  244. }
  245. //完成解压
  246. jpeg_finish_decompress( &cinfo );
  247. //释放所用的结构
  248. jpeg_destroy_decompress( &cinfo );
  249. bRet = true;
  250. } while (0);
  251. //释放申请的内存
  252. CC_SAFE_DELETE_ARRAY(row_pointer[0]);
  253. return bRet;
  254. }
  255. //读取PNG格式的图片数据。
  256. bool CCImage::_initWithPngData(void * pData, int nDatalen)
  257. {
  258. // PNG文件头信息长度值
  259. #define PNGSIGSIZE  8
  260. bool bRet = false;
  261. //定义存储PNG文件头信息的BYTE数组
  262. png_byte        header[PNGSIGSIZE]   = {0};
  263. //PNG的读取说明结构,这里是libpng用到的结构体。
  264. png_structp     png_ptr     =   0;
  265. //PNG的信息结构
  266. png_infop       info_ptr    = 0;
  267. do
  268. {
  269. // PNG文件头有效性判断
  270. CC_BREAK_IF(nDatalen < PNGSIGSIZE);
  271. // 存储文件头信息
  272. memcpy(header, pData, PNGSIGSIZE);
  273. CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));
  274. //初始化PNG的读取说明结构
  275. png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
  276. CC_BREAK_IF(! png_ptr);
  277. // 初始化 PNG信息结构
  278. info_ptr = png_create_info_struct(png_ptr);
  279. CC_BREAK_IF(!info_ptr);
  280. #if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
  281. CC_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));
  282. #endif
  283. //图片文件数据的信息结构
  284. tImageSource imageSource;
  285. imageSource.data    = (unsigned char*)pData;
  286. imageSource.size    = nDatalen;
  287. imageSource.offset  = 0;
  288. //设置读取PNG的回调函数
  289. png_set_read_fn(png_ptr, &imageSource, pngReadCallback);
  290. //读取PNG信息结构
  291. png_read_info(png_ptr, info_ptr);
  292. //取得图片数据的相关属性。
  293. m_nWidth = png_get_image_width(png_ptr, info_ptr);
  294. m_nHeight = png_get_image_height(png_ptr, info_ptr);
  295. m_nBitsPerComponent = png_get_bit_depth(png_ptr, info_ptr);
  296. png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
  297. //打印像素格式
  298. //CCLOG("color type %u", color_type);
  299. // 如果是调色板格式的PNG,将其转为RGB888的像素格式。
  300. if (color_type == PNG_COLOR_TYPE_PALETTE)
  301. {
  302. png_set_palette_to_rgb(png_ptr);
  303. }
  304. // 如果是像素格式少于1字节长度的灰度图,将其转为每像素占1字节的像素格式。
  305. if (color_type == PNG_COLOR_TYPE_GRAY && m_nBitsPerComponent < 8)
  306. {
  307. png_set_expand_gray_1_2_4_to_8(png_ptr);
  308. }
  309. // 将RNS块数据信息扩展为完整的ALPHA通道信息
  310. if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
  311. {
  312. png_set_tRNS_to_alpha(png_ptr);
  313. }
  314. // reduce images with 16-bit samples to 8 bits
  315. if (m_nBitsPerComponent == 16)
  316. {
  317. png_set_strip_16(png_ptr);
  318. }
  319. // 将灰度图格式扩展成RGB
  320. if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
  321. {
  322. png_set_gray_to_rgb(png_ptr);
  323. }
  324. // 读取PNG数据
  325. // m_nBitsPerComponent will always be 8
  326. m_nBitsPerComponent = 8;
  327. png_uint_32 rowbytes;
  328. //后面读取PNG信息是按行读取,将每一行的像素数据读取到相应内存块中,下面这个BYTE指针数组就是为了存储每行图片像素信息读取到的相应内存块位置。
  329. png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * m_nHeight );
  330. png_read_update_info(png_ptr, info_ptr);
  331. //取得图片每一行像素的字节数量
  332. rowbytes = png_get_rowbytes(png_ptr, info_ptr);
  333. //为图片数据申请内存。
  334. m_pData = new unsigned char[rowbytes * m_nHeight];
  335. CC_BREAK_IF(!m_pData);
  336. //将申请到的内存地址按行放入相应的读取结构中
  337. for (unsigned short i = 0; i < m_nHeight; ++i)
  338. {
  339. row_pointers[i] = m_pData + i*rowbytes;
  340. }
  341. //将图片文件数据读取到相应的内存地址。
  342. png_read_image(png_ptr, row_pointers);
  343. //结束读取。
  344. png_read_end(png_ptr, NULL);
  345. //计算每像素占字节数。不管是RGB888还是RGBA8888的像素格式,其实际每像素占用的字节数均是4,只不过RGB888中多余的1字节不被用到。
  346. png_uint_32 channel = rowbytes/m_nWidth;
  347. if (channel == 4)
  348. {
  349. //设置为带ALPHA通道
  350. m_bHasAlpha = true;
  351. //定义指针变量tmp指向图像数据地址。用于在后面存放图像数据。
  352. unsigned int *tmp = (unsigned int *)m_pData;
  353. //双循环遍历像素数据。
  354. for(unsigned short i = 0; i < m_nHeight; i++)
  355. {
  356. for(unsigned int j = 0; j < rowbytes; j += 4)
  357. {
  358. //将R,G,B,A四个BYTE值组成一个DWORD值。
  359. *tmp++ = CC_RGB_PREMULTIPLY_APLHA( row_pointers[i][j], row_pointers[i][j + 1],
  360. row_pointers[i][j + 2], row_pointers[i][j + 3] );
  361. }
  362. }
  363. //设置使用渐变ALPHA
  364. m_bPreMulti = true;
  365. }
  366. //释放row_pointers
  367. CC_SAFE_FREE(row_pointers);
  368. bRet = true;
  369. } while (0);
  370. if (png_ptr)
  371. {
  372. //释放png_ptr
  373. png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
  374. }
  375. return bRet;
  376. }
  377. //读取TIFF图片数据时的回调函数。
  378. //参1:文件数据内存。
  379. //参2:输出参数,读取到的图像数据复制到对应的内存地址中。
  380. //参3:图片数据长度。
  381. static tmsize_t _tiffReadProc(thandle_t fd, void* buf, tmsize_t size)
  382. {
  383. //将fd转化为图片文件数据的信息结构指针。
  384. tImageSource* isource = (tImageSource*)fd;
  385. uint8* ma;
  386. uint64 mb;
  387. //定义一次可以读取的数据长度。
  388. unsigned long n;
  389. //定义变量o统计每次循环读取的数据长度。
  390. unsigned long o;
  391. //定义变量统计读取完的数据长度。
  392. tmsize_t p;
  393. //让前面定义的uint8类型指针变量ma指向buf。用于在后面存放图像数据。
  394. ma=(uint8*)buf;
  395. //让前面定义的变量mb来统计剩余未读取的数据长度。
  396. mb=size;
  397. p=0;
  398. //使用while循环进行读取,判断条件为剩余未读的数据长度是否大于0。
  399. while (mb>0)
  400. {
  401. n=0x80000000UL;
  402. if ((uint64)n>mb)
  403. n=(unsigned long)mb;
  404. //如果尚未读完所有数据,则继续读取,否则出错返回0
  405. if((int)(isource->offset + n) <= isource->size)
  406. {
  407. memcpy(ma, isource->data+isource->offset, n);
  408. isource->offset += n;
  409. o = n;
  410. }
  411. else
  412. {
  413. return 0;
  414. }
  415. //读取完长度为o的数据,则对指针进行相应的偏移操作供下次进行读取操作。
  416. ma+=o;
  417. //更新未读取的剩余长度
  418. mb-=o;
  419. //更新读取完的数量长度
  420. p+=o;
  421. //下面这个if比较奇怪,因为是不可能为true的。在上一个if判断中已经设置了o=n。
  422. if (o!=n)
  423. {
  424. break;
  425. }
  426. }
  427. return p;
  428. }
  429. //将数据保存为tiff图像文件所调用的回调函数。这里未用。
  430. static tmsize_t _tiffWriteProc(thandle_t fd, void* buf, tmsize_t size)
  431. {
  432. CC_UNUSED_PARAM(fd);
  433. CC_UNUSED_PARAM(buf);
  434. CC_UNUSED_PARAM(size);
  435. return 0;
  436. }
  437. //在对TIFF图像文件进行解析时进行重定位时调用的回调函数。
  438. static uint64 _tiffSeekProc(thandle_t fd, uint64 off, int whence)
  439. {
  440. //将fd转化为图片文件数据的信息结构指针。
  441. tImageSource* isource = (tImageSource*)fd;
  442. uint64 ret = -1;
  443. do
  444. {
  445. //如果定位方式为从头开始计算
  446. if (whence == SEEK_SET)
  447. {
  448. CC_BREAK_IF(off > isource->size-1);
  449. ret = isource->offset = (uint32)off;
  450. }
  451. else if (whence == SEEK_CUR)
  452. {  //如果定位方式为从当前位置开始计算
  453. CC_BREAK_IF(isource->offset + off > isource->size-1);
  454. ret = isource->offset += (uint32)off;
  455. }
  456. else if (whence == SEEK_END)
  457. {   //如果定位方工业从文件尾部开始计算
  458. CC_BREAK_IF(off > isource->size-1);
  459. ret = isource->offset = (uint32)(isource->size-1 - off);
  460. }
  461. else
  462. {//其它方式也按照从头开始计算
  463. CC_BREAK_IF(off > isource->size-1);
  464. ret = isource->offset = (uint32)off;
  465. }
  466. } while (0);
  467. return ret;
  468. }
  469. //取得tiff图片文件大小的回调函数。
  470. static uint64 _tiffSizeProc(thandle_t fd)
  471. {
  472. tImageSource* pImageSrc = (tImageSource*)fd;
  473. return pImageSrc->size;
  474. }
  475. //关闭tiff图片文件读取的回调函数。
  476. static int _tiffCloseProc(thandle_t fd)
  477. {
  478. CC_UNUSED_PARAM(fd);
  479. return 0;
  480. }
  481. //将tiff图片文件映射到内存时调用的回调函数。
  482. static int _tiffMapProc(thandle_t fd, void** pbase, toff_t* psize)
  483. {
  484. CC_UNUSED_PARAM(fd);
  485. CC_UNUSED_PARAM(pbase);
  486. CC_UNUSED_PARAM(psize);
  487. return 0;
  488. }
  489. //解除tiff图片映射到内存的回调函数。
  490. static void _tiffUnmapProc(thandle_t fd, void* base, toff_t size)
  491. {
  492. CC_UNUSED_PARAM(fd);
  493. CC_UNUSED_PARAM(base);
  494. CC_UNUSED_PARAM(size);
  495. }
  496. //使用LibTiff读取TIFF格式的图片数据。
  497. bool CCImage::_initWithTiffData(void* pData, int nDataLen)
  498. {
  499. bool bRet = false;
  500. do
  501. {
  502. //设置图片文件数据的信息结构
  503. tImageSource imageSource;
  504. imageSource.data    = (unsigned char*)pData;
  505. imageSource.size    = nDataLen;
  506. imageSource.offset  = 0;
  507. //使用libtiff打开一个tif文件,设置对其进行操作的各行为的回调函数。如果成功打开文件返回一个TIFF结构指针。
  508. TIFF* tif = TIFFClientOpen("file.tif", "r", (thandle_t)&imageSource,
  509. _tiffReadProc, _tiffWriteProc,
  510. _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
  511. _tiffMapProc,
  512. _tiffUnmapProc);
  513. //有效性判断。
  514. CC_BREAK_IF(NULL == tif);
  515. uint32 w = 0, h = 0;
  516. uint16 bitsPerSample = 0, samplePerPixel = 0, planarConfig = 0;
  517. //定义变量nPixels存储图像数据像素数量。
  518. size_t npixels = 0;
  519. //读取相应的图片属性信息。
  520. //图片宽度。
  521. TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
  522. //图片高度。
  523. TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
  524. //图片色深。
  525. TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
  526. //每像素数据占的字节数
  527. TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplePerPixel);
  528. //图像的平面配置
  529. TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
  530. //取得像素数量
  531. npixels = w * h;
  532. //设置带ALPHA通道。
  533. m_bHasAlpha = true;
  534. m_nWidth = w;
  535. m_nHeight = h;
  536. m_nBitsPerComponent = 8;
  537. //申请相应的内存用来存储像素数据。
  538. m_pData = new unsigned char[npixels * sizeof (uint32)];
  539. //申请临时内存进行TIFF数据读取。
  540. uint32* raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
  541. if (raster != NULL)
  542. {
  543. //读取TIFF数据
  544. if (TIFFReadRGBAImageOriented(tif, w, h, raster, ORIENTATION_TOPLEFT, 0))
  545. {
  546. //下面是从TIFF数据中解析生成我们Cocos2d-x所用的图像数据。
  547. //这里定义指针变量指向TIFF数据
  548. unsigned char* src = (unsigned char*)raster;
  549. //这里定义指针变量指向我们Cocos2d-x所用的图像数据
  550. unsigned int* tmp = (unsigned int*)m_pData;
  551. /*
  552. //遍历每像素进行,对像素的RGBA值进行组合,将组合成的DWORD值写入到图像数据中。
  553. for(int j = 0; j < m_nWidth * m_nHeight * 4; j += 4)
  554. {
  555. *tmp++ = CC_RGB_PREMULTIPLY_APLHA( src[j], src[j + 1],
  556. src[j + 2], src[j + 3] );
  557. }
  558. */
  559. //ALPHA通道有效。
  560. m_bPreMulti = true;
  561. //上面循环将组合后的DWORD值写入图像数据太慢,这里直接进行内存拷贝可以达到同样目的。
  562. memcpy(m_pData, raster, npixels*sizeof (uint32));
  563. }
  564. //释放临时申请的内存。
  565. _TIFFfree(raster);
  566. }
  567. //关闭TIFF文件读取。
  568. TIFFClose(tif);
  569. bRet = true;
  570. } while (0);
  571. return bRet;
  572. }
  573. //读取RGBA8888格式的图片数据。
  574. //参1:数据地址
  575. //参2:数据长度
  576. //参3:图片宽度
  577. //参4:图片高度
  578. //参5:图片色深
  579. bool CCImage::_initWithRawData(void * pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent)
  580. {
  581. bool bRet = false;
  582. do
  583. {
  584. //宽高有效性判断
  585. CC_BREAK_IF(0 == nWidth || 0 == nHeight);
  586. //保存相关属性
  587. m_nBitsPerComponent = nBitsPerComponent;
  588. m_nHeight   = (short)nHeight;
  589. m_nWidth    = (short)nWidth;
  590. m_bHasAlpha = true;
  591. // 只支持 RGBA8888 格式
  592. int nBytesPerComponent = 4;
  593. int nSize = nHeight * nWidth * nBytesPerComponent;
  594. //为图像数据申请相应内存,将地址返回给m_pData。
  595. m_pData = new unsigned char[nSize];
  596. //内存申请成败判断
  597. CC_BREAK_IF(! m_pData);
  598. //将参数数据拷贝到m_pData指向的内存地址中。
  599. memcpy(m_pData, pData, nSize);
  600. bRet = true;
  601. } while (0);
  602. return bRet;
  603. }
  604. //将图像数据保存为图片文件,目前只支持PNG和JPG
  605. //参1:文件路径
  606. //参2:是否是RGB的像素格式
  607. bool CCImage::saveToFile(const char *pszFilePath, bool bIsToRGB)
  608. {
  609. bool bRet = false;
  610. do
  611. {
  612. //参数有效性判断
  613. CC_BREAK_IF(NULL == pszFilePath);
  614. //通过是否有扩展名判断参数有效性。
  615. std::string strFilePath(pszFilePath);
  616. CC_BREAK_IF(strFilePath.size() <= 4);
  617. //将路径名转为小写字符串
  618. std::string strLowerCasePath(strFilePath);
  619. for (unsigned int i = 0; i < strLowerCasePath.length(); ++i)
  620. {
  621. strLowerCasePath[i] = tolower(strFilePath[i]);
  622. }
  623. //通过扩展名转成相应的图片文件
  624. //PNG
  625. if (std::string::npos != strLowerCasePath.find(".png"))
  626. {
  627. CC_BREAK_IF(!_saveImageToPNG(pszFilePath, bIsToRGB));
  628. }
  629. //JPG
  630. else if (std::string::npos != strLowerCasePath.find(".jpg"))
  631. {
  632. CC_BREAK_IF(!_saveImageToJPG(pszFilePath));
  633. }
  634. else
  635. {
  636. //不支持其它格式
  637. break;
  638. }
  639. bRet = true;
  640. } while (0);
  641. return bRet;
  642. }
  643. //将图像数据保存为PNG图片
  644. bool CCImage::_saveImageToPNG(const char * pszFilePath, bool bIsToRGB)
  645. {
  646. bool bRet = false;
  647. do
  648. {
  649. //参数有效性判断
  650. CC_BREAK_IF(NULL == pszFilePath);
  651. //使用libpng来写PNG文件。
  652. //定义文件指针变量用于写文件
  653. FILE *fp;
  654. //定义libpng所用的一些信息结构
  655. png_structp png_ptr;
  656. png_infop info_ptr;
  657. png_colorp palette;
  658. png_bytep *row_pointers;
  659. //打开文件开始写入
  660. fp = fopen(pszFilePath, "wb");
  661. CC_BREAK_IF(NULL == fp);
  662. //创建写PNG的文件结构体,将其结构指针返回给png_ptr
  663. png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  664. //指针有效性判断
  665. if (NULL == png_ptr)
  666. {
  667. fclose(fp);
  668. break;
  669. }
  670. //创建PNG的信息结构体,将其结构指针返回给info_ptr。
  671. info_ptr = png_create_info_struct(png_ptr);
  672. if (NULL == info_ptr)
  673. {
  674. fclose(fp);
  675. png_destroy_write_struct(&png_ptr, NULL);
  676. break;
  677. }
  678. #if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
  679. if (setjmp(png_jmpbuf(png_ptr)))
  680. {
  681. fclose(fp);
  682. png_destroy_write_struct(&png_ptr, &info_ptr);
  683. break;
  684. }
  685. #endif
  686. //初始化png_ptr
  687. png_init_io(png_ptr, fp);
  688. //根据是否有ALPHA来写入相应的头信息
  689. if (!bIsToRGB && m_bHasAlpha)
  690. {
  691. png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA,
  692. PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  693. }
  694. else
  695. {
  696. png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB,
  697. PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  698. }
  699. //创建调色板
  700. palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color));
  701. //设置调色板
  702. png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
  703. //写入info_ptr
  704. png_write_info(png_ptr, info_ptr);
  705. //
  706. png_set_packing(png_ptr);
  707. //申请临时存储m_pData中每一行像素数据地址的内存空间,将申请到的内存地址返回给row_pointers。
  708. row_pointers = (png_bytep *)malloc(m_nHeight * sizeof(png_bytep));
  709. if(row_pointers == NULL)
  710. {
  711. fclose(fp);
  712. png_destroy_write_struct(&png_ptr, &info_ptr);
  713. break;
  714. }
  715. //根据是否有ALPHA分别处理写入像素数据到文件中。
  716. if (!m_bHasAlpha)
  717. {
  718. //如果没有ALPHA,只是RGB,这里将m_pData中数据,遍历每一行,将每一行的起始内存地址放入row_pointers指针数组中。
  719. for (int i = 0; i < (int)m_nHeight; i++)
  720. {
  721. row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 3;
  722. }
  723. //将row_pointers中指向的每一行数据写入文件。
  724. png_write_image(png_ptr, row_pointers);
  725. //释放内存
  726. free(row_pointers);
  727. row_pointers = NULL;
  728. }
  729. else
  730. {
  731. //如果带ALPHA通道。对是否是RGB格式又进行分别处理。
  732. //如果是RGB888格式
  733. if (bIsToRGB)
  734. {
  735. //创建临时的内存存放像素数据。每个像素3字节,分别存R,G,B值。
  736. unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3];
  737. if (NULL == pTempData)
  738. {
  739. fclose(fp);
  740. png_destroy_write_struct(&png_ptr, &info_ptr);
  741. break;
  742. }
  743. //双循环遍历每个像素,将R,G,B值保存到数组中。
  744. for (int i = 0; i < m_nHeight; ++i)
  745. {
  746. for (int j = 0; j < m_nWidth; ++j)
  747. {
  748. pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];
  749. pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];
  750. pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];
  751. }
  752. }
  753. //将数组中保存的每行像素的内存地址存入row_pointers数组中。
  754. for (int i = 0; i < (int)m_nHeight; i++)
  755. {
  756. row_pointers[i] = (png_bytep)pTempData + i * m_nWidth * 3;
  757. }
  758. //将row_pointers中指向的每一行数据写入文件。
  759. png_write_image(png_ptr, row_pointers);
  760. //释放内存
  761. free(row_pointers);
  762. row_pointers = NULL;
  763. CC_SAFE_DELETE_ARRAY(pTempData);
  764. }
  765. else
  766. {
  767. //如果是RGBA8888格式
  768. //将数组中保存的每行像素的内存地址存入row_pointers数组中。
  769. for (int i = 0; i < (int)m_nHeight; i++)
  770. {
  771. row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 4;
  772. }
  773. //将row_pointers中指向的每一行数据写入文件。
  774. png_write_image(png_ptr, row_pointers);
  775. //释放内存
  776. free(row_pointers);
  777. row_pointers = NULL;
  778. }
  779. }
  780. //结束写PNG文件
  781. png_write_end(png_ptr, info_ptr);
  782. //释放相应的信息结构
  783. png_free(png_ptr, palette);
  784. palette = NULL;
  785. png_destroy_write_struct(&png_ptr, &info_ptr);
  786. fclose(fp);
  787. bRet = true;
  788. } while (0);
  789. return bRet;
  790. }
  791. //将图像数据保存为JPG文件
  792. bool CCImage::_saveImageToJPG(const char * pszFilePath)
  793. {
  794. bool bRet = false;
  795. do
  796. {
  797. //参数有效性判断
  798. CC_BREAK_IF(NULL == pszFilePath);
  799. //使用libjpg库要用到的相关结构。
  800. struct jpeg_compress_struct cinfo;
  801. struct jpeg_error_mgr jerr;
  802. FILE * outfile;                 /* target file */
  803. JSAMPROW row_pointer[1];        /* pointer to JSAMPLE row[s] */
  804. int     row_stride;          /* physical row width in image buffer */
  805. //初始化相关结构
  806. cinfo.err = jpeg_std_error(&jerr);
  807. jpeg_create_compress(&cinfo);
  808. //开始写入文件
  809. CC_BREAK_IF((outfile = fopen(pszFilePath, "wb")) == NULL);
  810. //写入JPG头文件基本信息
  811. jpeg_stdio_dest(&cinfo, outfile);
  812. //填充JPG图像的属性信息结构
  813. cinfo.image_width = m_nWidth;
  814. cinfo.image_height = m_nHeight;
  815. cinfo.input_components = 3;
  816. cinfo.in_color_space = JCS_RGB;
  817. //将信息结构来设置JPG图像
  818. jpeg_set_defaults(&cinfo);
  819. //开始进行数据压缩输出
  820. jpeg_start_compress(&cinfo, TRUE);
  821. //设置每行的字节长度
  822. row_stride = m_nWidth * 3;
  823. //跟据图像数据是否有ALPHA通道来进行分别处理
  824. if (m_bHasAlpha)
  825. {
  826. //创建内存来存放图像的像素数据。
  827. unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3];
  828. if (NULL == pTempData)
  829. {
  830. jpeg_finish_compress(&cinfo);
  831. jpeg_destroy_compress(&cinfo);
  832. fclose(outfile);
  833. break;
  834. }
  835. //双循环遍历每一个图像像素的数据,取R,G,B值存放到临时申请的内存地址中,A值弃之不取。
  836. for (int i = 0; i < m_nHeight; ++i)
  837. {
  838. for (int j = 0; j < m_nWidth; ++j)
  839. {
  840. //因图像数据有A通道,所以找相应的像素地址时会由像素索引x4。
  841. pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];
  842. pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];
  843. pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];
  844. }
  845. }
  846. //将扫描行的数据写入JPG文件
  847. while (cinfo.next_scanline < cinfo.image_height) {
  848. row_pointer[0] = & pTempData[cinfo.next_scanline * row_stride];
  849. (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  850. }
  851. CC_SAFE_DELETE_ARRAY(pTempData);
  852. }
  853. else
  854. { //将扫描行的数据写入JPG文件
  855. while (cinfo.next_scanline < cinfo.image_height) {
  856. row_pointer[0] = & m_pData[cinfo.next_scanline * row_stride];
  857. (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  858. }
  859. }
  860. //结束数据压缩,关闭文件并释放相应信息结构。
  861. jpeg_finish_compress(&cinfo);
  862. fclose(outfile);
  863. jpeg_destroy_compress(&cinfo);
  864. bRet = true;
  865. } while (0);
  866. return bRet;
  867. }
  868. NS_CC_END

现在我们回到CCImage.cpp:

[cpp] view plaincopy

  1. #define __CC_PLATFORM_IMAGE_CPP__
  2. #include "platform/CCImageCommon_cpp.h"
  3. NS_CC_BEGIN
  4. //此处定义一个BitmapDC类在位图上进行文字绘制。
  5. class BitmapDC
  6. {
  7. public:
  8. //构造函数
  9. BitmapDC(HWND hWnd = NULL)
  10. : m_hDC(NULL)
  11. , m_hBmp(NULL)
  12. , m_hFont((HFONT)GetStockObject(DEFAULT_GUI_FONT))
  13. , m_hWnd(NULL)
  14. {
  15. //保存窗口句柄
  16. m_hWnd = hWnd;
  17. //取得窗口的hdc
  18. HDC hdc = GetDC(hWnd);
  19. //创建兼容的hdc
  20. m_hDC   = CreateCompatibleDC(hdc);
  21. //释放hdc
  22. ReleaseDC(hWnd, hdc);
  23. }
  24. //析构函数
  25. ~BitmapDC()
  26. {
  27. prepareBitmap(0, 0);
  28. if (m_hDC)
  29. {
  30. DeleteDC(m_hDC);
  31. }
  32. //创建字体
  33. HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
  34. if (hDefFont != m_hFont)
  35. {
  36. DeleteObject(m_hFont);
  37. m_hFont = hDefFont;
  38. }
  39. // release temp font resource
  40. if (m_curFontPath.size() > 0)
  41. {
  42. wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);
  43. if (pwszBuffer)
  44. {
  45. RemoveFontResource(pwszBuffer);
  46. SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);
  47. delete [] pwszBuffer;
  48. pwszBuffer = NULL;
  49. }
  50. }
  51. }
  52. //多字节转宽字符
  53. wchar_t * utf8ToUtf16(std::string nString)
  54. {
  55. wchar_t * pwszBuffer = NULL;
  56. do
  57. {
  58. if (nString.size() < 0)
  59. {
  60. break;
  61. }
  62. // utf-8 to utf-16
  63. int nLen = nString.size();
  64. int nBufLen  = nLen + 1;
  65. pwszBuffer = new wchar_t[nBufLen];
  66. CC_BREAK_IF(! pwszBuffer);
  67. memset(pwszBuffer,0,nBufLen);
  68. nLen = MultiByteToWideChar(CP_UTF8, 0, nString.c_str(), nLen, pwszBuffer, nBufLen);
  69. pwszBuffer[nLen] = ‘\0‘;
  70. } while (0);
  71. return pwszBuffer;
  72. }
  73. //设置使用的字体和大小
  74. bool setFont(const char * pFontName = NULL, int nSize = 0)
  75. {
  76. bool bRet = false;
  77. do
  78. {
  79. //创建字体
  80. std::string fontName = pFontName;
  81. std::string fontPath;
  82. HFONT       hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
  83. LOGFONTA    tNewFont = {0};
  84. LOGFONTA    tOldFont = {0};
  85. GetObjectA(hDefFont, sizeof(tNewFont), &tNewFont);
  86. if (fontName.c_str())
  87. {
  88. // 如果字体名称是ttf文件,取得其全路径名
  89. int nFindttf = fontName.find(".ttf");
  90. int nFindTTF = fontName.find(".TTF");
  91. if (nFindttf >= 0 || nFindTTF >= 0)
  92. {
  93. fontPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(fontName.c_str());
  94. int nFindPos = fontName.rfind("/");
  95. fontName = &fontName[nFindPos+1];
  96. nFindPos = fontName.rfind(".");
  97. fontName = fontName.substr(0,nFindPos);
  98. }
  99. tNewFont.lfCharSet = DEFAULT_CHARSET;
  100. strcpy_s(tNewFont.lfFaceName, LF_FACESIZE, fontName.c_str());
  101. }
  102. if (nSize)
  103. {
  104. tNewFont.lfHeight = -nSize;
  105. }
  106. //
  107. GetObjectA(m_hFont,  sizeof(tOldFont), &tOldFont);
  108. if (tOldFont.lfHeight == tNewFont.lfHeight
  109. && ! strcpy(tOldFont.lfFaceName, tNewFont.lfFaceName))
  110. {
  111. // already has the font
  112. bRet = true;
  113. break;
  114. }
  115. // 删除旧的字体
  116. if (m_hFont != hDefFont)
  117. {
  118. DeleteObject(m_hFont);
  119. // release old font register
  120. if (m_curFontPath.size() > 0)
  121. {
  122. wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);
  123. if (pwszBuffer)
  124. {
  125. if(RemoveFontResource(pwszBuffer))
  126. {
  127. SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);
  128. }
  129. delete [] pwszBuffer;
  130. pwszBuffer = NULL;
  131. }
  132. }
  133. fontPath.size()>0?(m_curFontPath = fontPath):(m_curFontPath.clear());
  134. if (m_curFontPath.size() > 0)
  135. {
  136. wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);
  137. if (pwszBuffer)
  138. {
  139. if(AddFontResource(pwszBuffer))
  140. {
  141. SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);
  142. }
  143. delete [] pwszBuffer;
  144. pwszBuffer = NULL;
  145. }
  146. }
  147. }
  148. m_hFont = NULL;
  149. // 字体不使用锐利字体
  150. tNewFont.lfQuality = ANTIALIASED_QUALITY;
  151. // 创建新字体
  152. m_hFont = CreateFontIndirectA(&tNewFont);
  153. if (! m_hFont)
  154. {
  155. // create failed, use default font
  156. m_hFont = hDefFont;
  157. break;
  158. }
  159. bRet = true;
  160. } while (0);
  161. return bRet;
  162. }
  163. //取得使用当前字体写出的字符串所占据的图像大小。
  164. SIZE sizeWithText(const wchar_t * pszText, int nLen, DWORD dwFmt, LONG nWidthLimit)
  165. {
  166. SIZE tRet = {0};
  167. do
  168. {
  169. CC_BREAK_IF(! pszText || nLen <= 0);
  170. RECT rc = {0, 0, 0, 0};
  171. DWORD dwCalcFmt = DT_CALCRECT;
  172. if (nWidthLimit > 0)
  173. {
  174. rc.right = nWidthLimit;
  175. dwCalcFmt |= DT_WORDBREAK
  176. | (dwFmt & DT_CENTER)
  177. | (dwFmt & DT_RIGHT);
  178. }
  179. // 使用当前字体。
  180. HGDIOBJ hOld = SelectObject(m_hDC, m_hFont);
  181. // 写字。
  182. DrawTextW(m_hDC, pszText, nLen, &rc, dwCalcFmt);
  183. SelectObject(m_hDC, hOld);
  184. tRet.cx = rc.right;
  185. tRet.cy = rc.bottom;
  186. } while (0);
  187. return tRet;
  188. }
  189. //创建一个指定大小的位图。
  190. bool prepareBitmap(int nWidth, int nHeight)
  191. {
  192. // 释放原来的位图
  193. if (m_hBmp)
  194. {
  195. DeleteObject(m_hBmp);
  196. m_hBmp = NULL;
  197. }       //如果大小有效则创建位图。
  198. if (nWidth > 0 && nHeight > 0)
  199. {
  200. m_hBmp = CreateBitmap(nWidth, nHeight, 1, 32, NULL);
  201. if (! m_hBmp)
  202. {
  203. return false;
  204. }
  205. }
  206. return true;
  207. }
  208. //在指定位置按照指定对齐方式写字。
  209. //参1:字符串
  210. //参2:指定拉置及大小
  211. //参3:文字对齐方式
  212. int drawText(const char * pszText, SIZE& tSize, CCImage::ETextAlign eAlign)
  213. {
  214. int nRet = 0;
  215. wchar_t * pwszBuffer = 0;
  216. do
  217. {
  218. CC_BREAK_IF(! pszText);
  219. DWORD dwFmt = DT_WORDBREAK;
  220. DWORD dwHoriFlag = eAlign & 0x0f;
  221. DWORD dwVertFlag = (eAlign & 0xf0) >> 4;
  222. //设置横向对齐方式。
  223. switch (dwHoriFlag)
  224. {
  225. case 1: // 左对齐
  226. dwFmt |= DT_LEFT;
  227. break;
  228. case 2: // 右对齐
  229. dwFmt |= DT_RIGHT;
  230. break;
  231. case 3: // 居中
  232. dwFmt |= DT_CENTER;
  233. break;
  234. }
  235. //取得字符串的字节长度。
  236. int nLen = strlen(pszText);
  237. int nBufLen  = nLen + 1;
  238. // 为转换后的宽字符串申请内存地址。
  239. pwszBuffer = new wchar_t[nBufLen];
  240. CC_BREAK_IF(! pwszBuffer);
  241. //内存数据置零
  242. memset(pwszBuffer, 0, sizeof(wchar_t)*nBufLen);
  243. // 将多字节转宽字符串。
  244. nLen = MultiByteToWideChar(CP_UTF8, 0, pszText, nLen, pwszBuffer, nBufLen);
  245. //取得写字符串占据的图像区域大小
  246. SIZE newSize = sizeWithText(pwszBuffer, nLen, dwFmt, tSize.cx);
  247. //建立RECT变量做为实际绘制占据的图像区域大小
  248. RECT rcText = {0};
  249. // 如果宽度为0,则使用显示字符串所需的图像大小。
  250. if (tSize.cx <= 0)
  251. {
  252. tSize = newSize;
  253. rcText.right  = newSize.cx;
  254. rcText.bottom = newSize.cy;
  255. }
  256. else
  257. {
  258. LONG offsetX = 0;
  259. LONG offsetY = 0;
  260. rcText.right = newSize.cx;
  261. //根据对齐方式计算横向偏移。
  262. if (1 != dwHoriFlag
  263. && newSize.cx < tSize.cx)
  264. {
  265. offsetX = (2 == dwHoriFlag) ? tSize.cx - newSize.cx                                 : (tSize.cx - newSize.cx) / 2;                                          }
  266. //如果指定矩形高度为0,则使用显示字符串所需的图像高度。
  267. if (tSize.cy <= 0)
  268. {
  269. tSize.cy = newSize.cy;
  270. dwFmt   |= DT_NOCLIP;
  271. rcText.bottom = newSize.cy; // store the text height to rectangle
  272. }
  273. else if (tSize.cy < newSize.cy)
  274. {
  275. // content height larger than text height need, clip text to rect
  276. rcText.bottom = tSize.cy;
  277. }
  278. else
  279. {
  280. rcText.bottom = newSize.cy;
  281. // content larger than text, need adjust vertical position
  282. dwFmt |= DT_NOCLIP;
  283. //根据对齐方式计算纵向偏移。
  284. offsetY = (2 == dwVertFlag) ? tSize.cy - newSize.cy     // 居
    下                        : (3 == dwVertFlag) ? (tSize.cy - newSize.cy) / 2   // 居
    中                        : 0;                                                // 居
    上                }
  285. //如果需要,调整偏移。
  286. if (offsetX || offsetY)
  287. {
  288. OffsetRect(&rcText, offsetX, offsetY);
  289. }
  290. }
  291. //创建相应大小的位图。
  292. CC_BREAK_IF(! prepareBitmap(tSize.cx, tSize.cy));
  293. // 使用当前字体和位图
  294. HGDIOBJ hOldFont = SelectObject(m_hDC, m_hFont);
  295. HGDIOBJ hOldBmp  = SelectObject(m_hDC, m_hBmp);
  296. //设置背景透明模式和写字的颜色
  297. SetBkMode(m_hDC, TRANSPARENT);
  298. SetTextColor(m_hDC, RGB(255, 255, 255)); // white color
  299. //写字
  300. nRet = DrawTextW(m_hDC, pwszBuffer, nLen, &rcText, dwFmt);
  301. //DrawTextA(m_hDC, pszText, nLen, &rcText, dwFmt);
  302. //还原为之前使用的字体和位图
  303. SelectObject(m_hDC, hOldBmp);
  304. SelectObject(m_hDC, hOldFont);
  305. } while (0);
  306. //释放内存
  307. CC_SAFE_DELETE_ARRAY(pwszBuffer);
  308. return nRet;
  309. }
  310. //成员变量m_hDC及get接口
  311. CC_SYNTHESIZE_READONLY(HDC, m_hDC, DC);
  312. //成员变量m_hBmp及get接口
  313. CC_SYNTHESIZE_READONLY(HBITMAP, m_hBmp, Bitmap);
  314. private:
  315. //友元类CCImage
  316. friend class CCImage;
  317. //成员变量m_hFont代表字体
  318. HFONT   m_hFont;
  319. //成员变量m_hWnd代表当前窗口句柄。
  320. HWND    m_hWnd;
  321. //成员m_curFontPath代表当前字体ttf文件全路径。
  322. std::string m_curFontPath;
  323. };
  324. //取得单例BitmapDC
  325. static BitmapDC& sharedBitmapDC()
  326. {
  327. static BitmapDC s_BmpDC;
  328. return s_BmpDC;
  329. }
  330. //CCImage的成员函数,使用相应的字体写字符串生成图像数据。
  331. //参1:字符串
  332. //参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。
  333. //参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。
  334. //参4:文字的对齐方式。
  335. //参5:字体名称
  336. //参6:字体大小
  337. bool CCImage::initWithString(
  338. const char *    pText,
  339. int             nWidth/* = 0*/,
  340. int             nHeight/* = 0*/,
  341. ETextAlign      eAlignMask/* = kAlignCenter*/,
  342. const char *    pFontName/* = nil*/,
  343. int             nSize/* = 0*/)
  344. {
  345. bool bRet = false;
  346. unsigned char * pImageData = 0;
  347. do
  348. {
  349. CC_BREAK_IF(! pText);
  350. //取得单例BitmapDC
  351. BitmapDC& dc = sharedBitmapDC();
  352. //设置使用的字体和大小
  353. if (! dc.setFont(pFontName, nSize))
  354. {
  355. CCLog("Can‘t found font(%s), use system default", pFontName);
  356. }
  357. // 写字
  358. SIZE size = {nWidth, nHeight};
  359. CC_BREAK_IF(! dc.drawText(pText, size, eAlignMask));
  360. //申请图像大小的内存,成功申请后将其地址返回给指针pImageData。
  361. pImageData = new unsigned char[size.cx * size.cy * 4];
  362. CC_BREAK_IF(! pImageData);
  363. //创建位图头信息结构
  364. struct
  365. {
  366. BITMAPINFOHEADER bmiHeader;
  367. int mask[4];
  368. } bi = {0};
  369. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  370. //取得写字符串的位图像素
  371. CC_BREAK_IF(! GetDIBits(dc.getDC(), dc.getBitmap(), 0, 0,
  372. NULL, (LPBITMAPINFO)&bi, DIB_RGB_COLORS));
  373. m_nWidth    = (short)size.cx;
  374. m_nHeight   = (short)size.cy;
  375. m_bHasAlpha = true;
  376. m_bPreMulti = false;
  377. m_pData     = pImageData;
  378. pImageData  = 0;
  379. m_nBitsPerComponent = 8;
  380. // 位图像素拷到pImageData中。
  381. bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0)
  382. ? - bi.bmiHeader.biHeight : bi.bmiHeader.biHeight;
  383. GetDIBits(dc.getDC(), dc.getBitmap(), 0, m_nHeight, m_pData,
  384. (LPBITMAPINFO)&bi, DIB_RGB_COLORS);
  385. // 双循环遍历每个像素。取得R,G,B值组成4字节的RGBA值保存。
  386. COLORREF * pPixel = NULL;
  387. for (int y = 0; y < m_nHeight; ++y)
  388. {
  389. pPixel = (COLORREF *)m_pData + y * m_nWidth;
  390. for (int x = 0; x < m_nWidth; ++x)
  391. {
  392. COLORREF& clr = *pPixel;
  393. if (GetRValue(clr) || GetGValue(clr) || GetBValue(clr))
  394. {
  395. clr |= 0xff000000;
  396. }
  397. ++pPixel;
  398. }
  399. }
  400. bRet = true;
  401. } while (0);
  402. return bRet;
  403. }
  404. 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函数中加入:

[cpp] view plaincopy

  1. else if (kFmtPic == eFmt)
  2. //读取自定义的图片数据。
  3. bRet = _initWithPicData(pData, nDataLen);
  4. 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

时间: 2024-10-06 13:56:33

Cocos2d-x之CCImage深入分析的相关文章

cocos2d-x PNG图片资源加密

实现原理 如果你对实现原理并不感兴趣,只是想直接使用的话可以跳过这节.首先我假设读者已经清楚PNG图像文件结构了,如果没有的话这里建议可以看看<揭秘数据解密的关键技术>第5章,这章里面专门对PNG图像格式进行了介绍.或者看看<PNG文件格式详解>这篇文章. 实现原理很简单,只是将PNG文件的文件头和每个数据块的名称给去掉,特殊的数据块(如IHDR和IEND)连数据块内容也一并给去掉.在这之后,已经找不到PNG的特征信息了.但是要怎么解密呢?我将去掉的那些信息按照一定格式保存起来,然

cocos2d

#ifndef __COCOS2D_H__ #define __COCOS2D_H__ // 0x00 HI ME LO // 00   02 01 00 #define COCOS2D_VERSION 0x00020100 // // all cocos2d include files // #include "ccConfig.h" // actions #include "actions/CCAction.h" #include "actions/C

cocos2d学习资源收集

    在知乎上看到的某个关于<自学 cocos2d 游戏开发应该按什么步骤进行?>这个问题的某个答案,感觉应该很不错,可以先收藏下来,以后需要了再回来看看~ Cocos2d-x网站列表 CocoaChina(官方网站,不解释) 泰然网(貌似最近有很多不错的文章,不过早期的文章质量一般) Cocos2d-x博客列表 老G的小屋 小满的专栏 子龙山人 红孩儿的游戏编程之路 Cocosdev 黑米GameDev街区 优秀cocos2d-x源码 Code4app 代码仓库 cocos2d-x与ios

cocos2d::Vector

v3.0 beta加入 定义在"COCOS2DX_ROOT/cocos/base"的"CCVector.h"头文件中. template<class T>class CC_DLL Vector; cocos2d::Vector<T>是一个封装好的能动态增长顺序访问的容器. cocos2d::Vector<T>中的元素是按序存取的,它的低层实现数据结构是标准模版库中的标准顺序容器std::vector. 在Cocos2d-x v3.

深入分析:Fragment与Activity交互的几种方式(一,使用Handler)

这里我不再详细介绍那写比较常规的方式,例如静态变量,静态方法,持久化,application全局变量,收发广播等等. 首先我们来介绍使用Handler来实现Fragment与Activity 的交互. 第一步,我们需要在Activity中定义一个方法用来设置Handler对象. public void setHandler(Handler handler) { mHandler = handler; } 第二步,在Fragment中的回调函数onAttach()中得到Fragment所在Acti

Cocos2D中Action的进阶使用技巧(一)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 假设认为写的不好请多提意见,假设认为不错请多多支持点赞.谢谢! hopy ;) 大家对Cocos2d中动作的使用大概都非常清楚了,事实上本身action的概念也不复杂. 可是在某些情况下,一般的动作不能满足我们需求的时候,我们就必须使用更高级的Action方法来解决这个问题. 比方,串行化(不是序列化哦,这是两个全然不同的概念)不同Action的执行-有些童鞋可能会说非常easy,直接用CCActionSequence不就结了,可是等等我

Android开发艺术探索——第七章:Android动画深入分析

Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

Cocos2d入门--3-- 向量的应用

 Cocos2d入门--3-- 向量的应用 小球向一个方向持续运动的Demo HelloWorldScene.h ...... //设置一个protected的属性 protected: cocos2d::Vec2 _vec; ...... HelloWorldScene.cpp //这个是通过随机数设置向量的方向 _vec.set(random(-0.1f, 1.0f), random(-1.0f, 1.0f)); //通过normalize这个标准化函数,能够使得向量的大小为1 _vec.n

Cocos2d入门--1-- 初涉相关属性或代码

 Cocos2d入门--1-- 初涉相关属性或代码 Cocos2d vision:  cocos2d-x-3.8.1 万丈高楼,起于累土.对于一个游戏框架的学习,其实在于框架功能的使用积累,学会了如何在cocos2d游戏引擎的基础上使用它提供的各种功能,并灵活运用, 以及学会查阅Cocos2d官方提供的API文档.相信自己也能开发出自己喜爱或者让别人羡慕的游戏. 目录: 1>认识origin和visibleSize以及cocos2d的基础绘画类DrawNode的简单实用 2>认识 CCLOG(