使用libpng读取PNG图片像素数据
libpng 的详细介绍:请点这里
libpng 的下载地址:请点这里
下面到重点内容了。如何用pnglib实现对文件的读写? 现在让我们来试着写一段代码来读取PNG像素数据(from File or Stream),并把读取的数据存储到一个数组rgba中,用来生成OpenGl纹理
typedef struct {
u8* pixelData;
int imageWidth;
int imageHeight;
}ImageInfo;
typedef struct {
u8* data;
int size;
int offset;
}ImageSource;
//—————————————————————————————————————-
static void pngReaderCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
ImageSource* isource = (ImageSource*)png_get_io_ptr(png_ptr);
if(isource->offset + length <= isource->size)
{
memcpy(data, isource->data + isource->offset, length);
isource->offset += length;
}
else
{
png_error(png_ptr,”pngReaderCallback failed”);
}
}
//—————————————————————————————————————-
ImageInfo* decodePNGFromStream(const u8* pixelData, const u32& dataSize)
{
png_structp png_ptr;
png_infop info_ptr;
int width, height, rowBytes;
png_byte color_type; //可以是PNG_COLOR_TYPE_RGB,PNG_COLOR_TYPE_PALETTE…….等
png_byte bit_depth;
png_colorp palette;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
if (!png_ptr)
{
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
TFC_DEBUG(“ReadPngFile: Failed to create png_ptr”);
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
TFC_DEBUG(“ReadPngFile: Failed to create info_ptr”);
}
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
TFC_DEBUG(“ReadPngFile: Failed to read the PNG file”);
}
ImageSource imgsource;
imgsource.data = (u8*)pixelData;
imgsource.size = dataSize;
imgsource.offset = 0;
//define our own callback function for I/O instead of reading from a file
png_set_read_fn(png_ptr,&imgsource, pngReaderCallback );
/* **************************************************
* The low-level read interface in libpng (http://www.libpng.org/pub/png/libpng-1.2.5-manual.html)
* **************************************************
*/
png_read_info(png_ptr, info_ptr);
width = info_ptr->width;
height = info_ptr->height;
color_type = info_ptr->color_type;
bit_depth = info_ptr->bit_depth;
rowBytes = info_ptr->rowbytes;
palette= info_ptr->palette;
// Convert stuff to appropriate formats!
if(color_type==PNG_COLOR_TYPE_PALETTE)
{
png_set_packing(png_ptr);
png_set_palette_to_rgb(png_ptr); //Expand data to 24-bit RGB or 32-bit RGBA if alpha available.
}
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_gray_1_2_4_to_8(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
if (bit_depth == 16)
png_set_strip_16(png_ptr);
//Expand paletted or RGB images with transparency to full alpha channels so the data will be available as RGBA quartets.
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png_ptr);
}
//png_read_update_info(png_ptr, info_ptr);
u8* rgba = new u8[width * height * 4]; //each pixel(RGBA) has 4 bytes
png_bytep * row_pointers;
row_pointers = (png_bytep*)png_malloc(sizeof(png_bytep) * height);
for (int y = 0; y < height; y++)
{
row_pointers[y] = (png_bytep)png_malloc(width<<2); //each pixel(RGBA) has 4 bytes
}
png_read_image(png_ptr, row_pointers);
//unlike store the pixel data from top-left corner, store them from bottom-left corner for OGLES Texture drawing…
int pos = (width * height * 4) – (4 * width);
for(int row = 0; row < height; row++)
{
for(int col = 0; col < (4 * width); col += 4)
{
rgba[pos++] = row_pointers[row][col]; // red
rgba[pos++] = row_pointers[row][col + 1]; // green
rgba[pos++] = row_pointers[row][col + 2]; // blue
rgba[pos++] = row_pointers[row][col + 3]; // alpha
}
pos=(pos – (width * 4)*2); //move the pointer back two rows
}
ImageInfo* imageInfo = (ImageInfo*)kdMalloc(sizeof(ImageInfo));
imageInfo->pixelData = rgba;
imageInfo->imageHeight = height;
imageInfo->imageWidth = width;
//clean up after the read, and free any memory allocated
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return imageInfo;
}
//—————————————————————————————————————-
ImageInfo* decodePNGFromFile(char* fileName)
{
char png_header[8];
png_structp png_ptr;
png_infop info_ptr;
int width, height, rowBytes;
png_byte color_type;
png_byte bit_depth;
png_colorp palette;
/* open file and test for it being a png */
FILE *file = fopen(fileName, “rb”);
fread(png_header, 1, 8, file);
if(png_sig_cmp((png_bytep)png_header, 0, 8))
{
TFC_DEBUG(“Not a PNG file…”);
fclose(file);
}
/* initialise structures for reading a png file */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info_ptr = png_create_info_struct(png_ptr);
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
TFC_DEBUG(“ReadPngFile: Failed to read the PNG file”);
fclose(file);
}
//I/O initialisation methods
png_init_io(png_ptr, file);
png_set_sig_bytes(png_ptr, 8); //Required!!!
/* **************************************************
* The high-level read interface in libpng (http://www.libpng.org/pub/png/libpng-1.2.5-manual.html)
* **************************************************
*/
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
width = info_ptr->width;
height = info_ptr->height;
unsigned char* rgba = new unsigned char[width * height * 4]; //each pixel(RGBA) has 4 bytes
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
//Original PNG pixel data stored from top-left corner, BUT OGLES Texture drawing is from bottom-left corner
//int pos = 0;
//for(int row = 0; row < height; row++)
//{
//for(int col = 0; col < (4 * width); col += 4)
//{
//rgba[pos++] = row_pointers[row][col]; // red
//rgba[pos++] = row_pointers[row][col + 1]; // green
//rgba[pos++] = row_pointers[row][col + 2]; // blue
//rgba[pos++] = row_pointers[row][col + 3]; // alpha
//}
//}
//unlike store the pixel data from top-left corner, store them from bottom-left corner for OGLES Texture drawing…
int pos = (width * height * 4) – (4 * width);
for(int row = 0; row < height; row++)
{
for(int col = 0; col < (4 * width); col += 4)
{
rgba[pos++] = row_pointers[row][col]; // red
rgba[pos++] = row_pointers[row][col + 1]; // green
rgba[pos++] = row_pointers[row][col + 2]; // blue
rgba[pos++] = row_pointers[row][col + 3]; // alpha
}
pos=(pos – (width * 4)*2); //move the pointer back two rows
}
ImageInfo* imageInfo = (ImageInfo*)kdMalloc(sizeof(ImageInfo));
imageInfo->pixelData = rgba;
imageInfo->imageHeight = height;
imageInfo->imageWidth = width;
//clean up after the read, and free any memory allocated
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(file);
return imageInfo;
}
//—————————————————————————————————————-
int createPNGTextureFromStream(const u8* pixelData, const u32& dataSize)
{
GLuint textureID;
glEnable(GL_TEXTURE_2D);
glGenTextures(1,&textureID);
glBindTexture(GL_TEXTURE_2D,textureID);
ImageInfo* imageInfo = decodePNGFromStream(pixelData, dataSize);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,imageInfo->imageWidth,imageInfo->imageHeight,0,
GL_RGBA,GL_UNSIGNED_BYTE,imageInfo->pixelData);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
delete[] imageInfo->pixelData;
delete imageInfo;
return textureID;
}
//—————————————————————————————————————-
void main()
{
//Testcase1: decoding png data from a raw png bufferstream and create the corresponding Texture
//假设我们从某个地方可以拿到一个unsigned char* 的PNG数据源pixelData.
int texId = createPNGTextureFromStream(pixelData, dataSize);
//Testcase2: decoding png data from a given png file and and create the corresponding Texture
char* fileName = “example.png”;
int texId = createPNGTextureFromFile(fileName);
//现在我们就可以用创建出来的textureID来绘制纹理了。。。。
}
接下来请看第2页精彩内容: http://www.linuxidc.com/Linux/2013-08/88651p2.htm
推荐阅读:
利用libpng中的函数读写PNG文件 http://www.linuxidc.com/Linux/20