您的位置:首页 > 移动开发 > Cocos引擎

cocos2d-x节点(CCImageCommon_cpp.h)API

2013-12-01 10:36 597 查看
本文来自http://blog.csdn.net/runaying ,引用必须注明出处!


cocos2d-x节点(CCImageCommon_cpp.h)API

温馨提醒:为了大家能更好学习,强烈推荐大家看看本人的这篇博客 Cocos2d-X权威指南笔记

初始化各种类型的图片,储存图片

///cocos2d-x-3.0alpha0/cocos2dx/platform
//初始化各种类型的图片,储存图片

#include "CCImage.h"

#include <string>
#include <ctype.h>

#ifdef EMSCRIPTEN
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#endif // EMSCRIPTEN

extern "C"
{
#include "png.h"
#include "tiffio.h"
#include "etc1.h"
#include "jpeglib.h"
}
#include "third_party/common/s3tc/s3tc.h"
#include "third_party/common/atitc/atitc.h"
#if defined(__native_client__) || defined(EMSCRIPTEN)
// TODO(sbc): 我敢肯定,所有平台应该以这种方式包括 webph headers
#include "webp/decode.h"
#else
#include "decode.h"
#endif

#include "ccMacros.h"
#include "platform/CCCommon.h"
#include "CCStdC.h"
#include "CCFileUtils.h"
#include "CCConfiguration.h"
#include "support/ccUtils.h"
#include "support/zip_support/ZipUtils.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/CCFileUtilsAndroid.h"
#endif

#define CC_GL_ATC_RGB_AMD                                          0x8C92
#define CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD                          0x8C93
#define CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD                      0x87EE

NS_CC_BEGIN

//////////////////////////////////////////////////////////////////////////
// pvr 结构的 struct 、 data

namespace
{
static const int PVR_TEXTURE_FLAG_TYPE_MASK = 0xff;

// Values taken from PVRTexture.h from http://www.imgtec.com enum class PVR2TextureFlag
{
Mipmap         = (1<<8),        // has mip map levels                       //具有mipmap级别
Twiddle        = (1<<9),        // is twiddled                              //旋弄
Bumpmap        = (1<<10),       // has normals encoded for a bump map       //使用法线进行编码的凹凸贴图
Tiling         = (1<<11),       // is bordered for tiled pvr                //与 tiled pvr 接壤
Cubemap        = (1<<12),       // is a cubemap/skybox                      //立方体贴图/天空盒
FalseMipCol    = (1<<13),       // are there false colored MIP levels       //彩色MIP级别是 false
Volume         = (1<<14),       // is this a volume texture                 //这是一个体积 texture
Alpha          = (1<<15),       // v2.1 is there transparency info in the texture //texture 的透明度
VerticalFlip   = (1<<16),       // v2.1 is the texture vertically flipped   //垂直翻转 texture
};

enum class PVR3TextureFlag
{
PremultipliedAlpha	= (1<<1)	// has premultiplied alpha                  //有预乘alpha
};

static const char gPVRTexIdentifier[5] = "PVR!";

// v2
enum class PVR2TexturePixelFormat : unsigned char
{
RGBA4444 = 0x10,
RGBA5551,
RGBA8888,
RGB565,
RGB555,          // unsupported
RGB888,
I8,
AI88,
PVRTC2BPP_RGBA,
PVRTC4BPP_RGBA,
BGRA8888,
A8,
};

// v3
enum class PVR3TexturePixelFormat : uint64_t
{
PVRTC2BPP_RGB  = 0ULL,
PVRTC2BPP_RGBA = 1ULL,
PVRTC4BPP_RGB  = 2ULL,
PVRTC4BPP_RGBA = 3ULL,

BGRA8888       = 0x0808080861726762ULL,
RGBA8888       = 0x0808080861626772ULL,
RGBA4444       = 0x0404040461626772ULL,
RGBA5551       = 0x0105050561626772ULL,
RGB565         = 0x0005060500626772ULL,
RGB888         = 0x0008080800626772ULL,
A8             = 0x0000000800000061ULL,
L8             = 0x000000080000006cULL,
LA88           = 0x000008080000616cULL,
};

// v2
typedef const std::map<PVR2TexturePixelFormat, Texture2D::PixelFormat> _pixel2_formathash;

static const _pixel2_formathash::value_type v2_pixel_formathash_value[] =
{
_pixel2_formathash::value_type(PVR2TexturePixelFormat::BGRA8888,	    Texture2D::PixelFormat::BGRA8888),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::RGBA8888,	    Texture2D::PixelFormat::RGBA8888),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::RGBA4444,	    Texture2D::PixelFormat::RGBA4444),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::RGBA5551,	    Texture2D::PixelFormat::RGB5A1),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::RGB565,	    Texture2D::PixelFormat::RGB565),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::RGB888,	    Texture2D::PixelFormat::RGB888),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::A8,	        Texture2D::PixelFormat::A8),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::I8,	        Texture2D::PixelFormat::I8),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::AI88,	        Texture2D::PixelFormat::AI88),

#ifdef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
_pixel2_formathash::value_type(PVR2TexturePixelFormat::PVRTC2BPP_RGBA,	    Texture2D::PixelFormat::PVRTC2A),
_pixel2_formathash::value_type(PVR2TexturePixelFormat::PVRTC4BPP_RGBA,	    Texture2D::PixelFormat::PVRTC4A),
#endif
};

static const int PVR2_MAX_TABLE_ELEMENTS = sizeof(v2_pixel_formathash_value) / sizeof(v2_pixel_formathash_value[0]);
static const _pixel2_formathash v2_pixel_formathash(v2_pixel_formathash_value, v2_pixel_formathash_value + PVR2_MAX_TABLE_ELEMENTS);

// v3
typedef const std::map<PVR3TexturePixelFormat, Texture2D::PixelFormat> _pixel3_formathash;
static _pixel3_formathash::value_type v3_pixel_formathash_value[] =
{
_pixel3_formathash::value_type(PVR3TexturePixelFormat::BGRA8888,	Texture2D::PixelFormat::BGRA8888),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::RGBA8888,	Texture2D::PixelFormat::RGBA8888),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::RGBA4444,	Texture2D::PixelFormat::RGBA4444),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::RGBA5551,	Texture2D::PixelFormat::RGB5A1),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::RGB565,	    Texture2D::PixelFormat::RGB565),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::RGB888,	    Texture2D::PixelFormat::RGB888),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::A8,	        Texture2D::PixelFormat::A8),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::L8,	        Texture2D::PixelFormat::I8),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::LA88,	    Texture2D::PixelFormat::AI88),

#ifdef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
_pixel3_formathash::value_type(PVR3TexturePixelFormat::PVRTC2BPP_RGB,	    Texture2D::PixelFormat::PVRTC2),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::PVRTC2BPP_RGBA,	    Texture2D::PixelFormat::PVRTC2A),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::PVRTC4BPP_RGB,	    Texture2D::PixelFormat::PVRTC4),
_pixel3_formathash::value_type(PVR3TexturePixelFormat::PVRTC4BPP_RGBA,	    Texture2D::PixelFormat::PVRTC4A),
#endif
};

static const int PVR3_MAX_TABLE_ELEMENTS = sizeof(v3_pixel_formathash_value) / sizeof(v3_pixel_formathash_value[0]);

static const _pixel3_formathash v3_pixel_formathash(v3_pixel_formathash_value, v3_pixel_formathash_value + PVR3_MAX_TABLE_ELEMENTS);

typedef struct _PVRTexHeader
{
unsigned int headerLength;
unsigned int height;
unsigned int width;
unsigned int numMipmaps;
unsigned int flags;
unsigned int dataLength;
unsigned int bpp;
unsigned int bitmaskRed;
unsigned int bitmaskGreen;
unsigned int bitmaskBlue;
unsigned int bitmaskAlpha;
unsigned int pvrTag;
unsigned int numSurfs;
} PVRv2TexHeader;

#ifdef _MSC_VER
#pragma pack(push,1)
#endif
typedef struct
{
uint32_t version;
uint32_t flags;
uint64_t pixelFormat;
uint32_t colorSpace;
uint32_t channelType;
uint32_t height;
uint32_t width;
uint32_t depth;
uint32_t numberOfSurfaces;
uint32_t numberOfFaces;
uint32_t numberOfMipmaps;
uint32_t metadataLength;
#ifdef _MSC_VER
} PVRv3TexHeader;
#pragma pack(pop)
#else
} __attribute__((packed)) PVRv3TexHeader;
#endif
}
//pvr structure end

//////////////////////////////////////////////////////////////////////////

// s3tc(dds) 结构的 struct 、 data
namespace
{
struct DDColorKey
{
uint32_t colorSpaceLowValue;
uint32_t colorSpaceHighValue;
};

struct DDSCaps
{
uint32_t caps;
uint32_t caps2;
uint32_t caps3;
uint32_t caps4;
};

struct DDPixelFormat
{
uint32_t size;
uint32_t flags;
uint32_t fourCC;
uint32_t RGBBitCount;
uint32_t RBitMask;
uint32_t GBitMask;
uint32_t BBitMask;
uint32_t ABitMask;
};

struct DDSURFACEDESC2
{
uint32_t size;
uint32_t flags;
uint32_t height;
uint32_t width;

union
{
uint32_t pitch;
uint32_t linearSize;
} DUMMYUNIONNAMEN1;

union
{
uint32_t backBufferCount;
uint32_t depth;
} DUMMYUNIONNAMEN5;

union
{
uint32_t mipMapCount;
uint32_t refreshRate;
uint32_t srcVBHandle;
} DUMMYUNIONNAMEN2;

uint32_t alphaBitDepth;
uint32_t reserved;
uint32_t surface;

union
{
DDColorKey ddckCKDestOverlay;
uint32_t emptyFaceColor;
} DUMMYUNIONNAMEN3;

DDColorKey ddckCKDestBlt;
DDColorKey ddckCKSrcOverlay;
DDColorKey ddckCKSrcBlt;

union
{
DDPixelFormat ddpfPixelFormat;
uint32_t FVF;
} DUMMYUNIONNAMEN4;

DDSCaps ddsCaps;
uint32_t textureStage;
} ;

#pragma pack(push,1)

struct S3TCTexHeader
{
char fileCode[4];
DDSURFACEDESC2 ddsd;
};

#pragma pack(pop)

}
//s3tc struct end

//////////////////////////////////////////////////////////////////////////

// atitc(ktx) 结构的 struct 、 data
namespace
{
struct ATITCTexHeader
{
//HEADER
char identifier[12];
uint32_t endianness;
uint32_t glType;
uint32_t glTypeSize;
uint32_t glFormat;
uint32_t glInternalFormat;
uint32_t glBaseInternalFormat;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
uint32_t numberOfArrayElements;
uint32_t numberOfFaces;
uint32_t numberOfMipmapLevels;
uint32_t bytesOfKeyValueData;
};
}
//atittc struct end

//////////////////////////////////////////////////////////////////////////

namespace
{
typedef struct
{
const unsigned char * data;
int size;
int offset;
}tImageSource;

static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
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");
}
}
}

//////////////////////////////////////////////////////////////////////////
// Implement Image                                  //实现图像
//////////////////////////////////////////////////////////////////////////

Image::Image()
: _data(0)
, _dataLen(0)
, _width(0)
, _height(0)
, _fileType(Format::UNKOWN)
, _renderFormat(Texture2D::PixelFormat::NONE)
, _preMulti(false)
, _hasPremultipliedAlpha(true)
, _numberOfMipmaps(0)
{

}

Image::~Image()
{
CC_SAFE_DELETE_ARRAY(_data);
}

bool Image::initWithImageFile(const char * strPath)
{
bool bRet = false;
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(strPath);

#ifdef EMSCRIPTEN
// Emscripten 包含 SDL的 re-implementation 它使用 HTML5 画布操作
// 因此,加载图像通过 IMG_Load (an SDL
// API) 速度超过了
SDL_Surface *iSurf = IMG_Load(fullPath.c_str());

int size = 4 * (iSurf->w * iSurf->h);
bRet = initWithRawData((void*)iSurf->pixels, size, iSurf->w, iSurf->h, 8, true);

unsigned int *tmp = (unsigned int *)_data;
int nrPixels = iSurf->w * iSurf->h;
for(int i = 0; i < nrPixels; i++)
{
unsigned char *p = _data + i * 4;
tmp[i] = CC_RGB_PREMULTIPLY_ALPHA( p[0], p[1], p[2], p[3] );
}

SDL_FreeSurface(iSurf);
#else
unsigned long bufferLen = 0;
unsigned char* buffer = FileUtils::getInstance()->getFileData(fullPath.c_str(), "rb", &bufferLen);

if (buffer != nullptr && bufferLen > 0)
{
bRet = initWithImageData(buffer, bufferLen);
}

CC_SAFE_DELETE_ARRAY(buffer);
#endif // EMSCRIPTEN

return bRet;
}

bool Image::initWithImageFileThreadSafe(const char *fullpath)
{
bool bRet = false;
unsigned long dataLen = 0;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
FileUtilsAndroid *fileUitls = (FileUtilsAndroid*)FileUtils::getInstance();
unsigned char *pBuffer = fileUitls->getFileDataForAsync(fullpath, "rb", &dataLen);
#else
unsigned char *pBuffer = FileUtils::getInstance()->getFileData(fullpath, "rb", &dataLen);
#endif
if (pBuffer != NULL && dataLen > 0)
{
bRet = initWithImageData(pBuffer, dataLen);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
return bRet;
}

bool Image::initWithImageData(const unsigned char * data, int dataLen)
{
bool ret = false;

do
{
CC_BREAK_IF(! data || dataLen <= 0);

unsigned char* unpackedData = nullptr;
int unpackedLen = 0;

//检测和解压压缩文件
if (ZipUtils::ccIsCCZBuffer(data, dataLen))
{
unpackedLen = ZipUtils::ccInflateCCZBuffer(data, dataLen, &unpackedData);
}
else if (ZipUtils::ccIsGZipBuffer(data, dataLen))
{
unpackedLen = ZipUtils::ccInflateMemory(const_cast<unsigned char*>(data), dataLen, &unpackedData);
}
else
{
unpackedData = const_cast<unsigned char*>(data);
unpackedLen = dataLen;
}

_fileType = detectFormat(unpackedData, unpackedLen);

switch (_fileType)
{
case Format::PNG:
ret = initWithPngData(unpackedData, unpackedLen);
break;
case Format::JPG:
ret = initWithJpgData(unpackedData, unpackedLen);
break;
case Format::TIFF:
ret = initWithTiffData(unpackedData, unpackedLen);
break;
case Format::WEBP:
ret = initWithWebpData(unpackedData, unpackedLen);
break;
case Format::PVR:
ret = initWithPVRData(unpackedData, unpackedLen);
break;
case Format::ETC:
ret = initWithETCData(unpackedData, unpackedLen);
break;
case Format::S3TC:
ret = initWithS3TCData(unpackedData, unpackedLen);
break;
case Format::ATITC:
ret = initWithATITCData(unpackedData, unpackedLen);
break;
default:
CCAssert(false, "unsupport image format!");
break;
}

if(unpackedData != data)
{
free(unpackedData);
}
} while (0);

return ret;
}

bool Image::isPng(const unsigned char * data, int dataLen)
{
if (dataLen <= 8)
{
return false;
}

static const unsigned char PNG_SIGNATURE[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};

return memcmp(PNG_SIGNATURE, data, sizeof(PNG_SIGNATURE)) == 0;
}

bool Image::isEtc(const unsigned char * data, int dataLen)
{
return etc1_pkm_is_valid((etc1_byte*)data) ? true : false;
}

bool Image::isS3TC(const unsigned char * data, int dataLen)
{

S3TCTexHeader *header = (S3TCTexHeader *)data;

if (strncmp(header->fileCode, "DDS", 3) != 0)
{
CCLOG("cocos2d: the file is not a dds file!");
return false;
}
return true;
}

bool Image::isATITC(const unsigned char *data, int dataLen)
{
ATITCTexHeader *header = (ATITCTexHeader *)data;

if (strncmp(&header->identifier[1], "KTX", 3) != 0)
{
CCLOG("cocos3d: the file is not a ktx file!");
return false;
}
return true;
}

bool Image::isJpg(const unsigned char * data, int dataLen)
{
if (dataLen <= 4)
{
return false;
}

static const unsigned char JPG_SOI[] = {0xFF, 0xD8};

return memcmp(data, JPG_SOI, 2) == 0;
}

bool Image::isTiff(const unsigned char * data, int dataLen)
{
if (dataLen <= 4)
{
return false;
}

static const char* TIFF_II = "II";
static const char* TIFF_MM = "MM";

return (memcmp(data, TIFF_II, 2) == 0 && *(static_cast<const unsigned char*>(data) + 2) == 42 && *(static_cast<const unsigned char*>(data) + 3) == 0) ||
(memcmp(data, TIFF_MM, 2) == 0 && *(static_cast<const unsigned char*>(data) + 2) == 0 && *(static_cast<const unsigned char*>(data) + 3) == 42);
}

bool Image::isWebp(const unsigned char * data, int dataLen)
{
if (dataLen <= 12)
{
return false;
}

static const char* WEBP_RIFF = "RIFF";
static const char* WEBP_WEBP = "WEBP";

return memcmp(data, WEBP_RIFF, 4) == 0
&& memcmp(static_cast<const unsigned char*>(data) + 8, WEBP_WEBP, 4) == 0;
}

bool Image::isPvr(const unsigned char * data, int dataLen)
{
if (dataLen < sizeof(PVRv2TexHeader) || dataLen < sizeof(PVRv3TexHeader))
{
return false;
}

const PVRv2TexHeader* headerv2 = static_cast<const PVRv2TexHeader*>(static_cast<const void*>(data));
const PVRv3TexHeader* headerv3 = static_cast<const PVRv3TexHeader*>(static_cast<const void*>(data));

return memcmp(&headerv2->pvrTag, gPVRTexIdentifier, strlen(gPVRTexIdentifier)) == 0 || CC_SWAP_INT32_BIG_TO_HOST(headerv3->version) == 0x50565203;
}

Image::Format Image::detectFormat(const unsigned char * data, int dataLen)
{
if (isPng(data, dataLen))
{
return Format::PNG;
}
else if (isJpg(data, dataLen))
{
return Format::JPG;
}
else if (isTiff(data, dataLen))
{
return Format::TIFF;
}
else if (isWebp(data, dataLen))
{
return Format::WEBP;
}
else if (isPvr(data, dataLen))
{
return Format::PVR;
}
else if (isEtc(data, dataLen))
{
return Format::ETC;
}
else if (isS3TC(data, dataLen))
{
return Format::S3TC;
}
else if (isATITC(data, dataLen))
{
return Format::ATITC;
}
else
{
return Format::UNKOWN;
}
}

int Image::getBitPerPixel()
{
return Texture2D::getPixelFormatInfoMap().at(_renderFormat).bpp;
}

bool Image::hasAlpha()
{
return Texture2D::getPixelFormatInfoMap().at(_renderFormat).alpha;
}

bool Image::isCompressed()
{
return Texture2D::getPixelFormatInfoMap().at(_renderFormat).compressed;
}

namespace
{
/*
* ERROR HANDLING:          //错误处理
*
* JPEG库的标准错误处理程序(jerror.c)你可以单独覆盖这几个“方法”。
* 这可以让你不使用大量重复代码来调整行为,你日后可能会对每个版本进行更新
*
* 发生致命错误时,我们覆盖“ERROR_EXIT”的方法,使控制返回给库的调用者
* 而不是调用标准的 exit() 方法,来做这一件事
*
* 我们使用C的setjmp/ longjmp的设施,将控制返回。这意味着调用JPEG库的程序必须首先执行的 setjmp()调用来建立返回点。
* 我们想要替代 ERROR_EXIT 做 longjmp()。但是,我们需要访问 setjmp 缓冲区的 ERROR_EXIT 例程
* 要做到这一点,我们做了一个私人的 JPEG 标准错误处理程序扩展。(如果我们使用 C + +,我们会说,我们制作了一个常规错误处理程序的子类)
*
* 下面是扩展错误处理程序的结构:
*/
struct MyErrorMgr
{
struct jpeg_error_mgr pub;	/* "public" fields */           //字段
jmp_buf setjmp_buffer;	/* for return to caller */          //返回给调用者
};

typedef struct MyErrorMgr * MyErrorPtr;

/*
* 这里的例程,将取代标准的 error_exit 方法:
*/

METHODDEF(void)
myErrorExit(j_common_ptr cinfo)
{
/* cinfo->err 是真正指向 MyErrorMgr 结构的 points, 所以它是一个强制指针 */
MyErrorPtr myerr = (MyErrorPtr) cinfo->err;

/* 始终显示该消息。 */
/* 如果我们选择,我们可以延后直到 returning. */
/* 内部消息函数不能在某些平台上显示错误消息,所以我们在这里把它改写。如果有版本冲突就编辑它。
*/
//(*cinfo->err->output_message) (cinfo);
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, buffer);
CCLOG("jpeg error: %s", buffer);

/* Return control to the setjmp point */    //调用 setjmp 返回控制点
longjmp(myerr->setjmp_buffer, 1);
}
}

bool Image::initWithJpgData(const unsigned char * data, int dataLen)
{
/* 这些都是标准的libjpeg结构用于读取(解压) */
struct jpeg_decompress_struct cinfo;
/* 我们用我们的私有JPEG错误处理程序扩展。
* Note 这个结构的生命周期必须和主JPEG结构参数一致,避免空指针问题
*/
struct MyErrorMgr jerr;
/* libjpeg 数据结构,用于存储一列,也就是,图像的扫描线 */
JSAMPROW row_pointer[1] = {0};
unsigned long location = 0;
unsigned int i = 0;

bool bRet = false;
do
{
/*我们设定的正常JPEG错误例程,然后覆盖 error_exit. */
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = myErrorExit;
/* 为我们的 ErrorExit 建立setjmp 来返回上下文. */
if (setjmp(jerr.setjmp_buffer)) {
/* 如果我们在这里获取 JPEG代码的错误信号
* 我们需要清理的JPEG对象,关闭输入文件,并返回。
*/
jpeg_destroy_decompress(&cinfo);
break;
}

/* 设置要解压的文件,然后读取 JPEG header */
jpeg_create_decompress( &cinfo );

#ifndef CC_TARGET_QT5
jpeg_mem_src( &cinfo, const_cast<unsigned char*>(data), dataLen );
#endif /* CC_TARGET_QT5 */

/* 读取 image header 其中包含的图像信息 */
#if (JPEG_LIB_VERSION >= 90)
// libjpeg 0.9 adds stricter types.     //libjpeg的0.9增加了更严格的类型。
jpeg_read_header( &cinfo, TRUE );
#else
jpeg_read_header( &cinfo, true );
#endif

// we only support RGB or grayscale         //我们只支持RGB或灰度
if (cinfo.jpeg_color_space == JCS_GRAYSCALE)
{
_renderFormat = Texture2D::PixelFormat::I8;
}else
{
cinfo.out_color_space = JCS_RGB;
_renderFormat = Texture2D::PixelFormat::RGB888;
}

/* Start decompression jpeg here */     //开始解压缩JPEG
jpeg_start_decompress( &cinfo );

/* init image info */
_width  = cinfo.output_width;
_height = cinfo.output_height;
_preMulti = false;
row_pointer[0] = new unsigned char[cinfo.output_width*cinfo.output_components];
CC_BREAK_IF(! row_pointer[0]);

_dataLen = cinfo.output_width*cinfo.output_height*cinfo.output_components;
_data = new unsigned char[_dataLen];
CC_BREAK_IF(! _data);

/* 现在实际上读取JPEG到原始缓冲区*/
/* 一次读一个扫描线 */
while( cinfo.output_scanline < cinfo.output_height )
{
jpeg_read_scanlines( &cinfo, row_pointer, 1 );
for( i=0; i<cinfo.output_width*cinfo.output_components;i++)
{
_data[location++] = row_pointer[0][i];
}
}

/*当读取被破坏的图像文件数据,可能会导致 jpeg_finish_decompress()  错误.
* 此外,jpeg_destroy_decompress()应该释放解压对象关联的所有内存。因此,它并不需要调用jpeg_finish_decompress()
*/
//jpeg_finish_decompress( &cinfo );
jpeg_destroy_decompress( &cinfo );
/* 解压完成后, 销毁对象,释放指针,关闭打开的文件 */
bRet = true;
} while (0);

CC_SAFE_DELETE_ARRAY(row_pointer[0]);
return bRet;
}

bool Image::initWithPngData(const unsigned char * data, int dataLen)
{
// 检查有效的PNG文件的字节长度
#define PNGSIGSIZE  8
bool bRet = false;
png_byte        header[PNGSIGSIZE]   = {0};
png_structp     png_ptr     =   0;
png_infop       info_ptr    = 0;

do
{
// png header len is 8 bytes
CC_BREAK_IF(dataLen < PNGSIGSIZE);

// check the data is png or not
memcpy(header, data, PNGSIGSIZE);
CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));

// init png_struct
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
CC_BREAK_IF(! png_ptr);

// init png_info
info_ptr = png_create_info_struct(png_ptr);
CC_BREAK_IF(!info_ptr);

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

// set the read call back function      //设置读回调函数
tImageSource imageSource;
imageSource.data    = (unsigned char*)data;
imageSource.size    = dataLen;
imageSource.offset  = 0;
png_set_read_fn(png_ptr, &imageSource, pngReadCallback);

// read png header info

// read png file info
png_read_info(png_ptr, info_ptr);

_width = png_get_image_width(png_ptr, info_ptr);
_height = png_get_image_height(png_ptr, info_ptr);
png_byte bit_depth = 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);

// 强制图片调色扩大到 24-bit RGB
// 它可能包含alpha通道
if (color_type == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(png_ptr);
}
// low-bit-depth(低比特深度的)灰度图像被扩展到 8 bits
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
{
bit_depth = 8;
png_set_expand_gray_1_2_4_to_8(png_ptr);
}
// 扩充TRNS数据块到一个完整的alpha通道
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png_ptr);
}
// 把 16-bit 采样的图像减少到 8 bits
if (bit_depth == 16)
{
png_set_strip_16(png_ptr);
}

// 提前扩大灰度,现在处理调色板和RGB
if (bit_depth < 8) {
png_set_packing(png_ptr);
}
// update info
png_read_update_info(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);

switch (color_type)
{
case PNG_COLOR_TYPE_GRAY:
_renderFormat = Texture2D::PixelFormat::I8;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
_renderFormat = Texture2D::PixelFormat::AI88;
break;
case PNG_COLOR_TYPE_RGB:
_renderFormat = Texture2D::PixelFormat::RGB888;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
_renderFormat = Texture2D::PixelFormat::RGBA8888;
break;
default:
break;
}

// read png data
png_uint_32 rowbytes;
png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * _height );

rowbytes = png_get_rowbytes(png_ptr, info_ptr);

_dataLen = rowbytes * _height;
_data = new unsigned char[_dataLen];
CC_BREAK_IF(!_data);

for (unsigned short i = 0; i < _height; ++i)
{
row_pointers[i] = _data + i*rowbytes;
}
png_read_image(png_ptr, row_pointers);

png_read_end(png_ptr, NULL);

_preMulti = false;

CC_SAFE_FREE(row_pointers);

bRet = true;
} while (0);

if (png_ptr)
{
png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
}
return bRet;
}

namespace
{
static tmsize_t tiffReadProc(thandle_t fd, void* buf, tmsize_t size)
{
tImageSource* isource = (tImageSource*)fd;
uint8* ma;
uint64 mb;
unsigned long n;
unsigned long o;
tmsize_t p;
ma=(uint8*)buf;
mb=size;
p=0;
while (mb>0)
{
n=0x80000000UL;
if ((uint64)n>mb)
n=(unsigned long)mb;

if((int)(isource->offset + n) <= isource->size)
{
memcpy(ma, isource->data+isource->offset, n);
isource->offset += n;
o = n;
}
else
{
return 0;
}

ma+=o;
mb-=o;
p+=o;
if (o!=n)
{
break;
}
}
return p;
}

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;
}

static uint64 tiffSeekProc(thandle_t fd, uint64 off, int whence)
{
tImageSource* isource = (tImageSource*)fd;
uint64 ret = -1;
do
{
if (whence == SEEK_SET)
{
CC_BREAK_IF(off >= (uint64)isource->size);
ret = isource->offset = (uint32)off;
}
else if (whence == SEEK_CUR)
{
CC_BREAK_IF(isource->offset + off >= (uint64)isource->size);
ret = isource->offset += (uint32)off;
}
else if (whence == SEEK_END)
{
CC_BREAK_IF(off >= (uint64)isource->size);
ret = isource->offset = (uint32)(isource->size-1 - off);
}
else
{
CC_BREAK_IF(off >= (uint64)isource->size);
ret = isource->offset = (uint32)off;
}
} while (0);

return ret;
}

static uint64 tiffSizeProc(thandle_t fd)
{
tImageSource* pImageSrc = (tImageSource*)fd;
return pImageSrc->size;
}

static int tiffCloseProc(thandle_t fd)
{
CC_UNUSED_PARAM(fd);
return 0;
}

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;
}

static void tiffUnmapProc(thandle_t fd, void* base, toff_t size)
{
CC_UNUSED_PARAM(fd);
CC_UNUSED_PARAM(base);
CC_UNUSED_PARAM(size);
}
}

bool Image::initWithTiffData(const unsigned char * data, int dataLen)
{
bool bRet = false;
do
{
// set the read call back function      //设置读回调函数
tImageSource imageSource;
imageSource.data    = data;
imageSource.size    = dataLen;
imageSource.offset  = 0;

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;
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;

_renderFormat = Texture2D::PixelFormat::RGBA8888;
_width = w;
_height = h;

_dataLen = npixels * sizeof (uint32);
_data = new unsigned char[_dataLen];

uint32* raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
if (raster != NULL)
{
if (TIFFReadRGBAImageOriented(tif, w, h, raster, ORIENTATION_TOPLEFT, 0))
{
/* 调用 TIFFReadRGBAImageOriented 后使用 alpha 分量,预乘栅格数据*/
_preMulti = true;

memcpy(_data, raster, npixels*sizeof (uint32));
}

_TIFFfree(raster);
}

TIFFClose(tif);

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

namespace
{
bool testFormatForPvr2TCSupport(PVR2TexturePixelFormat format)
{
if (!Configuration::getInstance()->supportsPVRTC())
{
if (format == PVR2TexturePixelFormat::PVRTC2BPP_RGBA ||
format == PVR2TexturePixelFormat::PVRTC4BPP_RGBA)
{
return false;
}
}

return true;
}

bool testFormatForPvr3TCSupport(PVR3TexturePixelFormat format)
{
if (!Configuration::getInstance()->supportsPVRTC())
{
if (format == PVR3TexturePixelFormat::PVRTC2BPP_RGB  ||
format == PVR3TexturePixelFormat::PVRTC2BPP_RGBA ||
format == PVR3TexturePixelFormat::PVRTC4BPP_RGB  ||
format == PVR3TexturePixelFormat::PVRTC4BPP_RGBA)
{
return false;
}
}

return true;
}
}

bool Image::initWithPVRv2Data(const unsigned char * data, int dataLen)
{
int dataLength = 0, dataOffset = 0, dataSize = 0;
int blockSize = 0, widthBlocks = 0, heightBlocks = 0;
int width = 0, height = 0;

//把 sizeof(PVRTexHeader) bytes 数据流作为 PVRTexHeader
const PVRv2TexHeader *header = static_cast<const PVRv2TexHeader *>(static_cast<const void*>(data));

//确保标签使用了正确的格式
if (memcmp(&header->pvrTag, gPVRTexIdentifier, strlen(gPVRTexIdentifier)) != 0)
{
return false;
}

Configuration *configuration = Configuration::getInstance();

_hasPremultipliedAlpha = false;
unsigned int flags = CC_SWAP_INT32_LITTLE_TO_HOST(header->flags);
PVR2TexturePixelFormat formatFlags = static_cast<PVR2TexturePixelFormat>(flags & PVR_TEXTURE_FLAG_TYPE_MASK);
bool flipped = (flags & (unsigned int)PVR2TextureFlag::VerticalFlip) ? true : false;
if (flipped)
{
CCLOG("cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool");
}

if (! configuration->supportsNPOT() &&
(header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height)))
{
CCLOG("cocos2d: ERROR: Loading an NPOT texture (%dx%d) but is not supported on this device", header->width, header->height);
return false;
}

if (!testFormatForPvr2TCSupport(formatFlags))
{
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%02X. Re-encode it with a OpenGL pixel format variant", formatFlags);
return false;
}

if (v2_pixel_formathash.find(formatFlags) == v2_pixel_formathash.end())
{
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%02X. Re-encode it with a OpenGL pixel format variant", formatFlags);
return false;
}

auto it = Texture2D::getPixelFormatInfoMap().find(v2_pixel_formathash.at(formatFlags));

if (it == Texture2D::getPixelFormatInfoMap().end())
{
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%02X. Re-encode it with a OpenGL pixel format variant", formatFlags);
return false;
}

_renderFormat = it->first;

//Reset num of mipmaps
_numberOfMipmaps = 0;

//Get size of mipmap
_width = width = CC_SWAP_INT32_LITTLE_TO_HOST(header->width);
_height = height = CC_SWAP_INT32_LITTLE_TO_HOST(header->height);

//Get ptr to where data starts..
dataLength = CC_SWAP_INT32_LITTLE_TO_HOST(header->dataLength);

//Move by size of header        //移动 header 的大小
_dataLen = dataLen - sizeof(PVRv2TexHeader);
_data = new unsigned char[_dataLen];
memcpy(_data, (unsigned char*)data + sizeof(PVRv2TexHeader), _dataLen);

// 计算每个 texture 级别的数据大小,计算每个块处的最小数目
while (dataOffset < dataLength)
{
switch (formatFlags) {
case PVR2TexturePixelFormat::PVRTC2BPP_RGBA:
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
break;
case PVR2TexturePixelFormat::PVRTC4BPP_RGBA:
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
break;
case PVR2TexturePixelFormat::BGRA8888:
if (Configuration::getInstance()->supportsBGRA8888() == false)
{
CCLOG("cocos2d: Image. BGRA8888 not supported on this device");
return false;
}
default:
blockSize = 1;
widthBlocks = width;
heightBlocks = height;
break;
}

// Clamp to minimum number of blocks    //收缩blocks 的数目到最小
if (widthBlocks < 2)
{
widthBlocks = 2;
}
if (heightBlocks < 2)
{
heightBlocks = 2;
}

dataSize = widthBlocks * heightBlocks * ((blockSize  * it->second.bpp) / 8);
int packetLength = (dataLength - dataOffset);
packetLength = packetLength > dataSize ? dataSize : packetLength;

//记录的Mipmap阵列,增量计数器
_mipmaps[_numberOfMipmaps].address = _data + dataOffset;
_mipmaps[_numberOfMipmaps].len = packetLength;
_numberOfMipmaps++;

dataOffset += packetLength;

//Update width and height to the next lower power of two
width = MAX(width >> 1, 1);
height = MAX(height >> 1, 1);
}

return true;
}

bool Image::initWithPVRv3Data(const unsigned char * data, int dataLen)
{
if (dataLen < sizeof(PVRv3TexHeader))
{
return false;
}

const PVRv3TexHeader *header = static_cast<const PVRv3TexHeader *>(static_cast<const void*>(data));

// validate version
if (CC_SWAP_INT32_BIG_TO_HOST(header->version) != 0x50565203)
{
CCLOG("cocos2d: WARNING: pvr file version mismatch");
return false;
}

// parse pixel format
PVR3TexturePixelFormat pixelFormat = static_cast<PVR3TexturePixelFormat>(header->pixelFormat);

if (!testFormatForPvr3TCSupport(pixelFormat))
{
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%016llX. Re-encode it with a OpenGL pixel format variant",
static_cast<unsigned long long>(pixelFormat));
return false;
}

if (v3_pixel_formathash.find(pixelFormat) == v3_pixel_formathash.end())
{
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%016llX. Re-encode it with a OpenGL pixel format variant",
static_cast<unsigned long long>(pixelFormat));
return false;
}

auto it = Texture2D::getPixelFormatInfoMap().find(v3_pixel_formathash.at(pixelFormat));

if (it == Texture2D::getPixelFormatInfoMap().end())
{
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%016llX. Re-encode it with a OpenGL pixel format variant",
static_cast<unsigned long long>(pixelFormat));
return false;
}

_renderFormat = it->first;

// flags
int flags = CC_SWAP_INT32_LITTLE_TO_HOST(header->flags);

// PVRv3 指定预乘 alpha 标志 -- 应该始终遵循 PVRv3 files
if (flags & (unsigned int)PVR3TextureFlag::PremultipliedAlpha)
{
_preMulti = true;
}

// sizing
int width = CC_SWAP_INT32_LITTLE_TO_HOST(header->width);
int height = CC_SWAP_INT32_LITTLE_TO_HOST(header->height);
_width = width;
_height = height;
int dataOffset = 0, dataSize = 0;
int blockSize = 0, widthBlocks = 0, heightBlocks = 0;

_dataLen = dataLen - (sizeof(PVRv3TexHeader) + header->metadataLength);
_data = new unsigned char[_dataLen];
memcpy(_data, static_cast<const unsigned char*>(data) + sizeof(PVRv3TexHeader) + header->metadataLength, _dataLen);

_numberOfMipmaps = header->numberOfMipmaps;
CCAssert(_numberOfMipmaps < MIPMAP_MAX, "Image: Maximum number of mimpaps reached. Increate the CC_MIPMAP_MAX value");

for (int i = 0; i < _numberOfMipmaps; i++)
{
switch ((PVR3TexturePixelFormat)pixelFormat)
{
case PVR3TexturePixelFormat::PVRTC2BPP_RGB :
case PVR3TexturePixelFormat::PVRTC2BPP_RGBA :
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
break;
case PVR3TexturePixelFormat::PVRTC4BPP_RGB :
case PVR3TexturePixelFormat::PVRTC4BPP_RGBA :
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
break;
case PVR3TexturePixelFormat::BGRA8888:
if( ! Configuration::getInstance()->supportsBGRA8888())
{
CCLOG("cocos2d: Image. BGRA8888 not supported on this device");
return false;
}
default:
blockSize = 1;
widthBlocks = width;
heightBlocks = height;
break;
}

// Clamp to minimum number of blocks
if (widthBlocks < 2)
{
widthBlocks = 2;
}
if (heightBlocks < 2)
{
heightBlocks = 2;
}

dataSize = widthBlocks * heightBlocks * ((blockSize  * it->second.bpp) / 8);
int packetLength = _dataLen - dataOffset;
packetLength = packetLength > dataSize ? dataSize : packetLength;

_mipmaps[i].address = _data + dataOffset;
_mipmaps[i].len = packetLength;

dataOffset += packetLength;
CCAssert(dataOffset <= _dataLen, "CCTexurePVR: Invalid lenght");

width = MAX(width >> 1, 1);
height = MAX(height >> 1, 1);
}

return true;
}

bool Image::initWithETCData(const unsigned char * data, int dataLen)
{
const etc1_byte* header = static_cast<const etc1_byte*>(data);

//check the data
if(!etc1_pkm_is_valid(header))
{
return  false;
}

_width = etc1_pkm_get_width(header);
_height = etc1_pkm_get_height(header);

if( 0 == _width || 0 == _height )
{
return false;
}

if(Configuration::getInstance()->supportsETC())
{
//老OpenGL版本没有定义 GL_ETC1_RGB8_OES, 添加宏让他们可以正常编译.
#ifdef GL_ETC1_RGB8_OES
_renderFormat = Texture2D::PixelFormat::ETC;
_dataLen = dataLen - ETC_PKM_HEADER_SIZE;
_data = new unsigned char[_dataLen];
memcpy(_data, static_cast<const unsigned char*>(data) + ETC_PKM_HEADER_SIZE, _dataLen);
return true;
#endif
}
else
{
//如果它不是GLES或移动设备不支持ATITC的,由软件解码texture
int bytePerPixel = 3;
unsigned int stride = _width * bytePerPixel;
_renderFormat = Texture2D::PixelFormat::RGB888;

_dataLen =  _width * _height * bytePerPixel;
_data = new unsigned char[_dataLen];

if (etc1_decode_image(static_cast<const unsigned char*>(data) + ETC_PKM_HEADER_SIZE, static_cast<etc1_byte*>(_data), _width, _height, bytePerPixel, stride) != 0)
{
_dataLen = 0;
CC_SAFE_DELETE_ARRAY(_data);
return false;
}

return true;
}
return false;
}

namespace
{
static const uint32_t makeFourCC(char ch0, char ch1, char ch2, char ch3)
{
const uint32_t fourCC = ((uint32_t)(char)(ch0) | ((uint32_t)(char)(ch1) << 8) | ((uint32_t)(char)(ch2) << 16) | ((uint32_t)(char)(ch3) << 24 ));
return fourCC;
}
}

bool Image::initWithS3TCData(const unsigned char * data, int dataLen)
{

const uint32_t FOURCC_DXT1 = makeFourCC('D', 'X', 'T', '1');
const uint32_t FOURCC_DXT3 = makeFourCC('D', 'X', 'T', '3');
const uint32_t FOURCC_DXT5 = makeFourCC('D', 'X', 'T', '5');

/* load the .dds file */

S3TCTexHeader *header = (S3TCTexHeader *)data;
unsigned char *pixelData = new unsigned char [dataLen - sizeof(S3TCTexHeader)];
memcpy((void *)pixelData, data + sizeof(S3TCTexHeader), dataLen - sizeof(S3TCTexHeader));

_width = header->ddsd.width;
_height = header->ddsd.height;
_numberOfMipmaps = header->ddsd.DUMMYUNIONNAMEN2.mipMapCount;
_dataLen = 0;
int blockSize = (FOURCC_DXT1 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC) ? 8 : 16;

/* caculate the dataLen */

int width = _width;
int height = _height;

if (Configuration::getInstance()->supportsS3TC())  //compressed data length
{
_dataLen = dataLen - sizeof(S3TCTexHeader);
_data = new unsigned char [_dataLen];
memcpy((void *)_data,(void *)pixelData , _dataLen);
}
else                                               //decompressed data length
{
for (int i = 0; i < _numberOfMipmaps && (width || height); ++i)
{
if (width == 0) width = 1;
if (height == 0) height = 1;

_dataLen += (height * width *4);

width >>= 1;
height >>= 1;
}
_data = new unsigned char [_dataLen];
}

/* load the mipmaps */

int encodeOffset = 0;
int decodeOffset = 0;
width = _width;  height = _height;

for (int i = 0; i < _numberOfMipmaps && (width || height); ++i)
{
if (width == 0) width = 1;
if (height == 0) height = 1;

int size = ((width+3)/4)*((height+3)/4)*blockSize;

if (Configuration::getInstance()->supportsS3TC())
{   //decode texture throught hardware

CCLOG("this is s3tc H decode");

if (FOURCC_DXT1 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC)
{
_renderFormat = Texture2D::PixelFormat::S3TC_DXT1;
}
else if (FOURCC_DXT3 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC)
{
_renderFormat = Texture2D::PixelFormat::S3TC_DXT3;
}
else if (FOURCC_DXT5 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC)
{
_renderFormat = Texture2D::PixelFormat::S3TC_DXT5;
}

_mipmaps[i].address = (unsigned char *)_data + encodeOffset;
_mipmaps[i].len = size;
}
else
{   //如果它不是GLES或移动设备不支持ATITC的,由软件解码texture
int bytePerPixel = 4;
unsigned int stride = width * bytePerPixel;
_renderFormat = Texture2D::PixelFormat::RGBA8888;

std::vector<unsigned char> decodeImageData(stride * height);
if (FOURCC_DXT1 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC)
{
s3tc_decode(pixelData + encodeOffset, &decodeImageData[0], width, height, S3TCDecodeFlag::DXT1);
}
else if (FOURCC_DXT3 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC)
{
s3tc_decode(pixelData + encodeOffset, &decodeImageData[0], width, height, S3TCDecodeFlag::DXT3);
}
else if (FOURCC_DXT5 == header->ddsd.DUMMYUNIONNAMEN4.ddpfPixelFormat.fourCC)
{
s3tc_decode(pixelData + encodeOffset, &decodeImageData[0], width, height, S3TCDecodeFlag::DXT5);
}

_mipmaps[i].address = (unsigned char *)_data + decodeOffset;
_mipmaps[i].len = (stride * height);
memcpy((void *)_mipmaps[i].address, (void *)&decodeImageData[0], _mipmaps[i].len);
decodeOffset += stride * height;
}

encodeOffset += size;
width >>= 1;
height >>= 1;
}

/* end load the mipmaps */

CC_SAFE_DELETE_ARRAY(pixelData);

return true;
}

bool Image::initWithATITCData(const unsigned char *data, int dataLen)
{
/* load the .ktx file */
ATITCTexHeader *header = (ATITCTexHeader *)data;
_width =  header->pixelWidth;
_height = header->pixelHeight;
_numberOfMipmaps = header->numberOfMipmapLevels;

int blockSize = 0;
switch (header->glInternalFormat)
{
case CC_GL_ATC_RGB_AMD:
blockSize = 8;
break;
case CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:
blockSize = 16;
break;
case CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
blockSize = 16;
break;
default:
break;
}

/* pixelData point to the compressed data address */        //pixelData指向的压缩的数据地址
unsigned char *pixelData = (unsigned char *)data + sizeof(ATITCTexHeader) + header->bytesOfKeyValueData + 4;

/* caculate the dataLen */
int width = _width;
int height = _height;

if (Configuration::getInstance()->supportsATITC())  //compressed data length
{
_dataLen = dataLen - sizeof(ATITCTexHeader) - header->bytesOfKeyValueData - 4;
_data = new unsigned char [_dataLen];
memcpy((void *)_data,(void *)pixelData , _dataLen);
}
else                                               //decompressed data length
{
for (int i = 0; i < _numberOfMipmaps && (width || height); ++i)
{
if (width == 0) width = 1;
if (height == 0) height = 1;

_dataLen += (height * width *4);

width >>= 1;
height >>= 1;
}
_data = new unsigned char [_dataLen];
}

/* load the mipmaps */
int encodeOffset = 0;
int decodeOffset = 0;
width = _width;  height = _height;

for (int i = 0; i < _numberOfMipmaps && (width || height); ++i)
{
if (width == 0) width = 1;
if (height == 0) height = 1;

int size = ((width+3)/4)*((height+3)/4)*blockSize;

if (Configuration::getInstance()->supportsATITC())
{
/* decode texture throught hardware */

CCLOG("this is atitc H decode");

switch (header->glInternalFormat)
{
case CC_GL_ATC_RGB_AMD:
_renderFormat = Texture2D::PixelFormat::ATC_RGB;
break;
case CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:
_renderFormat = Texture2D::PixelFormat::ATC_EXPLICIT_ALPHA;
break;
case CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
_renderFormat = Texture2D::PixelFormat::ATC_INTERPOLATED_ALPHA;
break;
default:
break;
}

_mipmaps[i].address = (unsigned char *)_data + encodeOffset;
_mipmaps[i].len = size;
}
else
{
/* 如果它不是GLES或移动设备不支持ATITC的,由软件解码texture */

int bytePerPixel = 4;
unsigned int stride = width * bytePerPixel;
_renderFormat = Texture2D::PixelFormat::RGBA8888;

std::vector<unsigned char> decodeImageData(stride * height);
switch (header->glInternalFormat)
{
case CC_GL_ATC_RGB_AMD:
atitc_decode(pixelData + encodeOffset, &decodeImageData[0], width, height, ATITCDecodeFlag::ATC_RGB);
break;
case CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:
atitc_decode(pixelData + encodeOffset, &decodeImageData[0], width, height, ATITCDecodeFlag::ATC_EXPLICIT_ALPHA);
break;
case CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
atitc_decode(pixelData + encodeOffset, &decodeImageData[0], width, height, ATITCDecodeFlag::ATC_INTERPOLATED_ALPHA);
break;
default:
break;
}

_mipmaps[i].address = (unsigned char *)_data + decodeOffset;
_mipmaps[i].len = (stride * height);
memcpy((void *)_mipmaps[i].address, (void *)&decodeImageData[0], _mipmaps[i].len);
decodeOffset += stride * height;
}

encodeOffset += (size + 4);
width >>= 1;
height >>= 1;
}
/* end load the mipmaps */

return true;
}

bool Image::initWithPVRData(const unsigned char * data, int dataLen)
{
return initWithPVRv2Data(data, dataLen) || initWithPVRv3Data(data, dataLen);
}

bool Image::initWithWebpData(const unsigned char * data, int dataLen)
{
bool bRet = false;
do
{
WebPDecoderConfig config;
if (WebPInitDecoderConfig(&config) == 0) break;
if (WebPGetFeatures(static_cast<const uint8_t*>(data), dataLen, &config.input) != VP8_STATUS_OK) break;
if (config.input.width == 0 || config.input.height == 0) break;

config.output.colorspace = MODE_RGBA;
_renderFormat = Texture2D::PixelFormat::RGBA8888;
_width    = config.input.width;
_height   = config.input.height;

int bufferSize = _width * _height * 4;
_data = new unsigned char[bufferSize];

config.output.u.RGBA.rgba = static_cast<uint8_t*>(_data);
config.output.u.RGBA.stride = _width * 4;
config.output.u.RGBA.size = bufferSize;
config.output.is_external_memory = 1;

if (WebPDecode(static_cast<const uint8_t*>(data), dataLen, &config) != VP8_STATUS_OK)
{
delete []_data;
_data = NULL;
break;
}

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

bool Image::initWithRawData(const unsigned char * data, int dataLen, int width, int height, int bitsPerComponent, bool preMulti)
{
bool bRet = false;
do
{
CC_BREAK_IF(0 == width || 0 == height);

_height   = height;
_width    = width;
_preMulti = preMulti;
_renderFormat = Texture2D::PixelFormat::RGBA8888;

// only RGBA8888 supported
int bytesPerComponent = 4;
_dataLen = height * width * bytesPerComponent;
_data = new unsigned char[_dataLen];
CC_BREAK_IF(! _data);
memcpy(_data, data, _dataLen);

bRet = true;
} while (0);

return bRet;
}

#if (CC_TARGET_PLATFORM != CC_PLATFORM_IOS)
bool Image::saveToFile(const char *pszFilePath, bool bIsToRGB)
{
//only support for Texture2D::PixelFormat::RGB888 or Texture2D::PixelFormat::RGBA8888 uncompressed data
if (isCompressed() || (_renderFormat != Texture2D::PixelFormat::RGB888 && _renderFormat != Texture2D::PixelFormat::RGBA8888))
{
CCLOG("cocos2d: Image: saveToFile is only support for Texture2D::PixelFormat::RGB888 or Texture2D::PixelFormat::RGBA8888 uncompressed data for now");
return false;
}

#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
assert(false);
return false;
#else
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]);
}

if (std::string::npos != strLowerCasePath.find(".png"))
{
CC_BREAK_IF(!saveImageToPNG(pszFilePath, bIsToRGB));
}
else if (std::string::npos != strLowerCasePath.find(".jpg"))
{
CC_BREAK_IF(!saveImageToJPG(pszFilePath));
}
else
{
break;
}

bRet = true;
} while (0);

return bRet;
#endif
}
#endif

bool Image::saveImageToPNG(const char * filePath, bool isToRGB)
{
bool bRet = false;
do
{
CC_BREAK_IF(NULL == filePath);

FILE *fp;
png_structp png_ptr;
png_infop info_ptr;
png_colorp palette;
png_bytep *row_pointers;

fp = fopen(filePath, "wb");
CC_BREAK_IF(NULL == fp);

png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

if (NULL == png_ptr)
{
fclose(fp);
break;
}

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 && CC_TARGET_PLATFORM != CC_PLATFORM_NACL)
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
#endif
png_init_io(png_ptr, fp);

if (!isToRGB && hasAlpha())
{
png_set_IHDR(png_ptr, info_ptr, _width, _height, 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, _width, _height, 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);

png_write_info(png_ptr, info_ptr);

png_set_packing(png_ptr);

row_pointers = (png_bytep *)malloc(_height * sizeof(png_bytep));
if(row_pointers == NULL)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}

if (hasAlpha())
{
for (int i = 0; i < (int)_height; i++)
{
row_pointers[i] = (png_bytep)_data + i * _width * 3;
}

png_write_image(png_ptr, row_pointers);

free(row_pointers);
row_pointers = NULL;
}
else
{
if (isToRGB)
{
unsigned char *pTempData = new unsigned char[_width * _height * 3];
if (NULL == pTempData)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}

for (int i = 0; i < _height; ++i)
{
for (int j = 0; j < _width; ++j)
{
pTempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4];
pTempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1];
pTempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2];
}
}

for (int i = 0; i < (int)_height; i++)
{
row_pointers[i] = (png_bytep)pTempData + i * _width * 3;
}

png_write_image(png_ptr, row_pointers);

free(row_pointers);
row_pointers = NULL;

CC_SAFE_DELETE_ARRAY(pTempData);
}
else
{
for (int i = 0; i < (int)_height; i++)
{
row_pointers[i] = (png_bytep)_data + i * _width * 4;
}

png_write_image(png_ptr, row_pointers);

free(row_pointers);
row_pointers = NULL;
}
}

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;
}
bool Image::saveImageToJPG(const char * filePath)
{
bool bRet = false;
do
{
CC_BREAK_IF(NULL == filePath);

struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile;                 /* 目标文件 */
JSAMPROW row_pointer[1];        /* pointer to JSAMPLE row[s] */
int     row_stride;          /* 图像缓冲区物理行宽度*/

cinfo.err = jpeg_std_error(&jerr);
/*现在,我们可以初始化JPEG压缩对象. */
jpeg_create_compress(&cinfo);

CC_BREAK_IF((outfile = fopen(filePath, "wb")) == NULL);

jpeg_stdio_dest(&cinfo, outfile);

cinfo.image_width = _width;    /* 图像的宽度和高度,以像素为单位 */
cinfo.image_height = _height;
cinfo.input_components = 3;       /* # 每个像素的颜色分量 */
cinfo.in_color_space = JCS_RGB;       /* 输入图像的色彩空间 */

jpeg_set_defaults(&cinfo);

jpeg_start_compress(&cinfo, TRUE);

row_stride = _width * 3; /* image_buffer 每行都是一个 JSAMPLEs */

if (hasAlpha())
{
unsigned char *pTempData = new unsigned char[_width * _height * 3];
if (NULL == pTempData)
{
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(outfile);
break;
}

for (int i = 0; i < _height; ++i)
{
for (int j = 0; j < _width; ++j)

{
pTempData[(i * _width + j) * 3] = _data[(i * _width + j) * 4];
pTempData[(i * _width + j) * 3 + 1] = _data[(i * _width + j) * 4 + 1];
pTempData[(i * _width + j) * 3 + 2] = _data[(i * _width + j) * 4 + 2];
}
}

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
{
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = & _data[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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: