Cocos2d-x之CCImage深入分析


Cocos2d-x之CCImage深入分析

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

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

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

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


[cpp]
#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,
ETextAlign eAlignMask = kAlignCenter,
const char * pFontName = 0,
int nSize = 0);

//取得图像数据地址
unsigned char * getData() { return m_pData; }
//取得图像数据的长度
int getDataLen() { return m_nWidth * m_nHeight; }
//是否有Alpha通道。
bool hasAlpha() { return m_bHasAlpha; }
//是否有Alpha渐变
bool isPremultipliedAlpha() { return m_bPreMulti; }

//将当前图片数据保存成指定的文件格式。
//参1:绝对路径名
//参2:是否保存成RGB格式
bool saveToFile(const char *pszFilePath,bool bIsToRGB = true);
//定义变量m_nWidth和get接口
CC_SYNTHESIZE_READONLY(unsigned short,m_nWidth,Width);
//定义变量m_nHeight和get接口
CC_SYNTHESIZE_READONLY(unsigned short,m_nHeight,Height);
//定义变量m_nBitsPerComponent和get接口
CC_SYNTHESIZE_READONLY(int,m_nBitsPerComponent,BitsPerComponent);

protected:
//读取JPG图片数据
//参1:数据地址
//参2:数据长度
bool _initWithJpgData(void *pData,int nDatalen);
//读取PNG图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中
bool _initWithPngData(void *pData,int nDatalen);
//读取TIFF图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中
bool _initWithTiffData(void* pData,int nDataLen);
//读取RGBA8888格式的图片数据。
//参1:数据地址
//参2:数据长度
//参3:图片宽度
//参4:图片高度
//参5:图片色深
bool _initWithRawData(void *pData,int nDatalen,int nWidth,int nHeight,int nBitsPerComponent);
//将图像数据保存为PNG图片
bool _saveImageToPNG(const char *pszFilePath,bool bIsToRGB = true);
//将图像数据保存为JPG图片
bool _saveImageToJPG(const char *pszFilePath);
//图像数据地址
unsigned char *m_pData;
//是否有Alpha
bool m_bHasAlpha;
//是否有Alpha渐变 bool m_bPreMulti;

private:
// 拷贝构造与重载等号拷贝
CCImage(const CCImage& rImg);
CCImage & operator=(const CCImage&);
};

NS_CC_END

#endif // __CC_IMAGE_H__

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

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

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

#define __CC_PLATFORM_IMAGE_CPP__

//这里引用CCImageCommon_cpp.h

#include "platform/CCImageCommon_cpp.h"

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

[cpp]
#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,imageType);
}
//释放读取文件所创建的内存。
CC_SAFE_DELETE_ARRAY(pBuffer);
return bRet;
}

//从内存中加载图片数据。
//参1:指向图片数据所处内存地址的指针。
//参2:图片数据长度
//参3:数据对应图片的格式,
//参4:数据对应图片的宽度
//参5:数据对应图片的高度
//参6:每像素的字节位数,即色深。
bool CCImage::initWithImageData(void * pData,
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,PNGSIGSIZE));

//初始化PNG的读取说明结构
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,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,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,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,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 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,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);
//指针有效性判断
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,8,PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);
}
else
{
png_set_IHDR(png_ptr,PNG_COLOR_TYPE_RGB,PNG_FILTER_TYPE_BASE);
}
//创建调色板
palette = (png_colorp)png_malloc(png_ptr,PNG_MAX_PALETTE_LENGTH * sizeof (png_color));
//设置调色板
png_set_PLTE(png_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,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,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,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,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,1);
}
}
//结束数据压缩,关闭文件并释放相应信息结构。
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);

bRet = true;
} while (0);
return bRet;
}

NS_CC_END

现在我们回到CCImage.cpp:

[cpp]
#define __CC_PLATFORM_IMAGE_CPP__
#include "platform/CCImageCommon_cpp.h"

NS_CC_BEGIN

//此处定义一个BitmapDC类在位图上进行文字绘制。
class BitmapDC
{
public:
//构造函数
BitmapDC(HWND hWnd = NULL)
: m_hDC(NULL)
,m_hBmp(NULL)
,m_hFont((HFONT)GetStockObject(DEFAULT_GUI_FONT))
,m_hWnd(NULL)
{
//保存窗口句柄
m_hWnd = hWnd;
//取得窗口的hdc
HDC hdc = GetDC(hWnd);
//创建兼容的hdc
m_hDC = CreateCompatibleDC(hdc);
//释放hdc
ReleaseDC(hWnd,hdc);
}
//析构函数
~BitmapDC()
{
prepareBitmap(0,0);
if (m_hDC)
{
DeleteDC(m_hDC);
}
//创建字体
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
if (hDefFont != m_hFont)
{
DeleteObject(m_hFont);
m_hFont = hDefFont;
}
// release temp font resource
if (m_curFontPath.size() > 0)
{
wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);
if (pwszBuffer)
{
RemoveFontResource(pwszBuffer);
SendMessage( m_hWnd,WM_FONTCHANGE,0);
delete [] pwszBuffer;
pwszBuffer = NULL;
}
}
}
//多字节转宽字符
wchar_t * utf8ToUtf16(std::string nString)
{
wchar_t * pwszBuffer = NULL;
do
{
if (nString.size() < 0)
{
break;
}
// utf-8 to utf-16
int nLen = nString.size();
int nBufLen = nLen + 1;
pwszBuffer = new wchar_t[nBufLen];
CC_BREAK_IF(! pwszBuffer);
memset(pwszBuffer,nBufLen);
nLen = MultiByteToWideChar(CP_UTF8,nString.c_str(),nLen,pwszBuffer,nBufLen);
pwszBuffer[nLen] = '\0';
} while (0);
return pwszBuffer;

}
//设置使用的字体和大小
bool setFont(const char * pFontName = NULL,int nSize = 0)
{
bool bRet = false;
do
{
//创建字体
std::string fontName = pFontName;
std::string fontPath;
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
LOGFONTA tNewFont = {0};
LOGFONTA tOldFont = {0};
GetObjectA(hDefFont,sizeof(tNewFont),&tNewFont);
if (fontName.c_str())
{
// 如果字体名称是ttf文件,取得其全路径名
int nFindttf = fontName.find(".ttf");
int nFindTTF = fontName.find(".TTF");
if (nFindttf >= 0 || nFindTTF >= 0)
{
fontPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(fontName.c_str());
int nFindPos = fontName.rfind("/");
fontName = &fontName[nFindPos+1];
nFindPos = fontName.rfind(".");
fontName = fontName.substr(0,nFindPos);
}
tNewFont.lfCharSet = DEFAULT_CHARSET;
strcpy_s(tNewFont.lfFaceName,LF_FACESIZE,fontName.c_str());
}
if (nSize)
{
tNewFont.lfHeight = -nSize;
}
//
GetObjectA(m_hFont,sizeof(tOldFont),&tOldFont);

if (tOldFont.lfHeight == tNewFont.lfHeight
&& ! strcpy(tOldFont.lfFaceName,tNewFont.lfFaceName))
{
// already has the font
bRet = true;
break;
}

// 删除旧的字体
if (m_hFont != hDefFont)
{
DeleteObject(m_hFont);
// release old font register
if (m_curFontPath.size() > 0)
{
wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);
if (pwszBuffer)
{
if(RemoveFontResource(pwszBuffer))
{
SendMessage( m_hWnd,0);
}
delete [] pwszBuffer;
pwszBuffer = NULL;
}
}
fontPath.size()>0?(m_curFontPath = fontPath):(m_curFontPath.clear());

if (m_curFontPath.size() > 0)
{
wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);
if (pwszBuffer)
{
if(AddFontResource(pwszBuffer))
{
SendMessage( m_hWnd,0);
}
delete [] pwszBuffer;
pwszBuffer = NULL;
}
}
}
m_hFont = NULL;

// 字体不使用锐利字体
tNewFont.lfQuality = ANTIALIASED_QUALITY;

// 创建新字体
m_hFont = CreateFontIndirectA(&tNewFont);
if (! m_hFont)
{
// create failed,use default font
m_hFont = hDefFont;
break;
}

bRet = true;
} while (0);
return bRet;
}
//取得使用当前字体写出的字符串所占据的图像大小。
SIZE sizeWithText(const wchar_t * pszText,int nLen,DWORD dwFmt,LONG nWidthLimit)
{
SIZE tRet = {0};
do
{
CC_BREAK_IF(! pszText || nLen <= 0);

RECT rc = {0,0};
DWORD dwCalcFmt = DT_CALCRECT;

if (nWidthLimit > 0)
{
rc.right = nWidthLimit;
dwCalcFmt |= DT_WORDBREAK
| (dwFmt & DT_CENTER)
| (dwFmt & DT_RIGHT);
}
// 使用当前字体。
HGDIOBJ hOld = SelectObject(m_hDC,m_hFont);

// 写字。
DrawTextW(m_hDC,pszText,&rc,dwCalcFmt);
SelectObject(m_hDC,hOld);

tRet.cx = rc.right;
tRet.cy = rc.bottom;
} while (0);

return tRet;
}
//创建一个指定大小的位图。
bool prepareBitmap(int nWidth,int nHeight)
{
// 释放原来的位图
if (m_hBmp)
{
DeleteObject(m_hBmp);
m_hBmp = NULL;
} //如果大小有效则创建位图。
if (nWidth > 0 && nHeight > 0)
{
m_hBmp = CreateBitmap(nWidth,1,32,NULL);
if (! m_hBmp)
{
return false;
}
}
return true;
}
//在指定位置按照指定对齐方式写字。
//参1:字符串
//参2:指定拉置及大小
//参3:文字对齐方式
int drawText(const char * pszText,SIZE& tSize,CCImage::ETextAlign eAlign)
{
int nRet = 0;
wchar_t * pwszBuffer = 0;
do
{
CC_BREAK_IF(! pszText);

DWORD dwFmt = DT_WORDBREAK;
DWORD dwHoriFlag = eAlign & 0x0f;
DWORD dwVertFlag = (eAlign & 0xf0) >> 4;
//设置横向对齐方式。
switch (dwHoriFlag)
{
case 1: // 左对齐
dwFmt |= DT_LEFT;
break;
case 2: // 右对齐
dwFmt |= DT_RIGHT;
break;
case 3: // 居中
dwFmt |= DT_CENTER;
break;
}
//取得字符串的字节长度。
int nLen = strlen(pszText);
int nBufLen = nLen + 1;
// 为转换后的宽字符串申请内存地址。
pwszBuffer = new wchar_t[nBufLen];
CC_BREAK_IF(! pwszBuffer);
//内存数据置零
memset(pwszBuffer,sizeof(wchar_t)*nBufLen);
// 将多字节转宽字符串。
nLen = MultiByteToWideChar(CP_UTF8,nBufLen);
//取得写字符串占据的图像区域大小
SIZE newSize = sizeWithText(pwszBuffer,dwFmt,tSize.cx);
//建立RECT变量做为实际绘制占据的图像区域大小
RECT rcText = {0};
// 如果宽度为0,则使用显示字符串所需的图像大小。
if (tSize.cx <= 0)
{
tSize = newSize;
rcText.right = newSize.cx;
rcText.bottom = newSize.cy;
}
else
{
LONG offsetX = 0;
LONG offsetY = 0;
rcText.right = newSize.cx;
//根据对齐方式计算横向偏移。
if (1 != dwHoriFlag
&& newSize.cx < tSize.cx)
{
offsetX = (2 == dwHoriFlag) ? tSize.cx - newSize.cx : (tSize.cx - newSize.cx) / 2; }
//如果指定矩形高度为0,则使用显示字符串所需的图像高度。
if (tSize.cy <= 0)
{
tSize.cy = newSize.cy;
dwFmt |= DT_NOCLIP;
rcText.bottom = newSize.cy; // store the text height to rectangle
}
else if (tSize.cy < newSize.cy)
{
// content height larger than text height need,clip text to rect
rcText.bottom = tSize.cy;
}
else
{
rcText.bottom = newSize.cy;

// content larger than text,need adjust vertical position
dwFmt |= DT_NOCLIP;

//根据对齐方式计算纵向偏移。
offsetY = (2 == dwVertFlag) ? tSize.cy - newSize.cy // 居下 : (3 == dwVertFlag) ? (tSize.cy - newSize.cy) / 2 // 居中 : 0; // 居上 }
//如果需要,调整偏移。
if (offsetX || offsetY)
{
OffsetRect(&rcText,offsetX,offsetY);
}
}
//创建相应大小的位图。
CC_BREAK_IF(! prepareBitmap(tSize.cx,tSize.cy));
// 使用当前字体和位图
HGDIOBJ hOldFont = SelectObject(m_hDC,m_hFont);
HGDIOBJ hOldBmp = SelectObject(m_hDC,m_hBmp);
//设置背景透明模式和写字的颜色
SetBkMode(m_hDC,TRANSPARENT);
SetTextColor(m_hDC,RGB(255,255,255)); // white color
//写字
nRet = DrawTextW(m_hDC,&rcText,dwFmt);
//DrawTextA(m_hDC,dwFmt);
//还原为之前使用的字体和位图
SelectObject(m_hDC,hOldBmp);
SelectObject(m_hDC,hOldFont);
} while (0);
//释放内存
CC_SAFE_DELETE_ARRAY(pwszBuffer);
return nRet;
}
//成员变量m_hDC及get接口
CC_SYNTHESIZE_READONLY(HDC,m_hDC,DC);
//成员变量m_hBmp及get接口
CC_SYNTHESIZE_READONLY(HBITMAP,m_hBmp,Bitmap);
private:
//友元类CCImage
friend class CCImage;
//成员变量m_hFont代表字体
HFONT m_hFont;
//成员变量m_hWnd代表当前窗口句柄。
HWND m_hWnd;
//成员m_curFontPath代表当前字体ttf文件全路径。
std::string m_curFontPath;
};
//取得单例BitmapDC
static BitmapDC& sharedBitmapDC()
{
static BitmapDC s_BmpDC;
return s_BmpDC;
}
//CCImage的成员函数,使用相应的字体写字符串生成图像数据。
//参1:字符串
//参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。
//参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。
//参4:文字的对齐方式。
//参5:字体名称
//参6:字体大小
bool CCImage::initWithString(
const char * pText,
ETextAlign eAlignMask/* = kAlignCenter*/,
const char * pFontName/* = nil*/,
int nSize/* = 0*/)
{
bool bRet = false;
unsigned char * pImageData = 0;
do
{
CC_BREAK_IF(! pText);
//取得单例BitmapDC
BitmapDC& dc = sharedBitmapDC();
//设置使用的字体和大小
if (! dc.setFont(pFontName,nSize))
{
CCLog("Can't found font(%s),use system default",pFontName);
}

// 写字
SIZE size = {nWidth,nHeight};
CC_BREAK_IF(! dc.drawText(pText,size,eAlignMask));

//申请图像大小的内存,成功申请后将其地址返回给指针pImageData。
pImageData = new unsigned char[size.cx * size.cy * 4];
CC_BREAK_IF(! pImageData);
//创建位图头信息结构
struct
{
BITMAPINFOHEADER bmiHeader;
int mask[4];
} bi = {0};
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
//取得写字符串的位图像素
CC_BREAK_IF(! GetDIBits(dc.getDC(),dc.getBitmap(),
NULL,(LPBITMAPINFO)&bi,DIB_RGB_COLORS));

m_nWidth = (short)size.cx;
m_nHeight = (short)size.cy;
m_bHasAlpha = true;
m_bPreMulti = false;
m_pData = pImageData;
pImageData = 0;
m_nBitsPerComponent = 8;
// 位图像素拷到pImageData中。
bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0)
? - bi.bmiHeader.biHeight : bi.bmiHeader.biHeight;
GetDIBits(dc.getDC(),m_pData,
(LPBITMAPINFO)&bi,DIB_RGB_COLORS);

// 双循环遍历每个像素。取得R,G,B值组成4字节的RGBA值保存。
COLORREF * pPixel = NULL;
for (int y = 0; y < m_nHeight; ++y)
{
pPixel = (COLORREF *)m_pData + y * m_nWidth;
for (int x = 0; x < m_nWidth; ++x)
{
COLORREF& clr = *pPixel;
if (GetRValue(clr) || GetGValue(clr) || GetBValue(clr))
{
clr |= 0xff000000;
}
++pPixel;
}
}

bRet = true;
} while (0);

return bRet;
}

NS_CC_END
CCImage类的代码都解析完了,其内部并没有对png,jpg,tiff做真正的解析,只是利用第三方的库进行相应的处理。经过处理后,CCImage会被图片文件的数据读取转换为Cocos2d-x所用的图像数据,存储在m_pData中。如果我们不是使用这些图片文件,那么我们就需要自已增加接口函数进行相应的处理。比如有时候做游戏不希望使用标准的图像格式以防止别人能够很容易打开图片进行查看,或者有时候将多个图片组成一个数据包。

例如:

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

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

(2)然后定义函数

bool _initWithPicData(void *pData,int nDatalen);

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

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

[cpp] else if (kFmtPic == eFmt) //读取自定义的图片数据。 bRet = _initWithPicData(pData,nDataLen); break;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


    本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
 下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是