《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 图像的标准库,适合需要高质量图片操作的场景。

使用方法

  1. 安装:

    • 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
  2. 配置项目:

    • 包含头文件:#include <png.h>。

    • 链接库:libpng16.lib(或类似,视版本而定)和 zlib.lib。

    • 依赖:zlib 动态库(zlib1.dll)或静态库。

  3. 调用接口:

    • 读取: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 是快速原型开发的理想选择,适合需要简单图片加载的场景。

使用方法

  1. 安装:

    • 下载:从 GitHub 获取(https://github.com/nothings/stb)。

    • 使用:复制 stb_image.h 到项目,定义宏:

      cpp

      #define STB_IMAGE_IMPLEMENTATION
      #include "stb_image.h"
    • vcpkg:无官方支持,推荐手动包含。

  2. 配置项目:

    • 包含头文件:#include "stb_image.h"。

    • 无需链接库(头文件库)。

  3. 调用接口:

    • 加载: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 个库的计划,每篇将包含类似结构。您可指定优先级:

  1. libwebsockets:WebSocket 通信,示例:实时聊天。

  2. SQLite:轻量数据库,示例:用户数据存储。

  3. FFmpeg:多媒体处理,示例:视频转码。

  4. WebRTC:实时音视频,示例:视频通话。

  5. CEF:嵌入浏览器,示例:Web 页面嵌入。

  6. SDL:多媒体开发,示例:音频播放。

  7. CrashRpt/CrashPad:异常捕获,示例:崩溃日志。

  8. ReactOS:系统参考,示例:API 分析。

  9. boost:网络通信,示例:异步 TCP 服务器。

  10. muduo:网络库,示例:高并发服务器。

  11. libevent:事件驱动,示例:事件循环。

  12. OpenCV:计算机视觉,示例:人脸检测。

  13. Yolo:目标检测,示例:物体识别。

  14. RabbitMQ:消息队列,示例:模块通信。

  15. Redis:键值存储,示例:会话缓存。

  16. ZooKeeper:分布式协调,示例:配置管理。

  17. MongoDB:文档数据库,示例:JSON 存储。

  18. MQTT:物联网通信,示例:设备消息。

  19. GStreamer:流媒体,示例:视频流播放。

  20. 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 个库将按计划继续,您可以:

  1. 指定优先库:如 SQLite、OpenCV 或 FFmpeg。

  2. 请求完整交付:分批生成所有库。

  3. 定制场景:提供具体需求(如视频处理、实时通信)。

请确认优先级或具体要求,我将提供下一批文章(建议下次提供 libwebsockets 和 SQLite)!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐