//data: 图片文件数据 dataLen: 文件长度
bool Image::initWithImageData(const unsigned char * data, ssize_t dataLen)
{
bool ret = false;
do
{
CC_BREAK_IF(! data || dataLen <= 0);
unsigned char* unpackedData = nullptr;
ssize_t unpackedLen = 0;
//解压缩pvr.ccz格式的图片
//detecgt and unzip the compress file
if (ZipUtils::isCCZBuffer(data, dataLen))
{
unpackedLen = ZipUtils::inflateCCZBuffer(data, dataLen, &unpackedData);
}
//解压缩pvr.gz格式的图片
else if (ZipUtils::isGZipBuffer(data, dataLen))
{
unpackedLen = ZipUtils::inflateMemory(const_cast(data), dataLen, &unpackedData);
}
else
{
unpackedData = const_cast(data);
unpackedLen = dataLen;
}
//识别文件类型
_fileType = detectFormat(unpackedData, unpackedLen);
switch (_fileType)
{
case Format::PNG:
ret = initWithPngData(unpackedData, unpackedLen);
break;
...
//后面就是依据文件类型来进行图片解码初始化
} while (0);
return ret;
}
这里先介绍下图片解码到载入再到显示的流程。以后再具体地介绍每种格式图片的解码。
Texture2D * TextureCache::addImage(const std::string &path)
{
...
//新建一个Image对象
image = new (std::nothrow) Image();
CC_BREAK_IF(nullptr == image);
//图片解码
bool bRet = image->initWithImageFile(fullpath);
CC_BREAK_IF(!bRet);
//新建一个2D的纹理
texture = new (std::nothrow) Texture2D();
//開始初始化纹理
if( texture && texture->initWithImage(image) )
{
...
}
}
//使用指定像素格式来初始化(默认是auto,依据图片解码的结果来确定)
bool Texture2D::initWithImage(Image *image, PixelFormat format)
{
...
//获取当前的OpenGL环境
Configuration *conf = Configuration::getInstance();
//推断纹理大小是否超出限制
int maxTextureSize = conf->getMaxTextureSize();
...
//获取mipmap贴图的数量
if (image->getNumberOfMipmaps() > 1)
{
CCLOG("cocos2d: WARNING: This image has more than 1 mipmaps and we will not convert the data format");
//载入mipmap贴图
initWithMipmaps(image->getMipmaps(), image->getNumberOfMipmaps(), image->getRenderFormat(), imageWidth, imageHeight);
return true;
}
else if (image->isCompressed())
{
...
initWithData(...)
...
return true;
}
else
{
...
initWithData(...)
...
return true;
}
}
bool Texture2D::initWithMipmaps(MipmapInfo* mipmaps, int mipmapsNum, PixelFormat pixelFormat, int pixelsWide, int pixelsHigh)
{
...
//设置像素的行字节对齐,在一定平台下有性能的提高。并且若不加注意,会导致glTexImage中系函数的读取越界
//Set the row align only when mipmapsNum == 1 and the data is uncompressed
if (mipmapsNum == 1 && !info.compressed)
{
unsigned int bytesPerRow = pixelsWide * info.bpp / 8;
if(bytesPerRow % 8 == 0)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
}
else if(bytesPerRow % 4 == 0)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
else if(bytesPerRow % 2 == 0)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
}
else
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
}else
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
...
//生成纹理索引
glGenTextures(1, &_name);
//bindTexture2函数中会调用glActiveTexture,glBindTexture来制定纹理单位和绑定纹理
GL::bindTexture2D(_name);
//依据mipmap贴图数和是否设置抗锯齿来选择纹理滤波方式,关于纹理滤波的选择后面会具体的再分析下
if (mipmapsNum == 1)
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _antialiasEnabled ? GL_LINEAR : GL_NEAREST);
}else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _antialiasEnabled ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST);
}
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _antialiasEnabled ?
GL_LINEAR : GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); for (int i = 0; i < mipmapsNum; ++i) { unsigned char *data = mipmaps[i].address; GLsizei datalen = mipmaps[i].len; //纹理映射一个指定的纹理图像的一部分到每一个开启了纹理映射的图元上。在当前段着色器或顶点着色器使用内建纹理搜索函数时。贴图被启用。
if (info.compressed) { //压缩格式生成纹理 glCompressedTexImage2D(GL_TEXTURE_2D, i, info.internalFormat, (GLsizei)width, (GLsizei)height, 0, datalen, data); } else { //生成2D纹理 glTexImage2D(GL_TEXTURE_2D, i, info.internalFormat, (GLsizei)width, (GLsizei)height, 0, info.format, info.type, data); } if (i > 0 && (width != height || ccNextPOT(width) != width )) { CCLOG("cocos2d: Texture2D. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%d != height=%d", i, width, height); } GLenum err = glGetError(); if (err != GL_NO_ERROR) { CCLOG("cocos2d: Texture2D: Error uploading compressed texture level: %u . glError: 0x%04X", i, err); return false; } //四分之中的一个大小的mipmap width = MAX(width >> 1, 1); height = MAX(height >> 1, 1); } _contentSize = Size((float)pixelsWide, (float)pixelsHigh); _pixelsWide = pixelsWide; _pixelsHigh = pixelsHigh; _pixelFormat = pixelFormat; _maxS = 1; _maxT = 1; //关闭alpha渐变 _hasPremultipliedAlpha = false; _hasMipmaps = mipmapsNum > 1; // shader setGLProgram(GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE)); }
对于非mipmaps的贴图直接指定为已mipmapsNum为1的形式进行初始化就可以,纹理纹理渲染完毕就可以增加到显示队列,当然这里仅仅是先简介下,关于渲染流程等我写完图片的解码部分再回来补充~
未完待续…