PNG编码解码库,仅支持最常见的24位色深和32位色深,所以叫PNG3224。其他色深不支持,以后也不打算支持。结尾图片包含有zip压缩包,压缩包包含有适用于Unix和windows的源码,以及Windows端zlib库。
png3224.h
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: PNG3224编解码器。
// 依赖 zlib,编译需要加 -lz 。
// 仅支持 RGB888 和 RGBA8888 两种颜色格式。
// 日期: 2019-09-17
// 修改: 2021-07-12 对于读写文件,将原先的Unix的接口换成C语言标准接口。
//
//==============================================================================
#ifndef _PNG3224_H_
#define _PNG3224_H_
#include <stdint.h>
// png色深,只支持24位RGB和32位的RGBA两种格式。
#define PNG3224_COLOR_DEPTH_24 24 // 24位的RGB格式
#define PNG3224_COLOR_DEPTH_32 32 // 32位的RGBA格式
#ifdef __cplusplus
extern "C" {
#endif
// 初步检查png文件格式,获取png部分信息。
// 成功返回0,格式不对或不支持或尺寸太大,返回-1。
int png3224_info(
uint32_t *width, // 返回图像宽度
uint32_t *height, // 返回图像高度
uint32_t *colorDepth, // 返回图像颜色深度
uint8_t *pngData, // png文件数据
uint32_t pngSize // png文件数据字节数
);
// png解码,输入png文件数据,输出32位色深 RGBX8888 格式的图像。
// 成功返回RGBX8888指针,格式不对,或不支持,或尺寸太大,或解码失败,返回NULL。
uint8_t* png3224_decode(
uint32_t *width, // 返回图像宽度
uint32_t *height, // 返回图像高度
uint32_t *colorDepth, // 返回图像色深
uint8_t *pngData, // png文件数据
uint32_t pngSize // png文件数据字节数
);
// png编码,输入是32位色深 RGBX8888 格式的图像,输出是png文件格式的数据。
// 成功返回png数据,编码码失败,返回NULL。
uint8_t* png3224_encode(
uint32_t *pngSize, // 返回png数据大小
uint8_t *imgData, // RGBX8888格式的图像数据
uint32_t width, // 图像宽度
uint32_t height, // 图像高度
uint32_t colorDepth // 图像色深
);
// 加载磁盘PNG文件的图像到内存,返回 RGBX8888 格式图像,以及宽高和色深。
uint8_t* png3224_load(
char *fileName, // png文件名
uint32_t *width, // 返回宽度
uint32_t *height, // 返回高度
uint32_t *colorDepth // 返回色深
);
// 保存内存 RGBX8888 格式图像到磁盘PNG文件,成功返回png文件大小,失败返回负数。
int png3224_save(
char *fileName, // png文件名
uint8_t *img, // 内存RGBX8888图像
uint32_t width, // 指示宽度
uint32_t height, // 指示高度
uint32_t colorDepth // 指示输出色深
);
#ifdef __cplusplus
}
#endif
#endif
png3224.c
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: PNG3224编解码器。
// 依赖 zlib,编译需要加 -lz 。
// 仅支持 RGB888 和 RGBA8888 两种颜色格式。
// 日期: 2019-09-17
// 修改: 2021-07-12 对于读写文件,将原先的Unix的接口换成C语言标准接口。
//
//==============================================================================
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include "png3224.h"
#define PNG3224_TYPE_CODE_IHDR 0x49484452 // I H D R = 49 48 44 52
#define PNG3224_TYPE_CODE_IDAT 0x49444154 // I D A T = 49 44 41 54
#define PNG3224_TYPE_CODE_IEND 0x49454e44 // I E N D = 49 45 4e 44
#define PNG3224_DEFAULT_ALPHA 255 // 透明通道默认值
#define PNG3224_MAX_PIXEL (8192*8192) // 允许的最大像素数
#define PNG3224_MAX_FILESIZE (64*1024*1024) // 允许的最大文件大小
// png文件的IHDR
typedef struct {
uint32_t Width;
uint32_t Height;
uint8_t BitDepth;
uint8_t ColorType;
uint8_t CompressionMethod;
uint8_t FilterMethod;
uint8_t InterlaceMethod;
} PNG3224_IHDR;
// png文件头8个字节
static uint8_t pngFileHeader[] = {
0x89, 0x50, 0x4e, 0x47,
0x0d, 0x0a, 0x1a, 0x0a
};
// CRC查询表
static uint32_t crcTable[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
// 计算CRC校验码
static uint32_t getCrcValue(
uint8_t *data,
uint32_t dataSize
){
uint32_t value = 0xffffffff;
while(dataSize--){
value = (value >> 8) ^ crcTable[(value ^ data[0]) & 0x0ff];
data++;
}
return ~value;
}
// 读取网络字节序的32位无符号整数
static uint32_t readU32(
uint8_t *data
){
uint32_t ret = data[0];
ret = (ret<<8) + data[1];
ret = (ret<<8) + data[2];
ret = (ret<<8) + data[3];
return ret;
}
// 写入网路字节序的32位无符号整数
static void writeU32(
uint8_t *outBuffer,
uint32_t value
){
outBuffer[0] = (value>>24) & 0xff;
outBuffer[1] = (value>>16) & 0xff;
outBuffer[2] = (value>>8) & 0xff;
outBuffer[3] = (value) & 0xff;
}
// 获取png文件的 IHDR 字段
static uint32_t getPngIHDR(
PNG3224_IHDR *ihdr,
uint8_t *pngData
){
uint32_t length = readU32(pngData+0);
uint32_t typeCode = readU32(pngData+4);
uint32_t crcValue1 = readU32(pngData+8+13);
uint32_t crcValue2 = getCrcValue(pngData+4, 13+4);
if(length != 13) return 0;
if(typeCode != PNG3224_TYPE_CODE_IHDR) return 0;
if(crcValue1 != crcValue2) return 0;
ihdr->Width = readU32(pngData+8);
ihdr->Height = readU32(pngData+12);
ihdr->BitDepth = pngData[16];
ihdr->ColorType = pngData[17];
ihdr->CompressionMethod = pngData[18];
ihdr->FilterMethod = pngData[19];
ihdr->InterlaceMethod = pngData[20];
return 13;
}
// 写入 IHDR 字段
static uint32_t putPngIHDR(
uint8_t *outBuffer,
uint32_t width,
uint32_t height,
uint32_t colorDepth
){
writeU32(outBuffer+0, 13);
writeU32(outBuffer+4, PNG3224_TYPE_CODE_IHDR);
writeU32(outBuffer+8, width);
writeU32(outBuffer+12, height);
outBuffer[16] = 8;
outBuffer[17] = (colorDepth == PNG3224_COLOR_DEPTH_32) ? 6 : 2;
outBuffer[18] = 0;
outBuffer[19] = 0;
outBuffer[20] = 0;
writeU32(outBuffer+21, getCrcValue(outBuffer+4, 17));
return 25;
}
// 获取png IDAT 数据块
static uint32_t getPngIDAT(
uint8_t *outBuffer,
uint8_t *pngData,
uint32_t pngSize
){
uint32_t length;
uint32_t typeCode;
uint32_t crcValue1;
uint32_t crcValue2;
uint32_t inOffset = 0;
uint32_t outOffset = 0;
while(pngSize-inOffset >= 12){
length = readU32(pngData + inOffset+0);
if(length > pngSize-inOffset-12) return 0;
typeCode = readU32(pngData + inOffset+4);
crcValue1 = readU32(pngData + inOffset+8+length);
crcValue2 = getCrcValue(pngData + inOffset+4, length+4);
if(crcValue1 != crcValue2) return 0;
if(typeCode == PNG3224_TYPE_CODE_IDAT){
if(length){
memcpy(outBuffer+outOffset, pngData+inOffset+8, length);
outOffset += length;
}
}
else if(typeCode == PNG3224_TYPE_CODE_IEND){
break;
}
inOffset += 12+length;
}
return outOffset;
}
// 写入 IDAT 数据块。
// 这里只分成一个 IDAT 块。
static uint32_t putPngIDAT(
uint8_t *outBuffer,
uint8_t *pngData,
uint32_t pngSize
){
writeU32(outBuffer+0, pngSize);
writeU32(outBuffer+4, PNG3224_TYPE_CODE_IDAT);
memcpy(outBuffer+8, pngData, pngSize);
writeU32(outBuffer+8+pngSize, getCrcValue(outBuffer+4, pngSize+4));
return pngSize + 12;
}
// 写入 IEND 数据块
static uint32_t putPngIEND(
uint8_t *outBuffer
){
writeU32(outBuffer+0, 0);
writeU32(outBuffer+4, PNG3224_TYPE_CODE_IEND);
writeU32(outBuffer+8, 0xAE426082);
return 12;
}
// zlib 解压缩
static uint32_t zlibDecode(
uint8_t *outBuffer,
uint32_t bufferSize,
uint8_t *data,
uint32_t dataSize
){
uLongf bufferLen = (uLongf)bufferSize;
if(uncompress(outBuffer, &bufferLen, data, dataSize) != Z_OK) return 0;
return (uint32_t)bufferLen;
}
// zlib压缩
static uint32_t zlibEncode(
uint8_t *outBuffer,
uint32_t bufferSize,
uint8_t *data,
uint32_t dataSize
){
uLongf bufferLen = (uLongf)bufferSize;
// if(compress(outBuffer, &bufferLen, data, dataSize) != Z_OK) return 0;
if(compress2(outBuffer, &bufferLen, data, dataSize, 1) != Z_OK) return 0;
return (uint32_t)bufferLen;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
static inline uint8_t paethPredictor(
int16_t a,
int16_t b,
int16_t c
){
int16_t p = a + b - c;
int16_t pa = p > a ? p-a : a-p;
int16_t pb = p > b ? p-b : b-p;
int16_t pc = p > c ? p-c : c-p;
if((pa <= pb) && (pa <= pc)) return a;
else if(pb <= pc) return b;
else return c;
}
static void filter0_Decode_32To32(
uint8_t *dst,
uint8_t *x,
uint32_t num
){
memcpy(dst, x, num*4);
}
static void filter1_Decode_32To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint32_t num
){
num--;
dst[0] = x[0];
dst[1] = x[1];
dst[2] = x[2];
dst[3] = x[3];
dst += 4;
x += 4;
while(num--){
dst[0] = x[0] + a[0];
dst[1] = x[1] + a[1];
dst[2] = x[2] + a[2];
dst[3] = x[3] + a[3];
dst += 4;
x += 4;
a += 4;
}
}
static void filter2_Decode_32To32(
uint8_t *dst,
uint8_t *x,
uint8_t *b,
uint32_t num
){
while(num--){
dst[0] = x[0] + b[0];
dst[1] = x[1] + b[1];
dst[2] = x[2] + b[2];
dst[3] = x[3] + b[3];
dst += 4;
x += 4;
b += 4;
}
}
static void filter3_Decode_32To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint8_t *b,
uint32_t num
){
num--;
dst[0] = x[0] + b[0]/2;
dst[1] = x[1] + b[1]/2;
dst[2] = x[2] + b[2]/2;
dst[3] = x[3] + b[3]/2;
dst += 4;
x += 4;
b += 4;
while(num--){
dst[0] = x[0] + ((uint16_t)a[0] + (uint16_t)b[0])/2;
dst[1] = x[1] + ((uint16_t)a[1] + (uint16_t)b[1])/2;
dst[2] = x[2] + ((uint16_t)a[2] + (uint16_t)b[2])/2;
dst[3] = x[3] + ((uint16_t)a[3] + (uint16_t)b[3])/2;
dst += 4;
x += 4;
a += 4;
b += 4;
}
}
static void filter4_Decode_32To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint8_t *b,
uint8_t *c,
uint32_t num
){
num--;
dst[0] = x[0] + paethPredictor(0, b[0], 0);
dst[1] = x[1] + paethPredictor(0, b[1], 0);
dst[2] = x[2] + paethPredictor(0, b[2], 0);
dst[3] = x[3] + paethPredictor(0, b[3], 0);
dst += 4;
x += 4;
b += 4;
while(num--){
dst[0] = x[0] + paethPredictor(a[0], b[0], c[0]);
dst[1] = x[1] + paethPredictor(a[1], b[1], c[1]);
dst[2] = x[2] + paethPredictor(a[2], b[2], c[2]);
dst[3] = x[3] + paethPredictor(a[3], b[3], c[3]);
dst += 4;
x += 4;
a += 4;
b += 4;
c += 4;
}
}
// PNG解码,将32位解码成32位。
static void pngDecode_32To32(
uint8_t *out,
uint8_t *in,
uint32_t width,
uint32_t height
){
uint32_t outScanLine = width * 4;
uint32_t inScanLine = width * 4 + 1;
while(height--){
if(in[0] == 0){
filter0_Decode_32To32(out, in+1, width);
}
else if(in[0] == 1){
filter1_Decode_32To32(out, in+1, out, width);
}
else if(in[0] == 2){
filter2_Decode_32To32(out, in+1, out-outScanLine, width);
}
else if(in[0] == 3){
filter3_Decode_32To32(out, in+1, out, out-outScanLine, width);
}
else{
filter4_Decode_32To32(out, in+1, out, out-outScanLine, out-outScanLine, width);
}
out += outScanLine;
in += inScanLine;
}
}
//////////////////////////////
static void filter0_Decode_24To32(
uint8_t *dst,
uint8_t *x,
uint32_t num
){
while(num--){
dst[0] = x[0];
dst[1] = x[1];
dst[2] = x[2];
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
}
}
static void filter1_Decode_24To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint32_t num
){
num--;
dst[0] = x[0];
dst[1] = x[1];
dst[2] = x[2];
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
while(num--){
dst[0] = x[0] + a[0];
dst[1] = x[1] + a[1];
dst[2] = x[2] + a[2];
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
a += 4;
}
}
static void filter2_Decode_24To32(
uint8_t *dst,
uint8_t *x,
uint8_t *b,
uint32_t num
){
while(num--){
dst[0] = x[0] + b[0];
dst[1] = x[1] + b[1];
dst[2] = x[2] + b[2];
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
b += 4;
}
}
static void filter3_Decode_24To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint8_t *b,
uint32_t num
){
num--;
dst[0] = x[0] + b[0]/2;
dst[1] = x[1] + b[1]/2;
dst[2] = x[2] + b[2]/2;
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
b += 4;
while(num--){
dst[0] = x[0] + ((uint16_t)a[0] + (uint16_t)b[0])/2;
dst[1] = x[1] + ((uint16_t)a[1] + (uint16_t)b[1])/2;
dst[2] = x[2] + ((uint16_t)a[2] + (uint16_t)b[2])/2;
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
a += 4;
b += 4;
}
}
static void filter4_Decode_24To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint8_t *b,
uint8_t *c,
uint32_t num
){
num--;
dst[0] = x[0] + paethPredictor(0, b[0], 0);
dst[1] = x[1] + paethPredictor(0, b[1], 0);
dst[2] = x[2] + paethPredictor(0, b[2], 0);
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
b += 4;
while(num--){
dst[0] = x[0] + paethPredictor(a[0], b[0], c[0]);
dst[1] = x[1] + paethPredictor(a[1], b[1], c[1]);
dst[2] = x[2] + paethPredictor(a[2], b[2], c[2]);
dst[3] = PNG3224_DEFAULT_ALPHA;
dst += 4;
x += 3;
a += 4;
b += 4;
c += 4;
}
}
// PNG解码,将24位解码成32位。
static void pngDecode_24To32(
uint8_t *out,
uint8_t *in,
uint32_t width,
uint32_t height
){
uint32_t outScanLine = width * 4;
uint32_t inScanLine = width * 3 + 1;
while(height--){
if(in[0] == 0){
filter0_Decode_24To32(out, in+1, width);
}
else if(in[0] == 1){
filter1_Decode_24To32(out, in+1, out, width);
}
else if(in[0] == 2){
filter2_Decode_24To32(out, in+1, out-outScanLine, width);
}
else if(in[0] == 3){
filter3_Decode_24To32(out, in+1, out, out-outScanLine, width);
}
else{
filter4_Decode_24To32(out, in+1, out, out-outScanLine, out-outScanLine, width);
}
out += outScanLine;
in += inScanLine;
}
}
//////////////////////////////
// 计算编码后所有字节值的累加值
static uint32_t filter1_perEncode_32To32(
uint8_t *x,
uint8_t *a,
uint32_t num
){
uint32_t ret = 0;
num--;
ret += x[0];
ret += x[1];
ret += x[2];
ret += x[3];
x += 4;
while(num--){
ret += (uint8_t)(x[0] - a[0]);
ret += (uint8_t)(x[1] - a[1]);
ret += (uint8_t)(x[2] - a[2]);
ret += (uint8_t)(x[3] - a[3]);
x += 4;
a += 4;
}
return ret + 1;
}
// 计算编码后所有字节值的累加值
static uint32_t filter2_perEncode_32To32(
uint8_t *x,
uint8_t *b,
uint32_t num
){
uint32_t ret = 0;
while(num--){
ret += (uint8_t)(x[0] - b[0]);
ret += (uint8_t)(x[1] - b[1]);
ret += (uint8_t)(x[2] - b[2]);
ret += (uint8_t)(x[3] - b[3]);
x += 4;
b += 4;
}
return ret + 2;
}
static void filter1_Encode_32To32(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint32_t num
){
num--;
dst[0] = x[0];
dst[1] = x[1];
dst[2] = x[2];
dst[3] = x[3];
dst += 4;
x += 4;
while(num--){
dst[0] = x[0] - a[0];
dst[1] = x[1] - a[1];
dst[2] = x[2] - a[2];
dst[3] = x[3] - a[3];
dst += 4;
x += 4;
a += 4;
}
}
static void filter2_Encode_32To32(
uint8_t *dst,
uint8_t *x,
uint8_t *b,
uint32_t num
){
while(num--){
dst[0] = x[0] - b[0];
dst[1] = x[1] - b[1];
dst[2] = x[2] - b[2];
dst[3] = x[3] - b[3];
dst += 4;
x += 4;
b += 4;
}
}
// PNG 编码,将32位编码成32位。
static void pngEncode_32To32(
uint8_t *out,
uint8_t *in,
uint32_t width,
uint32_t height
){
uint32_t outScanLine = width * 4 + 1;
uint32_t inScanLine = width * 4;
uint32_t filter1Value;
uint32_t filter2Value;
height--;
out[0] = 1;
filter1_Encode_32To32(out+1, in, in, width);
out += outScanLine;
in += inScanLine;
while(height--){
filter1Value = filter1_perEncode_32To32(in, in, width);
filter2Value = filter2_perEncode_32To32(in, in-inScanLine, width);
if(filter1Value <= filter2Value){
out[0] = 1;
filter1_Encode_32To32(out+1, in, in, width);
}
else{
out[0] = 2;
filter2_Encode_32To32(out+1, in, in-inScanLine, width);
}
out += outScanLine;
in += inScanLine;
}
}
//////////////////////////////
// 计算编码后所有字节值的累加值
static uint32_t filter1_perEncode_32To24(
uint8_t *x,
uint8_t *a,
uint32_t num
){
uint32_t ret = 0;
num--;
ret += x[0];
ret += x[1];
ret += x[2];
x += 4;
while(num--){
ret += (uint8_t)(x[0] - a[0]);
ret += (uint8_t)(x[1] - a[1]);
ret += (uint8_t)(x[2] - a[2]);
x += 4;
a += 4;
}
return ret + 1;
}
// 计算编码后所有字节值的累加值
static uint32_t filter2_perEncode_32To24(
uint8_t *x,
uint8_t *b,
uint32_t num
){
uint32_t ret = 0;
while(num--){
ret += (uint8_t)(x[0] - b[0]);
ret += (uint8_t)(x[1] - b[1]);
ret += (uint8_t)(x[2] - b[2]);
x += 4;
b += 4;
}
return ret + 2;
}
static void filter1_Encode_32To24(
uint8_t *dst,
uint8_t *x,
uint8_t *a,
uint32_t num
){
num--;
dst[0] = x[0];
dst[1] = x[1];
dst[2] = x[2];
dst += 3;
x += 4;
while(num--){
dst[0] = x[0] - a[0];
dst[1] = x[1] - a[1];
dst[2] = x[2] - a[2];
dst += 3;
x += 4;
a += 4;
}
}
static void filter2_Encode_32To24(
uint8_t *dst,
uint8_t *x,
uint8_t *b,
uint32_t num
){
while(num--){
dst[0] = x[0] - b[0];
dst[1] = x[1] - b[1];
dst[2] = x[2] - b[2];
dst += 3;
x += 4;
b += 4;
}
}
// PNG编码,将32位编码成24位。
static void pngEncode_32To24(
uint8_t *out,
uint8_t *in,
uint32_t width,
uint32_t height
){
uint32_t outScanLine = width * 3 + 1;
uint32_t inScanLine = width * 4;
uint32_t filter1Value;
uint32_t filter2Value;
height--;
out[0] = 1;
filter1_Encode_32To24(out+1, in, in, width);
out += outScanLine;
in += inScanLine;
while(height--){
filter1Value = filter1_perEncode_32To24(in, in, width);
filter2Value = filter2_perEncode_32To24(in, in-inScanLine, width);
if(filter1Value <= filter2Value){
out[0] = 1;
filter1_Encode_32To24(out+1, in, in, width);
}
else{
out[0] = 2;
filter2_Encode_32To24(out+1, in, in-inScanLine, width);
}
out += outScanLine;
in += inScanLine;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
int png3224_info(
uint32_t *width,
uint32_t *height,
uint32_t *colorDepth,
uint8_t *pngData,
uint32_t pngSize
){
PNG3224_IHDR IHDR;
if(pngSize < (8+(12+13)+12+12)) return -1;
if(memcmp(pngData, pngFileHeader, 8)) return -1;
if(getPngIHDR(&IHDR, pngData+8) == 0) return -1;
if(
((IHDR.ColorType != 2) && (IHDR.ColorType != 6)) ||
(IHDR.Width*IHDR.Height > PNG3224_MAX_PIXEL) ||
(IHDR.BitDepth != 8) ||
(IHDR.CompressionMethod) ||
(IHDR.FilterMethod) ||
(IHDR.InterlaceMethod)
){
return -1;
}
if(width){
*width = IHDR.Width;
}
if(height){
*height = IHDR.Height;
}
if(colorDepth){
if(IHDR.ColorType == 6) *colorDepth = PNG3224_COLOR_DEPTH_32;
else *colorDepth = PNG3224_COLOR_DEPTH_24;
}
return 0;
}
uint8_t* png3224_decode(
uint32_t *width,
uint32_t *height,
uint32_t *colorDepth,
uint8_t *pngData,
uint32_t pngSize
){
// 检查文件格式是否支持
PNG3224_IHDR IHDR;
if(pngData == NULL) return NULL;
if(pngSize < (8+(12+13)+12+12)) return NULL;
if(memcmp(pngData, pngFileHeader, 8)) return NULL;
if(getPngIHDR(&IHDR, pngData+8) == 0) return NULL;
if(
((IHDR.ColorType != 2) && (IHDR.ColorType != 6)) ||
(IHDR.Width*IHDR.Height > PNG3224_MAX_PIXEL) ||
(IHDR.BitDepth != 8) ||
(IHDR.CompressionMethod) ||
(IHDR.FilterMethod) ||
(IHDR.InterlaceMethod)
){
return NULL;
}
// 分配两块buffer
uint32_t channelNum = (IHDR.ColorType == 6) ? 4 : 3;
uint32_t buffer1Size = (IHDR.Width*channelNum+1) * IHDR.Height;
uint32_t buffer2Size = (IHDR.Width*IHDR.Height*4) > pngSize ? (IHDR.Width*IHDR.Height*4) : pngSize;
uint8_t *buffer1 = malloc(buffer1Size);
uint8_t *buffer2 = malloc(buffer2Size);
if((buffer1 == NULL) || (buffer2 == NULL)){
if(buffer1) free(buffer1);
if(buffer2) free(buffer2);
return NULL;
}
// 从文件封包中提取 zlib 压缩的图像数据到 buffer2。
uint32_t IDATsize = getPngIDAT(buffer2, pngData+33, pngSize-33);
if(IDATsize == 0){
free(buffer1);
free(buffer2);
return NULL;
}
// zlib 从 buffer2 解压缩到 buffer1
uint32_t zlibSize = zlibDecode(buffer1, buffer1Size, buffer2, IDATsize);
if(zlibSize != buffer1Size){
free(buffer1);
free(buffer2);
return NULL;
}
// PNG 解码,从 buffer1 解码到 buffer2。
if(channelNum == 4){
pngDecode_32To32(buffer2, buffer1, IHDR.Width, IHDR.Height);
}
else{
pngDecode_24To32(buffer2, buffer1, IHDR.Width, IHDR.Height);
}
free(buffer1);
// 返回图像和图像宽高
if(width){
*width = IHDR.Width;
}
if(height){
*height = IHDR.Height;
}
if(colorDepth){
if(channelNum == 4) *colorDepth = PNG3224_COLOR_DEPTH_32;
else *colorDepth = PNG3224_COLOR_DEPTH_24;
}
return buffer2;
}
uint8_t* png3224_encode(
uint32_t *pngSize,
uint8_t *imgData,
uint32_t width,
uint32_t height,
uint32_t colorDepth
){
//
if((pngSize == NULL) || (imgData == NULL)) return NULL;
if(height*width > PNG3224_MAX_PIXEL) return NULL;
if((colorDepth != PNG3224_COLOR_DEPTH_24) && (colorDepth != PNG3224_COLOR_DEPTH_32)) return NULL;
// 分配两块内存
uint32_t buffer1Size = height * (width * (colorDepth/8) + 1);
uint32_t buffer2Size = buffer1Size + 1024;
uint8_t *buffer1 = malloc(buffer1Size);
uint8_t *buffer2 = malloc(buffer2Size);
if((buffer1 == NULL) || (buffer2 == NULL)){
if(buffer1) free(buffer1);
if(buffer2) free(buffer2);
return NULL;
}
// PNG编码到buffer1
if(colorDepth == PNG3224_COLOR_DEPTH_32){
pngEncode_32To32(buffer1, imgData, width, height);
}
else{
pngEncode_32To24(buffer1, imgData, width, height);
}
// zlib 从 buffer1 压缩到 buffer2
uint32_t zlibSize = zlibEncode(buffer2, buffer2Size, buffer1, buffer1Size);
free(buffer1);
if(zlibSize == 0){
free(buffer2);
return NULL;
}
// 为输出分配内存
uint32_t pngBufferSize = (zlibSize + (8+(12+13)+12+12) + 7) & 0xfffffff8;
uint8_t *pngBuffer = malloc(pngBufferSize);
if(pngBuffer == NULL){
free(buffer2);
return NULL;
}
// 输出png文件格式数据
uint32_t outOffset = 8;
memcpy(pngBuffer, pngFileHeader, 8);
outOffset += putPngIHDR(pngBuffer+outOffset, width, height, colorDepth);
outOffset += putPngIDAT(pngBuffer+outOffset, buffer2, zlibSize);
outOffset += putPngIEND(pngBuffer+outOffset);
free(buffer2);
// 返回png数据字节数
*pngSize = outOffset;
return pngBuffer;
}
uint8_t* png3224_load(
char *fileName,
uint32_t *width,
uint32_t *height,
uint32_t *colorDepth
){
if((fileName == NULL) || (width == NULL) || (height == NULL)){
return NULL;
}
// 打开png文件
FILE *fp = fopen(fileName, "rb");
if(fp == NULL) return NULL;
// 获取文件大小
fseek(fp, 0, SEEK_END);
int fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if((fileSize < 0) || (fileSize > PNG3224_MAX_FILESIZE)){
fclose(fp);
return NULL;
}
// 根据png文件大小分配内存
uint8_t *pngData = malloc(fileSize+8);
if(pngData == NULL){
fclose(fp);
return NULL;
}
// 读取png文件内容
int readNum = fread(pngData, 1, fileSize, fp);
fclose(fp);
if(readNum != fileSize){
free(pngData);
return NULL;
}
// 解码出图像返回。
uint8_t *img = png3224_decode(width, height, colorDepth, pngData, fileSize);
free(pngData);
return img;
}
int png3224_save(
char *fileName,
uint8_t *img,
uint32_t width,
uint32_t height,
uint32_t colorDepth
){
// 一些验证
if(
(width == 0) || (height == 0) || (img == NULL) ||
(fileName == NULL) || (width*height > PNG3224_MAX_PIXEL) ||
((colorDepth != PNG3224_COLOR_DEPTH_24) && (colorDepth != PNG3224_COLOR_DEPTH_32))
){
return -1;
}
// 打开或创建png文件
FILE *fp = fopen(fileName, "wb");
if(fp == NULL) return -2;
// 将图像编码成png
uint32_t pngSize = 0;
uint8_t *pngData = png3224_encode(&pngSize, img, width, height, colorDepth);
if(pngData == NULL){
fclose(fp);
return -3;
}
// 写入磁盘。
int writeNum = fwrite(pngData, 1, pngSize, fp);
fclose(fp);
if(writeNum != pngSize) return -4;
return writeNum;
}
test.c
//==============================================================================
// Copyright (C) 2019 王小康. All rights reserved.
//
// 作者: 王小康
// 描述: PNG3224编解码器测试程序。
// 日期: 2019-09-17
//
//==============================================================================
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "png3224.h"
static void help(char *name){
printf("Usage : %s outPng Bit inPng\n", name);
printf("Example: %s out.png 24 in.png\n", name);
}
int main(int argc, char* argv[]){
if(argc < 4){
help(argv[0]);
return 0;
}
uint32_t width = 0;
uint32_t height = 0;
uint32_t colorDepth = 0;
uint8_t *img = png3224_load(argv[3], &width, &height, &colorDepth);
if(img == NULL) return 0;
printf("png3224_load(): %u x %u, %uBit/Pixel\n", width, height, colorDepth);
// do some thing
if(strcmp(argv[2], "24") == 0) colorDepth = PNG3224_COLOR_DEPTH_24;
if(strcmp(argv[2], "32") == 0) colorDepth = PNG3224_COLOR_DEPTH_32;
int pngSize = png3224_save(argv[1], img, width, height, colorDepth);
free(img);
if(pngSize < 0) return 0;
printf("png3224_save(): %d Bytes\n", pngSize);
return 0;
}
结尾图片包含有zip压缩包:
网友评论