#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 = , //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 = ,
int nHeight = ,
int nBitsPerComponent = );
//从字符串创建图片数据。
//参1:字符串
//参2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。
//参3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。
//参4:文字的对齐方式。
//参5:字体名称
//参6:字体大小
bool initWithString(
const char * pText,
int nWidth = ,
int nHeight = ,
ETextAlign eAlignMask = kAlignCenter,
const char * pFontName = ,
int nSize = );
//取得图像数据地址
unsigned char * getData() { return m_pData; }
//取得图像数据的长度
int getDataLen() { return m_nWidth * m_nHeight; }
//是否有Alpha通道。
bool hasAlpha() { return m_bHasAlpha; }
//是否有Alpha渐变
bool isPremultipliedAlpha() { return m_bPreMulti; }
//将当前图片数据保存成指定的文件格式。
//参1:绝对路径名
//参2:是否保存成RGB格式
bool saveToFile(const char *pszFilePath, bool bIsToRGB = true);
//定义变量m_nWidth和get接口
CC_SYNTHESIZE_READONLY(unsigned short, m_nWidth, Width);
//定义变量m_nHeight和get接口
CC_SYNTHESIZE_READONLY(unsigned short, m_nHeight, Height);
//定义变量m_nBitsPerComponent和get接口
CC_SYNTHESIZE_READONLY(int, m_nBitsPerComponent, BitsPerComponent);
protected:
//读取JPG图片数据
//参1:数据地址
//参2:数据长度
bool _initWithJpgData(void *pData, int nDatalen);
//读取PNG图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中
bool _initWithPngData(void *pData, int nDatalen);
//读取TIFF图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中
bool _initWithTiffData(void* pData, int nDataLen);
//读取RGBA8888格式的图片数据。
//参1:数据地址
//参2:数据长度
//参3:图片宽度
//参4:图片高度
//参5:图片色深
bool _initWithRawData(void *pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent);
//将图像数据保存为PNG图片
bool _saveImageToPNG(const char *pszFilePath, bool bIsToRGB = true);
//将图像数据保存为JPG图片
bool _saveImageToJPG(const char *pszFilePath);
//图像数据地址
unsigned char *m_pData;
//是否有Alpha
bool m_bHasAlpha;
//是否有Alpha渐变 bool m_bPreMulti;
private:
// 拷贝构造与重载等号拷贝
CCImage(const CCImage& rImg);
CCImage & operator=(const CCImage&);
};
NS_CC_END
#endif // __CC_IMAGE_H__
#ifndef __CC_PLATFORM_IMAGE_CPP__
//如果没有定义基于平台的CCImage的CPP标记宏,编译时打印出错。
#error "CCFileUtilsCommon_cpp.h can only be included for CCFileUtils.cpp in platform/win32(android,...)"
#endif /* __CC_PLATFORM_IMAGE_CPP__ */
#include "CCImage.h"
#include "CCCommon.h"
#include "CCStdC.h"
#include "CCFileUtils.h"
//libpng库的头文件
#include "png.h"
//libjpg库的头文件
#include "jpeglib.h"
//libtiff库的头文件
#include "tiffio.h"
#include <string>
#include <ctype.h>
//使用Cocos2d命名空间
NS_CC_BEGIN
//定义宏从RGB888或RGB5A1像素格式数据中返回一个RGBA8888的像素格式数据。
#define CC_RGB_PREMULTIPLY_APLHA(vr, vg, vb, va) \
(unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + )) >> ) | \
((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + ) >> ) << ) | \
((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + ) >> ) << ) | \
((unsigned)(unsigned char)(va) << ))
//图片文件数据的信息结构
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()
, m_nHeight()
, m_nBitsPerComponent()
, m_pData()
, 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 = ;
//调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(strPath), "rb", &nSize);
if (pBuffer != NULL && nSize > )
{
//如果读取成功,则将内存地址做为参数调用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 = ;
//调用文件操作函数库中的函数读取相应路径的文件到内存中,并返回内存的地址给指针变量pBuffer。
unsigned char *pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath, "rb", &nSize);
if (pBuffer != NULL && nSize > )
{
//如果读取成功,则将内存地址做为参数调用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 <= );
//根据不同的图片数据格式调用不同的函数创建相应的图片数据。
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 > )
{
unsigned char* pHead = (unsigned char*)pData;
if ( pHead[] == 0x89
&& pHead[] == 0x50
&& pHead[] == 0x4E
&& pHead[] == 0x47
&& pHead[] == 0x0D
&& pHead[] == 0x0A
&& pHead[] == 0x1A
&& pHead[] == 0x0A)
{
//通过对比如果是属于PNG格式则读取PNG格式的图片数据
bRet = _initWithPngData(pData, nDataLen);
break;
}
}
//判断是否是TIFF
if (nDataLen > )
{
unsigned char* pHead = (unsigned char*)pData;
if ( (pHead[] == 0x49 && pHead[] == 0x49)
|| (pHead[] == 0x4d && pHead[] == 0x4d)
)
{ //通过对比如果是属于TIFF格式则读取TIFF格式的图片数据
bRet = _initWithTiffData(pData, nDataLen);
break;
}
}
//判断是否是JPG
if (nDataLen > )
{
unsigned char* pHead = (unsigned char*)pData;
if ( pHead[] == 0xff
&& pHead[] == 0xd8)
{
bRet = _initWithJpgData(pData, nDataLen);
break;
}
}
}
} while ();
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[] = {};
unsigned long location = ;
unsigned int i = ;
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 = ;
row_pointer[] = new unsigned char[cinfo.output_width*cinfo.output_components];
CC_BREAK_IF(! row_pointer[]);
//为图片数据指针申请相应大小的内存。
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, );
for( i=; i<cinfo.image_width*cinfo.output_components;i++)
{
//将读取到的像素信息存入相应的内存中。
m_pData[location++] = row_pointer[][i];
}
}
//完成解压
jpeg_finish_decompress( &cinfo );
//释放所用的结构
jpeg_destroy_decompress( &cinfo );
bRet = true;
} while ();
//释放申请的内存
CC_SAFE_DELETE_ARRAY(row_pointer[]);
return bRet;
}
//读取PNG格式的图片数据。
bool CCImage::_initWithPngData(void * pData, int nDatalen)
{
// PNG文件头信息长度值
#define PNGSIGSIZE 8
bool bRet = false;
//定义存储PNG文件头信息的BYTE数组
png_byte header[PNGSIGSIZE] = {};
//PNG的读取说明结构,这里是libpng用到的结构体。
png_structp png_ptr = ;
//PNG的信息结构
png_infop info_ptr = ;
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, , , );
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 = ;
//设置读取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 < )
{
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 == )
{
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 = ;
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 = ; 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 == )
{
//设置为带ALPHA通道
m_bHasAlpha = true;
//定义指针变量tmp指向图像数据地址。用于在后面存放图像数据。
unsigned int *tmp = (unsigned int *)m_pData;
//双循环遍历像素数据。
for(unsigned short i = ; i < m_nHeight; i++)
{
for(unsigned int j = ; j < rowbytes; j += )
{
//将R,G,B,A四个BYTE值组成一个DWORD值。
*tmp++ = CC_RGB_PREMULTIPLY_APLHA( row_pointers[i][j], row_pointers[i][j + ],
row_pointers[i][j + ], row_pointers[i][j + ] );
}
}
//设置使用渐变ALPHA
m_bPreMulti = true;
}
//释放row_pointers
CC_SAFE_FREE(row_pointers);
bRet = true;
} while ();
if (png_ptr)
{
//释放png_ptr
png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : , );
}
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=;
//使用while循环进行读取,判断条件为剩余未读的数据长度是否大于0。
while (mb>)
{
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 ;
}
//读取完长度为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 ;
}
//在对TIFF图像文件进行解析时进行重定位时调用的回调函数。
static uint64 _tiffSeekProc(thandle_t fd, uint64 off, int whence)
{
//将fd转化为图片文件数据的信息结构指针。
tImageSource* isource = (tImageSource*)fd;
uint64 ret = -;
do
{
//如果定位方式为从头开始计算
if (whence == SEEK_SET)
{
CC_BREAK_IF(off > isource->size-);
ret = isource->offset = (uint32)off;
}
else if (whence == SEEK_CUR)
{ //如果定位方式为从当前位置开始计算
CC_BREAK_IF(isource->offset + off > isource->size-);
ret = isource->offset += (uint32)off;
}
else if (whence == SEEK_END)
{ //如果定位方工业从文件尾部开始计算
CC_BREAK_IF(off > isource->size-);
ret = isource->offset = (uint32)(isource->size- - off);
}
else
{//其它方式也按照从头开始计算
CC_BREAK_IF(off > isource->size-);
ret = isource->offset = (uint32)off;
}
} while ();
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 ;
}
//将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 ;
}
//解除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 = ;
//使用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 = , h = ;
uint16 bitsPerSample = , samplePerPixel = , planarConfig = ;
//定义变量nPixels存储图像数据像素数量。
size_t npixels = ;
//读取相应的图片属性信息。
//图片宽度。
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 = ;
//申请相应的内存用来存储像素数据。
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, ))
{
//下面是从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 ();
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( == nWidth || == nHeight);
//保存相关属性
m_nBitsPerComponent = nBitsPerComponent;
m_nHeight = (short)nHeight;
m_nWidth = (short)nWidth;
m_bHasAlpha = true;
// 只支持 RGBA8888 格式
int nBytesPerComponent = ;
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 ();
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() <= );
//将路径名转为小写字符串
std::string strLowerCasePath(strFilePath);
for (unsigned int i = ; 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 ();
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, , 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, , 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 = ; i < (int)m_nHeight; i++)
{
row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * ;
}
//将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 * ];
if (NULL == pTempData)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
//双循环遍历每个像素,将R,G,B值保存到数组中。
for (int i = ; i < m_nHeight; ++i)
{
for (int j = ; j < m_nWidth; ++j)
{
pTempData[(i * m_nWidth + j) * ] = m_pData[(i * m_nWidth + j) * ];
pTempData[(i * m_nWidth + j) * + ] = m_pData[(i * m_nWidth + j) * + ];
pTempData[(i * m_nWidth + j) * + ] = m_pData[(i * m_nWidth + j) * + ];
}
}
//将数组中保存的每行像素的内存地址存入row_pointers数组中。
for (int i = ; i < (int)m_nHeight; i++)
{
row_pointers[i] = (png_bytep)pTempData + i * m_nWidth * ;
}
//将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 = ; i < (int)m_nHeight; i++)
{
row_pointers[i] = (png_bytep)m_pData + i * m_nWidth * ;
}
//将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 ();
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[]; /* 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 = ;
cinfo.in_color_space = JCS_RGB;
//将信息结构来设置JPG图像
jpeg_set_defaults(&cinfo);
//开始进行数据压缩输出
jpeg_start_compress(&cinfo, TRUE);
//设置每行的字节长度
row_stride = m_nWidth * ;
//跟据图像数据是否有ALPHA通道来进行分别处理
if (m_bHasAlpha)
{
//创建内存来存放图像的像素数据。
unsigned char *pTempData = new unsigned char[m_nWidth * m_nHeight * ];
if (NULL == pTempData)
{
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(outfile);
break;
}
//双循环遍历每一个图像像素的数据,取R,G,B值存放到临时申请的内存地址中,A值弃之不取。
for (int i = ; i < m_nHeight; ++i)
{
for (int j = ; j < m_nWidth; ++j)
{
//因图像数据有A通道,所以找相应的像素地址时会由像素索引x4。
pTempData[(i * m_nWidth + j) * ] = m_pData[(i * m_nWidth + j) * ];
pTempData[(i * m_nWidth + j) * + ] = m_pData[(i * m_nWidth + j) * + ];
pTempData[(i * m_nWidth + j) * + ] = m_pData[(i * m_nWidth + j) * + ];
}
}
//将扫描行的数据写入JPG文件
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[] = & pTempData[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, );
}
CC_SAFE_DELETE_ARRAY(pTempData);
}
else
{ //将扫描行的数据写入JPG文件
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[] = & m_pData[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, );
}
}
//结束数据压缩,关闭文件并释放相应信息结构。
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
bRet = true;
} while ();
return bRet;
}
NS_CC_END