libpng 是一套免费的、公开源代码的程序库,支持对 PNG 图形文件的创建、读写等操作。可以到www.libpng.org 上下载源代码。libpng 使用 zlib 程序库作为压缩引擎,zlib 也是著名的 gzip (GNU zip) 所采用的压缩引擎。zlib 是通用的压缩库,提供了一套 in-memory 压缩和解压函数,并能检测解压出来的数据的完整性(integrity)。zlib 也支持读写 gzip (.gz) 格式的文件这些都是免费和公开的哦。在下载了pbglib,并且要下载zlib,如何才能加到代码中呢?以VC6.为例来说明。其他CBuild等编译器相信也一样的。比如我们从网上下载了一个lpng1210.zip和zlib-1.2.3.tar.tar。首先把他们解压。在lpng的目录中寻找 projects/visualc6/libpng.dsw。并在zlib中寻找zlib-1.2.3/projects/visualc6 /zlib.dsw。用vc打开libpng工程文件,把zlib的工程文件也添加进去。设置好在setting中设置好的zlib.lib和pbg.lib的输出路径,以及include路径(这个简单方法相信大家都明白吧)。运行后,就生成了两个静态库了。lib做好以后,就可以将lib加到我们要做的工程中去。在link中设置好,并将png.h和zlib.h都加进去。这样就可以正式的使用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来绘制纹理了。。。。 }
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
附录 让我们打开pnglib下面的一个example.c
/* Read a PNG file. You may want to return an error code if the read * fails (depending upon the failure). There are two \"prototypes\" given * here - one where we are given the filename, and we need to open the * file, and the other where we are given an open file (possibly with * some or all of the magic bytes read - see comments above). */
#ifdef open_file /* prototype 1 */
void read_png(char *file_name) /* We need to open the file */ {
定义两个很重要的pngwen文件指针。png_info里面放的是pngchuck和png的数据。 png_structp png_ptr; png_infop info_ptr;
这些是读出图像需要的参数,图像高。宽、深度、图像类型、交错类型。 unsigned int sig_read = 0; png_uint_32 width, height;
int bit_depth, color_type, interlace_type; FILE *fp;
打开文件,把文件指针给png_prt if ((fp = fopen(file_name, \"rb\")) == NULL) return (ERROR);
#else no_open_file /* prototype 2 */
void read_png(FILE *fp, unsigned int sig_read) /* file is already open */ {
png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height;
int bit_depth, color_type, interlace_type; #endif no_open_file /* only use one prototype! */
/* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also supply the * the compiler header file version, so that we know if the application * was compiled with a compatible version of the library. REQUIRED */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp user_error_ptr, user_error_fn, user_warning_fn);
if (png_ptr == NULL) {
fclose(fp); return (ERROR); }
/* Allocate/initialize the memory for image information. REQUIRED. */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) {
fclose(fp);
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); return (ERROR); }
/* Set error handling if you are using the setjmp/longjmp method (this is * the normal method of doing things with libpng). REQUIRED unless you * set up your own error handlers in the png_create_read_struct() earlier. */
if (setjmp(png_jmpbuf(png_ptr))) {
/* Free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); fclose(fp);
/* If we get here, we had a problem reading the file */ return (ERROR); }
/* One of the following I/O initialization methods is REQUIRED */ #ifdef streams /* PNG file I/O method 1 */
/* Set up the input control if you are using standard C streams */ png_init_io(png_ptr, fp);
#else no_streams /* PNG file I/O method 2 */
/* If you are using replacement read functions, instead of calling * png_init_io() here you would call: */
png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);
/* where user_io_ptr is a structure you want available to the callbacks */ #endif no_streams /* Use only one I/O method! */
/* If we have already read some of the signature */ png_set_sig_bytes(png_ptr, sig_read);
#ifdef hilevel /*
* If you have enough memory to read in the entire image at once, * and you need to specify only transforms that can be controlled * with one of the PNG_TRANSFORM_* bits (this presently excludes * dithering, filling, setting background, and doing gamma * adjustment), then you can read the entire image (including * pixels) into the info structure with this call: */
png_read_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL); #else
/* OK, you're doing it the hard way, with the lower-level functions */
/* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). REQUIRED */
png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
/* Set up the data transformations you want. Note that these are all * optional. Only call them if you want/need them. Many of the * transformations only work on specific types of images, and many * are mutually exclusive. */
/* tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr);
/* Strip alpha bytes from the input data without combining with the * background (not recommended). */
png_set_strip_alpha(png_ptr);
/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */
png_set_packing(png_ptr);
/* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ png_set_packswap(png_ptr);
/* Expand paletted colors into true RGB triplets */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_rgb(png_ptr);
/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_gray_1_2_4_to_8(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);
/* Set the background color to draw transparent and alpha images over. * It is possible to set the red, green, and blue components directly
* for paletted images instead of supplying a palette index. Note that * even if the PNG file supplies a background, you are not required to * use it - you should use the (solid) application background if it has one. */
png_color_16 my_background, *image_background;
if (png_get_bKGD(png_ptr, info_ptr, &image_background)) png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); else
png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
/* Some suggestions as to how to get a screen gamma value */
/* Note that screen gamma is the display_exponent, which includes * the CRT_exponent and any correction for viewing conditions */ if (/* We have a user-defined screen gamma value */) {
screen_gamma = user-defined screen_gamma; }
/* This is one way that applications share the same screen gamma value */ else if ((gamma_str = getenv(\"SCREEN_GAMMA\")) != NULL) {
screen_gamma = atof(gamma_str); }
/* If we don't have another value */ else {
screen_gamma = 2.2; /* A good guess for a PC monitors in a dimly lit room */
screen_gamma = 1.7 or 1.0; /* A good guess for Mac systems */
}
/* Tell libpng to handle the gamma conversion for you. The final call * is a good guess for PC generated images, but it should be configurable * by the user at run time by the user. It is strongly suggested that * your application support gamma correction. */
int intent;
if (png_get_sRGB(png_ptr, info_ptr, &intent)) png_set_gamma(png_ptr, screen_gamma, 0.45455); else {
double image_gamma;
if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) png_set_gamma(png_ptr, screen_gamma, image_gamma); else
png_set_gamma(png_ptr, screen_gamma, 0.45455); }
/* Dither RGB files down to 8 bit palette or reduce palettes * to the number of colors available on your screen. */
if (color_type & PNG_COLOR_MASK_COLOR) {
int num_palette; png_colorp palette;
/* This reduces the image to the application supplied palette */ if (/* we have our own palette */) {
/* An array of colors to which the image should be dithered */
png_color std_color_cube[MAX_SCREEN_COLORS];
png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS, MAX_SCREEN_COLORS, png_uint_16p_NULL, 0); }
/* This reduces the image to the palette supplied in the file */ else if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) {
png_uint_16p histogram = NULL;
png_get_hIST(png_ptr, info_ptr, &histogram);
png_set_dither(png_ptr, palette, num_palette, max_screen_colors, histogram, 0); } }
/* invert monochrome files to have 0 as white and 1 as black */ png_set_invert_mono(png_ptr);
/* If you want to shift the pixel values from the range [0,255] or * [0,65535] to the original [0,7] or [0,31], or whatever range the * colors were originally in: */
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) {
png_color_8p sig_bit;
png_get_sBIT(png_ptr, info_ptr, &sig_bit); png_set_shift(png_ptr, sig_bit); }
/* flip the RGB pixels to BGR (or RGBA to BGRA) */
if (color_type & PNG_COLOR_MASK_COLOR) png_set_bgr(png_ptr);
/* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ png_set_swap_alpha(png_ptr);
/* swap bytes of 16 bit files to least significant byte first */ png_set_swap(png_ptr);
/* Add filler (or alpha) byte (before/after each RGB triplet) */ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
/* Turn on interlace handling. REQUIRED if you are not using * png_read_image(). To see how to handle interlacing passes, * see the png_read_row() method below: */
number_passes = png_set_interlace_handling(png_ptr);
/* Optional call to gamma correct and add the background to the palette * and update info structure. REQUIRED if you are expecting libpng to * update the palette for you (ie you selected such a transform above). */
png_read_update_info(png_ptr, info_ptr);
/* Allocate the memory to hold the image using the fields of info_ptr. */
/* The easiest way to read the image: */ png_bytep row_pointers[height];
for (row = 0; row < height; row++) {
row_pointers[row] = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
}
/* Now it's time to read the image. One of these methods is REQUIRED */ #ifdef entire /* Read the entire image in one go */ png_read_image(png_ptr, row_pointers);
#else no_entire /* Read the image one or more scanlines at a time */ /* The other way to read images - deal with interlacing: */
for (pass = 0; pass < number_passes; pass++) {
#ifdef single /* Read the image a single row at a time */ for (y = 0; y < height; y++) {
png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1); }
#else no_single /* Read the image several rows at a time */ for (y = 0; y < height; y += number_of_rows) {
#ifdef sparkle /* Read the image using the \"sparkle\" effect. */ png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, number_of_rows);
#else no_sparkle /* Read the image using the \"rectangle\" effect */ png_read_rows(png_ptr, png_bytepp_NULL, &row_pointers[y], number_of_rows);
#endif no_sparkle /* use only one of these two methods */ }
/* if you want to display the image after every pass, do so here */
#endif no_single /* use only one of these two methods */ }
#endif no_entire /* use only one of these two methods */
/* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); #endif hilevel
/* At this point you have read the entire image */
/* clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
/* close the file */ fclose(fp);
/* that's it */ return (OK); }
zlib 与 libpng 的配置与使用
Solstice 2003/04/3
说明:本文节选自我主页上的一篇文章,原文介绍了1) Wave 文件的格式、2)读取 Wave 文件内容,并显示文件的基本信息、3)压缩库 zlib 的安装与简单应用、4)PNG 库 libpng 的安装、5)使用 libpng 生成 PNG 文件、6)绘制 Wave 文件的波形,这里只截取3、4、5这三部分内容。本文涉及的源码可从我的主页下载(http://www.chenshuo.com )。
PNG 格式的图片在网络上非常流行,几乎所有浏览器都支持这种格式。 PNG 代表 Portable Network Graphics ——可移植网络图形格式。我偏爱 PNG 图片的另一个原因是,在 LaTeX 生成的 PDF 文件中,可以直接嵌入 PNG 文件。 PNG 与 GIF 类似,是无损压缩的光栅图形格式。与 GIF 文件不同,编写生成 PNG 文件的软件不需要支付任何版权费用。因此, PNG 的非官方名称为 Png's Not Gif ,够搞笑,是吧?
尽管 PNG 文件的格式并不复杂,我还是决定用一套现成的程序库来读写它,不要总是自己重新发明轮子嘛。我们先来看看怎么安装使用 PNG 文件的官方程序库—— libpng 和 zlib 。常见的 Linux 系统都配备了这两个程序库,因此我只打算介绍在 Windows 下的安装方法。
以下的操作以免费的 Borland C++ Compiler 5.5.1 free 编译器为例, Microsoft Visual C++ 安装方法大致与此类似,但我没有条件测试。
zlib 的安装
libpng 是一套免费的、公开源代码的程序库,支持对 PNG 图形文件的创建、读写等操作。 libpng 使用 zlib 程序库作为压缩引擎, zlib 也是著名的 gzip (GNU zip) 所采用的压缩引擎。
我们首先安装 zlib ,从其官方网站下载最新的源程序,不妨假设文件名是 zlib-1.1.4.tar.gz 。网址: http://www.gzip.org/zlib/ 。
在 D:/ 建立 libpng 目录,将 zlib-1.1.4.tar.gz 释放到这个目录。尽管没有合适的 makefile ,我们仍然可以直接编译链接 zlib.lib :
D:/libpng/zlib-1.1.4>bcc32 -c -O2 -6 -w-8004 -w-8057 -w-8012 *.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland adler32.c: compress.c: crc32.c: deflate.c: example.c: gzio.c: infblock.c: infcodes.c: inffast.c: inflate.c: inftrees.c:
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- baoaiwan.cn 版权所有 赣ICP备2024042794号-3
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务