拓冰建站拓冰建站
首页 / 资讯中心 / 正文

libspng 编码指南:基于 Source SDK 2013 内嵌库的 PNG 编码 API 与实战

libspng 编码指南基于 Source SDK 2013 内嵌库的 PNG 编码 API 与实战【免费下载链接】source-sdk-2013The 2013 edition of the Source SDK项目地址: https://gitcode.com/GitHub_Trending/so/source-sdk-2013导读本文以 Source SDK 2013 仓库内嵌的第三方库 libspngsrc/thirdparty/libspng官方编码文档 encode.md 为主体完整讲解如何用 C 语言将原始像素数据编码为 PNG 文件。你将掌握编码上下文的创建与输出目标配置、spng_encode_image()/spng_encode_row()/spng_encode_scanline()等核心 API 的用法、渐进式编码与隔行interlace图像的处理套路以及压缩级别、过滤策略等编码选项的调优方法。文中所有结论均有仓库源码佐证可直接用于游戏纹理、截图导出等工具的 PNG 输出模块。编码基础上下文、输出与收尾与解码一样libspng 的所有编码操作都围绕spng_ctx上下文对象展开但编码上下文必须显式指定类型spng_ctx *ctx spng_ctx_new(SPNG_CTX_ENCODER);SPNG_CTX_ENCODER定义于 context.md 的spng_ctx_flags枚举中值为 2。从 spng.c 的实现可以看到spng_encode_image()内部会检查ctx-encode_only非编码上下文会直接返回SPNG_ECTXTYPE因此该标志是编码的硬性前提。设置输出目标在发生任何隐式的写操作之前必须先用以下三者之一指定输出spng_set_png_file(ctx, FILE *file)输出到FILE*见 context.mdspng_set_png_stream(ctx, spng_write_fn *rw_func, void *user)输出到自定义写回调见 context.md写回调签名如下需处理length字节并返回 0 或SPNG_IO_ERRORtypedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length)启用SPNG_ENCODE_TO_BUFFER选项通过spng_set_option()编码器自行创建并管理内部输出缓冲区。使用内部缓冲区时缓冲区在spng_ctx_free()时被释放——除非你通过spng_get_png_buffer()取走它取走后由调用方负责free。以上两种文件/流设置每个上下文只能调用一次见 context.md。显式收尾Finalize无论采用哪种输出方式PNG 都必须被显式收尾有两种途径在spng_encode_image()时传入SPNG_ENCODE_FINALIZE标志在图像编码完成后调用spng_encode_chunks()它会补齐图像数据后的所有挂起块并写入文件结束标记IEND。核心 API 逐个击破spng_encode_chunks()int spng_encode_chunks(spng_ctx *ctx)编码所有已存储的块具体是编码到 IDAT图像数据流之前还是之后取决于编码器的当前状态。如果图像已经编码完毕该函数还会写入 IEND 标记完成收尾。在spng_encode_image()之前调用它是可选的用于先写出 tEXt、pHYs 等元数据块。spng_encode_image()int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags)一次性编码整张图像是使用最频繁的入口img指向长度为len的源像素缓冲区len必须等于给定格式fmt的期望图像大小目标 PNG 的宽度、高度、颜色类型、位深和隔行方法必须先通过spng_set_ihdr()设置见 chunk.md若颜色类型为SPNG_COLOR_TYPE_INDEXED调色板索引色还必须先通过spng_set_plte()设置调色板见 chunk.md——spng.c 中会直接检查ctx-stored.plte缺失时返回SPNG_ENOPLTE该函数可能内部调用spng_encode_chunks()写出图像数据前的挂起块若设置了SPNG_ENCODE_FINALIZE在最后一行扫描线处理完毕时会自动补齐图像数据后的块并写入 IEND。通常情况下图像数据之后只有 12 字节的 IEND 标记。从 spng.c 的实现还可以看到几个重要的校验细节非渐进模式下img为 NULL 直接失败len与计算出的image_size不一致返回SPNG_EBUFSIZ图像尺寸计算溢出返回SPNG_EOVERFLOW。另外编码器会基于颜色类型和位深做过滤优化——调色板图像与低位深8 bit图像不受益于行过滤会被自动禁用过滤spng.c这一点在使用SPNG_FILTER_CHOICE选项时需要注意。支持的格式与标志组合输入格式PNG 格式标志说明SPNG_FMT_PNG任意格式*全部需要时自动从主机字节序转换SPNG_FMT_RAW任意格式*全部不转换假定大端字节序* PNG 标准定义的任意颜色类型与位深组合参考 W3C PNG 规范中的有效组合表。其他要点16 位图像默认按主机字节序处理SPNG_FMT_RAW除外所有格式的 alpha 通道一律为直通straightalpha不支持预乘 alpha见 context.md压缩级别等参数可通过spng_set_option()调整注意编码器选项会基于 PNG 格式与压缩级别自动优化若你手动覆盖过滤等选项可能关闭部分优化。渐进式编码Progressive Encoding设置SPNG_ENCODE_PROGRESSIVE标志后spng_encode_image()仅用于以fmt和flags初始化编码器img、len参数被忽略可传 NULL/0。若同时设置SPNG_ENCODE_FINALIZEPNG 会在最后一行扫描线处理完时被收尾。非隔行图像的渐进式编码很直观对每一行调用spng_encode_row()最后一行返回SPNG_EOIint error; size_t image_width image_size / ihdr.height; for(i 0; i ihdr.height; i) { void *row image image_width * i; error spng_encode_row(ctx, row, image_width); if(error) break; } if(error SPNG_EOI) /* success */隔行图像spng_ihdr.interlaced_method为 1的行会被多次、非顺序地访问必须配合spng_get_row_info()见 context.md获取当前行号int error; struct spng_row_info row_info; do { error spng_get_row_info(ctx, row_info); if(error) break; void *row image image_width * row_info.row_num; error spng_encode_row(ctx, row, len); } while(!error) if(error SPNG_EOI) /* success */struct spng_row_info包含scanline_idx、row_num、pass、filter四个字段其中row_num即当前行在原始图像中的行号见 context.md。仓库的写模糊测试 spng_write_fuzzer.c 正是用这套spng_get_row_info()spng_encode_row()组合对渐进模式做覆盖测试的。spng_encode_scanline()int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len)用于图像数据已按多个 pass 拆好的隔行 PNG直接编码一条扫描线不做隔行重组。使用前提同样是先以SPNG_ENCODE_PROGRESSIVE调用spng_encode_image()完成初始化。最后一条扫描线及之后的调用返回SPNG_EOI。spng_encode_row()int spng_encode_row(spng_ctx *ctx, const void *row, size_t len)编码一行必要时自动执行隔行拆分。同样要求先以SPNG_ENCODE_PROGRESSIVE初始化。对非隔行图像其行为与spng_encode_scanline()完全一致最后一行及之后的调用返回SPNG_EOI。spng_get_png_buffer()void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error)当启用了SPNG_ENCODE_TO_BUFFER时在spng_encode_image()之后且 PNG 已收尾调用返回编码完成的 PNG 缓冲区。成功时缓冲区所有权移交调用方必须自行free若未调用本函数或编码出错内部缓冲区由spng_ctx_free()统一释放。编码选项压缩与过滤的精细控制通过spng_set_option(ctx, option, value)可调整以下编码相关选项完整定义见 context.md 与 spng.h选项默认值说明SPNG_IMG_COMPRESSION_LEVELZ_DEFAULT_COMPRESSION图像压缩级别0-9SPNG_IMG_WINDOW_BITS15*图像 zlib 窗口位数9-15SPNG_IMG_MEM_LEVEL8图像的 zlibmemLevelSPNG_IMG_COMPRESSION_STRATEGYZ_FILTERED*图像压缩策略SPNG_TEXT_COMPRESSION_LEVELZ_DEFAULT_COMPRESSION文本块压缩级别0-9SPNG_TEXT_WINDOW_BITS15文本块 zlib 窗口位数9-15SPNG_TEXT_MEM_LEVEL8文本块的 zlibmemLevelSPNG_TEXT_COMPRESSION_STRATEGYZ_DEFAULT_STRATEGY文本块压缩策略SPNG_FILTER_CHOICESPNG_FILTER_CHOICE_ALL*配置或禁用行过滤SPNG_ENCODE_TO_BUFFER0编码到内部缓冲区* 未显式设置时该选项可能被自动优化。要点解读未在表中列出的选项对编码器无效SPNG_FILTER_CHOICE的取值来自spng_filter_choice枚举context.mdSPNG_DISABLE_FILTERING 0完全禁用过滤SPNG_FILTER_CHOICE_NONE/SUB/UP/AVG/PAETH可单独或按位组合SPNG_FILTER_CHOICE_ALL 8|16|32|64|128从 spng.c 可以看到自动优化逻辑压缩级别为 0 时过滤无意义会被禁用调色板/低位深图像自动禁用过滤显式设置SPNG_FILTER_CHOICE_NONE等价于禁用过滤且此时压缩策略自动回退为Z_DEFAULT_STRATEGY。内存占用说明除上下文缓冲区开销外内部写缓冲区可能增长到整块chunk的长度编码到内部缓冲区时其大小可能超过最终 PNG 文件长度编码一张图像至少需要保持 2 行像素在内存中未来版本可能增至 3 行。完整实战编码到内部缓冲区仓库自带的 example.c 给出了一条完整的编码链路可直接作为模板int encode_image(void *image, size_t length, uint32_t width, uint32_t height, enum spng_color_type color_type, int bit_depth) { int fmt; int ret 0; spng_ctx *ctx NULL; struct spng_ihdr ihdr {0}; /* zero-initialize to set valid defaults */ /* Creating an encoder context requires a flag */ ctx spng_ctx_new(SPNG_CTX_ENCODER); /* Encode to internal buffer managed by the library */ spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1); /* Alternatively you can set an output FILE* or stream with spng_set_png_file() or spng_set_png_stream() */ /* Set image properties, this determines the destination image format */ ihdr.width width; ihdr.height height; ihdr.color_type color_type; ihdr.bit_depth bit_depth; /* Valid color type, bit depth combinations are defined by the PNG standard */ spng_set_ihdr(ctx, ihdr); /* When encoding fmt is the source format */ /* SPNG_FMT_PNG is a special value that matches the format in ihdr */ fmt SPNG_FMT_PNG; /* SPNG_ENCODE_FINALIZE will finalize the PNG with the end-of-file marker */ ret spng_encode_image(ctx, image, length, fmt, SPNG_ENCODE_FINALIZE); if(ret) { printf(spng_encode_image() error: %s\n, spng_strerror(ret)); goto encode_error; } size_t png_size; void *png_buf NULL; /* Get the internal buffer of the finished PNG */ png_buf spng_get_png_buffer(ctx, png_size, ret); if(png_buf NULL) { printf(spng_get_png_buffer() error: %s\n, spng_strerror(ret)); } /* User owns the buffer after a successful call */ free(png_buf); encode_error: spng_ctx_free(ctx); return ret; }几点实战建议struct spng_ihdr务必零初始化{0}以获取合法的默认值编码时fmt指的是源数据格式SPNG_FMT_PNG是特殊值表示与ihdr中设定的目标格式一致若目标是调色板索引色SPNG_COLOR_TYPE_INDEXED记得先spng_set_plte()若改用输出流方式可参考 spng_write_fuzzer.c 中stream_write_fn回调的写法——注意写回调内部需要自行管理剩余空间并在溢出时返回SPNG_IO_EOF。进一步阅读上下文创建、选项设置与行信息查询docs/context.mdIHDR、PLTE 等块的读写语义与spng_set_*()系列函数docs/chunk.md完整头文件声明枚举与函数原型spng/spng.h编码实现细节校验、过滤优化、DEFLATE 初始化spng/spng.c编码路径的模糊测试用例tests/spng_write_fuzzer.c【免费下载链接】source-sdk-2013The 2013 edition of the Source SDK项目地址: https://gitcode.com/GitHub_Trending/so/source-sdk-2013创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门