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。
    */
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丁金金

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值