AllenYao 发表于 2006-11-16 15:21:25

PNG格式解码,难!

本想直接调用libpng来解码PNG文件,无奈libpng的说明我看不太懂,不会用。于是,又想自己编码读取PNG文件。结果,深陷数据压缩算法中……感觉自己这方面的知识少得可怜,学习学习也好。继续努力中……

下面这个地址是在网上找到的关于PNG文件格式的中文说明:
http://www.ismyway.com/png/index.htm

根据学到的一点知识,写了一个小小程序,没有什么作用,只当是学习记录吧

/******************************************************************************
* readpng.c
* allen
* [email protected]
* 2006/11/16
******************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int png_header_print(char *filename);
int png_chunks_list(char *filename);
int png_chunk_read(FILE *fp);

/******************************************************************************
*
******************************************************************************/
int main(int argc, char *argv[])
{
        if (argc != 2)
        {
                printf("usage: %s filename\n", argv[0]);
                return 0;
        }
        printf("\n\tFile Name : [%s]\n\n", argv[1]);
       
        png_header_print(argv[1]);
       
        printf("\n");
       
        png_chunks_list(argv[1]);
       
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_header_print(char *filename)
{
        int i, num;
        FILE *fp;
        char tmp[100];
        unsigned char pngflag[8], pngIHDR[25];
       
        fp = fopen(filename, "rb");
        if (NULL == fp)
        {
                return 0;
        }
       
        fread(pngflag, sizeof(pngflag), 1, fp);
        printf("\t------------ PNG signature ------------\n\t");
        for(i=0; i<sizeof(pngflag); i++)
        {
                printf("[%02x]%c", pngflag[i], i == (sizeof(pngflag)-1) ? '\n' : ' ');
        }
        printf("\n");
       
        fread(pngIHDR, sizeof(pngIHDR), 1, fp);
       
        printf("\t---------- IHDR Image header ----------\n");
        memcpy(&num, pngIHDR, 4);
        printf("\tLENGTH   : [%d]\n", htonl(num));
       
        memset(tmp, 0x00, sizeof(tmp));
        memcpy(tmp, pngIHDR+4, 4);
        printf("\tCHUNK TYPE : [%s]\n", tmp);
       
        printf("\tCHUNK DATA\n");

        memcpy(&num, pngIHDR+8, 4);
        printf("\t\tWidth            (4 bytes): [%d]\n", htonl(num));
        memcpy(&num, pngIHDR+12, 4);
        printf("\t\tHeight             (4 bytes): [%d]\n", htonl(num));
       
        printf("\t\tBitdepth         (1 bytes): [%d]\n", (int)pngIHDR[16]);
        printf("\t\tColor Type         (1 bytes): [%d]\n", (int)pngIHDR[17]);
        printf("\t\tCompression method (1 bytes): [%d]\n", (int)pngIHDR[18]);
        printf("\t\tFilter method      (1 bytes): [%d]\n", (int)pngIHDR[19]);
        printf("\t\tInterlace method   (1 bytes): [%d]\n", (int)pngIHDR[20]);
       
        printf("\tCRC : [%02x] [%02x] [%02x] [%02x]\n",
             pngIHDR[21], pngIHDR[22], pngIHDR[23], pngIHDR[24]);
       
        fclose(fp);
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_chunks_list(char *filename)
{
        FILE *fp;
        int rc = 0;
        unsigned char png_sign[8] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
        unsigned char file_sign[8];
       
        fp = fopen(filename, "rb");
        if (NULL == fp)
        {
                return 0;
        }
       
        rc = fread(file_sign, sizeof(file_sign), 1, fp);
        if ((rc != 1) ||
          (memcmp(file_sign, png_sign, sizeof(png_sign)) != 0))
        {
                printf("Error : %s is not a png file!\n", filename);
                printf("rc = [%d]\n", rc);
                printf("[%02x] [%02x] [%02x] [%02x] [%02x] [%02x] [%02x] [%02x]\n",
                     file_sign[0], file_sign[1], file_sign[2], file_sign[3],
                     file_sign[4], file_sign[5], file_sign[6], file_sign[7]);
                return 0;
        }
       
        while(!feof(fp))
        {
                if (png_chunk_read(fp) <= 0)
                        break;
        }
        fclose(fp);
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_chunk_read(FILE *fp)
{
        int len = 0;
        unsigned char name[5], data[65535], crc[4];
       
        if (fread(&len, 4, 1, fp) != 1)
                return 0;
        len = htonl(len);
       
        memset(name, 0x00, sizeof(name));
        if (fread(name, 4, 1, fp) != 1)
                return 0;
       
        if (len > 0)
        {
                memset(data, 0x00, sizeof(data));
                if (fread(data, len, 1, fp) != 1)
                        return 0;
        }
       
        memset(crc, 0x00, sizeof(crc));
        if (fread(crc, 4, 1, fp) != 1)
                return 0;
       
        printf("LENGTH:[%8d]    TYPE:[%s]    DATA:[...]    CRC:[%02x%02x%02x%02x]\n",
             len, name, crc[0], crc[1], crc[2], crc[3]);
        return 1;
}

AllenYao 发表于 2006-11-17 09:39:02

取得一点点进步!通过对网上相关资料的查询,得到一个解压缩函数,运用到对PNG文件的IDAT数据块进行解压。经试验,目前这个函数对于不带透明效果的PNG文件可以解压IDAT数据块。下面的这个小程序演示了这一过程。测试用的PNG文件是使用PHOTOSHOP软件制作的10X10像素(不带透明效果)。通过这个程序,我观察到,对于这个10X10像素的PNG文件,不带透明效果,且是真彩色的,它的图象是由一行一行组成的,每行10像素,每个像素3个字节。这样一行就有30个字节了,另外,每一行数据前面还会添加一个字节。目前我还不知道是干什么用的。

/******************************************************************************
* readpng.c
* allen
* [email protected]
* 2006/11/16
******************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include "zlib.h"

#define CHUNK 65536

unsigned char idat[CHUNK], img[CHUNK];
int idat_len = 0, img_len = 0;

int png_header_print(char *filename);
int png_chunks_list(char *filename);
int png_chunk_read(FILE *fp);

int inf(unsigned char *source, int src_len, unsigned char *dest);
int decode(unsigned char *dest, int *destLen, unsigned char *source, int sourceLen);
/******************************************************************************
*
******************************************************************************/
int main(int argc, char *argv[])
{
        int i, j, rc;

        if (argc != 2)
        {
                printf("usage: %s filename\n", argv[0]);
                return 0;
        }
        printf("\n\tFile Name : [%s]\n\n", argv[1]);
       
        /*
        png_header_print(argv[1]);
       
        printf("\n");
        */
        png_chunks_list(argv[1]);
       
       
        if (idat_len > 0)
        {
                /*
                printf("*IDAT* Detail:\n");
               
                for(i=0; i<idat_len; i++)
                {
                        printf("[%02x]%c", idat[i], (i+1) % 8 ? ' ' : '\n');
                }
                */
                img_len = sizeof(img);
                memset(img, 0x00, sizeof(img));

                rc = decode(img, &img_len, idat, idat_len);
                printf("decode return : [%d]\n", rc);
                printf("idat_len : [%d]    img_len : [%d]\n", idat_len, img_len);

                for(i=0; i<img_len; i=i+31)
                {
                        printf("Line[%02x]:\n", img[i]);
                        for (j=1; j<31; j++)
                                printf("[%02x]%c", img[i+j], j%10?' ':'\n');
                        printf("\n");
                }
                printf("\n");
        }
       
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_header_print(char *filename)
{
        int i, num;
        FILE *fp;
        char tmp[100];
        unsigned char pngflag[8], pngIHDR[25];
       
        fp = fopen(filename, "rb");
        if (NULL == fp)
        {
                return 0;
        }
       
        fread(pngflag, sizeof(pngflag), 1, fp);
        printf("\t------------ PNG signature ------------\n\t");
        for(i=0; i<sizeof(pngflag); i++)
        {
                printf("[%02x]%c", pngflag[i], i == (sizeof(pngflag)-1) ? '\n' : ' ');
        }
        printf("\n");
       
        fread(pngIHDR, sizeof(pngIHDR), 1, fp);
       
        printf("\t---------- IHDR Image header ----------\n");
        memcpy(&num, pngIHDR, 4);
        printf("\tLENGTH   : [%d]\n", htonl(num));
       
        memset(tmp, 0x00, sizeof(tmp));
        memcpy(tmp, pngIHDR+4, 4);
        printf("\tCHUNK TYPE : [%s]\n", tmp);
       
        printf("\tCHUNK DATA\n");

        memcpy(&num, pngIHDR+8, 4);
        printf("\t\tWidth            (4 bytes): [%d]\n", htonl(num));
        memcpy(&num, pngIHDR+12, 4);
        printf("\t\tHeight             (4 bytes): [%d]\n", htonl(num));
       
        printf("\t\tBitdepth         (1 bytes): [%d]\n", (int)pngIHDR[16]);
        printf("\t\tColor Type         (1 bytes): [%d]\n", (int)pngIHDR[17]);
        printf("\t\tCompression method (1 bytes): [%d]\n", (int)pngIHDR[18]);
        printf("\t\tFilter method      (1 bytes): [%d]\n", (int)pngIHDR[19]);
        printf("\t\tInterlace method   (1 bytes): [%d]\n", (int)pngIHDR[20]);
       
        printf("\tCRC : [%02x] [%02x] [%02x] [%02x]\n",
             pngIHDR[21], pngIHDR[22], pngIHDR[23], pngIHDR[24]);
       
        fclose(fp);
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_chunks_list(char *filename)
{
        FILE *fp;
        int rc = 0;
        unsigned char png_sign[8] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
        unsigned char file_sign[8];
       
        fp = fopen(filename, "rb");
        if (NULL == fp)
        {
                return 0;
        }
       
        rc = fread(file_sign, sizeof(file_sign), 1, fp);
        if ((rc != 1) ||
          (memcmp(file_sign, png_sign, sizeof(png_sign)) != 0))
        {
                printf("Error : %s is not a png file!\n", filename);
                printf("rc = [%d]\n", rc);
                printf("[%02x] [%02x] [%02x] [%02x] [%02x] [%02x] [%02x] [%02x]\n",
                     file_sign[0], file_sign[1], file_sign[2], file_sign[3],
                     file_sign[4], file_sign[5], file_sign[6], file_sign[7]);
                return 0;
        }
       
        while(!feof(fp))
        {
                if (png_chunk_read(fp) <= 0)
                        break;
        }
        fclose(fp);
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_chunk_read(FILE *fp)
{
        int len = 0;
        unsigned char name[5], data[65535], crc[4];
       
        if (fread(&len, 4, 1, fp) != 1)
                return 0;
        len = htonl(len);
       
        memset(name, 0x00, sizeof(name));
        if (fread(name, 4, 1, fp) != 1)
                return 0;
       
        if (len > 0)
        {
                memset(data, 0x00, sizeof(data));
                if (fread(data, 1, len, fp) != len)
                        return 0;
        }
       
        memset(crc, 0x00, sizeof(crc));
        if (fread(crc, 4, 1, fp) != 1)
                return 0;
       
        printf("LENGTH:[%8d]    TYPE:[%s]    DATA:[...]    CRC:[%02x%02x%02x%02x]\n",
             len, name, crc[0], crc[1], crc[2], crc[3]);
       
        if ((memcmp(name, "IDAT", 4) == 0) && (len > 0))
        {
                /*
                idat = (unsigned char *)malloc(len);
                memset(idat, 0x00, len);
                */
                memcpy(idat, data, len);
                idat_len = len;
        }
        return 1;
}

/* Decompress from file source to file dest until stream ends or EOF.
   inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
   allocated for processing, Z_DATA_ERROR if the deflate data is
   invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
   the version of the library linked do not match, or Z_ERRNO if there
   is an error reading or writing the files. */
int inf(unsigned char *source, int src_len, unsigned char *dest)
{
    int ret;
    unsigned have;
    z_stream strm;
    unsigned char in[CHUNK];
    unsigned char out[CHUNK];

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit(&strm);
    if (ret != Z_OK)
      return ret;

    /* decompress until deflate stream ends or end of file */
    do
    {
            memcpy(in, source, src_len);
      strm.avail_in = src_len;
      strm.next_in = in;

      /* run inflate() on input until output buffer not full */
      do {
            strm.avail_out = CHUNK;
            strm.next_out = out;
            ret = inflate(&strm, Z_NO_FLUSH);
            /*assert(ret != Z_STREAM_ERROR);   state not clobbered */
            switch (ret) {
            case Z_NEED_DICT:
                ret = Z_DATA_ERROR;   /* and fall through */
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                (void)inflateEnd(&strm);
                return ret;
            }
            have = CHUNK - strm.avail_out;
            memcpy(dest, out, have);
            img_len = have;
      } while (strm.avail_out == 0);

      /* done when inflate() says it's done */
    } while (ret != Z_STREAM_END);

    /* clean up and return */
    (void)inflateEnd(&strm);
    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}

int decode(unsigned char *dest, int *destLen, unsigned char *source, int sourceLen)
{
        z_stream stream;
        int err;
   
        stream.next_in = source;
        stream.avail_in = sourceLen;
   
        stream.next_out = dest;
        stream.avail_out = *destLen;
       
    /*      if   ((uLong)stream.avail_out   !=   *destLen)   return   Z_BUF_ERROR;   */
   
        stream.zalloc = Z_NULL;
        stream.zfree = Z_NULL;
        stream.opaque = Z_NULL;
   
        err = inflateInit(&stream);
        if (err != Z_OK)
        {
                printf("inflateInit error!\n");
                return err;
        }
   
        err = inflate(&stream, Z_FINISH);
        if (err != Z_STREAM_END)
        {
                printf("inflate error! err=[%d]\n", err);
                inflateEnd(&stream);
                return err == Z_OK ? Z_BUF_ERROR : err;
        }
        *destLen = stream.total_out;
   
        err = inflateEnd(&stream);
        return err;
}

asmcos 发表于 2006-11-17 10:01:56

egui 里面有png的demo的例子,包括透明的例子.
看egui桌面图标.

AllenYao 发表于 2006-11-19 16:32:37

我对PNG的分析过程中,遇到一个障碍,那就是“Filter type 3: Average”!PNG图像为了加大压缩率对像素阵列进行了重新筛选(Filter)。四种筛选方式中,唯独第3种让我摸不着头绪。PNG标准中描述的还原公式为:“Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)”。但是对于结果超出255却没有具体说明。

/******************************************************************************
* showpng.c - 对PNG文件进行简单地解码, 还原像素序列, 并在framebuffer中显示出来
*             目前它对PNG文件中含有Filter3的解码有问题, 且只能处理24位的图像.
*
* Author : allen
* Email: [email protected]
* Date   : 2006/11/19
******************************************************************************/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <math.h>
#include "zlib.h"

#define CHUNK 65536

struct png_info
{
        int width;
        int height;
        char bit_depth;
        char colour_type;
        char compression_method;
        char filter_method;
        char interlace_method;
        int bpp;
        unsigned char *idat;
        long idat_len;
        unsigned char *img;
        long img_len;
};

int png_reader(struct png_info *pnginfo, char *filename);
int idat_read(struct png_info *pnginfo, FILE *fp);

int decompress(unsigned char *dest, long *destLen, unsigned char *source, long sourceLen);
int decode(struct png_info *pnginfo, unsigned char *src, long src_len);
unsigned char reconstruct(int f, unsigned char x, unsigned char a,
                        unsigned char b, unsigned char c);
unsigned char paeth(unsigned char a, unsigned char b, unsigned char c);

char *fbp = 0;
int xres = 0;
int yres = 0;
int bits_per_pixel = 0;

intshow_png(char *bmpfile);
/******************************************************************************
*
******************************************************************************/
int main( int argc, char *argv[] )
{
    int fbfd = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;

    if (argc != 2)
    {
            printf("usage: %s filename\n", argv[0]);
            return 0;
    }
    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (!fbfd)
    {
      printf("Error: cannot open framebuffer device.\n");
      exit(1);
    }

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
    {
      printf("Error reading fixed information.\n");
      exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
    {
      printf("Error reading variable information.\n");
      exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );
    xres = vinfo.xres;
    yres = vinfo.yres;
    bits_per_pixel = vinfo.bits_per_pixel;

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
                     fbfd, 0);
    if ((int)fbp == -1)
    {
      printf("Error: failed to map framebuffer device to memory.\n");
      exit(4);
    }

        show_png(argv[1]);

    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

/******************************************************************************
*
******************************************************************************/
int show_png(char *bmpfile)
{
        int rc;
        int i, j, pos;
        long tmp_len;
        struct png_info pnginfo;
        unsigned char tmp[CHUNK];
        long int location = 0;
       
        /*从PNG文件中读取需要的内容*/
        png_reader(&pnginfo, bmpfile);

        tmp_len = sizeof(tmp);
        memset(tmp, 0x00, sizeof(tmp));

        /*解压缩图像信息*/
        rc = decompress(tmp, &tmp_len, pnginfo.idat, pnginfo.idat_len);
        if (rc != 0)
        {
                printf("Decompress Error!\n");
                free(pnginfo.idat);
                free(pnginfo.img);
                return 0;
        }
       
        /*解码图像信息*/
        rc = decode(&pnginfo, tmp, tmp_len);
        if (rc != 0)
        {
                printf("Decode Error!\n");
                free(pnginfo.idat);
                free(pnginfo.img);
                return 0;
        }

        /*显示图像信息*/
        for (i=0; i<pnginfo.height; i++)
        {
                for (j=0; j<pnginfo.width; j++)
                {
                        location = i * xres * bits_per_pixel / 8 + j * bits_per_pixel / 8;
                        pos = i * (pnginfo.width * pnginfo.bpp) + (j * pnginfo.bpp);
                        *(fbp + location + 0) = *(pnginfo.img + pos + 2); /*Blue */
                        *(fbp + location + 1) = *(pnginfo.img + pos + 1); /*Green*/
                        *(fbp + location + 2) = *(pnginfo.img + pos + 0); /*Red*/
                        *(fbp + location + 3) = 0x00;                     /*Reserved*/
                }
        }

        free(pnginfo.idat);
        free(pnginfo.img);
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int png_reader(struct png_info *pnginfo, char *filename)
{
        int num;
        FILE *fp;
        unsigned char png_sign[8] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
        unsigned char file_sign[8];
        unsigned char pngIHDR[25];
       
        fp = fopen(filename, "rb");
        if (NULL == fp)
        {
                printf("Error : can not open [%s]!\n", filename);
                return -1;
        }
       
        if (fread(file_sign, 1, sizeof(file_sign), fp) != sizeof(file_sign))
        {
                printf("Error : can not read [%s]!\n", filename);
                fclose(fp);
                return -2;
        }
       
        if (memcmp(file_sign, png_sign, sizeof(png_sign)) != 0)
        {
                printf("Error : [%s] is not a png file!\n", filename);
                fclose(fp);
                return -3;
        }
       
        if (fread(pngIHDR, 1, sizeof(pngIHDR), fp) != sizeof(pngIHDR))
        {
                printf("Error : can not read IHDR chunk!\n");
                fclose(fp);
                return -4;
        }
       
        memcpy(&num, pngIHDR, 4);
        if ((htonl(num) != 13) || (memcmp("IHDR", pngIHDR+4, 4) != 0))
        {
                printf("Error : can not read IHDR chunk!\n");
                fclose(fp);
                return -5;
        }
       
        memcpy(&num, pngIHDR+8, 4);
        pnginfo->width = htonl(num);
       
        memcpy(&num, pngIHDR+12, 4);
        pnginfo->height = htonl(num);
       
        pnginfo->bit_depth          = (int)pngIHDR[16];
        pnginfo->colour_type      = (int)pngIHDR[17];
        pnginfo->compression_method = (int)pngIHDR[18];
        pnginfo->filter_method      = (int)pngIHDR[19];
        pnginfo->interlace_method   = (int)pngIHDR[20];
       
        /*我现在测试用的PNG图片都是24位的, 所以BYTE PER PIXEL就是3*/
        pnginfo->bpp = 3;
       
        if (!idat_read(pnginfo, fp))
        {
                printf("Error : can not read IDAT chunk!\n");
                fclose(fp);
                return -6;
        }
        fclose(fp);
       
        pnginfo->img_len = (pnginfo->width * 3) * pnginfo->height;
        pnginfo->img = malloc(pnginfo->img_len);
        memset(pnginfo->img, 0x00, pnginfo->img_len);
       
        return 0;
}

/******************************************************************************
*
******************************************************************************/
int idat_read(struct png_info *pnginfo, FILE *fp)
{
        int len = 0, find = 0;
        unsigned char name[5], data[65535], crc[4];
       
        while(!find)
        {
          if (fread(&len, 1, 4, fp) != 4)
                  break;
          len = htonl(len);
          
          memset(name, 0x00, sizeof(name));
          if (fread(name, 1, 4, fp) != 4)
                  break;
          
          if (len > 0)
          {
                  memset(data, 0x00, sizeof(data));
                  if (fread(data, 1, len, fp) != len)
                          break;
          }
          
          memset(crc, 0x00, sizeof(crc));
          if (fread(crc, 1, 4, fp) != 4)
                  break;
          
          if ((memcmp(name, "IDAT", 4) == 0) && (len > 0))
          {
                  find = 1;
                  break;
          }
        }
        if (find)
        {
                pnginfo->idat = malloc(len);
                memcpy(pnginfo->idat, data, len);
                pnginfo->idat_len = len;
        }
        return find;
}

/******************************************************************************
*
******************************************************************************/
int decompress(unsigned char *dest, long *destLen,
               unsigned char *source, long sourceLen)
{
        z_stream stream;
        int err;
   
        stream.next_in = source;
        stream.avail_in = sourceLen;
   
        stream.next_out = dest;
        stream.avail_out = *destLen;
       
    /*      if   ((uLong)stream.avail_out   !=   *destLen)   return   Z_BUF_ERROR;   */
   
        stream.zalloc = Z_NULL;
        stream.zfree = Z_NULL;
        stream.opaque = Z_NULL;
   
        err = inflateInit(&stream);
        if (err != Z_OK)
        {
                printf("inflateInit error!\n");
                return err;
        }
   
        err = inflate(&stream, Z_FINISH);
        if (err != Z_STREAM_END)
        {
                printf("inflate error! err=[%d]\n", err);
                inflateEnd(&stream);
                return err == Z_OK ? Z_BUF_ERROR : err;
        }
        *destLen = stream.total_out;
   
        err = inflateEnd(&stream);
        return err;
}

/******************************************************************************
*
******************************************************************************/
int decode(struct png_info *pnginfo, unsigned char *src, long src_len)
{
        /*int pos;*/
        int i, j, k, linebytes;
        int f, width, height, bpp;
        long tmp_size = 0;
        unsigned char *tmp, x, a, b, c;
       
        bpp = pnginfo->bpp;
        linebytes = pnginfo->width * bpp;
        width = pnginfo->width + 1;
        height = pnginfo->height + 1;
        tmp_size = height * width * bpp;
        /*
        printf("pnginfo->width:%d\n", pnginfo->width);
        printf("pnginfo->height:%d\n", pnginfo->height);
        printf("width:%d\n", width);
        printf("height:%d\n", height);
        printf("tmp_size:%ld\n", tmp_size);
        */
        tmp = malloc(tmp_size);
        memset(tmp, 0x00, tmp_size);
       
        /*将解压缩过的数据拷贝到临时数据中*/
        for (i=1; i<height; i++)
        {
                memcpy(tmp+width*bpp*i+bpp, src+(linebytes+1)*(i-1)+1, linebytes);
        }
        /*
        for (i=0; i<height; i++)
        {
                for (j=0; j<width; j++)
                {
                        pos = i * (width * 3) + (j * 3);
                        printf("[%02x][%02x][%02x] ", *(tmp+pos), *(tmp+pos+1), *(tmp+pos+2));
                }
                printf("\n");
        }
        printf("------------------------------------------------------------\n");
        */

        /*对每一个像素进行解码*/
        for (i=1; i<height; i++)
        {
                f = *(src + (linebytes + 1) * (i - 1));
                for (j=1; j<width; j++)
                {
                        for (k=0; k<3; k++)
                        {
                                x = *(tmp + (linebytes + bpp) * i + j * bpp + k);
                                a = *(tmp + (linebytes + bpp) * i + (j - 1) * bpp + k);
                                b = *(tmp + (linebytes + bpp) * (i - 1) + j * bpp + k);
                                c = *(tmp + (linebytes + bpp) * (i - 1) + (j - 1) * bpp + k);
                                *(tmp + (linebytes + bpp) * i + j * bpp + k) = reconstruct(f, x, a, b, c);
                        }
                }
        }

        /*打印一下解码后的数据
        for (i=0; i<height; i++)
        {
                for (j=0; j<width; j++)
                {
                        pos = i * (width * 3) + (j * 3);
                        printf("[%02x][%02x][%02x] ", *(tmp+pos), *(tmp+pos+1), *(tmp+pos+2));
                }
                printf("\n");
        }*/
       
        /*拷贝解码后的数据到图像阵列中*/
        for (i=1; i<height; i++)
        {
                memcpy(pnginfo->img+linebytes*(i-1), tmp+width*bpp*i+bpp, linebytes);
        }
       
        free(tmp);
        return 0;
}

/******************************************************************************
*
******************************************************************************/
unsigned char reconstruct(int f, unsigned char x, unsigned char a,
                        unsigned char b, unsigned char c)
{
        unsigned char ret = 0x00;

        switch(f)
        {
                case 0:
                        ret = x;
                        break;
                case 1:
                        ret = x + a;
                        break;
                case 2:
                        ret = x + b;
                        break;
                case 3:
                        ret = x + floor((a + b) / 2);
                        break;
                case 4:
                        ret = x + paeth(a, b, c);
                        break;
        }
        return ret;
}

/******************************************************************************
*
******************************************************************************/
unsigned char paeth(unsigned char a, unsigned char b, unsigned char c)
{
        unsigned char p, pa, pb, pc, pr;
       
        p = a + b - c;
    pa = abs(p - a);
    pb = abs(p - b);
    pc = abs(p - c);
    if ((pa <= pb) && (pa <= pc))
            pr = a;
    else if (pb <= pc)
            pr = b;
    else
            pr = c;
    return pr;
}
页: [1]
查看完整版本: PNG格式解码,难!