Cocos2d-x之CCImage深入分析


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

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

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

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


[cpp] view plain copy
#ifndef __CC_IMAGE_H__
#define __CC_IMAGE_H__
//派生于CCObject
#include "cocoa/CCObject.h"
//Cocos2d命名空间
NS_CC_BEGIN

class CC_DLL CCImage : public CCObject
{
public:
//构造函数
CCImage();
//析构函数
~CCImage();
//支持的图片类型
typedef enum
{
kFmtJpg = 0, //JPG
kFmtPng, //PNG
kFmtTiff, //TIFF
kFmtRawData, //数据流,要求为RGBA8888
kFmtUnKnown //无效
}EImageFormat;
//对齐方式
typedef enum
{
kAlignCenter = 0x33, //横向纵向都居中.
kAlignTop = 0x13, //横向居中,纵向居上.
kAlignTopRight = 0x12, //横向居右,纵向居上.
kAlignRight = 0x32, //横向居中,纵向居中.
kAlignBottomRight = 0x22, //横向居右,纵向居下.
kAlignBottom = 0x23, //横向居中,纵向居下.
kAlignBottomLeft = 0x21, //横向居左,纵向居下.
kAlignLeft = 0x31, //横向居左,纵向居中.
kAlignTopLeft = 0x11, //横向居左,纵向居上.
}ETextAlign;

//从指定的路径载入一个所支持的格式的图片文件。
bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng);

//从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。
bool initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType = kFmtPng);

//从内存中加载图片数据。
//参1:指向图片数据所处内存地址的指针。
//参2:图片数据长度
//参3:数据对应图片的格式,
//参4:数据对应图片的宽度
//参5:数据对应图片的高度
//参6:每像素的字节位数,即色深。
bool initWithImageData(void * pData,
int nDataLen,
EImageFormat eFmt = kFmtUnKnown,
int nWidth = 0,
int nHeight = 0,
int nBitsPerComponent = 8);

//从字符串创建图片数据。
//参1:字符串
//参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。
//参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。
//参4:文字的对齐方式。
//参5:字体名称
//参6:字体大小
bool initWithString(
const char * pText,
int nWidth = 0,
int nHeight = 0,
ETextAlign eAlignMask = kAlignCenter,
const char * pFontName = 0,
int nSize = 0);

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

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

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

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

NS_CC_END

#endif // __CC_IMAGE_H__

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

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

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

#define __CC_PLATFORM_IMAGE_CPP__

//这里引用CCImageCommon_cpp.h

#include "platform/CCImageCommon_cpp.h"

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

[cpp] view plain copy
#ifndef __CC_PLATFORM_IMAGE_CPP__
//如果没有定义基于平台的CCImage的CPP标记宏,编译时打印出错。
#error "CCFileUtilsCommon_cpp.h can only be included for CCFileUtils.cpp in platform/win32(android,...)"
#endif /* __CC_PLATFORM_IMAGE_CPP__ */

#include "CCImage.h"
#include "CCCommon.h"
#include "CCStdC.h"
#include "CCFileUtils.h"
//libpng库的头文件
#include "png.h"
//libjpg库的头文件
#include "jpeglib.h"
//libtiff库的头文件
#include "tiffio.h"
#include <string>
#include <ctype.h>
//使用Cocos2d命名空间
NS_CC_BEGIN

//定义宏从RGB888或RGB5A1像素格式数据中返回一个RGBA8888的像素格式数据。
#define CC_RGB_PREMULTIPLY_APLHA(vr, vg, vb, va) \
(unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \
((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \
((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \
((unsigned)(unsigned char)(va) << 24))

//图片文件数据的信息结构
typedef struct
{
unsigned char* data;
int size;
int offset;
}tImageSource;
//读取PNG文件数据的回调函数
//参1:PNG文件数据指针
//参2:返回的图片数据地址
//参3:要从PNG文件中读取的图片数据的长度,其值 = 每像素字节数X图片的宽X图片的高。
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
//从一个PNG文件数据指针指针中返回图片文件数据的信息结构
tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);
//如果要读取的长度有效。则将相应长度的图像数据拷贝到返回的图片数据地址中。
if((int)(isource->offset + length) <= isource->size)
{
memcpy(data, isource->data+isource->offset, length);
isource->offset += length;
}
else
{
png_error(png_ptr, "pngReaderCallback failed");
}
}

//////////////////////////////////////////////////////////////////////////
//构造函数
CCImage::CCImage()
: m_nWidth(0)
, m_nHeight(0)
, m_nBitsPerComponent(0)
, m_pData(0)
, m_bHasAlpha(false)
, m_bPreMulti(false)
{

}
//析构函数
CCImage::~CCImage()
{
//释放图像数据占用的内存
CC_SAFE_DELETE_ARRAY(m_pData);
}
//从指定的路径载入一个所支持的格式的图片文件。
bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/)
{
bool bRet = false;
unsigned long nSize = 0;
//调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(strPath), "rb", &nSize);
if (pBuffer != NULL && nSize > 0)
{
//如果读取成功,则将内存地址做为参数调用initWithImageData函数来加载图片数据。
bRet = initWithImageData(pBuffer, nSize, eImgFmt);
}
//释放读取文件所创建的内存。
CC_SAFE_DELETE_ARRAY(pBuffer);
return bRet;
}

//从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。
bool CCImage::initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType)
{
bool bRet = false;
unsigned long nSize = 0;
//调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。
unsigned char *pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath, "rb", &nSize);
if (pBuffer != NULL && nSize > 0)
{
//如果读取成功,则将内存地址做为参数调用initWithImageData函数来加载图片数据。
bRet = initWithImageData(pBuffer, nSize, imageType);
}
//释放读取文件所创建的内存。
CC_SAFE_DELETE_ARRAY(pBuffer);
return bRet;
}

//从内存中加载图片数据。
//参1:指向图片数据所处内存地址的指针。
//参2:图片数据长度
//参3:数据对应图片的格式,
//参4:数据对应图片的宽度
//参5:数据对应图片的高度
//参6:每像素的字节位数,即色深。
bool CCImage::initWithImageData(void * pData,
int nDataLen,
EImageFormat eFmt/* = eSrcFmtPng*/,
int nWidth/* = 0*/,
int nHeight/* = 0*/,
int nBitsPerComponent/* = 8*/)
{
bool bRet = false;
do
{
//参数有效性判断
CC_BREAK_IF(! pData || nDataLen <= 0);
//根据不同的图片数据格式调用不同的函数创建相应的图片数据。
if (kFmtPng == eFmt)
{
//读取PNG格式的图片数据。
bRet = _initWithPngData(pData, nDataLen);
break;
}
else if (kFmtJpg == eFmt)
{
//读取JPG格式的图片数据。
bRet = _initWithJpgData(pData, nDataLen);
break;
}
else if (kFmtTiff == eFmt)
{
//读取TIFF格式的图片数据。
bRet = _initWithTiffData(pData, nDataLen);
break;
}
else if (kFmtRawData == eFmt)
{
//读取RGBA8888格式的图片数据。
bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent);
break;
}
else
{
// 如果未指定数据的格式.则通过对比相应格式的文件头信息判断格式。
//判断是否是PNG
if (nDataLen > 8)
{
unsigned char* pHead = (unsigned char*)pData;
if ( pHead[0] == 0x89
&& pHead[1] == 0x50
&& pHead[2] == 0x4E
&& pHead[3] == 0x47
&& pHead[4] == 0x0D
&& pHead[5] == 0x0A
&& pHead[6] == 0x1A
&& pHead[7] == 0x0A)
{
//通过对比如果是属于PNG格式则读取PNG格式的图片数据
bRet = _initWithPngData(pData, nDataLen);
break;
}
}
//判断是否是TIFF
if (nDataLen > 2)
{
unsigned char* pHead = (unsigned char*)pData;
if ( (pHead[0] == 0x49 && pHead[1] == 0x49)
|| (pHead[0] == 0x4d && pHead[1] == 0x4d)
)
{ //通过对比如果是属于TIFF格式则读取TIFF格式的图片数据
bRet = _initWithTiffData(pData, nDataLen);
break;
}
}
//判断是否是JPG
if (nDataLen > 2)
{
unsigned char* pHead = (unsigned char*)pData;
if ( pHead[0] == 0xff
&& pHead[1] == 0xd8)
{
bRet = _initWithJpgData(pData, nDataLen);
break;
}
}
}
} while (0);
return bRet;
}
//读取JPG格式的图片数据。
bool CCImage::_initWithJpgData(void * data, int nSize)
{
//此处使用libjpeg库来读取JPG,这里需要建立相应的结构变量。
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1] = {0};
unsigned long location = 0;
unsigned int i = 0;

bool bRet = false;
do
{
//下面是使用libjpeg来进行JPG格式数据的读取。

cinfo.err = jpeg_std_error( &jerr );
jpeg_create_decompress( &cinfo );
jpeg_mem_src( &cinfo, (unsigned char *) data, nSize );
jpeg_read_header( &cinfo, true );

// JPG只能支持RGB的像素格式
if (cinfo.jpeg_color_space != JCS_RGB)
{
if (cinfo.jpeg_color_space == JCS_GRAYSCALE || cinfo.jpeg_color_space == JCS_YCbCr)
{
cinfo.out_color_space = JCS_RGB;
}
}
else
{
break;
}
//开始解压JPG。
jpeg_start_decompress( &cinfo );

//设置相应成员变量。
m_nWidth = (short)(cinfo.image_width);
m_nHeight = (short)(cinfo.image_height);
m_bHasAlpha = false;
m_bPreMulti = false;
m_nBitsPerComponent = 8;
row_pointer[0] = new unsigned char[cinfo.output_width*cinfo.output_components];
CC_BREAK_IF(! row_pointer[0]);
//为图片数据指针申请相应大小的内存。
m_pData = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components];
CC_BREAK_IF(! m_pData);

//将像素信息读取到图片数据指针指向的内存中。
while( cinfo.output_scanline < cinfo.image_height )
{
//每次读取一个扫描行的像素信息
jpeg_read_scanlines( &cinfo, row_pointer, 1 );
for( i=0; i<cinfo.image_width*cinfo.output_components;i++)
{
//将读取到的像素信息存入相应的内存中。
m_pData[location++] = row_pointer[0][i];
}
}
//完成解压
jpeg_finish_decompress( &cinfo );
//释放所用的结构
jpeg_destroy_decompress( &cinfo );
bRet = true;
} while (0);
//释放申请的内存
CC_SAFE_DELETE_ARRAY(row_pointer[0]);
return bRet;
}
//读取PNG格式的图片数据。
bool CCImage::_initWithPngData(void * pData, int nDatalen)
{
// PNG文件头信息长度值
#define PNGSIGSIZE 8
bool bRet = false;
//定义存储PNG文件头信息的BYTE数组
png_byte header[PNGSIGSIZE] = {0};
//PNG的读取说明结构,这里是libpng用到的结构体。
png_structp png_ptr = 0;
//PNG的信息结构
png_infop info_ptr = 0;

do
{
// PNG文件头有效性判断
CC_BREAK_IF(nDatalen < PNGSIGSIZE);

// 存储文件头信息
memcpy(header, pData, PNGSIGSIZE);
CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));

//初始化PNG的读取说明结构
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
CC_BREAK_IF(! png_ptr);

// 初始化 PNG信息结构
info_ptr = png_create_info_struct(png_ptr);
CC_BREAK_IF(!info_ptr);

#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
CC_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));
#endif

//图片文件数据的信息结构
tImageSource imageSource;
imageSource.data = (unsigned char*)pData;
imageSource.size = nDatalen;
imageSource.offset = 0;
//设置读取PNG的回调函数
png_set_read_fn(png_ptr, &imageSource, pngReadCallback);

//读取PNG信息结构
png_read_info(png_ptr, info_ptr);
//取得图片数据的相关属性。
m_nWidth = png_get_image_width(png_ptr, info_ptr);
m_nHeight = png_get_image_height(png_ptr, info_ptr);
m_nBitsPerComponent = png_get_bit_depth(png_ptr, info_ptr);
png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
//打印像素格式
//CCLOG("color type %u", color_type);

// 如果是调色板格式的PNG,将其转为RGB888的像素格式。
if (color_type == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(png_ptr);
}
// 如果是像素格式少于1字节长度的灰度图,将其转为每像素占1字节的像素格式。
if (color_type == PNG_COLOR_TYPE_GRAY && m_nBitsPerComponent < 8)
{
png_set_expand_gray_1_2_4_to_8(png_ptr);
}
// 将RNS块数据信息扩展为完整的ALPHA通道信息
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png_ptr);
}
// reduce images with 16-bit samples to 8 bits
if (m_nBitsPerComponent == 16)
{
png_set_strip_16(png_ptr);
}
// 将灰度图格式扩展成RGB
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_gray_to_rgb(png_ptr);
}

// 读取PNG数据
// m_nBitsPerComponent will always be 8
m_nBitsPerComponent = 8;
png_uint_32 rowbytes;
//后面读取PNG信息是按行读取,将每一行的像素数据读取到相应内存块中,下面这个BYTE指针数组就是为了存储每行图片像素信息读取到的相应内存块位置。
png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * m_nHeight );

png_read_update_info(png_ptr, info_ptr);
//取得图片每一行像素的字节数量
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
//为图片数据申请内存。
m_pData = new unsigned char[rowbytes * m_nHeight];
CC_BREAK_IF(!m_pData);
//将申请到的内存地址按行放入相应的读取结构中
for (unsigned short i = 0; i < m_nHeight; ++i)
{
row_pointers[i] = m_pData + i*rowbytes;
}
//将图片文件数据读取到相应的内存地址。
png_read_image(png_ptr, row_pointers);
//结束读取。
png_read_end(png_ptr, NULL);
//计算每像素占字节数。不管是RGB888还是RGBA8888的像素格式,其实际每像素占用的字节数均是4,只不过RGB888中多余的1字节不被用到。
png_uint_32 channel = rowbytes/m_nWidth;
if (channel == 4)
{
//设置为带ALPHA通道
m_bHasAlpha = true;
//定义指针变量tmp指向图像数据地址。用于在后面存放图像数据。
unsigned int *tmp = (unsigned int *)m_pData;
//双循环遍历像素数据。
for(unsigned short i = 0; i < m_nHeight; i++)
{
for(unsigned int j = 0; j < rowbytes; j += 4)
{
//将R,G,B,A四个BYTE值组成一个DWORD值。
*tmp++ = CC_RGB_PREMULTIPLY_APLHA( row_pointers[i][j], row_pointers[i][j + 1],
row_pointers[i][j + 2], row_pointers[i][j + 3] );
}
}
//设置使用渐变ALPHA
m_bPreMulti = true;
}
//释放row_pointers
CC_SAFE_FREE(row_pointers);

bRet = true;
} while (0);

if (png_ptr)
{
//释放png_ptr
png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
}
return bRet;
}
//读取TIFF图片数据时的回调函数。
//参1:文件数据内存。
//参2:输出参数,读取到的图像数据复制到对应的内存地址中。
//参3:图片数据长度。
static tmsize_t _tiffReadProc(thandle_t fd, void* buf, tmsize_t size)
{
//将fd转化为图片文件数据的信息结构指针。
tImageSource* isource = (tImageSource*)fd;
uint8* ma;
uint64 mb;
//定义一次可以读取的数据长度。
unsigned long n;
//定义变量o统计每次循环读取的数据长度。
unsigned long o;
//定义变量统计读取完的数据长度。
tmsize_t p;
//让前面定义的uint8类型指针变量ma指向buf。用于在后面存放图像数据。
ma=(uint8*)buf;
//让前面定义的变量mb来统计剩余未读取的数据长度。
mb=size;
p=0;
//使用while循环进行读取,判断条件为剩余未读的数据长度是否大于0。
while (mb>0)
{
n=0x80000000UL;
if ((uint64)n>mb)
n=(unsigned long)mb;

//如果尚未读完所有数据,则继续读取,否则出错返回0
if((int)(isource->offset + n) <= isource->size)
{
memcpy(ma, isource->data+isource->offset, n);
isource->offset += n;
o = n;
}
else
{
return 0;
}
//读取完长度为o的数据,则对指针进行相应的偏移操作供下次进行读取操作。
ma+=o;
//更新未读取的剩余长度
mb-=o;
//更新读取完的数量长度
p+=o;
//下面这个if比较奇怪,因为是不可能为true的。在上一个if判断中已经设置了o=n。
if (o!=n)
{
break;
}
}
return p;
}
//将数据保存为tiff图像文件所调用的回调函数。这里未用。
static tmsize_t _tiffWriteProc(thandle_t fd, void* buf, tmsize_t size)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(buf);
CC_UNUSED_PARAM(size);
return 0;
}

//在对TIFF图像文件进行解析时进行重定位时调用的回调函数。
static uint64 _tiffSeekProc(thandle_t fd, uint64 off, int whence)
{
//将fd转化为图片文件数据的信息结构指针。
tImageSource* isource = (tImageSource*)fd;
uint64 ret = -1;
do
{
//如果定位方式为从头开始计算
if (whence == SEEK_SET)
{
CC_BREAK_IF(off > isource->size-1);
ret = isource->offset = (uint32)off;
}
else if (whence == SEEK_CUR)
{ //如果定位方式为从当前位置开始计算
CC_BREAK_IF(isource->offset + off > isource->size-1);
ret = isource->offset += (uint32)off;
}
else if (whence == SEEK_END)
{ //如果定位方工业从文件尾部开始计算
CC_BREAK_IF(off > isource->size-1);
ret = isource->offset = (uint32)(isource->size-1 - off);
}
else
{//其它方式也按照从头开始计算
CC_BREAK_IF(off > isource->size-1);
ret = isource->offset = (uint32)off;
}
} while (0);

return ret;
}
//取得tiff图片文件大小的回调函数。
static uint64 _tiffSizeProc(thandle_t fd)
{
tImageSource* pImageSrc = (tImageSource*)fd;
return pImageSrc->size;
}
//关闭tiff图片文件读取的回调函数。
static int _tiffCloseProc(thandle_t fd)
{
CC_UNUSED_PARAM(fd);
return 0;
}
//将tiff图片文件映射到内存时调用的回调函数。
static int _tiffMapProc(thandle_t fd, void** pbase, toff_t* psize)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(pbase);
CC_UNUSED_PARAM(psize);
return 0;
}
//解除tiff图片映射到内存的回调函数。
static void _tiffUnmapProc(thandle_t fd, void* base, toff_t size)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(base);
CC_UNUSED_PARAM(size);
}
//使用LibTiff读取TIFF格式的图片数据。
bool CCImage::_initWithTiffData(void* pData, int nDataLen)
{
bool bRet = false;
do
{
//设置图片文件数据的信息结构
tImageSource imageSource;
imageSource.data = (unsigned char*)pData;
imageSource.size = nDataLen;
imageSource.offset = 0;
//使用libtiff打开一个tif文件,设置对其进行操作的各行为的回调函数。如果成功打开文件返回一个TIFF结构指针。
TIFF* tif = TIFFClientOpen("file.tif", "r", (thandle_t)&imageSource,
_tiffReadProc, _tiffWriteProc,
_tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
_tiffMapProc,
_tiffUnmapProc);
//有效性判断。
CC_BREAK_IF(NULL == tif);

uint32 w = 0, h = 0;
uint16 bitsPerSample = 0, samplePerPixel = 0, planarConfig = 0;
//定义变量nPixels存储图像数据像素数量。
size_t npixels = 0;
//读取相应的图片属性信息。
//图片宽度。
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
//图片高度。
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
//图片色深。
TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
//每像素数据占的字节数
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplePerPixel);
//图像的平面配置
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
//取得像素数量
npixels = w * h;
//设置带ALPHA通道。
m_bHasAlpha = true;
m_nWidth = w;
m_nHeight = h;
m_nBitsPerComponent = 8;
//申请相应的内存用来存储像素数据。
m_pData = new unsigned char[npixels * sizeof (uint32)];
//申请临时内存进行TIFF数据读取。
uint32* raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
if (raster != NULL)
{
//读取TIFF数据
if (TIFFReadRGBAImageOriented(tif, w, h, raster, ORIENTATION_TOPLEFT, 0))
{
//下面是从TIFF数据中解析生成我们Cocos2d-x所用的图像数据。
//这里定义指针变量指向TIFF数据
unsigned char* src = (unsigned char*)raster;
//这里定义指针变量指向我们Cocos2d-x所用的图像数据
unsigned int* tmp = (unsigned int*)m_pData;

/*
//遍历每像素进行,对像素的RGBA值进行组合,将组合成的DWORD值写入到图像数据中。
for(int j = 0; j < m_nWidth * m_nHeight * 4; j += 4)
{
*tmp++ = CC_RGB_PREMULTIPLY_APLHA( src[j], src[j + 1],
src[j + 2], src[j + 3] );
}
*/
//ALPHA通道有效。
m_bPreMulti = true;
//上面循环将组合后的DWORD值写入图像数据太慢,这里直接进行内存拷贝可以达到同样目的。
memcpy(m_pData, raster, npixels*sizeof (uint32));
}
//释放临时申请的内存。
_TIFFfree(raster);
}


//关闭TIFF文件读取。
TIFFClose(tif);

bRet = true;
} while (0);
return bRet;
}
//读取RGBA8888格式的图片数据。
//参1:数据地址
//参2:数据长度
//参3:图片宽度
//参4:图片高度
//参5:图片色深
bool CCImage::_initWithRawData(void * pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent)
{
bool bRet = false;
do
{
//宽高有效性判断
CC_BREAK_IF(0 == nWidth || 0 == nHeight);
//保存相关属性
m_nBitsPerComponent = nBitsPerComponent;
m_nHeight = (short)nHeight;
m_nWidth = (short)nWidth;
m_bHasAlpha = true;

// 只支持 RGBA8888 格式
int nBytesPerComponent = 4;
int nSize = nHeight * nWidth * nBytesPerComponent;
//为图像数据申请相应内存,将地址返回给m_pData。
m_pData = new unsigned char[nSize];
//内存申请成败判断
CC_BREAK_IF(! m_pData);
//将参数数据拷贝到m_pData指向的内存地址中。
memcpy(m_pData, pData, nSize);

bRet = true;
} while (0);
return bRet;
}
//将图像数据保存为图片文件,目前只支持PNG和JPG
//参1:文件路径
//参2:是否是RGB的像素格式
bool CCImage::saveToFile(const char *pszFilePath, bool bIsToRGB)
{
bool bRet = false;

do
{
//参数有效性判断
CC_BREAK_IF(NULL == pszFilePath);
//通过是否有扩展名判断参数有效性。
std::string strFilePath(pszFilePath);
CC_BREAK_IF(strFilePath.size() <= 4);
//将路径名转为小写字符串
std::string strLowerCasePath(strFilePath);
for (unsigned int i = 0; i < strLowerCasePath.length(); ++i)
{
strLowerCasePath[i] = tolower(strFilePath[i]);
}
//通过扩展名转成相应的图片文件
//PNG
if (std::string::npos != strLowerCasePath.find(".png"))
{
CC_BREAK_IF(!_saveImageToPNG(pszFilePath, bIsToRGB));
}
//JPG
else if (std::string::npos != strLowerCasePath.find(".jpg"))
{
CC_BREAK_IF(!_saveImageToJPG(pszFilePath));
}
else
{
//不支持其它格式
break;
}

bRet = true;
} while (0);

return bRet;
}
//将图像数据保存为PNG图片
bool CCImage::_saveImageToPNG(const char * pszFilePath, bool bIsToRGB)
{
bool bRet = false;
do
{
//参数有效性判断
CC_BREAK_IF(NULL == pszFilePath);
//使用libpng来写PNG文件。
//定义文件指针变量用于写文件
FILE *fp;
//定义libpng所用的一些信息结构
png_structp png_ptr;
png_infop info_ptr;
png_colorp palette;
png_bytep *row_pointers;
//打开文件开始写入
fp = fopen(pszFilePath, "wb");
CC_BREAK_IF(NULL == fp);
//创建写PNG的文件结构体,将其结构指针返回给png_ptr
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
//指针有效性判断
if (NULL == png_ptr)
{
fclose(fp);
break;
}
//创建PNG的信息结构体,将其结构指针返回给info_ptr。
info_ptr = png_create_info_struct(png_ptr);
if (NULL == info_ptr)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, NULL);
break;
}
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
#endif
//初始化png_ptr
png_init_io(png_ptr, fp);
//根据是否有ALPHA来写入相应的头信息
if (!bIsToRGB && m_bHasAlpha)
{
png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
}
else
{
png_set_IHDR(png_ptr, info_ptr, m_nWidth, m_nHeight, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
}
//创建调色板
palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color));
//设置调色板
png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
//写入info_ptr
png_write_info(png_ptr, info_ptr);
//
png_set_packing(png_ptr);
//申请临时存储m_pData中每一行像素数据地址的内存空间,将申请到的内存地址返回给row_pointers。
row_pointers = (png_bytep *)malloc(m_nHeight * sizeof(png_bytep));
if(row_pointers == NULL)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
//根据是否有ALPHA分别处理写入像素数据到文件中。
if (!m_bHasAlpha)
{
//如果没有ALPHA,只是RGB,这里将m_pData中数据,遍历每一行,将每一行的起始内存地址放入row_pointers指针数组中。
for (int i = 0; i < (int)m_nHeight; i++)
{
row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 3;
}
//将row_pointers中指向的每一行数据写入文件。
png_write_image(png_ptr, row_pointers);

//释放内存
free(row_pointers);
row_pointers = NULL;
}
else
{
//如果带ALPHA通道。对是否是RGB格式又进行分别处理。
//如果是RGB888格式
if (bIsToRGB)
{
//创建临时的内存存放像素数据。每个像素3字节,分别存R,G,B值。
unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3];
if (NULL == pTempData)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
//双循环遍历每个像素,将R,G,B值保存到数组中。
for (int i = 0; i < m_nHeight; ++i)
{
for (int j = 0; j < m_nWidth; ++j)
{
pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];
pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];
pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];
}
}
//将数组中保存的每行像素的内存地址存入row_pointers数组中。
for (int i = 0; i < (int)m_nHeight; i++)
{
row_pointers[i] = (png_bytep)pTempData + i * m_nWidth * 3;
}
//将row_pointers中指向的每一行数据写入文件。
png_write_image(png_ptr, row_pointers);
//释放内存
free(row_pointers);
row_pointers = NULL;

CC_SAFE_DELETE_ARRAY(pTempData);
}
else
{
//如果是RGBA8888格式
//将数组中保存的每行像素的内存地址存入row_pointers数组中。
for (int i = 0; i < (int)m_nHeight; i++)
{
row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * 4;
}
//将row_pointers中指向的每一行数据写入文件。
png_write_image(png_ptr, row_pointers);
//释放内存

free(row_pointers);
row_pointers = NULL;
}
}
//结束写PNG文件
png_write_end(png_ptr, info_ptr);
//释放相应的信息结构
png_free(png_ptr, palette);
palette = NULL;
png_destroy_write_struct(&png_ptr, &info_ptr);

fclose(fp);

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

//将图像数据保存为JPG文件
bool CCImage::_saveImageToJPG(const char * pszFilePath)
{
bool bRet = false;
do
{
//参数有效性判断
CC_BREAK_IF(NULL == pszFilePath);
//使用libjpg库要用到的相关结构。
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile; /* target file */
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
//初始化相关结构
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
//开始写入文件
CC_BREAK_IF((outfile = fopen(pszFilePath, "wb")) == NULL);
//写入JPG头文件基本信息
jpeg_stdio_dest(&cinfo, outfile);
//填充JPG图像的属性信息结构
cinfo.image_width = m_nWidth;
cinfo.image_height = m_nHeight;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
//将信息结构来设置JPG图像
jpeg_set_defaults(&cinfo);
//开始进行数据压缩输出
jpeg_start_compress(&cinfo, TRUE);
//设置每行的字节长度
row_stride = m_nWidth * 3;
//跟据图像数据是否有ALPHA通道来进行分别处理
if (m_bHasAlpha)
{
//创建内存来存放图像的像素数据。
unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * 3];
if (NULL == pTempData)
{
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(outfile);
break;
}
//双循环遍历每一个图像像素的数据,取R,G,B值存放到临时申请的内存地址中,A值弃之不取。
for (int i = 0; i < m_nHeight; ++i)
{
for (int j = 0; j < m_nWidth; ++j)

{
//因图像数据有A通道,所以找相应的像素地址时会由像素索引x4。
pTempData[(i * m_nWidth + j) * 3] = m_pData[(i * m_nWidth + j) * 4];
pTempData[(i * m_nWidth + j) * 3 + 1] = m_pData[(i * m_nWidth + j) * 4 + 1];
pTempData[(i * m_nWidth + j) * 3 + 2] = m_pData[(i * m_nWidth + j) * 4 + 2];
}
}
//将扫描行的数据写入JPG文件
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = & pTempData[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}

CC_SAFE_DELETE_ARRAY(pTempData);
}
else
{ //将扫描行的数据写入JPG文件
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = & m_pData[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
}
//结束数据压缩,关闭文件并释放相应信息结构。
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);

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

NS_CC_END

现在我们回到CCImage.cpp:

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

NS_CC_BEGIN

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

bRet = true;
} while (0);

return bRet;
}

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

例如:

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

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

(2)然后定义函数

bool _initWithPicData(void *pData, int nDatalen);

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

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

[cpp] view plain copy
else if (kFmtPic == eFmt)

//读取自定义的图片数据。
bRet = _initWithPicData(pData, nDataLen);
break;

猜你喜欢

转载自blog.csdn.net/sun___shine/article/details/59563551