PNG编解码库PNG3224

2021-07-12  本文已影响0人  客昂康

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压缩包:


上一篇下一篇

猜你喜欢

热点阅读