纹理解析

纹理解析
绘图时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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值