美文网首页
PNG编解码库PNG3224

PNG编解码库PNG3224

作者: 客昂康 | 来源:发表于2021-07-12 20:01 被阅读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压缩包:


    相关文章

      网友评论

          本文标题:PNG编解码库PNG3224

          本文链接:https://www.haomeiwen.com/subject/xtjdpltx.html