😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:
本文未经允许,不得转发!!!
🎄一、概述
前面的文章介绍过怎么将 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);
else
png_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);
else
png_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