|
楼主 |
发表于 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却没有具体说明。
[code:1]
/******************************************************************************
* 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;
int show_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;
}
[/code:1] |
|