IJG JPEG库使用说明
版权所有:1994-1998,Thomas G. Lane
本文档是IJG软件的一部分。
分发和使用的条件请参看README文档。
本文档讲述如何在应用程序中使用IJG(IndependentJPEG Group) JPEG库。如果你想写一个使用本库的程序,请你读本文档。
文件example.c为调用JPEG库提供了详细的注释代码大纲。也可以参考jpeglib.h(要在应用程序中包含这个文件)中详细的数据结构定义和函数参数列表。当然,还是要以库源码为准。
请注意,API在IJG第四以及更早的版本发生了主要的改变。在老的版本中存在一些内在的限制,并且它让我们在添加功能是很难做到尽量少的改变API。当我们在重写第五版本时,牺牲了向后兼容,但是我们认为性能的提升会证明这样做事正确的。
目录:
1. 概览
1) 库所提供的功能
2) 典型的用法大纲
2. 库的基础用法
1) 数据格式
2) 压缩细节
3) 解压细节
4) 使用技巧:包含文件、链接、等等
3. 进阶功能
1) 压缩参数选择
2) 解压参数选择
3) 具体的颜色表
4) 异常处理
5) 数据压缩操作(源和目的控制器)
6) I/O中断
7) JPEG进阶支持
8) Buffered-image 模式
9) 缩略数据流和多样图像
10) 特殊标记
11) 原始(降低采样率)图像数据
12) 真正的原始数据:DC(离散余弦变换)系数
13) 单步行进监测
14) 内存管理器
15) 内存使用
16) 库的编译时刻选项
17) 关于可移植性的考虑
18) 给MS-DOS编程者的注释
在你试图写一个包含本库的程序之前,你至少应该读完《概览》和《基础用法》部分。如果你需要,你可以读进阶的部分。
第一章 概览
库所提供的功能
IJG JPEG库给读和写JPEG压缩图片文件提供c代码。应用程序兼容或者支持图像数据扫描线,使用直接的未压缩的图像格式。所有的颜色转换和其他预处理/后处理的细节可以在库中控制。
库中包含大量的不适用与JPEG标准但是典型JPEG应用程序所必要的代码。这些函数在图像JPEG压缩预处理之前或在解压缩后处理之后。它们包含颜色表转换,减采样/增补采样和颜色量子化。应用程序在兼容或支持明确的图像格式时间接的选择使用这代码。例如:如果需要颜色表输出,然后解压缩库自动调用颜色量子化。
JPEG可能要处理一个广泛的质量与速度的权衡,解压缩的后处理更是如此。解压缩库提供多样的实现,它覆盖了多数可用的权衡,范围从非常高质量到快速预览操作。在压缩过程我们通常不提供低质量的选择,因为压缩通常不是时间紧迫的。要明确低质量的模式可能不符JPEG标准的明确要求,然而,它们是对观察者有用的。
库提供一个函数相关的单词“not”。我们处理一个ISOJPEG标准的子集,有很多底线,连续的扩展,和改进JPEG过程的支持。(我们的子集包含所有目前常用的功能)不支持ISO的操作包括:
*分级存储
*无损JPEG
*熵算术编码(因法律因素而不支持)
*DNL标记
*非整型出样比
我们精确的支持8和12bit的数据,但是这是一个编译时刻的选择,而不是一个运行时刻的选择,因此很难在一个程序中使用者这两个过程。
库只处理JPEG数据流的交换——尤其是使用一个广泛的JFIF文件格式。库可以被代码环境用作处理交换或内嵌在更复杂的文件格式的小型JPEG数据流。(例如,本库被用再开放的LIBTIFF库中来使TIFF支持JPEG压缩)
典型的用法大纲
粗略的JPEG压缩操作大纲是:
分配和初始化JPEG对象
指定压缩数据目的位置(例如,一个文件)
设置压缩参数,包含图像的大小和颜色表
jpeg_start_compression(…)
while(scanlines remain to be written)
jpeg_write_scanlines(…)
jpeg_finish_compression(…)
释放JPEG压缩对象
一个JPEG压缩对象在JPEG库中控制参数和工作状态。我们从图像的压缩开始创建对象,图像的压缩结束摧毁对象,同一个对象可以被一个系列的图像压缩操作重用。重用使得设置序列图像的参数变得很简单。JPEG对象的重用涉及处理缩略JPEG数据流同样重要,我们稍后讨论。
jpeg_write_scanlines()支持从内存缓冲中压缩图像数据。如果应用程序想要对文件到文件的压缩操作,则从源文件中读取图像数据是应用程序的责任。库在调用“目标数据控制器”时发出压缩数据,典型的方式是写到文件中,但是应用程序可以自己提供都其他事情的目标控制器。
同样的,一个粗略的JPEG解压缩操作大纲是:
分配和初始化一个JPEG解压缩对象
指定压缩文件源数据(例如:一个文件)
调用jpeg_read_header()来获取图像信息
设置解压缩参数
jpeg_start_decompress
while(scan lines remain to be read)
jpeg_read_scanlines(…)
jpeg_finish_decompress(…)
释放JPEG解压缩对象
和压缩操作比较发现从数据流的头中读取信息是一个单独的步骤。因为图像的大小、颜色表等等信息是对于应用程序选择解压缩参数有帮助的。例如,应用程序可以选择比配图像内部可利用的屏幕尺寸的输出换算系数。
解压库通过调用一个数据源控制器来获得压缩数据,典型的如从文件中读取数据,但是从用户的源控制器的其他行为中也可以获得数据。解压缩数据被通过jpeg_read_scanlines()投递到内存缓冲中。
调用jpeg_abort()可以中断一个不完整的压缩或解压缩操作,或者,你不需要保留JPEG对象,调用jpeg_destory()可以很简单的释放掉它。
JPEG压缩和解压缩对象是两个独立的结构体烈性。不过,他们分享同一个域,并且某种像jpeg_destroy()的例行程序可以工作在两个对象的类型上。
JPEG库没有静态变量:所有变量的状态都在压缩或解压缩对象当中。因此,同时处理多个压缩和解压缩操作要使用多个JPEG对象。
如果适当的源/目标控制正被使用,压缩和解压缩都可以在一个增长的内存到内存方式下工作。参考I/O中断部分以获得更多细节。
第二章 库的基础用法
数据格式
在研究过程化细节之前,理解JPEG库要求和返回的图像数据的格式是很有帮助的。
标准的图像格式输入是一个方形矩阵像素,每一个像素有同样数量的“成分”或“样本”变量(颜色通道)。你必须明确颜色表解释成分是多少个部分。大多数应用程序使用RGB数据(每个像素三个部分)或者灰度数据(每个像素一个部分)。请注意:RGB数据是每个像素三个样本,灰度数据每个像素只有一个。一个值得注意的事是当人们管理时丢失这个内容,才发现他们的程序不能操作灰度JPEG文件。
这里不提供颜色表输入。JPEG通常是全色彩或全灰度的(或者有些时候有其他颜色表,例如CMYK)。你可以额外的给全色彩图像格式提供一个颜色表。不过JPEG常常在源数据被颜色映射过时因为抖动噪声而工作的不那么好。这个讨论的跟多细节在JPEG FAQ中并且其他参考文献在README文件中列举。
像素被扫描线存储,每次扫描是从左到右移动。每个像素组份的变量在行里是相邻的,例如:在24位RGB颜色中是R,G,B,R,G,B,R,G,B…每一个扫描是一个JSAMPLE类型的数据数组——典型的是“unsigned char”类型,除非你已经更改了jmorecfg.h(你同样也可以修改jmorecfg.h来更改RGB像素布局,像B,G,R顺序。但是你要在这么做之前看文件列出的限制。)
一个二维像素数组是通过指向扫描行开始的指针列表形成,所以扫描行不需要在内存上物理相邻。就算你一次只处理一个扫描行,你必须建立一个指向一个元素的数组指针来符合这个结构。指向JSAMPLE行的指针是JSAMPROW类型的,指向指针数组的指针是JSAMPARRAY类型的。
库在每一次调用时接受或支持一个或更多复杂的扫描行。每次处理一行的一部分是不可能的。扫描总是从头处理到尾。如果你有全部的内存你可以一次调用全部的图像,但是通常一次处理一个扫描行采样。
为了达到最佳效果,源数据变量应该用精确的支持BITS_IN_JSAMPLE(通常8位)。例如,如果你选择压缩每个通道只有6位的数据,那么编辑BITS_IN_JSAMPLE = 12。(参考后续“库编译时刻选项”)
解压缩返回数据格式有同样的细节,除了支持颜色表输出外。(再声明一次,JPEG文件从来没有颜色表。但是你可以要求解压缩动态执行颜色量子化来投递给颜色表输出。)如果你要求颜色表输出然后返回每一个像素包含一个单独JSAMPLE的,变量是一个颜色表索引的数据数组。颜色表被描绘成一个二维JSAMPARRY在每一行控制一个颜色组份的变量,就是说colormap[i][j]是第i颜色组的像素变量j(映射索引)。注意颜色表索引存储在JSAMPLEs中,JSAMPLE的大小限制了颜色的最大值(例如,8位的库最多有256色)
压缩细节
在这里我们重游JPEG的压缩大纲。
1. 分配和初始化一个JPEG压缩对象。
一个压缩对象是一个“结构体jpeg_compress_struct”。(它也有一些辅助的结构通过malloc()被分配,但是应用程序不能直接控制这些结构体。)如果一个单独的例程实现整个压缩过程,这个结构体可以仅仅是一个常规调用时的局部变量。否则,它必须是静态的或被malloc()动态分配的。
你也将会需要一个结构体来代表一个JPEG异常处理。库中有关这部分的是“结构体jpeg_error_mgr”。如果你提供你自己的异常处理,一般的你要把结构体jpeg_error_mgr嵌入到一个更大结构体重,这个一会在《异常处理》中讨论。那么现在我们假设你仅仅使用默认的异常处理。默认的异常处理将指向JPEG异常/警告stderr(标准异常处理)消息。
你必须初始化异常处理结构体,在一个JPEG对象的“err(异常)”区存储一个指针,然后调用jpeg_create_compress()来初始化JPEG对象的剩余部分。
如果你使用默认的异常处理,这是典型的代码的步骤:
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
…
cinfo.err = jpeg_std_error(&jerr);
jpeg_ceate_ compress(&cinfo);
jpeg_create_compress分配一小块内存,所以如果你内存溢出,它可能失败。那么它将通过异常处理退出,这就是为什么异常要被首先初始化。
2. 指定压缩数据的目标位置(例如一个文件)
正如上文所言,JPEG库传送压缩数据给“数据目标”模块。库包含一个知道如何写标准输入输出流(stdio stream)的数据目标模块。如果你想做其他的事情,你可以使用自己的目标模块,这个同样一会讨论。
如果你使用标准目标模块,你必须事先打开标准输入输出流对象(target)。典型代码的步骤看起来像是这样:
FILE* outfile;
…
if((outfile – fopen(filename,”wb”))==NULL){
fprintf(stderr,”can’topen %
”, filename);
exit(1);}
jpeg_stdio_dest(%cinfo,outfile);
最后一行调用了标准目标模块。
警告:无改动地传送二进制压缩数据到输出文件是必不可少的。在非Unix系统标准输入输出库可能执行换行符转换或在其他方面改变二进制数据。这种行为令人惊讶,你可能需要使用一个“b”选项来fopen(正如上面显示),或者使用setmode()或者其他例程来在二进制模式中取得标准输入输出流。参考cjpeg.c和djpeg.c已经在多种平台上实现的代码。
你可以在设置其他参数后选择数据目标(步骤3),如果这更方便的话。你可以在调用jpeg_start_copress()和jpeg_finish_compress()之间不改变目标。
3. 设置压缩参数,包含图像大小和颜色表。
你必须在下面支持设置JPEG对象的源图像信息(cinfo结构体):
image_width 图像宽,单位像素
image_height 图像高,单位像素
input_components 颜色通道数量(每一个像素采样)
in_color_space 源图像颜色表
但愿图像的尺寸明确的。JPEG在每个方向支持1到64K像素的图像尺寸。输入颜色表是典型的RGB或灰度,相应的input_components是3或者1.(参考“具体的颜色表”来获取更多信息。)in_color_space域一定是被枚举J_COLOR_SPACE中特定的一个赋值的,通常是JCS_RGB或JCS_GRAYSCALE。
JPEG有很多压缩参数来决定图像如何编码。大多数程序不需要或不想知道所有的这些参数。你可以通过调用jpeg_set_defaults()来设置所有合理的默认参数;如果你想更改参数变量,你也可以那么做。“压缩参数选择”部分有全部参数的讨论。
你必须明确的在调用jpeg_set_defaults()之前设置in_color_space值,因为默认值依赖源图像的颜色表。不过,其他三个源图像参数需要在你调用jpeg_start_compress()之前是有效的。多次调用jpeg_set_defaults()是没有副作用的,如果它合理的发生。
典型的24位RGB源图像代码:
cinfo.image_width = Width; /* image width and height, in pixels */
cinfo.image_height = Height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space =JCS_RGB; /* colorspace of input image */
jpeg_set_defaults(&cinfo);
/* Make optional parametersettings here */
4. jpeg_start_compress(…);
当你建立完成数据目标并且设置完所有必要的源图像信息和其他参数的时候,调用jpeg_start_compress()来开始压缩过程。这将初始化内部状态,分配工作内存,并且发送内存的前几个字节(地址)给JPEG数据流header。
典型的代码:
jpeg_start_compress(&cinfo, TRUE);
参数“TRUE”确保JPEG内部修改的数据流全部被写入。这是符合大多数情况的。如果你认为你可能想使用一个缩略数据流,请在之前阅读“缩略数据流”部分。
一旦你调用jpeg_start_compress(),在你没有接受压缩过程之前,你可能没有修改任何JPEG参数或者其他JPEG对象域。
5. jpeg_write_scanlines(…);(剩余扫描行被写入)
现在单次或多次调用jpeg_write_scanlines()来写入所有需要的图像数据。你可以在每次调用时跳过一个或多个扫描行,但上限是图像的总高度。大多数应用程序每次跳过一个或几个扫描行是合理的。被扫面过的数据期望的格式是已经在“数据格式”讨论过的。
图像数据应该被从顶到底的扫面顺序写入。JPEG说明文件包含了一些模糊的措辞,像默认程序项目如何从顶到底(一个英语难以理解的解释…)但是你想要你的文件和其他别人兼容,你将使用从顶到底的顺序。如果源数据必须按从底到顶的顺序读取,你可以使用JPEG库虚拟数组方法来有效的反转数据。这部分的例子可以再样例程序cjpeg中找到。
库主张在next_scanline域中统计JPEG对象到目前为止已经写入的扫描数。通常你仅使用这个变量来做循环控制器,那么循环测试看起来像是这样:
while(cinfo.next_scanline< cinfo.image_height);
这一步的代码在很大程度上依赖你存储数据的方法。文件example.c中显示了在使用全尺寸二维源数组每个元素包含3字节RGB像素情况下的代码:
JSAMPROWrow_pointer[1]; /* pointer to a singlerow */
introw_stride; /* physical row widthin buffer */
row_stride=image_width* 3;// JSAMPLEs per row in image_buffer
while (cinfo.next_scanline <cinfo.image_height) {
row_pointer[0]=
&image_buffer[cinfo.next_scanline*row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_write_scanlines()返回一个扫描行已经写入的数。这通常等于扫描过的数,所以你基本可以忽略返回值。在以下两种情况不同:
*如果你尝试写入多于图像高度的扫描行,额外的扫描行将被忽略。
*如果你使用中断数据目标控制器,输出缓冲溢出将引起压缩过程在合理的扫描全部内存之前返回。这个特性在“I/O中断”中讨论。正常的stdio目标控制器不会引起这件事请的发生。
在多种情况下,返回值与next_scanline的值一同改变。
6. jpeg_finish_compress(…);
在所有图像数据在被写入之后,调用jpeg_finish_compress()来结束压缩过程。这一步是来却保最近的缓冲区中的数据写到数据目标中所必不可少。
jpeg_finish_compress()也会释放JPEG对象分配了的工作内存。
典型的代码:
jpeg_finish_compress(&cinfo);
如果使用标准目标控制器,不要忘记在最后的时候关闭输出数据流(如果必要的话)。
如果你需要一个多种的操作模式,例如霍夫曼编码最优化,jpeg_finish_compress()将要在第一次传递过后再传递一次使用的数据缓冲。在这种情况下jpeg_finish_compress()可能占用很长时间来结束。当使用默认的压缩参数则不发生这种情况。
在写入必要的扫描行总数前调用jpeg_finish_compress()是错误的。如果你希望中断压缩,你可以调用一会要讨论的jpeg_abort()。
在压缩过程结束后,你可以像一会要讨论的那样来配置JPEG对象,或者你可以使用它来压缩其他的图像。在适当的饭后步骤2、3或者4的情况下。如果你不需要改变目标控制器,新的数据流将用和上次相同的参数被写入。注意你可以在过程中自由的改变输入图像的尺寸,但是如果你改变了颜色表,你必须调用jpeg_set_defaults()来调整成新的颜色表,并且你将需要重新执行步骤3。
7. 释放JPEG压缩对象
当你完成的压缩对象的使用时,掉用jpeg_destroy_compress()来摧毁它。它将释放所有辅助的内存(不管之前对象的状态如何)。或者你可以调用jpeg_destory()来摧毁那些为压缩或解压缩工作的对象——如果你在压缩和解压缩中共用代码,这可以给你带来方便。(事实上,除了传递定义的指针类型之外这些例程都是等价的。为了避免ANSI C编译器的牢骚,jpeg_destroy()应该传递一个j_common_ptr。)
如果你从malloc()分配一个jpeg_compress_struct结构体,释放它是你自己的责任——jpeg_destroy()不会释放的。异常处理的结构体也是一样的。
典型的代码:
jpeg_destroy_compress(&cinfo);
8. 中断
如果你决定在结束之前中断一个压缩过程,你可以用两种方法来清理:
*如果你部在需要JPEG对象,仅仅调用jpeg_destroy_compress()或者jpeg_destroy()来释放内存。在调用jpeg_create_compress()之后的任何时候都是合法的——事实上,即使jpeg_create_compress()调用失败时它还是安全的。
*如果你想重用JPEG对象,调用jpeg_abort_compress(),或者在同时工作在压缩和解压缩的对象上调用jpeg_abort()。这将释放所有工作的内存,并且返回对象一个空闲的状态。在对象被成功建立之后的任何时间里是允许调用jpeg_abort()的。
注意如果清理数据目标是需要的,那么这是你的责任,这些例程都不会调用term_desrination()。(参考随后“压缩数据控制”来了解更多。)
jpeg_destroy()和jpeg_abort()是只有在JPEG对象安全时调用,如果返回一个错误,要调用error_exit(参考“异常处理”获得更多消息)。例如对象内部的状态可能是不正常的。这两个例程将返回对象一个已知的状态。
解压细节
在这里我们重游在大纲中给出的JPEG解压概览。
1. 分配和初始化JPEG解压对象。
这与我们之前讨论过的压缩对象的初始化一样,对了对象是一个“jpeg_decompress_struct结构体”并且你要调用jpeg_create_decompress()。异常处理也是同样的。
典型的代码:
struct jpeg_decompress_structcinfo;
struct jpeg _error_mgr jerr;
…
cinfo.err =jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
(在IJG和这里的代码,我在压缩和解压缩过程都使用了名字为“cinfo”的变量)
2. 明确压缩数据源(例如一个文件)
像前文提到过的,JPEG库从数据源模块读入压缩数据。库包含一个已知如何从stdio流中读取数据的数据源模块。如果你想做一些其他的事情,你可以使用你自己的源模块,这个一会再讨论。
如果你使用标准数据源模块,你必须在这之前打开源stdio流。这个步骤典型的代码看起来是:
FILE * infile;
…
if ((infile = fopen(filename, “rb”)) == NULL{
fprintf(stderr, “can’topen %s
”,filename);
exit(1);
}
jpeg_stdion_src(&cinfo ninfile);
在最后一行里调用标准源模块。
注意:关键的一点,二进制压缩数据输入时不能发生改变。在非Unix系统的srdio库中可能执行换行符的转换或者二进制数据走样。这个行为是令人惊讶的,你可能需要使用一个“b”选项来调用fopen(像上面示例那样),或者使用setmode()或其他例程来把stdio流输入到二进制模块中。参考cjpeg.c和djpeg.c的代码来了解如何在其他系统上正常工作的。
你不可以在调用jpeg_read_header()和jpeg_finish_decompress()时改变数据源。如果你想从一个单个源文件中读取一个JPEG图像序列,你应该不重新初始化JPEG对象和数据源模块,并按顺序重做jpeg_read_header()到jpeg_finish_decompress(),这个步骤保护被抛弃的缓冲输入数据。
3. 调用jpeg_red_header()来给图形信息赋值
这步典型的代码:
jpeg_read_header(&cinfo,TURE);
函数将读取源数据流header的标记,更新起始压缩数据意图。在返回时,图像的尺寸和其他信息将存储在JPEG对象中。应用程序可能希望在选择解压缩参数之前商讨尺寸或其他信息。
如果需要更复杂的代码:
*使用一个中断了的数据源——在这种情况下jpeg_read_header()可能在读取完全部头数据之前返回。参考下文“I/O中断”。正常的stdio源控制器将不会发生这种情况。
*缩略JPEG文件将被处理——参考缩略数据流部分。标准应用程序只在JPEG文件内部修改,不需要担心这种情况。
如果你仅仅是想要在JPEG文件中找到图像尺寸和其他头信息的时,在这个时候停止也是被允许的。在这种情况下当你完成了使用JPEG对象时,调用jpeg_destroy(),或者调用jpeg_abort()来在选择一个新的数据源和读取其他头之前返回一个空闲的状态。
4. 设置解压缩参数
jpeg_read_header()基于图像的属性(特性,颜色表)来设置合适的默认解压缩参数。如果你想在开始解压缩之前修改默认值。例如,默认从一个颜色文件中创建一个全色彩输出。如果你希望颜色映射表输出,你必须手动修改参数。其他项还有图像规模和多种速度/质量权衡供选择。下文“解压缩参数部分”将给出更多信息。
如果默认参数是合适的,这一步不需要做什么事情。
注意默认值是在每次调用jpeg_read_header()时被设置的。如果你重用解压缩对象,你不能期望你设置的参数在过程中保存,正如你也不在在压缩过程这样做。你必须在每次设置所需参数的值。
5. jpeg_start_decompress(…)
当参数的值是符合要求的,调用jpeg_start_cdcompress()来开始解压缩过程。这将初始化内部状态,分配工作内存,并且准备返回数据。
典型的代码:
jpeg_start_decompress(&cinfo);
如果你需要一个多通道的操作模式,例如2通道的颜色量子化,jpeg_start_decompress()将做需要在数据输出开始之前的任何事情。在这种情况下jpeg_start_decompress()结束可能花费很长的时间。仅仅浏览(非进展性的)JPEG文件和默认解压缩参数,jpeg_start_decompress()将很快返回。
在你调用之后,在JPEG对象中是允许包含任何需要的模式,和最终输出图像的尺寸,如果颜色映射表输出是需要的,所以选择颜色映射表。通常包含的域:
output_width image width and height , asscaled
output_height
out_color_compress 颜色表中颜色组份数
output_components 返回每像素颜色组份数
colormap 若有的话,选择颜色映射表
actual_number_of_colors 颜色映射的条目
当量子化时output_components是1(颜色映射表索引),否则它等于out_color_components。这是一个在输出配置中发送每个像素的JSAMPLE的值。
通常你将需要分配数据缓冲来控制传入的图像。在你的输出缓冲中,你每个扫描行需要output_width*output_components JSAMPLEs,总共需要返回output_height个扫描行。
注意:如果你使用JPEG库内部内存管理来分配数据缓冲(就像djpeg做的),然后管理器拟定你需要一个比调用jpeg_start_decompress()之前更大的缓冲区。这是一个有一点棘手的问题,因为output_XXX域不是正常合法的。你可以通过调用jpeg_calc_output_dimensions()来使他们合法,然后设置重要的参数。(浏览输出颜色表和量化标志)
6. 当调用jpeg_read_scanlines(…)(读取剩余扫描行)
现在你可以通过一次或多次调用jpeg_read_scanlines()读取解压缩图像数据。在每一次调用中你要传递读取的最大扫描行的数量(例如,你工作缓冲区的高度),jpeg_read_scanlines()将返回多行。返回值是确实读取了行数。返回数据的格式时在“数据格式”下讨论过的。不要忘记灰度和颜色JPEGs将返回不同的数据格式。
图像数据被一从顶到底的顺序返回扫描行。如果你必须写出底到顶的顺序,你可能使用JPEG库中的虚拟数组的方法来有效的反转数据。这部分的例子可以在djpeg中找到。
库主张在JPEG对象的output_scanline域中记录扫描行目前为止返回了的数目。通常你可以仅仅使用这个值来控制循环,那么循环看起来应该是这个样子:
while(cinfo.output_scanline<cinfo.output_height)
(注意测试不应该违反image_height,除非你从来不使用扫描。image_height域试试原始图像未扫描的高度。)
返回值通常等于output_scanline的值。
如果你不中断数据源,假定jpeg_read_scanlines()每次调用至少读取一个扫描行,直到到达图像的底部是安全的。
如果你使用一个比一个扫描行大的缓冲区,假定jpeg_read_scanlines()填满它是不安全的。(当前实现每次调用只返回几行扫描行,不论你的缓冲区有多么大。)所以你必须提供一个循环来一再的调用jpeg_read_scanlines()知道全部图像被读取完。
7. jpeg_finish_decompress(…)
在所有图像数据被读取之后,调用jpeg_finish_decompress()来结束解压缩过程。这是因为要释放掉已经被JPEG对象分配了的工作内存。
典型的代码:
jpeg_finish_decompress(&cinfo);
如果使用stdion源控制器,如果必要的话不要忘记去关闭源stdio流。
在读取全部扫描行之前来调用jpeg_finish_decompress()是错误的。如果你希望中断解压缩过程,调用我们一会要讨论的jpeg_abort()。
在街上解压缩过程之后,你可能希望像接下来讨论的样子配置JPEG对象,或者你可能使用它来解压缩其他图像。在这种情况之下,返回到合适的步骤2或者3。如果你不需要改变源控制器,下一副图像将从同样的源中读取。
8. 释放JPEG解压缩对象
当你完成了JPEG解压缩对象时,调用jpeg_destroy_decompress()或者调用jpeg_destroy()。之前在压缩过程时讨论的销毁方式同样适用这里。
典型的代码:
jpeg_destroy_decompress(&cinfo);
9. 中断
你可以调用jpeg_destroy_decompress()或者jpeg_destroy()来中断解压缩过程,如果你一点也不需要JPEG对象,或者jpeg_abort_decompress()或jpeg_abort(),如果你想重用对象。之前在压缩过程时讨论过的中断方式也同样适用这里。
使用技巧:包含文件、链接、等等
应用程序适用JPEG库应该包含头文件jpeglib.h来获得数据类型和例程的定义。在包含jpeglib.h之前,包含系统头文件来定义FILE类型和size_t。在ANSI-conforming系统中,包含<stdio.h>就足够了,在老一点的Unix系统中,你可能需要<sys/types.h>来定义size_t。
如果应用程序需要提供初始化JPEG库异常代码,同样包含jerror.h来定义那些符号。
jpeglib.h间接的包含了文件jconfig.h和jmorecfg.h。如果你在系统字典中安装JPEG头文件,你将要安装这四个文件:jpeglib.h,jerror.h,jconfig.h和jmorecfg.h。
一个更合适的在你的应用程序中方法来包含JPEG代码是准备一个库文件(libjpeg.a,或者一个非Unix机子上相应的名字)并且在你链接的步骤时引用它。如果你仅仅使用库的一半(仅仅是压缩或者是解压缩),只从库中包含匹配的文件就可以了,除非你的连接器绝望的脑瘫了。被提供的生成文件libjpeg.a自动的建立(参考install.doc)。
如果你想到了你可以建立一个JPEG的分享库,我们不真正的建议你那么做。分享库的麻烦是某时你可能尝试用一个新的没有再编译调用的应用程序库版本来替代。这通常是不能工作的,因为参数结构体定义通常在每个新版本中改变了。换句话说,库的API在各个版本中是在二进制上不确定兼容的,我们只有尽量确保源代码兼容。(事后,它可能聪明的从应用程序中隐藏了参数结构体而不是引进接入函数。无论如何,都是太迟了。)
在一些系统中,如果你的程序中断了,你的应用程序可能需要设置一个信号处理函数来确保临时的文件被删除了。如果你是在MS-DOS和使用jmemdos.c内存控制器结束返回的话,这是非常重要的;它可能尝试为临时文件占用额外的内蒙,并且那个空间不能自动的释放掉。参考cjpeg.c或者djpeg.c来获取信号处理的例子。
值得指出的是核心JPEG库事实上不需要stdio库:只有默认源/目标控制器和异常处理需要他。如果你用jmemnobs.c来取代这些模块,你可以在一个没有stdio环境中使用库(或者其他你自己设计的内存控制)。更多的最小系统库需求信息在jinclude.h中。