cocos2d源码分析(六):Texture2D
2019-01-11 本文已影响0人
奔向火星005
Texture2D管理一个OpenGL纹理的相关信息,主要的成员如下:
class CC_DLL Texture2D : public Ref
{
public:
//...
protected:
Texture2D::PixelFormat _pixelFormat; //像素格式
int _pixelsWide; //图像的像素宽
int _pixelsHigh; //图像的像素高
GLuint _name; //纹理名称,由glGenTextures生成
GLfloat _maxS; //最大纹理横轴坐标,默认为1
GLfloat _maxT; //最大纹理纵轴坐标,默认为1
Size _contentSize; //图像大小
bool _hasPremultipliedAlpha; //是预乘了透明通道
bool _hasMipmaps; //是否有mipmaps
//关联的着色器
GLProgram* _shaderProgram; //对应SHADER_NAME_POSITION_TEXTURE
static const PixelFormatInfoMap _pixelFormatInfoTables;
bool _antialiasEnabled; //是否抗锯齿
NinePatchInfo* _ninePatchInfo;
bool _valid;
std::string _filePath; //图像文件路径
Texture2D* _alphaTexture; //还没用过
}
下面简单过一下它的初始化流程:
bool Texture2D::initWithImage(Image *image, PixelFormat format)
{
if (image == nullptr)
{
CCLOG("cocos2d: Texture2D. Can't create Texture. UIImage is nil");
return false;
}
int imageWidth = image->getWidth();
int imageHeight = image->getHeight();
this->_filePath = image->getFilePath();
Configuration *conf = Configuration::getInstance();
//返回当前机型支持的最大尺寸图片,用glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize);获取
int maxTextureSize = conf->getMaxTextureSize();
//若图片超过OpenGL允许的最大纹理尺寸,则失败返回
if (imageWidth > maxTextureSize || imageHeight > maxTextureSize)
{
CCLOG("cocos2d: WARNING: Image (%u x %u) is bigger than the supported %u x %u", imageWidth, imageHeight, maxTextureSize, maxTextureSize);
return false;
}
unsigned char* tempData = image->getData();
Size imageSize = Size((float)imageWidth, (float)imageHeight);
PixelFormat pixelFormat = ((PixelFormat::NONE == format) || (PixelFormat::AUTO == format)) ? image->getRenderFormat() : format;
PixelFormat renderFormat = image->getRenderFormat();
size_t tempDataLen = image->getDataLen();
if (image->getNumberOfMipmaps() > 1) //是否有mipmaps
{
if (pixelFormat != image->getRenderFormat())
{
CCLOG("cocos2d: WARNING: This image has more than 1 mipmaps and we will not convert the data format");
}
initWithMipmaps(image->getMipmaps(), image->getNumberOfMipmaps(), image->getRenderFormat(), imageWidth, imageHeight);
// set the premultiplied tag
_hasPremultipliedAlpha = image->hasPremultipliedAlpha();
return true;
}
else if (image->isCompressed()) //是否是压缩格式
{
if (pixelFormat != image->getRenderFormat())
{
CCLOG("cocos2d: WARNING: This image is compressed and we can't convert it for now");
}
initWithData(tempData, tempDataLen, image->getRenderFormat(), imageWidth, imageHeight, imageSize);
// set the premultiplied tag
_hasPremultipliedAlpha = image->hasPremultipliedAlpha();
return true;
}
else
{
unsigned char* outTempData = nullptr;
ssize_t outTempDataLen = 0;
pixelFormat = convertDataToFormat(tempData, tempDataLen, renderFormat, pixelFormat, &outTempData, &outTempDataLen); //将源数据转换为目标格式的数据
initWithData(outTempData, outTempDataLen, pixelFormat, imageWidth, imageHeight, imageSize);
if (outTempData != nullptr && outTempData != tempData)
{
free(outTempData);
}
// set the premultiplied tag
_hasPremultipliedAlpha = image->hasPremultipliedAlpha(); //最后又赋值
return true;
}
}
initWithMipmaps的代码为:
bool Texture2D::initWithMipmaps(MipmapInfo* mipmaps, int mipmapsNum, PixelFormat pixelFormat, int pixelsWide, int pixelsHigh)
{
//the pixelFormat must be a certain value
CCASSERT(pixelFormat != PixelFormat::NONE && pixelFormat != PixelFormat::AUTO, "the \"pixelFormat\" param must be a certain value!");
CCASSERT(pixelsWide>0 && pixelsHigh>0, "Invalid size");
if (mipmapsNum <= 0)
{
CCLOG("cocos2d: WARNING: mipmap number is less than 1");
return false;
}
auto formatItr = _pixelFormatInfoTables.find(pixelFormat);
if(formatItr == _pixelFormatInfoTables.end())
{
CCLOG("cocos2d: WARNING: unsupported pixelformat: %lx", (unsigned long)pixelFormat );
return false;
}
const PixelFormatInfo& info = formatItr->second;
if (info.compressed && !Configuration::getInstance()->supportsPVRTC()
&& !Configuration::getInstance()->supportsETC()
&& !Configuration::getInstance()->supportsS3TC()
&& !Configuration::getInstance()->supportsATITC())
{
CCLOG("cocos2d: WARNING: PVRTC/ETC images are not supported");
return false;
}
//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);
}
if(_name != 0)
{
GL::deleteTexture(_name);
_name = 0;
}
glGenTextures(1, &_name); //生成纹理名称
GL::bindTexture2D(_name); //调用activeTexture和glBindTexture,绑定_name到纹理单元0,如果有_alphaTexture,则将它绑定到纹理单元1
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 );
#if CC_ENABLE_CACHE_TEXTURE_DATA
if (_antialiasEnabled)
{
TexParams texParams = {(GLuint)(_hasMipmaps?GL_LINEAR_MIPMAP_NEAREST:GL_LINEAR),GL_LINEAR,GL_NONE,GL_NONE};
VolatileTextureMgr::setTexParameters(this, texParams);
}
else
{
TexParams texParams = {(GLuint)(_hasMipmaps?GL_NEAREST_MIPMAP_NEAREST:GL_NEAREST),GL_NEAREST,GL_NONE,GL_NONE};
VolatileTextureMgr::setTexParameters(this, texParams);
}
#endif
// clean possible GL error
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
cocos2d::log("OpenGL error 0x%04X in %s %s %d\n", err, __FILE__, __FUNCTION__, __LINE__);
}
// Specify OpenGL texture image
int width = pixelsWide;
int height = pixelsHigh;
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
{
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);
}
err = glGetError();
if (err != GL_NO_ERROR)
{
CCLOG("cocos2d: Texture2D: Error uploading compressed texture level: %u . glError: 0x%04X", i, err);
return false;
}
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;
_hasPremultipliedAlpha = false;
_hasMipmaps = mipmapsNum > 1;
// shader
setGLProgram(GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE));
return true;
}