《C++开源库介绍与使用》(libpng 和 stb_image)的完整文章
《C++开源库介绍与使用》提供详细内容,逐一为每个开源库撰写完整的文章,包括功能介绍、使用方法、实例代码、经典案例分析、问题排查、经验总结、资料扩展和中文解释。(libpng 和 stb_image)的完整文章,延续前文(jsoncpp、RapidJson、TinyXML、libcurl、zip.cpp/unzip.cpp、libqr)的结构和深度。结合您之前的VC++背景(如zlib、时间函数、IM软件),我将确保代码可集成到现有项目,并提供跨库协作案例。
我将按顺序逐批交付,您可随时指定优先级(如SQLite、OpenCV)或定制场景(如实时聊天)。本响应包含两个库的详细内容、后续计划和综合案例。如果需要调整,请告知!
一、libpng:PNG图片处理库
功能介绍
libpng 是一个广泛使用的 C 库,用于读写 PNG(Portable Network Graphics)格式图片,支持无损压缩和透明度。核心功能:
-
读取 PNG:解析 PNG 文件,获取像素数据、元信息(如宽高、颜色类型)。
-
写入 PNG:创建 PNG 文件,支持自定义像素数据和压缩级别。
-
跨平台:支持 Windows、Linux、嵌入式系统。
-
依赖:依赖 zlib(参考第14篇)进行压缩。
-
应用场景:图像处理、游戏开发、IM 软件中图片传输。
libpng 是处理 PNG 图像的标准库,适合需要高质量图片操作的场景。
使用方法
-
安装:
-
vcpkg(推荐):
bash
vcpkg install libpng:x64-windows vcpkg integrate install -
手动下载:从 http://www.libpng.org/pub/png/libpng.html 或 GitHub(https://github.com/glennrp/libpng)。
-
编译:
bash
git clone https://github.com/glennrp/libpng.git cd libpng mkdir build && cd build cmake .. -G "Visual Studio 16 2019" -DZLIB_LIBRARY=<zlib_path>/zlib.lib -DZLIB_INCLUDE_DIR=<zlib_path> msbuild libpng.sln
-
-
配置项目:
-
包含头文件:#include <png.h>。
-
链接库:libpng16.lib(或类似,视版本而定)和 zlib.lib。
-
依赖:zlib 动态库(zlib1.dll)或静态库。
-
-
调用接口:
-
读取:png_create_read_struct 初始化,png_read_image 读取像素。
-
写入:png_create_write_struct 初始化,png_write_image 写入像素。
-
清理:png_destroy_* 释放资源。
-
实例代码
以下代码展示如何读取 PNG 图片和生成新的 PNG 图片,包含日志记录(参考第7篇时间函数)和中文支持。
cpp
#include <windows.h>
#include <png.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#pragma comment(lib, "libpng16.lib")
#pragma comment(lib, "zlib.lib")
// 日志记录
void LogMessage(const std::wstring& message) {
std::wofstream log("png_log.txt", std::ios::app);
log << L"[" << std::time(nullptr) << L"] " << message << std::endl;
}
// 读取 PNG 文件
bool ReadPng(const std::string& filename, std::vector<unsigned char>& pixels, int& width, int& height) {
FILE* fp = fopen(filename.c_str(), "rb");
if (!fp) {
LogMessage(L"无法打开 PNG 文件: " + std::wstring(filename.begin(), filename.end()));
return false;
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png) {
fclose(fp);
LogMessage(L"无法创建 PNG 读结构");
return false;
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, nullptr, nullptr);
fclose(fp);
LogMessage(L"无法创建 PNG 信息结构");
return false;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, nullptr);
fclose(fp);
LogMessage(L"PNG 读取错误");
return false;
}
png_init_io(png, fp);
png_read_info(png, info);
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
png_byte color_type = png_get_color_type(png, info);
png_byte bit_depth = png_get_bit_depth(png, info);
// 转换为 RGBA
if (bit_depth == 16) png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png);
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
pixels.resize(width * height * 4);
std::vector<png_bytep> row_pointers(height);
for (int y = 0; y < height; ++y) {
row_pointers[y] = pixels.data() + y * width * 4;
}
png_read_image(png, row_pointers.data());
png_destroy_read_struct(&png, &info, nullptr);
fclose(fp);
LogMessage(L"读取 PNG 成功: 宽=" + std::to_wstring(width) + L", 高=" + std::to_wstring(height));
return true;
}
// 写入 PNG 文件
bool WritePng(const std::string& filename, const std::vector<unsigned char>& pixels, int width, int height) {
FILE* fp = fopen(filename.c_str(), "wb");
if (!fp) {
LogMessage(L"无法创建 PNG 文件: " + std::wstring(filename.begin(), filename.end()));
return false;
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png) {
fclose(fp);
LogMessage(L"无法创建 PNG 写结构");
return false;
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_write_struct(&png, nullptr);
fclose(fp);
LogMessage(L"无法创建 PNG 信息结构");
return false;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct(&png, &info);
fclose(fp);
LogMessage(L"PNG 写入错误");
return false;
}
png_init_io(png, fp);
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, info);
std::vector<png_bytep> row_pointers(height);
for (int y = 0; y < height; ++y) {
row_pointers[y] = const_cast<unsigned char*>(pixels.data()) + y * width * 4;
}
png_write_image(png, row_pointers.data());
png_write_end(png, nullptr);
png_destroy_write_struct(&png, &info);
fclose(fp);
LogMessage(L"写入 PNG 成功: " + std::wstring(filename.begin(), filename.end()));
return true;
}
int wmain() {
LogMessage(L"程序启动");
// 1. 读取 PNG
std::vector<unsigned char> pixels;
int width, height;
if (!ReadPng("input.png", pixels, width, height)) {
std::wcerr << L"读取 PNG 失败" << std::endl;
return 1;
}
std::wcout << L"读取 PNG: 宽=" << width << L", 高=" << height << std::endl;
// 2. 修改像素(示例:反转颜色)
for (size_t i = 0; i < pixels.size(); i += 4) {
pixels[i] = 255 - pixels[i]; // R
pixels[i + 1] = 255 - pixels[i + 1]; // G
pixels[i + 2] = 255 - pixels[i + 2]; // B
// pixels[i + 3] = alpha
}
// 3. 写入新 PNG
if (!WritePng("output.png", pixels, width, height)) {
std::wcerr << L"写入 PNG 失败" << std::endl;
return 1;
}
std::wcout << L"已写入 output.png" << std::endl;
LogMessage(L"程序结束");
std::wcout << L"按 Enter 退出..." << std::endl;
std::wcin.get();
return 0;
}
经典案例 analysis
-
案例:IM 软件头像处理
-
场景:IM 客户端(参考第9篇剪切板、第11篇分层窗口)需加载用户头像(PNG),并生成缩略图。
-
实现:
-
读取头像:
cpp
std::vector<unsigned char> pixels; int width, height; ReadPng("avatar.png", pixels, width, height); -
生成缩略图(简化示例):
cpp
std::vector<unsigned char> thumb_pixels(100 * 100 * 4); // 缩放逻辑... WritePng("thumb.png", thumb_pixels, 100, 100);
-
-
优点:libpng 支持高质量 PNG,透明度处理完善。
-
挑战:复杂图像处理需额外算法,内存管理需注意。
-
问题排查与经验总结
-
问题 1:PNG 读取失败
-
原因:文件损坏或格式不支持。
-
解决:检查 setjmp 错误,使用 png_get_error_ptr 捕获详细信息。
-
-
问题 2:内存泄漏
-
原因:未正确释放 png_structp 或 png_infop。
-
解决:始终调用 png_destroy_*:
cpp
png_destroy_read_struct(&png, &info, nullptr);
-
-
经验:
-
使用 RGBA 格式简化处理,注意字节对齐。
-
对于大图像,分块读取减少内存占用。
-
调试时记录图像元信息(宽、高、颜色类型)。
-
资料扩展
-
官网:http://www.libpng.org/pub/png/libpng.html
-
GitHub:https://github.com/glennrp/libpng
-
文档:http://www.libpng.org/pub/png/libpng-manual.txt
-
社区:StackOverflow 搜索“libpng example”。
中文说明
libpng 是一个 PNG 图片处理库,依赖 zlib(第14篇),适合 IM 软件处理头像或图片分享。示例代码展示了读取和写入 PNG,结合日志记录(参考第7篇时间函数)便于调试。注意内存管理和格式转换,可与 libqr(第6篇)结合生成二维码 PNG,或与 libcurl(第4篇)上传图片。
二、stb_image:图片解析库
功能介绍
stb_image 是一个单文件 C 库(stb_image.h),用于加载多种图片格式(JPEG、PNG、BMP、TGA 等),以简单性和易用性著称。核心功能:
-
加载图片:支持多种格式,获取像素数据、宽高、通道数。
-
轻量级:无需编译,无外部依赖,头文件直接使用。
-
跨平台:支持 Windows、Linux、嵌入式系统。
-
局限性:仅支持读取,不支持写入(写入需 stb_image_write.h)。
-
应用场景:游戏开发、图像预览、IM 软件图片加载。
stb_image 是快速原型开发的理想选择,适合需要简单图片加载的场景。
使用方法
-
安装:
-
下载:从 GitHub 获取(https://github.com/nothings/stb)。
-
使用:复制 stb_image.h 到项目,定义宏:
cpp
#define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" -
vcpkg:无官方支持,推荐手动包含。
-
-
配置项目:
-
包含头文件:#include "stb_image.h"。
-
无需链接库(头文件库)。
-
-
调用接口:
-
加载:stbi_load 读取图片文件,stbi_load_from_memory 读取内存数据。
-
释放:stbi_image_free 释放像素数据。
-
元信息:返回宽、高、通道数。
-
实例代码
以下代码展示如何加载 PNG/JPEG 图片并显示基本信息,包含日志记录和中文支持。
cpp
#include <windows.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <iostream>
#include <fstream>
#include <sstream>
void LogMessage(const std::wstring& message) {
std::wofstream log("stb_image_log.txt", std::ios::app);
log << L"[" << std::time(nullptr) << L"] " << message << std::endl;
}
// 加载图片
bool LoadImage(const std::string& filename, std::vector<unsigned char>& pixels, int& width, int& height, int& channels) {
int desired_channels = 4; // 强制 RGBA
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &channels, desired_channels);
if (!data) {
LogMessage(L"无法加载图片: " + std::wstring(filename.begin(), filename.end()) + L", 错误: " +
std::wstring(stbi_failure_reason(), stbi_failure_reason() + strlen(stbi_failure_reason())));
return false;
}
pixels.assign(data, data + width * height * desired_channels);
stbi_image_free(data);
LogMessage(L"加载图片成功: 宽=" + std::to_wstring(width) + L", 高=" + std::to_wstring(height) +
L", 通道=" + std::to_wstring(desired_channels));
return true;
}
int wmain() {
LogMessage(L"程序启动");
// 加载图片
std::vector<unsigned char> pixels;
int width, height, channels;
std::string filename = "test.jpg"; // 或 test.png
if (!LoadImage(filename, pixels, width, height, channels)) {
std::wcerr << L"加载图片失败" << std::endl;
return 1;
}
std::wcout << L"图片信息: 宽=" << width << L", 高=" << height << L", 通道=" << channels << std::endl;
// 示例:显示前几个像素的 RGBA 值
for (size_t i = 0; i < std::min<size_t>(16, pixels.size()); i += 4) {
std::wcout << L"像素[" << i / 4 << L"]: R=" << (int)pixels[i]
<< L", G=" << (int)pixels[i + 1]
<< L", B=" << (int)pixels[i + 2]
<< L", A=" << (int)pixels[i + 3] << std::endl;
}
LogMessage(L"程序结束");
std::wcout << L"按 Enter 退出..." << std::endl;
std::wcin.get();
return 0;
}
经典案例 analysis
-
案例:IM 软件图片预览
-
场景:IM 客户端(参考第9篇剪切板)需加载聊天中接收的图片进行预览。
-
实现:
-
加载图片:
cpp
std::vector<unsigned char> pixels; int width, height, channels; LoadImage("chat_image.jpg", pixels, width, height, channels); -
显示:结合分层窗口(第11篇)渲染像素数据。
-
-
优点:stb_image 支持多格式,集成简单。
-
挑战:不支持写入,需配合其他库(如 libpng)。
-
问题排查与经验总结
-
问题 1:图片加载失败
-
原因:文件损坏或格式不支持。
-
解决:检查 stbi_failure_reason():
cpp
if (!data) { std::wcerr << L"错误: " << stbi_failure_reason() << std::endl; }
-
-
问题 2:内存溢出
-
原因:大图片占用过多内存。
-
解决:检查图片尺寸,必要时分块加载。
-
-
经验:
-
使用 desired_channels 强制统一格式(如 RGBA)。
-
调试时打印宽高和通道数,验证数据正确性。
-
若需写入,使用 stb_image_write.h。
-
资料扩展
-
GitHub:https://github.com/nothings/stb
-
文档:https://github.com/nothings/stb/blob/master/stb_image.h(内嵌注释)
-
教程:https://learnopengl.com/Getting-started/Textures(结合 OpenGL)
-
社区:StackOverflow 搜索“stb_image example”。
中文说明
stb_image 是一个单文件图片加载库,支持 JPEG、PNG 等格式,适合 IM 软件预览聊天图片。示例代码展示了图片加载和像素信息提取,结合日志记录(参考第7篇时间函数)便于调试。注意释放内存和格式支持,可与 libpng(第7篇)结合保存图片,或与 libcurl(第4篇)下载图片。
三、后续库计划
以下是剩余 18 个库的计划,每篇将包含类似结构。您可指定优先级:
-
libwebsockets:WebSocket 通信,示例:实时聊天。
-
SQLite:轻量数据库,示例:用户数据存储。
-
FFmpeg:多媒体处理,示例:视频转码。
-
WebRTC:实时音视频,示例:视频通话。
-
CEF:嵌入浏览器,示例:Web 页面嵌入。
-
SDL:多媒体开发,示例:音频播放。
-
CrashRpt/CrashPad:异常捕获,示例:崩溃日志。
-
ReactOS:系统参考,示例:API 分析。
-
boost:网络通信,示例:异步 TCP 服务器。
-
muduo:网络库,示例:高并发服务器。
-
libevent:事件驱动,示例:事件循环。
-
OpenCV:计算机视觉,示例:人脸检测。
-
Yolo:目标检测,示例:物体识别。
-
RabbitMQ:消息队列,示例:模块通信。
-
Redis:键值存储,示例:会话缓存。
-
ZooKeeper:分布式协调,示例:配置管理。
-
MongoDB:文档数据库,示例:JSON 存储。
-
MQTT:物联网通信,示例:设备消息。
-
GStreamer:流媒体,示例:视频流播放。
-
Live555:实时流,示例:RTSP 服务器。
四、综合案例:IM 软件图片分享
案例:IM 软件图片分享与备份
-
功能:
-
使用 stb_image 加载聊天图片。
-
使用 libpng 保存处理后的图片。
-
使用 zip.cpp 压缩图片文件。
-
使用 libqr 生成分享链接二维码。
-
使用 libcurl 上传 ZIP。
-
-
代码框架:
cpp
#include <stb_image.h> #include <png.h> #include <zip.h> #include <qrencode.h> #include <curl/curl.h> int main() { // 加载图片 std::vector<unsigned char> pixels; int width, height, channels; LoadImage("chat.jpg", pixels, width, height, channels); // 保存处理后的图片 WritePng("processed.png", pixels, width, height); // 压缩图片 CompressFileToZip("processed.png", "images.zip"); // 生成二维码 std::string url = "https://server/images.zip"; QRcode* qr = QRcode_encodeString(url.c_str(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); SaveQRCodeAsAscii(qr, "share_qr.txt"); QRcode_free(qr); // 上传 ZIP CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "https://server/upload"); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); std::ifstream zipFile("images.zip", std::ios::binary); curl_easy_setopt(curl, CURLOPT_READDATA, &zipFile); curl_easy_perform(curl); curl_easy_cleanup(curl); return 0; }
五、总结与下一步
本响应提供了 libpng 和 stb_image 的完整文章,包括功能介绍、实例代码、案例分析和中文说明。代码可集成到您的 IM 软件(参考第9篇、第11篇),与 zlib(第14篇)、libcurl(第4篇)、libqr(第6篇)结合使用。剩余 18 个库将按计划继续,您可以:
-
指定优先库:如 SQLite、OpenCV 或 FFmpeg。
-
请求完整交付:分批生成所有库。
-
定制场景:提供具体需求(如视频处理、实时通信)。
请确认优先级或具体要求,我将提供下一批文章(建议下次提供 libwebsockets 和 SQLite)!
更多推荐


所有评论(0)