【开源库 | libpng】使用 libpng 读写 png 文件详细教程(附带源码)

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、libpng 读取 png 图片文件步骤
    • ✨2.1、打开文件并检查是否png文件
    • ✨2.2、初始化libpng的数据结构 :png_ptr, info_ptr
    • ✨2.3、设置错误返回点
    • ✨2.4、初始化 io
    • ✨2.5、读取png文件信息
    • ✨2.6、读取实际的rgb数据
    • ✨2.7、释放一些相关的资源和内存
  • 🎄三、libpng 写入 png 图片文件步骤
    • ✨3.1、1、打开要写入的png文件
    • ✨2.2、初始化libpng的数据结构 :png_ptr, info_ptr
    • ✨2.3、设置错误返回点
    • ✨2.4、初始化 io
    • ✨2.5、设置图片属性
    • ✨2.6、写文件头
    • ✨2.7、写入实际的rgb数据
    • ✨2.8、写入文件尾
    • ✨2.9、释放一些相关的资源和内存
  • 🎄四、总结


在这里插入图片描述

在这里插入图片描述

🎄一、概述

前面的文章介绍过怎么将 libpng 库进行编译、交叉编译。需要了解的可以参考:zlib-1.2.11库、libpng-1.6.36库编译及交叉编译。

这篇文章主要介绍怎么使用 libpng 库读写png图片文件。如果需要了解更多的关于 libpng 库的操作,可以看看 libpng 库源码中的 example.c 文件。


在这里插入图片描述

🎄二、libpng 读取 png 图片文件步骤

✨2.1、打开文件并检查是否png文件

libpng 读取 png 图片文件的第一个步骤就是打开 png 图片,因为png图片文件最前面有8个字节的png标识,所以先使用png_sig_cmp函数检测一下当前文件是否是 png 文件。

// 1、打开文件并检查是否png文件
/* Open the prospective PNG file. */
if ((fp = fopen(file_name, "rb")) == NULL)goto read_end;/* Read in some of the signature bytes. */
if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK)goto read_end;/* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.* Return nonzero (true) if they match.*/
if (0 != png_sig_cmp((png_const_bytep)buf, 0, PNG_BYTES_TO_CHECK))goto read_end;

✨2.2、初始化libpng的数据结构 :png_ptr, info_ptr

libpng库读取文件时需要用到两个指针png_structp, png_infop,分别标识 内部表述结构体png图片信息结构体,第二步需要初始化这两结构体。

// 2、初始化libpng的数据结构 :png_ptr, info_ptr
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL)
{goto read_end;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{goto read_end;
}

✨2.3、设置错误返回点

// 3、设置错误返回点
if (setjmp(png_jmpbuf(png_ptr)))
{/* Free all of the memory associated with the png_ptr and info_ptr. */png_destroy_read_struct(&png_ptr, &info_ptr, NULL);if (NULL != fp){fclose(fp);fp = NULL;}/* If we get here, we had a problem reading the file. */return -1;}

✨2.4、初始化 io

初始化 io, 把png结构体和文件流io进行绑定 。

// 4、初始化 io, 把png结构体和文件流io进行绑定
png_init_io(png_ptr, fp);

✨2.5、读取png文件信息

读取文件信息,包括颜色类型、位深度、宽、高、通道数量等。

位深度,指 ARGB 单个通道的颜色占用了多少个bit,如 RGB888 的位深度是8,RGB555的位深度是5。

通道数量,指多少个颜色通道,ARGB是4,RGB是3。

// 5.读取文件信息
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
int color_type = png_get_color_type(png_ptr, info_ptr);       // 颜色类型
p_pic_data->bit_depth = png_get_bit_depth(png_ptr, info_ptr); // 位深度
p_pic_data->width = png_get_image_width(png_ptr, info_ptr);   // 宽
p_pic_data->height = png_get_image_height(png_ptr, info_ptr); // 高
p_pic_data->channels = png_get_channels(png_ptr, info_ptr);   // 通道数量

✨2.6、读取实际的rgb数据

libpng 库读取rgb数据的函数有如下几个:
png_get_rowbytes:获取每一行的 rgb 数据;
png_get_rows:获取整个图片的 rgb 数据;
png_read_row:读取一行的 rgb 数据;
png_read_rows:读取多行的 rgb 数据;
png_read_image:读取整个图片的 rgb 数据

// 6.读取实际的rgb数据
int i, j, k;
int size, pos = 0;
png_bytepp row_pointers;                                                 // 实际存储rgb数据的buf
row_pointers = png_get_rows(png_ptr, info_ptr);                          // 也可以分别每一行获取png_get_rowbytes();
size = p_pic_data->width * p_pic_data->height * p_pic_data->channels;    // 申请内存先计算空间
if (p_pic_data->channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA) // 颜色深度32位,带有 Alpha 通道
{const int stride = 4; // 跨度,一个像素表示的字节数p_pic_data->rgba = (png_bytep)malloc(size);if (NULL == p_pic_data->rgba){printf("malloc rgba failed ...\n");goto read_end;}// 从row_pointers里读出实际的rgba数据出来for (i = 0; i < p_pic_data->height; i++){for (j = 0; j < p_pic_data->width * stride; j += stride){for (k = 0; k < stride; k++){p_pic_data->rgba[pos++] = row_pointers[i][j + k];}}}
}

✨2.7、释放一些相关的资源和内存

读取完成后,需要释放相关的内存和资源。

read_end: // 释放一些相关的资源和内存if (NULL != fp){fclose(fp);fp = NULL;}if (png_ptr != NULL){if (info_ptr == NULL)png_destroy_read_struct(&png_ptr, NULL, NULL);elsepng_destroy_read_struct(&png_ptr, &info_ptr, NULL);}return ret;

在这里插入图片描述

🎄三、libpng 写入 png 图片文件步骤

✨3.1、1、打开要写入的png文件

首先,打开要写入的 png 文件,后面libpng库也会用到这个文件指针。

// 1、打开要写入的png文件
if ((fp = fopen(file_name, "wb+")) == NULL)goto write_end;

✨2.2、初始化libpng的数据结构 :png_ptr, info_ptr

libpng库写文件时需要用到两个指针png_structp, png_infop,分别标识 内部表述结构体png图片信息结构体,第二步需要初始化这两结构体。

注意,这里使用的是 png_create_write_struct ,与读取时有区别。

// 2、初始化libpng的数据结构 :png_ptr, info_ptr
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL)
{goto write_end;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{goto write_end;
}

✨2.3、设置错误返回点

// 3、设置错误返回点
if (setjmp(png_jmpbuf(png_ptr)))
{goto write_end;
}

✨2.4、初始化 io

初始化 io, 把png结构体和文件流io进行绑定 。

// 4、初始化 io, 把png结构体和文件流io进行绑定
png_init_io(png_ptr, fp);

✨2.5、设置图片属性

设置图片属性:颜色类型、宽、高、位深等。

// 5、设置图片属性
int color_type = PNG_COLOR_TYPE_RGB_ALPHA;
int interlace = 0;
png_set_IHDR(png_ptr, info_ptr, p_pic_data->width, p_pic_data->height, p_pic_data->bit_depth, color_type,(!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

✨2.6、写文件头

// 6、写文件头
png_write_info(png_ptr, info_ptr);

✨2.7、写入实际的rgb数据

libpng 库写入rgb数据的函数有如下几个:
png_write_row:写入一行的 rgb 数据;
png_write_rows:写入多行的 rgb 数据;
png_write_image:写入整个图片的 rgb 数据;

// 7、写入图片信息
png_bytep p_row_pointers;
int i = 0;
for (i = 0; i < p_pic_data->height; i++)
{p_row_pointers = (png_bytep)(p_pic_data->rgba + (i * p_pic_data->width * p_pic_data->channels));png_write_rows(png_ptr, &p_row_pointers, 1);
}

✨2.8、写入文件尾

// 8、写入文件尾png_write_end(png_ptr, info_ptr);

✨2.9、释放一些相关的资源和内存

写入完成后,需要释放相关的内存和资源。

write_end: // 释放一些相关的资源和内存if (NULL != fp){fclose(fp);fp = NULL;}if (png_ptr != NULL){if (info_ptr == NULL)png_destroy_read_struct(&png_ptr, NULL, NULL);elsepng_destroy_read_struct(&png_ptr, &info_ptr, NULL);}return ret;

在这里插入图片描述

🎄四、总结

本文先介绍了使用 libpng 库读取png文件的步骤,然后又介绍了使用 libpng 库写入png文件的步骤,最后给出了源码地址。

根据上面的步骤,你应该可以自己写一个读写png图片的 .c 源文件了,如果还有问题可以下载下面源码,本文源码地址:https://download.csdn.net/download/wkd_007/91134800。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考:
libpng库编码图片为png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/diannao/88494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nuttx之nxsched_add_readytorun(non-SMP)

声明&#xff1a;此处代码分析&#xff0c;来源与 nuttx 12.8.0版本。 在分析之前&#xff0c;需要一图镇楼。 /***************************************************************************** Name: nxsched_add_readytorun** Description:* This function adds a TCB …

Nuttx之nxsched_add_blocked

声明&#xff1a;此处代码分析&#xff0c;来源与 nuttx 12.8.0版本。 在分析之前&#xff0c;需要一图镇楼。 /***************************************************************************** Name: nxsched_add_blocked** Description:* This function adds a TCB to o…

python 包含虚拟环境venv项目的移动

python 包含虚拟环境venv项目的移动 在ubuntu环境下&#xff0c;移动一个包含venv虚拟环境的项目后&#xff0c;在执行时会报错: 错误1&#xff1a; Traceback (most recent call last):File "app.py", line 2, in <module>from flask import Flask, request…

WPF中实现TreeView的SelectedItem双向绑定到ViewModel

WPF中实现TreeView的SelectedItem双向绑定到ViewModel WPF中实现TreeView的SelectedItem双向绑定到ViewModel问题背景解决方案一&#xff1a;附加行为&#xff08;推荐&#xff09;实现步骤优点 解决方案二&#xff1a;通过IsSelected属性绑定实现步骤注意事项 两种方案对比补充…

类型转换运算符重载

C 类型转换函数详解 类型转换函数是C中用于实现类类型与其他类型之间相互转换的特殊成员函数&#xff0c;分为两种主要形式&#xff1a;转换构造函数和类型转换运算符。 1. 转换构造函数 (Conversion Constructor) 基本概念 转换构造函数是一种特殊的构造函数&#xff0c;它…

ES10(ES2019)新特性整理

一、Array.prototype.flat() 和 flatMap()&#xff08;数组扁平化&#xff09; &#xff08;1&#xff09;flat(depth) 将嵌套数组“拉平”到指定深度&#xff08;默认 depth1&#xff09;。 const arr [1, [2, [3]]]; arr.flat(); // [1, 2, [3]]&#xff08;默认深度 …

基于 LCD1602 的超声波测距仪设计与实现:从原理到应用

具体材料可在主页资源里下载 超声波测距技术作为非接触式测量的重要手段&#xff0c;在工业检测、智能家居、机器人避障等领域有着广泛应用。本文将详细介绍一款基于 STC89C51 单片机与 LCD1602 显示屏的超声波测距系统&#xff0c;从硬件架构到软件实现&#xff0c;完整呈现一…

2.5G/5G/10G自协商An

IEEE 802.3 协议中&#xff0c;**2.5GBASE-T、5GBASE-T 和 10GBASE-T** 的链路自协商&#xff08;auto-negotiation&#xff0c;简称 AN&#xff09;是在物理层&#xff08;PHY&#xff09;完成的。它的作用是&#xff1a; * **让连接双方&#xff08;主机和对端&#xff09;自…

闲庭信步使用SV搭建图像测试平台:第五课——使用task

&#xff08;本系列只需要modelsim即可完成数字图像的处理&#xff0c;每个工程都搭建了全自动化的仿真环境&#xff0c;只需要双击top_tb.bat文件就可以完成整个的仿真&#xff0c;大大降低了初学者的门槛&#xff01;&#xff01;&#xff01;&#xff01;如需要该系列的工程…

Android数据库GreenDao的使用

简介 GreenDao 是一个轻量级的对象关系映射&#xff08;ORM&#xff09;库&#xff0c;用于简化 Android 应用中的数据库操作。它提供了以下主要功能&#xff1a; 简化数据库操作&#xff1a;通过注解定义实体类&#xff0c;GreenDao 自动生成 DAO&#xff08;数据访问对象&a…

24小时留言板

title: 24小时留言板 date: 2025-06-25 23:32:53 tags: 代码工具 24小时留言板 核心效果如图所示 代码解析 # TodoController 代码解析## 整体架构 这是一个基于Spring WebFlux的响应式控制器&#xff0c;结合Redis发布\订阅机制实现实时更新的待办事项系统。关键组件包括&a…

深入理解Redis整数集合(intset)的升级策略:内存优化的核心魔法

引言 作为Redis中最节省内存的数据结构之一&#xff0c;整数集合&#xff08;intset&#xff09; 专门用于高效存储整型数据。但你可能不知道&#xff0c;它背后藏着一个精妙的「动态升级」机制——能在不浪费内存的前提下&#xff0c;灵活适配不同大小的整数。今天我们就来扒…

高性能计算(HPC)集群和工作流:intel-oneapi-hpc-toolkit安装与使用

成功安装了 Intel oneAPI HPC Toolkit&#xff01;这个工具包包含了很多强大的工具&#xff0c;可以帮助你优化和加速高性能计算&#xff08;HPC&#xff09;任务&#xff0c;特别是在使用 Intel 的硬件&#xff08;如 Xeon 处理器和 GPU&#xff09;时。 接下来&#xff0c;…

QT vscode cmake 编译 undefined reference to `vtable for 问题解决

编译时出现undefined reference to vtable for 问题&#xff0c;是没有添加头文件到目标&#xff0c;添加即可&#xff1a; 如果使用的是qt5, 没有qt_add_executable, 使用qt 5的 自动处理即可&#xff1a; # 启用 Qt 自动处理功能 set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC …

linux内核奔溃转储之kexec、kdump

一、kexec是什么&#xff1f; kexec 是 Linux 内核提供的一种关键技术&#xff0c;允许系统‌在不经过完整硬件重启&#xff08;BIOS/UEFI 初始化&#xff09;的情况下&#xff0c;直接从当前正在运行的内核加载并启动另一个新内核‌。以下是其核心要点&#xff1a; ‌定义与核…

标题:2025金融护网行动实战指南:从合规防御到智能免疫的体系化进阶

引言 2025年&#xff0c;随着《中国人民银行业务领域网络安全事件报告管理办法》正式实施&#xff0c;金融护网行动已从“合规检查”升级为“能力对抗”。面对AI驱动的自适应攻击、勒索病毒与黑灰产协同威胁&#xff0c;金融机构需构建“技术-管理-人才”三位一体的智能防御体…

NEO4j的安装部署

windows neo4j新版本安装需要部署jdk17&#xff0c;下面这个版本是jdk8最新的支持版本 neo4j-community-3.5.9-windows.zipIndex of /doc/neo4j/3.5.9/ 启动 dos面板中启动 neo4j.bat console linux neo4j新版本安装需要部署jdk17&#xff0c;下面这个版本是jdk8最新的支…

八股文——JAVA基础:说一下C++与java的区别

首先&#xff0c;c与java都是面向对象编程&#xff0c;都包含封装、继承、多态的特性。但是c多继承&#xff0c;而java只能单继承与多实现。 其次&#xff0c;java无法直接访问内存&#xff0c;java通过引用对向&#xff0c;比如new一个对象&#xff0c;拿到的对象实例实际上是…

Vue3 Composition API 深度解析:告别Options API的局限性

目录 一、为什么需要Composition API&#xff1f; 二、核心概念&#xff1a;setup() 函数 三、响应式核心&#xff1a;ref() 和 reactive() 1. ref - 处理基本类型/对象 2. reactive - 处理对象 四、生命周期钩子新写法 五、强大的逻辑复用&#xff1a;组合式函数 六、响…

IoT/HCIP实验-5/基于NB-IoT的智慧农业实验(平台侧开发+端侧编码+基础调试分析)

文章目录 概述扩展板 E53_IA1智慧农业平台测开发功能定义/模型开发编解码插件开发-消息编解码插件开发-关联编解码插件开发-部署注册实际设备 智慧农业端侧编码工程配置数据结构定义数据收集任务数据上报任务设备接入过程正确设置接入参数命令响应任务 程序调试其他 概述 本实…