libpng库的开发

来源:互联网 发布:云计算与人工智能 编辑:程序博客网 时间:2024/05/30 04:20

1、libpng是什么?
libpng是一款C语言编写的比较底层的读写PNG文件的跨平台的库,全称是可携式网络图像(portable network graphics),
借助它,你可以轻松读写PNG文件的每一行像素。因为PNG文件是经过压缩而且格式复杂的图形文件(有的PNG文件甚至像GIF文件一样带动画效果,但是不像jpg那样是有损压缩,png是无损压缩的),而且PNG可以是带透明通道的真彩色图像、不带透明通道的真彩色图像、索引颜色、灰度颜色等各种格式,如果大家都自己写程序分析PNG文件就会显得很麻烦、很累。因此,通过使用libpng你就能直接使用现成的函数、程序来读写PNG文件了。

2、怎样让自己的程序可以使用libpng库?
有很多种方法。https://ftp-osl.osuosl.org/pub/libpng/src/ 这里是libpng和zlib的下载位置,遵循GPL协议。 
方法1:上网下载libpng的DLL、LIB文件以及头文件,然后在自己的程序里,包含png.h,链接libpng.lib,就可以了。但是这样的话你的程序需要libpng.dll才能运行,而libpng使用了zlib所以可能你还需要zlib.dll才能运行。因此你还需要下载zlib的头文件、lib、DLL。
方法2:直接下载libpng的源码和zlib的源码,然后把.c文件和.h文件都加入到自己的工程里面。这招最好使因为这样便于调试。只是你的程序会很大因为你的程序直接集成了libpng和zlib。
方法3:下载libpng的源码和zlib的源码,自己将其编译为DLL或LIB,然后包含png.h,链接LIB文件,就能使用。不过你如果没编译好也可能会出问题。

3.PNG文件结构的分析。
    介绍:与BMP格式相比,PNG格式稍微复杂些。PNG图像支持从0~255级次的多层透明色,使用无损压缩的zlib压缩算法压缩图像数据。通常使用 zlib压缩过的图像文件大小比BMP使用的RLE压缩的效果好,BMP的RLE压缩算法只支持压缩8位以下的图像,对于16位以上的真彩色图像不支持图 像压缩,PNG使用的zlib压缩算法支持任何色深的图像数据压缩,压缩后的图像数据可以完整还原,相比之下JPG需要牺牲图像质量使用有损压缩来获得大 的压缩率。特别注意:PNG图像格式使用Big-Endian顺序存储数据。

PNG的文件结构

对于一个PNG文件来说,其文件头总是由位固定的字节来描述的:

十进制数

137 80 78 71 13 10 26 10

十六进制数

89 50 4E 47 0D 0A 1A 0A

其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理。文件中剩余的部分由3个以上的PNG的数据块(Chunk)按照特定的顺序组成,因此,一个标准的PNG文件结构应该如下:

PNG文件标志

PNG数据块

……

PNG数据块

所以一个最小的PNG文件由一个PNG文件标志和三个PNG数据块组成。

PNG的文件表示没什么好说的,就是上面固定的8个字节(是存放在文件开头的前8个字节),就是用来检查该文件是否是png文件,比如libpng源码库中中有下面一段代码来用于检测一个文件是否是png文件。

example.c 244
/* Check to see if a file is a PNG file using png_sig_cmp().  png_sig_cmp()
* returns zero if the image is a PNG and nonzero if it isn't a PNG.
*
* The function check_if_png() shown here, but not used, returns nonzero (true)
* if the file can be opened and is a PNG, 0 (false) otherwise.
*
* If this call is successful, and you are going to keep the file open,
* you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once
* you have created the png_ptr, so that libpng knows your application
* has read that many bytes from the start of the file.  Make sure you
* don't call png_set_sig_bytes() with more than 8 bytes read or give it
* an incorrect number of bytes read, or you will either have read too
* many bytes (your fault), or you are telling libpng to read the wrong
* number of magic bytes (also your fault).
*
* Many applications already read the first 2 or 4 bytes from the start
* of the image to determine the file type, so it would be easiest just
* to pass the bytes to png_sig_cmp() or even skip that if you know
* you have a PNG file, and call png_set_sig_bytes().
*/
#define PNG_BYTES_TO_CHECK 4
int check_if_png(char *file_name, FILE **fp)
{
  char buf[PNG_BYTES_TO_CHECK];

  /* Open the prospective PNG file. */
  if ((*fp = fopen(file_name, "rb")) == NULL)
      return 0;

  /* Read in some of the signature bytes */
  if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK)
      return 0;

  /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
      Return nonzero (true) if they match */

  return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
}
png.c 65
/* Checks whether the supplied bytes match the PNG signature.  We allow
* checking less than the full 8-byte signature so that those apps that
* already read the first few bytes of a file to determine the file type
* can simply check the remaining bytes for extra assurance.  Returns
* an integer less than, equal to, or greater than zero if sig is found,
* respectively, to be less than, to match, or be greater than the correct
* PNG signature (this is the same behavior as strcmp, memcmp, etc).
*/
int PNGAPI
png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
{
  png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};

  if (num_to_check > 8)
      num_to_check = 8;

  else if (num_to_check < 1)
      return (-1);

  if (start > 7)
      return (-1);

  if (start + num_to_check > 8)
      num_to_check = 8 - start;

  return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));//是比较内存区域buf1和buf2的前count个字节,相等返回0,不等返回非0.
}

下面着重介绍数据块的组成。

紧跟在PNG文件标志后面的数据是数据块(chunks),数据块(chunks)分为两类:关键数据块(critical chunks)和辅助数据块(ancillary chunks)。

关键数据块(critical chunk)在PNG文件中是必须有的,而辅助数据块(ancillary chunks)是可选的。

关键数据块(critical chunks)由4部分组成:文件头数据块(IHDR)、调色板数据块(PLTE)、图像数据块(IDAT)和图像结束数据(IEND),其中调色板数据块(PLTE)根据图像的色深可选。

数据块名称

允许多

个数据块

位 置

文件头数据块(IHDR)

不允许

第一个数据块

调色板数据块(PLTE)

不允许

第二个数据块,可选

图像数据块(IDAT)

允许

如果有调色板数据块(PLTE),则是第三个数据块,如果没有调色板数据块(PLTE),则时第二个数据块。如果有多个图像数据块,则必须按图像数据连续存储

图像结束数据(IEND)

不允许

最后一个数据块


辅助数据块(ancillary chunks)一共有14个,这些辅助数据块包含了很多信息,辅助数据块不是必须包含的。

数据块名称

允许多个

数据块

位 置

基色和白色点数据块(cHRM

不允许

在PLTE和IDAT之前

图像γ数据块(gAMA

不允许

在PLTE和IDAT之前

ICCP(iCCP)

允许

在PLTE之后IDAT之前如果有iCCP,则无sRGB

数据块名称

允许多个

数据块

位 置

样本有效位数据块(sBIT

不允许

在PLTE和IDAT之前

标准RPG颜色(sRGB

不允许

在PLTE之后IDAT之前如

果有sRGB,则无iCCP

背景颜色数据块(bKGD

不允许

在PLTE之后IDAT之前

图像直方图数据块(hIST

不允许

在PLTE之后IDAT之前

图像透明数据块(tRNS

不允许

在PLTE之后IDAT之前

物理像素尺寸数据块(pHYs

不允许

在IDAT之前

建议调色板(sPLT

允许

在IDAT之前

图像最后修改时间数据块(tIME

不允许

无限制

国际文本数据(iTXt

允许

无限制

文本信息数据块(tEXt

允许

无限制

压缩文本数据块(zTXt

允许

无限制

PNG中每个数据块的格式由4个部分组成:

名称

字节数 

说明

Length (长度)

4字节

指定数据块中数据域的长度,其长度不超过(231-1)字节

Chunk Type Code (数据块类型码)

4字节

数据块类型码由ASCII字母(A-Z和a-z)组成 

Chunk Data (数据块数据) 

可变长度

存储按照Chunk Type Code指定的数据

CRC (循环冗余检测)

4字节

存储用来检测是否有错误的循环冗余码

CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的。CRC具体算法定义在ISO 3309和ITU-T V.42中,其值按下面的CRC码生成多项式进行计算:

x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1

下面,我们依次来了解一下各个关键数据块的结构吧。

IHDR

文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。

文件头数据块由13 + 12(长度信息、数据类型码、循环检测)字节组成,它的格式如下表所示。

域的名称

字节数 

说明

Width

4 bytes

图像宽度,以像素为单位

Height

4 bytes

图像高度,以像素为单位

Bit depth

1 byte 

图像深度: 
索引彩色图像:1,2,4或8 
灰度图像:1,2,4,8或16 
真彩色图像:8或16

ColorType

1 byte 

颜色类型:
0:灰度图像, 1,2,4,8或16 
2:真彩色图像,8或16 
3:索引彩色图像,1,2,4或8 
4:带α通道数据的灰度图像,8或16 
6:带α通道数据的真彩色图像,8或16

Compression method

1 byte 

压缩方法(LZ77派生算法)

Filter method

1 byte 

滤波器方法

Interlace method 

1 byte 

隔行扫描方法:
0:非隔行扫描 
1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法)


PLTE

调色板数据块PLTE(palette chunk)包含有与索引彩色图像(indexed-color image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。

PLTE数据块是定义图像的调色板信息,PLTE可以包含1~256个调色板信息,每一个调色板信息由3个字节组成:

字段名

大小(单

位:字节)

描 述

btRed

1

红色颜色值

btGreen

1

绿色颜色值

btBlue

1

蓝色颜色值


因此,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。

对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。

真彩色图像和带α通道数据的真彩色图像也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。

IDAT

图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。

IDAT存放着图像真正的数据信息,因此,如果能够了解IDAT的结构,我们就可以很方便的生成PNG图像。

图像数据块中的图像数据可能是经过变种的LZ77压缩编码DEFLATE压缩的,关于DEFLATE详细介绍可以参考《DEFLATE Compressed Data Format Specification version 1.3》,网址: http://www.ietf.org/rfc/rfc1951.txt 

IEND

图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。

如果我们仔细观察PNG文件,我们会发现,文件的结尾12个字符看起来总应该是这样的:

00 00 00 00 49 45 4E 44 AE 42 60 82

不难明白,由于数据块结构的定义,IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82。


PNG的辅助数据块(ancillary chunks)一共有14个,可以分为5类,上面已有,由于时间关系不能将全部辅助数据块(ancillary chunks)的详细结构进行说明,如果读者有兴趣请参考 http://www.w3.org/TR/REC-png.html。


使用libpng的编程方法:

1.判断文件是否为png文件。

2.初始化libpng数据结

png_info  png_structp png_ptr;

png_infop  info_ptr;

3.设置错误的返回点。

4.设置数据源

5.读取png数据

6.对数据进行处理。

7.释放libpng内存。

下面为一段测试代码:png_to_bmp bmp_to_png

#include <stdio.h>

#include <png.h> 

#include <stdlib.h>

#include <malloc.h>

#include <string.h>


typedef unsigned char  BYTE;

typedef unsigned short WORD;

typedef unsigned long  DWORD;


#pragma pack(1)

typedef struct tagBITMAPFILEHEADER{

    WORD    bfType;                // the flag of bmp, value is "BM"

    DWORD    bfSize;                // size BMP file ,unit is bytes

    DWORD    bfReserved;            // 0

    DWORD    bfOffBits;            // must be 54


}BITMAPFILEHEADER;


typedef struct tagBITMAPINFOHEADER{

    DWORD    biSize;                // must be 0x28

    DWORD    biWidth;          //

    DWORD    biHeight;          //

    WORD            biPlanes;          // must be 1

    WORD            biBitCount;            //

    DWORD    biCompression;        //

    DWORD    biSizeImage;      //

    DWORD    biXPelsPerMeter;  //

    DWORD    biYPelsPerMeter;  //

    DWORD    biClrUsed;            //

    DWORD    biClrImportant;        //

}BITMAPINFOHEADER;

#pragma pack(4)

/******************************图片数据*********************************/

typedef struct _pic_data pic_data;

struct _pic_data

{

int width, height; /* 尺寸 */

int bit_depth;  /* 位深 */

int flag;  /* 一个标志,表示是否有alpha通道 */

unsigned char *rgba; /* 图片数组 */

};

/**********************************************************************/

/*

    写入bmp数据为头

    fp:文件指针

    width:图像的宽度

    height:图像的高度

*/

int write_bmp_header(FILE *fp,int width, int height)

{

    BITMAPFILEHEADER  bf;

  BITMAPINFOHEADER  bi;

  

    //Set BITMAPINFOHEADER

    bi.biSize = 40;

    bi.biWidth = width;

    bi.biHeight = height;

    bi.biPlanes = 1;

    bi.biBitCount = 24;

    bi.biCompression = 0;

    bi.biSizeImage = height*width*3;

    bi.biXPelsPerMeter = 0;

    bi.biYPelsPerMeter = 0;

    bi.biClrUsed = 0;

    bi.biClrImportant = 0;


//Set BITMAPFILEHEADER

  bf.bfType = 0x4d42;

  bf.bfSize = 54 + bi.biSizeImage;    

    bf.bfReserved = 0;

  bf.bfOffBits = 54;

  fwrite(&bf, 14, 1, fp); //先写54字节的头部数据

  fwrite(&bi, 40, 1, fp); 

  

    return 0;

}

/*

    将rgb数组数据写入bmp文件中

    filename:bmp文件名

    out:存在rgb数据的数组 格式为bgr bgr ...

*/

int write_bmp(const char *filename, pic_data *out)

{

    FILE *fp;

    int width, height;

    int i, j, count=0, linesize=0;

    unsigned char  * lineData = NULL;

        

    width = out->width;

    height = out->height;

    linesize = width*3;

    count = height /2;

    

    if((fp = fopen(filename, "wb+")) == NULL){

        perror("fopen bmp error");

        return -1;

    }

    //write_bmp_header(fp, width, height);

    

  lineData = (unsigned char*)malloc(linesize); 

  if(lineData == NULL){

      perror("malloc lineData error");

      return -1;

  }

        

    for(i=0; i<count; i++){    

      memcpy(lineData, out->rgba + (i*linesize), linesize);

      memcpy(out->rgba + (i*linesize), out->rgba+ (height - i -1)*linesize, linesize); 

      memcpy(out->rgba+ (height - i -1)*linesize, lineData, linesize);  

  }  

  fwrite(out->rgba, width*height*3, 1, fp); //图片旋转

    free(lineData);

  fclose(fp);  

    return 0;

}

#define PNG_BYTES_TO_CHECK 4

#define HAVE_ALPHA 1

#define NO_ALPHA 0

int check_if_png(char *file_name, FILE **fp)

{

  unsigned char buf[PNG_BYTES_TO_CHECK];


  /* Open the prospective PNG file. */

  if ((*fp = fopen(file_name, "rb")) == NULL)

      return 0;


  /* Read in some of the signature bytes */

  if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK)

      return 0;


  /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.

      Return nonzero (true) if they match */

    printf("buf0 =%x buf1=%x buf2=%x buf3=%x\n",buf[0], buf[1], buf[2], buf[3]);

  return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); //0错误 非0正确

}


/*

    获取png的数据

    filepath:文件名

    out:存放数据的rgb数组,格式bgr bgr ...

*/

int detect_png(char *filepath, pic_data *out)

/* 用于解码png图片 */

{

FILE *pic_fp;

int ret = -1;

/* 初始化各种结构 */

png_structp png_ptr;

png_infop  info_ptr;


  /*检测是否为png文件*/

  

if((ret = check_if_png(filepath,&pic_fp)) ==0)

{

        printf("not png file");

        return -1;

}


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)); // 这句很重要


rewind(pic_fp);

/*开始读文件*/

png_init_io(png_ptr, pic_fp); //文件指针赋值 

//png_ptr->io_ptr = (png_voidp)fp;

// png_voidp io_ptr;          /* ptr to application struct for I/O functions */ 

// 读文件了

png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);

//#define PNG_TRANSFORM_EXPAND        0x0010    /* read only */


int color_type,channels; //typedef unsigned char png_byte;


/*获取宽度,高度,位深,颜色类型*/

channels      = png_get_channels(png_ptr, info_ptr); /*获取通道数*/

out->bit_depth = png_get_bit_depth(png_ptr, info_ptr); /* 获取位深 */

color_type    = png_get_color_type(png_ptr, info_ptr); /*颜色类型*/


int i,j;

int size, pos = 0;

int temp;

/* row_pointers里边就是rgba数据 */

png_bytep* row_pointers; //二级指针

row_pointers = png_get_rows(png_ptr, info_ptr); //获取二维数组的数据

out->width = png_get_image_width(png_ptr, info_ptr);

out->height = png_get_image_height(png_ptr, info_ptr);


printf("channels=%d depth=%d color_type=%d width=%d height=%d\n",channels, out->bit_depth,color_type,out->width,out->height);

size = out->width * out->height; /* 计算图片的总像素点数量 */


if(channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA) //6

{/*如果是RGB+alpha通道,或者RGB+其它字节*/ 

  size *= (3*sizeof(unsigned char)); /* 每个像素点占4个字节内存 */

  out->flag = HAVE_ALPHA;    /* 标记 */

  out->rgba = (unsigned char*) malloc(size);

  if(out->rgba == NULL)

  {/* 如果分配内存失败 */

  fclose(pic_fp);

  puts("错误(png):无法分配足够的内存供存储数据!");

  return 1;

  }


  temp = (4 * out->width);/* 每行有4 * out->width个字节 */

  for(i = 0; i < out->height; i++)

  {

  for(j = 0; j < temp; j += 4)

  {/* 一个字节一个字节的赋值 */

  // out->rgba[0][pos] = row_pointers[i][j]; // red

  // out->rgba[1][pos] = row_pointers[i][j+1]; // green

  // out->rgba[2][pos] = row_pointers[i][j+2];  // blue

  //  out->rgba[3][pos] = row_pointers[i][j+3]; // alpha

  //  ++pos;

      //out->rgba[pos++] = row_pointers[i][j+3]; 忽略

      //pos++;

      char ch = row_pointers[i][j+3];

      out->rgba[pos++] = row_pointers[i][j+2];

      out->rgba[pos++] = row_pointers[i][j+1];

    out->rgba[pos++] = row_pointers[i][j+0]; 

  }

  }

}

else if(channels == 3 || color_type == PNG_COLOR_TYPE_RGB)//2

{/* 如果是RGB通道 */

  size *= (3*sizeof(unsigned char)); /* 每个像素点占3个字节内存 */

  out->flag = NO_ALPHA;    /* 标记 */

  out->rgba = (unsigned char*) malloc(size);

  printf("malloc\n");

  if(out->rgba == NULL)

  {/* 如果分配内存失败 */

  fclose(pic_fp);

  puts("错误(png):无法分配足够的内存供存储数据!");

  return 1;

  }


  temp = (3 * out->width);/* 每行有3 * out->width个字节 */

  for(i = 0; i < out->height; i++)

  {

  for(j = 0; j < temp; j += 3)

  {/* 一个字节一个字节的赋值 */

  // out->rgba[0][pos] = row_pointers[i][j]; // red

  //out->rgba[1][pos] = row_pointers[i][j+1]; // green

  // out->rgba[2][pos] = row_pointers[i][j+2];  // blue

  // ++pos;


      out->rgba[pos++] = row_pointers[i][j+2];

      out->rgba[pos++] = row_pointers[i][j+1];

    out->rgba[pos++] = row_pointers[i][j+0];  

  

  }

  }

}

else return 1;


/* 撤销数据占用的内存 */

png_destroy_read_struct(&png_ptr, &info_ptr, 0); 

//free(out->rgba);

return 0;

}


/*

    写入数据到png文件

    file_name:写入数据的文件名

    graph:数据的rgb数组 格式为bgr bgr排放

*/

int write_png_file(char *file_name , pic_data *graph)

/* 功能:将LCUI_Graph结构中的数据写入至png文件 */

{

int j, i, temp, pos;

png_byte color_type;


png_structp png_ptr;

png_infop info_ptr; 


png_bytep * row_pointers;

/* create file */

FILE *fp = fopen(file_name, "wb");

if (!fp)

{

  printf("[write_png_file] File %s could not be opened for writing", file_name);

  return -1;

}



/* initialize stuff */

png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);


if (!png_ptr)

{

  printf("[write_png_file] png_create_write_struct failed");

  return -1;

}

info_ptr = png_create_info_struct(png_ptr);

if (!info_ptr)

{

  printf("[write_png_file] png_create_info_struct failed");

  return -1;

}

if (setjmp(png_jmpbuf(png_ptr)))

{

  printf("[write_png_file] Error during init_io");

  return -1;

}

png_init_io(png_ptr, fp);



/* write header */

if (setjmp(png_jmpbuf(png_ptr)))

{

  printf("[write_png_file] Error during writing header");

  return -1;

}

/* 判断要写入至文件的图片数据是否有透明度,来选择色彩类型 */

if(graph->flag == HAVE_ALPHA) color_type = PNG_COLOR_TYPE_RGB_ALPHA;

else color_type = PNG_COLOR_TYPE_RGB;


png_set_IHDR(png_ptr, info_ptr, graph->width, graph->height,

  graph->bit_depth, color_type, PNG_INTERLACE_NONE,

  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);


png_write_info(png_ptr, info_ptr);



/* write bytes */

if (setjmp(png_jmpbuf(png_ptr)))

{

  printf("[write_png_file] Error during writing bytes");

  return -1;

}

if(graph->flag == HAVE_ALPHA) temp = (4 * graph->width);

else temp = (3 * graph->width);


pos = 0;

row_pointers = (png_bytep*)malloc(graph->height*sizeof(png_bytep));

for(i = 0; i < graph->height; i++)

{

  row_pointers[i] = (png_bytep)malloc(sizeof(unsigned char)*temp);

  for(j = 0; j < temp; j += 3)

  {

  // row_pointers[i][j]  = graph->rgba[0][pos]; // red

  // row_pointers[i][j+1] = graph->rgba[1][pos]; // green

  // row_pointers[i][j+2] = graph->rgba[2][pos];  // blue

      row_pointers[i][j+2] = graph->rgba[pos++];

      row_pointers[i][j+1] = graph->rgba[pos++];

      row_pointers[i][j+0] = graph->rgba[pos++];

  //if(graph->flag == HAVE_ALPHA) 

  // row_pointers[i][j+3] = graph->rgba[3][pos]; // alpha

  //++pos;

  }

}

png_write_image(png_ptr, row_pointers);


/* end write */

if (setjmp(png_jmpbuf(png_ptr)))

{

  printf("[write_png_file] Error during end of write");

  return -1;

}

png_write_end(png_ptr, NULL);


    /* cleanup heap allocation */

for (j=0; j<graph->height; j++)

  free(row_pointers[j]);

free(row_pointers);


    fclose(fp);

    return 0;

}


int main(int argc, char *argv[]) //规则图片效果较好

{

    if(argc == 3){ //将png图片转化成bmp图片,argv[1]为png文件名 argv[2]为bmp文件名。

        pic_data out;

        detect_png(argv[1], &out);

        write_bmp(argv[2], &out);       

        free(out.rgba);

    }

    return 0;

}



参考资料:

PNG文件格式白皮书:http://www.w3.org/TR/REC-png.html
为数不多的中文PNG格式说明:http://dev.gameres.com/Program/Visual/Other/PNGFormat.htm
RFC-1950(ZLIB Compressed Data Format Specification):ftp://ds.internic.net/rfc/rfc1950.txt
RFC-1950(DEFLATE Compressed Data Format Specification):ftp://ds.internic.net/rfc/rfc1951.txt

LZ77算法的JAVA实现:http://jazzlib.sourceforge.net/
LZ77算法的JAVA实现,包括J2ME版本:http://www.jcraft.com/jzlib/index.html

使用libpng,libjpeg从文件读取和从内存读取2中方式的实现:

1.http://blog.csdn.net/bigapple88/article/details/5644418

2.http://blog.csdn.net/dj0379/article/details/4340300