内存png读取程序
//pngReader.h
#ifndef _PNGREADER_H_
#define _PNGREADER_H_
#include "gfx/mstBitmap.h"
#include "png.h"
/* Transparency parameters */
#define PNG_ALPHA -2 /* Use alpha channel in PNG file, if there is one */
#define PNG_SOLID -1 /* No transparency */
#define PNG_STENCIL 0 /* Sets alpha to 0 for r=g=b=0, 1 otherwise */
#define HEADER_BYTES_TO_CHECK 8
typedef struct
{
LPBYTE data;
DWORD size;
LPDWORD bytesRead;
} ReadInfo, *ReadInfoPtr;
class PngReader
{
public:
PngReader() {};
~PngReader() {};
static MstBitmap* readPngStream( void* stream,int size);
static MstBitmap* readPngFile( const char* fileName);
static void png_error(png_structp png_ptr, png_const_charp message);
static void png_warning(png_structp png_ptr, png_const_charp message);
protected:
static void pngReadDataFn(png_structp png_ptr, png_bytep data, png_size_t length);
private:
};
#endif
//pngReader.cpp
#include <windows.h>
#include "pngReader.h"
#include <assert.h>
#include <iostream>
#include <GL/gl.h>
extern "C"
{
#include <zlib.h>
#include <png.h>
}
/**主要参考《libpng实现内存内位图的压缩及解压缩.mht》和osg2.2\src\osgplugins\pngReaderWriterPNG.cpp完成。
当bmds/trans,为透明png时,color为(PNG_COLOR_TYPE_RGB_ALPHA),即(PNG_COLOR_MASK_COLOR)|4(PNG_COLOR_MASK_ALPHA)。
当bmds/vect,png不透明时,color为(PNG_COLOR_TYPE_PALETTE),即(PNG_COLOR_MASK_COLOR)|1(PNG_COLOR_MASK_PALETTE)。
*/
MstBitmap* PngReader::readPngStream(void* stream,int size)
{
if( !stream|| size<max(1, HEADER_BYTES_TO_CHECK))
return FALSE;
if(png_sig_cmp( (unsigned char *)stream, (png_size_t)0, HEADER_BYTES_TO_CHECK))//libpng新版本用png_sig_cmp取代png_check_sig
return NULL;
//是否剔除Alpha信息,剔除时,选择PNG_SOLID
int trans = PNG_ALPHA;//PNG_SOLID;
png_structp png;
png_infop info;
png_infop endinfo;
png_bytep data;
png_bytep *row_p;
double fileGamma;
png_uint_32 width, height;
int depth, color;
png_uint_32 i;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info = png_create_info_struct(png);
endinfo = png_create_info_struct(png);
png_set_error_fn(png, NULL, PngReader::png_error, PngReader::png_warning);
if(setjmp(png_jmpbuf(png)))
return NULL;
//由于是内存源,比对标志后不需要恢复当前文件指针,所以一定要设置成零,不然会造成后续png_read_info等函数指针越界等连锁错误。
//png_set_sig_bytes(png, 8);/*HEADER_BYTES_TO_CHECK*/
png_set_sig_bytes(png, 0);
ReadInfo readInfo;
readInfo.data = (unsigned char *)stream;
readInfo.size = size;
unsigned int bytes = 0;
readInfo.bytesRead = (LPDWORD)&bytes;
png_set_read_fn(png, &readInfo, PngReader::pngReadDataFn);
png_read_info(png, info);
png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);
if(!(width > 0 && height > 0) )
return NULL;
if ( (color&PNG_COLOR_MASK_ALPHA) && trans != PNG_ALPHA)
{
png_set_strip_alpha(png);//剔除alpha信息
color &= ~PNG_COLOR_MASK_ALPHA;
}
if (color == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
if (color == PNG_COLOR_TYPE_GRAY && depth < 8)
png_set_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png);
trans = PNG_ALPHA;
}
// Make sure that files of small depth are packed properly.
if (depth < 8)
png_set_packing(png);
/*--GAMMA--*/
// checkForGammaEnv();
double screenGamma = 2.2 / 1.0;
if (png_get_gAMA(png, info, &fileGamma))
png_set_gamma(png, screenGamma, fileGamma);
else
png_set_gamma(png, screenGamma, 1.0/2.2);
png_read_update_info(png, info);
//必须在png_read_update_info后调用png_get_rowbytes,否则得到的是转化前的rowBytes;
//必须注意! 否则后面分配内存出错导致png_read_image或delete[] row_p出现奇怪的错误。
png_uint_32 rowBytes = png_get_rowbytes(png, info);
data = (png_bytep) new unsigned char [png_get_rowbytes(png, info)/*rowBytes*/ *height];
row_p = new png_bytep [height];
//bool StandardOrientation = true;
bool StandardOrientation = false;
for (i = 0; i < height; i++)
{
if (StandardOrientation)
row_p[height - 1 - i] = &data[rowBytes*i];
else
row_p[i] = &data[rowBytes*i];
}
png_read_image(png, row_p); //Decode
delete[] row_p;
png_read_end(png, endinfo);
png_destroy_read_struct(&png, &info, &endinfo);
GLenum pixelFormat = 0;
GLenum dataType = depth<=8 ? GL_UNSIGNED_BYTE :GL_UNSIGNED_SHORT;
switch(color)
{
case(PNG_SOLID): pixelFormat = GL_LUMINANCE; break;
case(PNG_ALPHA): pixelFormat = GL_ALPHA; break;
case(PNG_COLOR_TYPE_GRAY): pixelFormat = GL_LUMINANCE ; break;
case(PNG_COLOR_TYPE_GRAY_ALPHA): pixelFormat = GL_LUMINANCE_ALPHA; break;
case(PNG_COLOR_TYPE_RGB): pixelFormat = GL_RGB; break;
case(PNG_COLOR_TYPE_PALETTE): pixelFormat = GL_RGB; break;
case(PNG_COLOR_TYPE_RGB_ALPHA): pixelFormat = GL_RGBA; break;
default: break;
}
// Some paletted images contain alpha information. To be
// able to give that back to the calling program, we need to
// check the number of channels in the image. However, the
// call might not return correct information unless
// png_read_end is called first. See libpng man page.
if (pixelFormat == GL_RGB && png_get_channels(png, info) == 4)
pixelFormat = GL_RGBA;
int internalFormat = pixelFormat;
MstBitmap *pbi = (MstBitmap*)malloc(sizeof(MstBitmap));//存放JPEG数据
if (pbi != NULL)
{
pbi->width = width;
pbi->height = height;
pbi->pixelFormat = pixelFormat;
pbi->internalFormat = internalFormat;
pbi->dataType = dataType;
pbi->data = (unsigned char *)data;
}
return pbi;
}
void PngReader::pngReadDataFn(png_structp png, png_bytep data, png_size_t length)
{
if(!png )
return;
ReadInfoPtr pwd=(ReadInfoPtr)png_get_io_ptr(png);
if(!pwd || !pwd->bytesRead)
return;
if(pwd->data)
{
if(*pwd->bytesRead+length>pwd->size)
return;
memcpy(data, pwd->data+ (*pwd->bytesRead), length);
*pwd->bytesRead += length;
}
}
void PngReader::png_warning(png_structp png, png_const_charp message)
{
fprintf(stdout, "libpng warning: %s\n", message);
}
void PngReader::png_error(png_structp png, png_const_charp message)
{
PngReader::png_warning(png, message);
}

浙公网安备 33010602011771号