使用C语言实现简单的PNG图像读取

42 篇文章 0 订阅

概述

首先,关于png图像的结构:PNG文件的结构PNG格式的数据结构。这两篇文章说的比较细。我简单地说一下我使用到的地方:

注:①引于PNG格式的数据结构。②引于PNG文件的结构

“png文件的前8个字节为固定的文件头信息,表明为png文件,其后便为IHDR。
IHDR的前1-4字节表示IHDR的长度(00 00 00 0D),可知长度为13。5-8字节(49 48 44 52)为数据块类型码,表明数据块为IHDR。9-16字节存储了图像的宽高信息(00 00 20 00 00 00 20 00),可知图片的宽高为512x512。其后的5个字节分别表示了图像的色深、颜色类型、滤波器方法、隔行扫描方法,最后四个字节为CRC循环冗余检测。” ①

IHDR由13字节组成:

名称字节数说明
Width4字节图像宽度(像素)
Height4字节图像高度(像素)
Bit depth1字节图像深度:表示每个采样点占用的bit数
索引(indexed)彩色图像:1,2,4或8;
灰度图像:1,2,4,8或16;
真彩色图像:8或16
Colour type1字节颜色类型:
0:灰度图像, 深度为1,2,4,8或16 bit;
2:真彩色(truecolor)图像,深度为8或16 bit;
3:索引彩色图像,深度为1,2,4或8 bit;
4:带α通道数据的灰度图像,深度为8或16 bit;
6:带α通道数据的真彩色(truecolor with alpha)图像,深度为8或16 bit
Compression method1字节压缩方法:
0:LZ77派生算法(目前仅定义了0)
其他值:无效;为未来扩展的压缩方法预留
Filter method1字节滤波器方法:
0(目前仅定义了0)
其他值:无效
Interlace method1字节隔行扫描方法:
0:非隔行扫描;
1: Adam7隔行扫描方法

”②

作为初学者,我个人的想法是创建一个图片结构体,将整张图片以byte[]形式存储,然后设置偏移值跳过不需要的信息,然后使用联合将需要转换的信息转换为相应的数据类型并储存即可。

联合部分:使用联合将4个byte与1个int共用一片存储空间,反向读取整个图片的byte[]数组,对于4字节长的width与height信息,正向存储于联合的byte[]数组,达到byte[]转int的效果。

代码


/*
 * 处理PNG图片的c文件
 * */
#include <stdio.h>
#include <windows.h>
//定义PNG图片体
typedef struct pictureTypedPNG{
    //存储图片路径
    char* path;
    //图像宽度、图像高度
    int width, height;
    //图片大小
    long long pictureSize;
    //图像深度、颜色类型、压缩方法、滤波器方法、隔行扫描方法
    byte depth, colorType, compressionMethod, filterMethod, interlaceMethod;
    //图像本体存储在这里
    byte* body;
}png;

//定义byte转int的联合体
typedef union byteToInt{
    __attribute__((unused)) byte b[4];//只做转换用,不直接调用
    int i;
}byteToInt;

//图片读取
png* getPNG(char* path){
    png* p = (png*)malloc(sizeof(png));
    FILE *fp = fopen(path, "rb");//打开文件。
    if (fp == NULL) // 打开文件失败
        return p;
    //存储路径
    p->path = (char*) malloc((sizeof(char) * strlen(path)));
    strcpy(p->path, path);
    //获取文件大小
    fseek(fp, 0, SEEK_END);//定位文件指针到文件尾。
    p->pictureSize = ftell(fp);//获取文件指针偏移量,即文件大小。
    fseek(fp, 0, SEEK_SET);//定位文件指针到文件头。
    //获取图片体
    p->body = (byte*) malloc(sizeof(byte) * p->pictureSize);//分配存储图片文件的内存
    fread(p->body, 1, p->pictureSize, fp);//读取图片体
    //多byte转int
    byteToInt bti;
    //获取图片部分信息。设置偏移值滤除文件头、IHDR标识信息
    int offset = 8 + 8;
    //获取图像宽度
    for(int i = 3, j = 0; i >= 0; i--, j++){
        bti.b[i] = p->body[j + offset];
    }
    p->width = bti.i;
    //获取图像高度
    for(int i = 3, j = 0; i >= 0; i--, j++){
        bti.b[i] = p->body[j + offset + 4];
    }
    p->height = bti.i;
    //获取图像深度
    p->depth = p->body[8 + offset];
    //获取颜色类型
    p->colorType = p->body[9 + offset];
    //获取压缩方法
    p->compressionMethod = p->body[10 + offset];
    //获取滤波器方法
    p->filterMethod = p->body[11 + offset];
    //获取隔行扫描方法
    p->interlaceMethod = p->body[12 + offset];
    fclose(fp);//关闭文件。
    return p;
}
void printPNG(const png* p){
    const char* colorType = NULL;
    const char* compressionMethod = NULL;
    const char* filterMethod = NULL;
    const char* interlaceMethod = NULL;
    switch(p->colorType){
        case 0: colorType = "灰度图像";break;
        case 2: colorType = "真彩色图像";break;
        case 3: colorType = "索引彩色图像";break;
        case 4: colorType = "带α通道的灰度图像";break;
        case 6: colorType = "带α通道的真彩色图像";break;
    }
    if(!p->compressionMethod){
        compressionMethod = "LZ77派生算法";
    }
    if(!p->filterMethod){
        filterMethod = "0";
    }
    if(p->interlaceMethod){
        interlaceMethod = "Adam7隔行扫描方法";
    }else{
        interlaceMethod = "非隔行扫描";
    }
    printf("图像路径:%s\n"
           "图像大小:%lld\n"
           "图像宽度(像素):%d\n"
           "图像高度(像素):%d\n"
           "图像深度:%hhu\n"
           "颜色类型:%s\n"
           "压缩方法:%s\n"
           "滤波器方法:%s\n"
           "隔行扫描方法:%s\n", p->path, p->pictureSize, p->width, p->height,
           p->depth, colorType, compressionMethod, filterMethod, interlaceMethod);
}
int main() {
    printf("请输入图片路径: \n");
    char* path = (char*)malloc(sizeof(char) * 100);
    fgets(path, 100, stdin);
    int len = (int)strlen(path);
    *(path + len - 1) = '\0';
    png* picture = getPNG(path);
    printPNG(picture);
    return 0;
}

实现效果 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿白|

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值