本文主要是介绍libjpeg_example.txt,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
/*
- 示例.txt
- 该文件说明了如何使用IJG代码作为子程序库
- 读取或写入JPEG图像文件。你应该看看这段代码
- 与文档文件 libjpeg.txt 结合使用。
- 这段代码按原样不会做任何有用的事情,但它可能会有所帮助
- 用于构建调用 JPEG 库的例程的骨架。
- 我们以 JPEG 代码中使用的相同编码风格呈现这些例程
*(ANSI函数定义等);但你当然可以自由地编写你的代码 - 如果您愿意,可以采用不同风格的例程。
*/
/* 这个例子是原始 libjpeg 文档的一部分,并且已被
- 自 1994 年以来未发生变化。正如 libjpeg.txt 中所述,“严重地
- 注释了调用 JPEG 库的骨架代码。”这并不意味着
- 被编译为独立程序,因为它没有 main() 函数并且
- 不从真实图像缓冲区压缩/解压缩到真实图像缓冲区(推论:
- put_scanline_someplace() 不是一个真正的函数。)首次使用
- 通过查看 tjexample.c 可以更好地服务 libjpeg-turbo,它使用
- 更简单的 TurboJPEG API,或者位于 cjpeg.c 和 djpeg.c,它们是
- 可以(并且)编译成独立版本的 libjpeg API 使用示例
- 程式。请注意,此示例以及 cjpeg.c 和中的示例
- djpeg.c,将磁盘 I/O 与 JPEG 压缩/解压缩交错,因此没有
- 这些示例适用于基准测试目的。
*/
#include <stdio.h>
/*
- 包含 JPEG 库用户的文件。
- 您需要包含至少定义的系统标头
- typedefs FILE 和 size_t 才能包含 jpeglib.h。
*(stdio.h 对于符合 ANSI 的系统就足够了。) - 您可能还希望包含“jerror.h”。
*/
#include“jpeglib.h”
/*
- <setjmp.h> 用于可选的错误恢复机制,如图所示
- 示例的第二部分。
*/
#include <setjmp.h>
/******************** JPEG 压缩示例接口 ********************/
/* 示例的这半部分展示了如何将数据输入 JPEG 压缩器。
- 我们提供了一个最小版本,无需担心诸如此类的改进
- 作为错误恢复(如果出现错误,JPEG 代码将直接 exit())。
*/
/*
- 图像数据格式:
- 标准输入图像格式是像素的矩形阵列,其中
- 每个像素具有相同数量的“分量”值(颜色通道)。
- 每个像素行都是一个 JSAMPLE 数组(通常是无符号字符)。
- 如果您正在使用颜色数据,则每个像素的颜色值
- 必须在行中相邻;例如,R,G,B,R,G,B,R,G,B,… 对于 24 位
- RGB 颜色。
- 对于这个例子,我们假设这个数据结构与方式匹配
- 我们的应用程序已将图像存储在内存中,因此我们只需传递一个
- 指向我们的图像缓冲区的指针。特别是,假设图像是
- RGB 颜色并由以下方式描述:
*/
外部 JSAMPLE image_buffer; / 指向 R、G、B 顺序数据的大数组 /
外部 int 图像高度; / 图像的行数 /
外部 int image_width; / 图像的列数 */
/*
- JPEG 压缩示例例程。我们假设目标文件名
- 和压缩品质因数被传入。
*/
全局(无效)
write_JPEG_file(char 文件名, int 质量)
{
/ 该结构体包含 JPEG 压缩参数和指向
- 工作空间(由 JPEG 库根据需要分配)。
- 可以有多个这样的结构,代表多个
- 压缩/解压缩过程同时存在。我们提到
- 将任何一个结构(及其关联的工作数据)作为“JPEG 对象”。
/
结构jpeg_compress_struct cinfo;
/ 该结构代表一个 JPEG 错误处理程序。是单独声明的 - 因为应用程序通常希望提供专门的错误处理程序
*(有关示例,请参阅此文件的后半部分)。但在这里我们只是
*采取简单的方法并使用标准错误处理程序,这将 - 如果压缩失败,则在 stderr 上打印一条消息并调用 exit()。
- 请注意,该结构必须与主要 JPEG 参数一样长
- 结构体,以避免悬空指针问题。
/
struct jpeg_error_mgr jerr;
/ 更多东西 /
文件输出文件; /* 目标文件 /
JSAMPROW row_pointer[1]; / 指向 JSAMPLE 行的指针 /
int row_stride; / 图像缓冲区中的物理行宽度 */
/* 步骤1:分配并初始化JPEG压缩对象 */
/* 我们必须首先设置错误处理程序,以防初始化
- 步骤失败。 (不太可能,但如果您内存不足,则可能会发生。)
- 该例程填充struct jerr的内容,并返回jerr的
- 我们放入 cinfo 中链接字段的地址。
/
cinfo.err = jpeg_std_error(&jerr);
/ 现在我们可以初始化 JPEG 压缩对象。 */
jpeg_create_compress(&cinfo);
/* 步骤2:指定数据目的地(例如文件)/
/ 注意:步骤 2 和 3 可以按任意顺序完成。 */
/* 这里我们使用库提供的代码将压缩数据发送到
- 标准输入输出流。您还可以编写自己的代码来执行其他操作。
- 非常重要:如果您使用的机器是 fopen(),请使用“b”选项
- 需要它才能写入二进制文件。
*/
if ((outfile = fopen(文件名, “wb”)) == NULL) {
fprintf(stderr, “无法打开 %s\n”, 文件名);
退出(1);
}
jpeg_stdio_dest(&cinfo, 输出文件);
/* 步骤3:设置压缩参数 */
/* 首先我们提供输入图像的描述。
- cinfo结构体的四个字段必须填写:
/
cinfo.image_width = image_width; / 图像宽度和高度,以像素为单位 /
cinfo.image_height = image_height;
cinfo.input_components = 3; / 每个像素的颜色分量数 /
cinfo.in_color_space = JCS_RGB; / 输入图像的色彩空间 /
/ 现在使用库的例程设置默认压缩参数。
*(在调用此函数之前,您必须至少设置 cinfo.in_color_space, - 因为默认值取决于源色彩空间。)
/
jpeg_set_defaults(&cinfo);
/ 现在您可以设置任何您想要的非默认参数。 - 这里我们只是举例说明质量(量化表)缩放的使用:
/
jpeg_set_quality(&cinfo,quality,TRUE / 限制为基线 JPEG 值 */);
/* 第四步:启动压缩机 */
/* TRUE 确保我们将编写完整的交换 JPEG 文件。
- 除非您非常确定自己在做什么,否则请传递 TRUE。
*/
jpeg_start_compress(&cinfo, TRUE);
/* 步骤5: while (扫描线仍待写入) /
/ jpeg_write_scanlines(…); */
/* 这里我们使用库的状态变量 cinfo.next_scanline 作为
- 循环计数器,这样我们就不必自己跟踪。
- 为了简单起见,我们每次调用都会传递一条扫描线;你可以通过
不过,如果您愿意的话,可以更多。
/
行步幅=图像宽度3; / image_buffer 中每行的 JSAMPLE */
while (cinfo.next_scanline < cinfo.image_height) {
/* jpeg_write_scanlines 需要一个指向扫描线的指针数组。
- 这里数组只有一个元素长,但你可以通过
- 一次多于一条扫描线(如果这样更方便的话)。
*/
row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
(void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
/* 第6步:完成压缩 */
jpeg_finish_compress(&cinfo);
/* finish_compress之后,我们可以关闭输出文件。 */
fclose(输出文件);
/* 第7步:释放JPEG压缩对象 */
/* 这是重要的一步,因为它将释放大量内存。 */
jpeg_destroy_compress(&cinfo);
/* 我们就完成了! */
}
/*
- 一些要点:
- 在上面的循环中,我们忽略了jpeg_write_scanlines的返回值,
- 这是实际写入的扫描线数。我们可以逃脱
- 这样做是因为我们只依赖 cinfo.next_scanline 的值,
- 将正确递增。如果你保持额外的循环
- 变量,那么你应该小心地正确地增加它们。
- 实际上,对于输出到 stdio 流你不必担心,因为
- 然后 jpeg_write_scanlines 将写入所有传递的行(否则退出
- 出现致命错误)。仅当您使用数据时才会发生部分写入
- 可以要求暂停压缩机的目标模块。
*(如果您不知道它的用途,则不需要它。) - 如果压缩器需要全图像缓冲区(用于熵编码
*优化或多次扫描JPEG文件),它会创建临时 - 文件中包含任何不符合最大内存设置的内容。
*(请注意,如果使用默认参数,则不需要临时文件。) - 在某些系统上,您可能需要设置信号处理程序以确保
- 如果程序中断,临时文件将被删除。请参阅 libjpeg.txt。
- 如果您想要 JPEG,则必须按从上到下的顺序提供扫描线
- 文件与其他人的兼容。如果您不能轻松阅读
- 按照这个顺序你的数据,你需要一个中间数组来保存
- 图像。有关处理自下而上的示例,请参阅 rdtarga.c 或 rdbmp.c
- 使用 JPEG 代码的内部虚拟数组机制的源数据。
*/
/******************** JPEG解压样本接口 ********************/
/* 示例的这半部分展示了如何从 JPEG 解压缩器读取数据。
- 它比上面的更加精致,我们展示了:
- (a) 如何修改JPEG库的标准错误报告行为;
- (b) 如何使用库的内存管理器分配工作空间。
- 只是为了让这个例子与第一个例子有点不同,我们将
- 假设我们不打算将整个图像放入内存中
- 缓冲区,但将其逐行发送到其他地方。我们需要一个——
- 扫描线高 JSAMPLE 数组作为工作缓冲区,我们将让 JPEG
- 内存管理器为我们分配它。这个方法其实还是蛮有用的
- 因为我们不需要记住单独释放缓冲区:它
- 当 JPEG 对象被清理后会自动消失。
*/
/*
- 错误处理:
- JPEG库的标准错误处理程序(jerror.c)分为
- 几个可以单独覆盖的“方法”。这可以让你
- 调整行为而不重复大量代码,您可能会这样做
- 必须随未来的每个版本进行更新。
- 我们这里的例子展示了如何重写“error_exit”方法,以便
- 当发生致命错误时,控制权返回给库的调用者,
- 而不是像标准 error_exit 方法那样调用 exit() 。
- 我们使用 C 的 setjmp/longjmp 工具来返回控制。这意味着
- 调用 JPEG 库的例程必须首先执行 setjmp() 调用
- 建立返回点。我们希望替换 error_exit 来执行
- 长跳转()。但我们需要使 setjmp 缓冲区可供访问
- 错误退出例程。为此,我们对
- 标准 JPEG 错误处理程序对象。 (如果我们使用 C++,我们会说我们
*正在创建常规错误处理程序的子类。) - 这是扩展的错误处理程序结构:
*/
结构 my_error_mgr {
结构jpeg_error_mgr pub; /“公共”字段/
jmp_buf 设置jmp_buffer; /* 返回给调用者 */
};
typedef struct my_error_mgr *my_error_ptr;
/*
- 这是将替换标准 error_exit 方法的例程:
*/
方法定义(无效)
my_error_exit(j_common_ptr cinfo)
{
/* cinfo->err 实际上指向 my_error_mgr 结构,因此强制指针 */
my_error_ptr myerr = (my_error_ptr)cinfo->err;
/* 始终显示消息。 /
/ 如果我们选择的话,我们可以将其推迟到返回后。 */
(*cinfo->err->output_message) (cinfo);
/* 将控制权返回给setjmp点 */
longjmp(myerr->setjmp_buffer, 1);
}
METHODDEF(int) do_read_JPEG_file(struct jpeg_decompress_struct cinfo,
字符文件名);
/*
- JPEG 解压缩的示例例程。我们假设源文件名
- 被传入。我们希望成功时返回 1,错误时返回 0。
*/
全局(整数)
read_JPEG_file(char 文件名)
{
/ 该结构体包含 JPEG 解压缩参数和指向
- 工作空间(由 JPEG 库根据需要分配)。
*/
struct jpeg_decompress_struct cinfo;
返回 do_read_JPEG_file(&cinfo, 文件名);
}
/*
- 我们从一个单独的函数中调用 libjpeg API,因为修改
- setjmp() 下面的本地非易失性 jpeg_decompress_struct 实例
- 返回点,然后在 setjmp() 返回后访问实例将
- 导致未定义的行为,可能会覆盖全部或部分
- 结构。
*/
方法定义(int)
do_read_JPEG_file(struct jpeg_decompress_struct *cinfo, char 文件名)
{
/ 我们使用我们的私有扩展 JPEG 错误处理程序。
- 请注意,该结构必须与主要 JPEG 参数一样长
- 结构体,以避免悬空指针问题。
/
结构 my_error_mgr jerr;
/ 更多东西 */
文件 infile; / 源文件 /
JSAMPARRAY 缓冲区; / 输出行缓冲区 /
int row_stride; / 输出缓冲区中的物理行宽度 */
/* 在此示例中,我们希望在执行其他操作之前打开输入文件,
- 以便下面的 setjmp() 错误恢复可以假设文件已打开。
- 非常重要:如果您使用的机器是 fopen(),请使用“b”选项
- 需要它才能读取二进制文件。
*/
if ((infile = fopen(文件名, “rb”)) == NULL) {
fprintf(stderr, “无法打开 %s\n”, 文件名);
返回0;
}
/* 步骤1:分配并初始化JPEG解压对象 */
/* 我们设置正常的 JPEG 错误例程,然后覆盖 error_exit。 /
cinfo->err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/ 建立 setjmp 返回上下文以供 my_error_exit 使用。 /
如果(setjmp(jerr.setjmp_buffer)){
/ 如果我们到达这里,JPEG 代码已发出错误信号。
- 我们需要清理JPEG对象,关闭输入文件,然后返回。
/
jpeg_destroy_decompress(cinfo);
fclose(infile);
返回0;
}
/ 现在我们可以初始化 JPEG 解压缩对象了。 */
jpeg_create_decompress(cinfo);
/* 步骤2:指定数据源(例如文件) */
jpeg_stdio_src(cinfo, infile);
/* 步骤3:使用jpeg_read_header()读取文件参数 */
(void)jpeg_read_header(cinfo, TRUE);
/* 我们可以忽略 jpeg_read_header 的返回值,因为
- (a) stdio 数据源不可能暂停,并且
- (b) 我们通过 TRUE 来拒绝仅包含表格的 JPEG 文件作为错误。
- 有关详细信息,请参阅 libjpeg.txt。
*/
/* 第四步:设置解压参数 */
/* 在这个例子中,我们不需要更改任何默认设置
- jpeg_read_header(),所以我们在这里什么都不做。
*/
/* 步骤5:启动解压器 */
(void)jpeg_start_decompress(cinfo);
/* 我们可以忽略返回值,因为不可能暂停
- 使用 stdio 数据源。
*/
/* 在阅读之前我们可能需要自己做一些设置
- 数据。在 jpeg_start_decompress() 之后,我们得到了正确的缩放比例
- 可用的输出图像尺寸以及输出色彩图
- 如果我们要求颜色量化。
- 在本例中,我们需要创建一个大小合适的输出工作缓冲区。
/
/ 输出缓冲区中每行 JSAMPLE /
row_stride = cinfo->output_width * cinfo->output_components;
/ 创建一个单行高的样本数组,当处理完图像后该数组就会消失 */
buffer = (*cinfo->mem->alloc_sarray)
((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1);
/* 步骤6: while (扫描线仍有待读取) /
/ jpeg_read_scanlines(…); */
/* 这里我们使用库的状态变量 cinfo->output_scanline 作为
- 循环计数器,这样我们就不必自己跟踪。
/
while (cinfo->output_scanline < cinfo->output_height) {
/ jpeg_read_scanlines 需要一个指向扫描线的指针数组。 - 这里数组只有一个元素长,但你可以要求
- 一次多于一条扫描线(如果这样更方便的话)。
/
(void)jpeg_read_scanlines(cinfo, 缓冲区, 1);
/ 假设 put_scanline_someplace 需要一个指针和样本计数。 */
put_scanline_someplace(缓冲区[0], row_stride);
}
/第七步:完成解压/
(void)jpeg_finish_decompress(cinfo);
/* 我们可以忽略返回值,因为不可能暂停
- 使用 stdio 数据源。
*/
/* 第8步:释放JPEG解压对象 */
/* 这是重要的一步,因为它将释放大量内存。 */
jpeg_destroy_decompress(cinfo);
/* finish_decompress后,我们可以关闭输入文件。
- 这里我们推迟它,直到不可能再出现 JPEG 错误为止,
- 从而简化上面的setjmp错误逻辑。 (其实我不
- 认为 jpeg_destroy 可以执行错误退出,但为什么要假设任何事情…)
*/
fclose(infile);
/* 此时您可能想要检查是否有任何损坏的数据
- 发生警告(测试 jerr.pub.num_warnings 是否为非零)。
*/
/* 我们就完成了! */
返回1;
}
/*
- 一些要点:
- 上面的代码中,我们忽略了jpeg_read_scanlines的返回值,
- 这是实际读取的扫描线数。我们可以逃脱
- 这是因为我们一次只要求一行并且我们没有使用
- 一个暂停的数据源。有关详细信息,请参阅 libjpeg.txt。
- 我们在 jpeg_start_decompress() 之后调用 alloc_sarray() 有点作弊;
*我们应该提前做好,以确保空间 - 根据 JPEG max_memory 设置计算。在某些系统中,上述
- 代码可能会出现内存不足错误。然而,一般情况下我们不
- 在 jpeg_start_decompress() 之前知道输出图像尺寸,除非我们
- 调用jpeg_calc_output_dimensions()。有关详细信息,请参阅 libjpeg.txt。
- 扫描线按照 JPEG 文件中出现的顺序返回,
- 标准是从上到下。如果您必须从下到上发出数据,
- 您可以使用JPEG内存管理器提供的虚拟数组之一
- 反转数据。有关示例,请参见 wrbmp.c。
- 与压缩一样,某些操作模式可能需要临时文件。
- 在某些系统上,您可能需要设置信号处理程序以确保
- 如果程序中断,临时文件将被删除。请参阅 libjpeg.txt。
*/
这篇关于libjpeg_example.txt的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!