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

发布于:2025-06-27 ⋅ 阅读:(15) ⋅ 点赞:(0)

😁博客主页😁:🚀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


网站公告

今日签到

点亮在社区的每一天
去签到