纹理解析
绘图时opengl能够显示的是纹理,美术给的是图片。这样存在一个从不同图片类型到opengl纹理的转换。我们新建一个类TextureManager来做这件事,并且实现常用的png,jpg,bmp的转换。
/********************************************************************
Copyright(C), 2012-2013,
FileName:TextureManager.h
Description:
Author:cloud
Created:2014/10/23
history:
23:10:2014 10:36 by
*********************************************************************/
#pragma once
#include <map>
#include <string>
#include "GLFix.h"
#include "Singleton.h"
namespace cloud
{
enum PixelType
{
Pixel_I8,
Pixel_RGB888,
Pixel_RGB,
Pixel_RGBA
};
struct GLPixel
{
GLPixel(GLuint src,GLenum dest,GLenum format):
_srcPixelType(src),
_desPixelType(dest),
_dataFormat(format){};
GLuint _srcPixelType;
GLenum _desPixelType;
GLenum _dataFormat;
};
enum PictureType
{
Format_PNG,
Format_BMP,
Format_JPG,
Format_UNKNOWN
};
struct Texture
{
public:
GLuint _textureID;
int _textureWidth;
int _textureHeight;
};
class TextureManager :public Singleton<TextureManager>
{
public:
typedef std::map<std::string,Texture> TextureCache;
typedef std::pair<std::string,Texture> CacheValue;
typedef std::map<PixelType,GLPixel> PixelMap;
typedef std::pair<PixelType,GLPixel> PixelValue;
DECLARE_SINGLETON_CREATE_DESTROY
TextureManager();
~TextureManager();
GLuint createTexture(const char * texturePath,int& textureWidth,int& textureHeight);
GLuint createTextureFromImage( const char * texturePath, int &textureWidth, int &textureHeight );
GLuint createTextureFromBuffer( const int& textureWidth, const int& textureHeight, const unsigned char* textureData ,const PixelType& pixelType);
void removeTexture(const char * imagePath);
void removeAllTexture();
PictureType checkPictureFormat(const unsigned char* data,const unsigned int& dataLen);
bool isBMP(const unsigned char* data,const unsigned int& dataLen);
bool loadBMP(const unsigned char* data, const unsigned int& dataLen, int& imageWidth,int& imageHeight,unsigned char** textureData,PixelType& pixelType);
bool isPNG(const unsigned char* data,const unsigned int& dataLen);
bool loadPNG(const unsigned char* data, const unsigned int& dataLen, int& imageWidth,int& imageHeight,unsigned char** textureData,PixelType& pixelType);
bool isJPG(const unsigned char* data,const unsigned int& dataLen);
bool loadJPG(const unsigned char* data, const unsigned int& dataLen, int& imageWidth,int& imageHeight,unsigned char** textureData,PixelType& pixelType);
protected:
TextureCache _textureCache;
PixelMap _pixelMap;
};
}
/********************************************************************
Copyright(C), 2012-2013,
FileName:TextureManager.cpp
Description:
Author:cloud
Created:2014/10/23
history:
23:10:2014 10:36 by
*********************************************************************/
#include "TextureManager.h"
#include "FileUtils.h"
#include "png.h"
#include "pngconf.h"
#include "jpeglib.h"
namespace cloud
{
struct MyErrorMgr
{
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct MyErrorMgr * MyErrorPtr;
/*
* Here's the routine that will replace the standard error_exit method:
*/
METHODDEF(void)
myErrorExit(j_common_ptr cinfo)
{
/* cinfo->err really points to a MyErrorMgr struct, so coerce pointer */
MyErrorPtr myerr = (MyErrorPtr) cinfo->err;
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
/* internal message function cann't show error message in some platforms, so we rewrite it here.
* edit it if has version confilict.
*/
//(*cinfo->err->output_message) (cinfo);
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, buffer);
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}
typedef struct
{
const unsigned char * data;
size_t 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_SINGLETON_ALL(TextureManager);
TextureManager::TextureManager()
{
_pixelMap.insert(PixelValue(Pixel_I8,GLPixel(GL_LUMINANCE,GL_LUMINANCE,GL_UNSIGNED_BYTE)));
_pixelMap.insert(PixelValue(Pixel_RGB888,GLPixel(GL_RGB,GL_RGB,GL_UNSIGNED_BYTE)));
_pixelMap.insert(PixelValue(Pixel_RGB,GLPixel(GL_RGB,GL_RGB,GL_UNSIGNED_BYTE)));
_pixelMap.insert(PixelValue(Pixel_RGBA,GLPixel(GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE)));
}
GLuint TextureManager::createTexture(const char * texturePath,int& textureWidth,int& textureHeight)
{
TextureCache::const_iterator it = _textureCache.find(texturePath);
if (it != _textureCache.end())
{
textureWidth = it->second._textureWidth;
textureHeight = it->second._textureHeight;
return it->second._textureID;
}
else
{
return createTextureFromImage(texturePath, textureWidth, textureHeight);
}
}
GLuint TextureManager::createTextureFromImage( const char * texturePath, int &textureWidth, int &textureHeight )
{
unsigned char* data = NULL;
unsigned int dataLen = 0;
unsigned char* textureData = NULL;
Texture texure;
texure._textureID = 0;
PixelType pixelType;
if(FileUtils::getInstance()->readFile(texturePath,&data,dataLen))
{
PictureType pitctureType = checkPictureFormat(data,dataLen);
bool success = false;
switch (pitctureType)
{
case cloud::Format_PNG:
success = loadPNG(data,dataLen,textureWidth,textureHeight,&textureData,pixelType);
break;
case cloud::Format_BMP:
success = loadBMP(data,dataLen,textureWidth,textureHeight,&textureData,pixelType);
break;
case cloud::Format_JPG:
success = loadJPG(data,dataLen,textureWidth,textureHeight,&textureData,pixelType);
break;
case cloud::Format_UNKNOWN:
break;
default:
break;
}
if (success)
{
texure._textureID = createTextureFromBuffer(textureWidth,textureHeight,textureData,pixelType);
if (texure._textureID != 0)
{
texure._textureWidth = textureWidth;
texure._textureHeight = textureHeight;
_textureCache.insert(CacheValue(texturePath,texure));
}
}
}
if(textureData != NULL)
free(textureData);
if(data != NULL)
free(data);
return texure._textureID;
}
GLuint TextureManager::createTextureFromBuffer( const int& textureWidth, const int& textureHeight, const unsigned char* textureData , const PixelType& pixelType)
{
PixelMap::iterator it = _pixelMap.find(pixelType);
if (it != _pixelMap.end())
{
GLuint textureID;
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0,it->second._srcPixelType, textureWidth, textureHeight, 0, it->second._desPixelType, it->second._dataFormat, textureData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
return textureID;
}
return 0;
}
PictureType TextureManager::checkPictureFormat(const unsigned char* data,const unsigned int& dataLen)
{
if (isBMP(data,dataLen))
{
return Format_BMP;
}
else if (isPNG(data,dataLen))
{
return Format_PNG;
}
else if (isJPG(data,dataLen))
{
return Format_JPG;
}
return Format_UNKNOWN;
}
bool TextureManager::isBMP(const unsigned char* data,const unsigned int& dataLen)
{
unsigned char header[54]; // Each BMP file begins by a 54-bytes header
if (dataLen < 54)
{
return false;
}
memcpy(header,data,sizeof(header));
if (header[0] != 'B' || header[1] != 'M')
{
return false;
}
return true;
}
bool TextureManager::loadBMP(const unsigned char* data, const unsigned int& dataLen, int& imageWidth,int& imageHeight,unsigned char** textureData,PixelType& pixelType)
{
//Data read from the header of the BMP file
// Each BMP file begins by a 54-bytes header
unsigned int dataPos; //Position in the file where the actual data begins
unsigned int imageSize; //= width*height*3
dataPos = *(int*)&(data[0x0A]);
imageSize = *(int*)&(data[0x22]);
imageWidth = *(int*)&(data[0x12]);
imageHeight = *(int*)&(data[0x16]);
if (imageSize == 0)
{
imageSize = imageWidth * imageHeight * 3;
}
if (dataPos == 0)
{
dataPos = 54;
}
unsigned char* picBuffer = (unsigned char* )malloc(imageSize);
memcpy(picBuffer,data + dataPos,imageSize);
*textureData = picBuffer;
pixelType = Pixel_RGB;
return true;
}
#define PNG_BYTES_TO_CHECK 4
bool TextureManager::isPNG(const unsigned char* data,const unsigned int& dataLen)
{
char buf[PNG_BYTES_TO_CHECK];
png_structp png_ptr;
png_infop info_ptr;
png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
info_ptr = png_create_info_struct( png_ptr );
setjmp( png_jmpbuf(png_ptr) );
/* 读取PNG_BYTES_TO_CHECK个字节的数据 */
memcpy( buf, data, PNG_BYTES_TO_CHECK);
/* 若读到的数据并没有PNG_BYTES_TO_CHECK个字节 */
if( dataLen < PNG_BYTES_TO_CHECK ) {
png_destroy_read_struct( &png_ptr, &info_ptr, 0);
return false;
}
/* 检测数据是否为PNG的签名 */
int temp = png_sig_cmp( (png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK );
/* 如果不是PNG的签名,则说明该文件不是PNG文件 */
if( temp != 0 ) {
png_destroy_read_struct( &png_ptr, &info_ptr, 0);
return false;
}
return true;
}
bool TextureManager::loadPNG(const unsigned char* data, const unsigned int& dataLen, int& imageWidth,int& imageHeight,unsigned char** textureData,PixelType& pixelType)
{
png_structp png_ptr;
png_infop info_ptr;
png_bytep* row_pointers;
int x, y,color_type;
png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
info_ptr = png_create_info_struct( png_ptr );
setjmp( png_jmpbuf(png_ptr) );
/* 开始读文件 */
// png_init_io( png_ptr, fp );
// 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);
/* 读取PNG图片信息 */
png_read_png( png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0 );
/* 获取图像的色彩类型 */
color_type = png_get_color_type( png_ptr, info_ptr );
/* 获取图像的宽高 */
imageWidth = png_get_image_width( png_ptr, info_ptr );
imageHeight = png_get_image_height( png_ptr, info_ptr );
/* 获取图像的所有行像素数据,row_pointers里边就是rgba数据 */
row_pointers = png_get_rows( png_ptr, info_ptr );
/* 根据不同的色彩类型进行相应处理 */
int length = 4;
switch( color_type )
{
case PNG_COLOR_TYPE_RGB_ALPHA:
length = 4;
pixelType = Pixel_RGBA;
break;
case PNG_COLOR_TYPE_RGB:
length = 3;
pixelType = Pixel_RGB;
break;
};
unsigned char* picBuffer = (unsigned char* )malloc(imageWidth * imageHeight * length);
unsigned int pos = 0;
switch( color_type ) {
case PNG_COLOR_TYPE_RGB_ALPHA:
for( y=0; y<imageHeight; ++y ) {
for( x=0; x<imageWidth *4; ) {
/* 以下是RGBA数据,需要你自己补充代码,保存RGBA数据 */
picBuffer[pos++] = row_pointers[y][x++]; // red
picBuffer[pos++] = row_pointers[y][x++]; // green
picBuffer[pos++] = row_pointers[y][x++]; // blue
picBuffer[pos++] = row_pointers[y][x++]; // alpha
}
}
break;
case PNG_COLOR_TYPE_RGB:
for( y=0; y<imageHeight; ++y ) {
for( x=0; x<imageWidth *3; ) {
picBuffer[pos++] = row_pointers[y][x++]; // red
picBuffer[pos++] = row_pointers[y][x++]; // green
picBuffer[pos++] = row_pointers[y][x++]; // blue
}
}
break;
/* 其它色彩类型的图像就不读了 */
default:
png_destroy_read_struct( &png_ptr, &info_ptr, 0);
return false;
}
png_destroy_read_struct( &png_ptr, &info_ptr, 0);
*textureData = picBuffer;
return true;
}
bool TextureManager::isJPG(const unsigned char* data,const unsigned int& dataLen)
{
if (dataLen <= 4)
{
return false;
}
static const unsigned char JPG_SOI[] = {0xFF, 0xD8};
return memcmp(data, JPG_SOI, 2) == 0;
}
bool TextureManager::loadJPG(const unsigned char* data, const unsigned int& dataLen, int& imageWidth,int& imageHeight,unsigned char** textureData,PixelType& pixelType)
{
/* these are standard libjpeg structures for reading(decompression) */
struct jpeg_decompress_struct cinfo;
/* We use our private extension JPEG error handler.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
struct MyErrorMgr jerr;
/* libjpeg data structure for storing one row, that is, scanline of an image */
JSAMPROW row_pointer[1] = {0};
unsigned long location = 0;
unsigned int i = 0;
bool bRet = false;
/* We set up the normal JPEG error routines, then override error_exit. */
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = myErrorExit;
/* Establish the setjmp return context for MyErrorExit to use. */
if (setjmp(jerr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_decompress(&cinfo);
return false;
}
/* setup decompression process and source, then read JPEG header */
jpeg_create_decompress( &cinfo );
jpeg_mem_src( &cinfo, const_cast<unsigned char*>(data), dataLen );
/* reading the image header which contains image information */
#if (JPEG_LIB_VERSION >= 90)
// libjpeg 0.9 adds stricter types.
jpeg_read_header( &cinfo, TRUE );
#else
jpeg_read_header( &cinfo, true );
#endif
// we only support RGB or grayscale
if (cinfo.jpeg_color_space == JCS_GRAYSCALE)
{
pixelType = Pixel_I8;
}else
{
cinfo.out_color_space = JCS_RGB;
pixelType = Pixel_RGB888;
}
/* Start decompression jpeg here */
jpeg_start_decompress( &cinfo );
/* init image info */
imageWidth = cinfo.output_width;
imageHeight = cinfo.output_height;
row_pointer[0] = static_cast<unsigned char*>(malloc(cinfo.output_width*cinfo.output_components * sizeof(unsigned char)));
unsigned int picLen = cinfo.output_width*cinfo.output_height*cinfo.output_components;
unsigned char* picBuffer = static_cast<unsigned char*>(malloc(picLen * sizeof(unsigned char)));
/* now actually read the jpeg into the raw buffer */
/* read one scan line at a time */
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++)
{
picBuffer[location++] = row_pointer[0][i];
}
}
*textureData = picBuffer;
/* When read image file with broken data, jpeg_finish_decompress() may cause error.
* Besides, jpeg_destroy_decompress() shall deallocate and release all memory associated
* with the decompression object.
* So it doesn't need to call jpeg_finish_decompress().
*/
//jpeg_finish_decompress( &cinfo );
jpeg_destroy_decompress( &cinfo );
/* wrap up decompression, destroy objects, free pointers and close open files */
if (row_pointer[0] != nullptr)
{
free(row_pointer[0]);
};
return true;
}
void TextureManager::removeTexture(const char * imagePath)
{
TextureCache::iterator it = _textureCache.find(imagePath);
if (it != _textureCache.end())
{
glDeleteTextures(1, &it->second._textureID);
_textureCache.erase(it);
}
}
void TextureManager::removeAllTexture()
{
TextureCache::iterator it = _textureCache.begin();
while (it != _textureCache.end())
{
glDeleteTextures(1, &it->second._textureID);
++it;
}
_textureCache.clear();
}
TextureManager::~TextureManager()
{
removeAllTexture();
}
}
renderer里的测试代码
int imgWidth;
int imgHeight;
GLuint texture = TextureManager::getInstance()->createTexture("uvtemplate.bmp",imgWidth,imgHeight);
//GLuint texture = TextureManager::getInstance()->createTexture("uvtemplate.png",imgWidth,imgHeight);
//GLuint texture = TextureManager::getInstance()->createTexture("uvtemplate.jpg",imgWidth,imgHeight);
glBindTexture(GL_TEXTURE_2D,texture);
bmp的解析比较简单,直接解析即可。png,jpg用到了libpng,libjpg开源库。libpng又用到zlib。win32要link libzlib.lib,android 直接在android.mk里引用ndk中的zlib LOCAL_LDLIBS := -lz。ios 直接引用库 libz.dylib