JPEG 元数据格式规范和使用说明

JPEG 元数据
缩写流
表格来源
色彩空间转换和传统标记
缩略图
渐进式编码
本机元数据格式树结构和编辑
图像元数据 DTD
流式元数据 DTD

NOTE:当不再需要 JPEG 读取器和写入器对象时调用 dispose() 很重要,因为它们会消耗大量本机资源,而这些资源无法通过垃圾收集充分回收。读取器和写入器都在其终结器中调用 dispose(),但在本机代码耗尽本机内存之前可能不会调用这些终结器。

JPEG writer 不支持替换像素。

JPEG 元数据

JPEG 元数据由 JPEG 流中标记段中包含的数据组成。从读取返回的图像元数据对象描述了该图像的 SOI 标记和 EOI 标记之间的标记段的内容。传递到写入中的图像元数据对象确定该图像的 SOI 标记和 EOI 标记之间的流内容,受任何 ImageWriteParam 中的控制。

流元数据仅用于在包含缩略图像的流的开头找到(或放置)的纯表格图像。 Tables-only images 不被视为图像并且不消耗图像索引。从读取返回的流元数据对象描述了流开头的 SOI 标记和 EOI 标记之间的标记段的内容,如果存在的话。如果流的前面不存在纯表格图像,则 ImageReadergetStreamMetadata 方法返回 null 。如果向编写器提供了流元数据,则将在流的开头写入包含流元数据对象中的表的单个表图像。如果流元数据对象不包含表,则将写入默认表。由于流元数据的唯一目的是在缩写流的前面指定仅表图像,因此流元数据参数仅在 ImageWriter.prepareWriteSequence 方法上有用。它在所有其他方法上都被忽略。

ImageWriter.getDefaultStreamMetadata 方法返回一个包含来自 ImageWriteParam 参数的表的对象,如果它是一个 JPEGImageWriteParam 并且包含表。否则,返回的对象将包含默认表。

ImageWriter.getDefaultImageMetadata 方法返回一个元数据对象包含no如果 ImageWriteParam 参数包含表格,则为表格。否则返回的元数据对象将包含默认的视觉无损表。当然,只有 JPEGImageWriteParam 可能包含表格。

如果在读取器上设置输入时 ignoreMetadata 设置为 true,则流元数据将不可用,但图像元数据可用。

缩写流

从一个操作到下一个操作,读取器和写入器都保留他们的表,因此允许很自然地使用缩写流,但有一些小限制:
  1. 缩略流可能只包含一个仅包含表格的图像,该图像必须在流中排在第一位。随后的仅表格图像将导致未定义的行为。
  2. 必须按顺序完整阅读缩写流。不允许在任一方向上进行随机访问。同一张图片可能会被读取多次。如果使用与最近一次调用不同或比最近一次调用大一的图像索引进行调用(如果未进行任何调用,则为 0),则抛出 IllegalArgumentException
这些限制意味着流可能包含缩写图像,中间散布着包含表格的图像。根据 JPEG 的要求,流中出现的任何表格都会重写以前的表格,而不管以前表格的来源如何。

请注意,一旦读取了纯表格图像,它的内容就可以作为流元数据从阅读器中获得,直到从另一个流中读取另一个纯表格图像或重置阅读器。更改输入不会重置流元数据。这对于从一个文件中读取表格,然后更改输入以读取包含一系列图像的缩略流非常有用。这些表将自动使用,并将作为“流”元数据保持可用。

缩略流是使用 ImageWriter 的序列方法编写的。流元数据用于在流的开头写入仅表图像,并使用 ImageWriter.prepareWriteSequence 设置表以供使用。如果没有向 ImageWriter.prepareWriteSequence 提供流元数据,则不会写入仅表图像。如果将不包含表格的流元数据提供给 ImageWriter.prepareWriteSequence ,则会写入包含默认视觉无损表格的纯表格图像。

表格来源

如果表存在于它们的元数据对象中,则图像被写入表,或者如果它们的元数据对象中没有表,则没有表。如果不存在元数据对象,则写入表。用于压缩的表格取自以下来源之一,按顺序查阅:

  1. 如果存在 ImageWriteParam 并且压缩模式设置为 EXPLICIT ,则使用使用质量设置构建的默认表。仅当元数据包含表或没有元数据时才写入它们,但它们会替换元数据中的表。
  2. 如果有 ImageWriteParam 并且压缩模式设置为 DEFAULT ,则使用默认的视觉无损表。仅当元数据包含表或没有元数据时才写入它们,但它们会替换元数据中的表。
  3. 否则 ImageWriteParam 上的压缩模式必须是 MODE_COPY_FROM_METADATA ,在这种情况下使用以下内容:
    1. 图像元数据中的表(如果存在)
    2. 流元数据中的表(如果存在)
    3. JPEGImageWriteParam 中的表(如果存在)
    4. 默认视觉无损表
    只有从图像元数据中获取表格时才会写入表格。
这种排序实现了设计意图,即表应该包含在 JPEGImageWriteParam s 中,仅作为在没有其他源可用时指定表的一种方式,并且只有在使用已知的非标准表进行压缩写入没有表的缩写流时才会发生这种情况。

读取时,仅当表未被任何先前读取设置时,才会查询 JPEGImageReadParam 中的表。从 JPEGImageReadParam 设置的表被正在读取的流中存在的任何表覆盖。

请注意,如果没有为特定图像指定图像元数据对象,则会使用默认对象,其中包括默认表。

色彩空间转换和传统标记

颜色空间转换由图像读取和写入的目标类型控制。读取 Raster 时,不执行颜色空间转换,并且忽略任何目标类型。如果在这种情况下指定了目标类型,则会向任何监听器发送警告。写入 Raster 时,任何目标类型都用于解释带。这可能会导致写入 JFIF 或 Adobe 标头,或者将不同的组件 ID 写入框架和扫描标头。如果元数据对象中存在的值与目标类型不匹配,则使用目标类型并向任何监听器发送警告。

可选的 ColorSpace 支持: 如下所述,标准插件对 PhotoYCC (YCC)、PhotoYCCA (YCCA)、RGBA 和 YCbCrA 颜色空间的处理取决于用于解释 JPEG 数据的库的功能。因此,所有相应的行为都是可选的。如果在解码时支持不可用,则颜色空间将被视为无法识别,并且可以使用指定数量的组件通道的适当默认颜色空间。写入时,如果在编码前没有合适的转换可以被抛出异常。但是,在支持这些颜色空间的情况下,行为必须记录在案。

读取时,流的内容按照通常的 JPEG 约定进行解释,如下所示:

  • 如果存在 JFIF APP0 标记段,则颜色空间已知为灰度或 YCbCr。如果还存在包含嵌入式 ICC 配置文件的 APP2 标记段,则根据 JFIF 规范中给出的公式将 YCbCr 转换为 RGB,并假定 ICC 配置文件引用生成的 RGB 空间。
  • 如果存在 Adobe APP14 标记段,则通过查询 transform 标志来确定色彩空间。 transform 标志采用以下三个值之一:
    • 2 - 图像编码为 YCCK(在编码时从 CMYK 隐式转换)。
    • 1 - 图像编码为 YCbCr(在编码时从 RGB 隐式转换)。
    • 0 - 未知。假设 3 通道图像为 RGB,假设 4 通道图像为 CMYK。
  • 如果两个标记段都不存在,则遵循以下过程:单通道图像假定为灰度图像,双通道图像假定为具有 alpha 通道的灰度图像。对于 3 通道和 4 通道图像,会参考组件 ID。如果 3 通道图像的这些值为 1-3,则图像被假定为 YCbCr。根据上述 可选的色彩空间支持 的可用性,如果 4 通道图像的这些值为 1-4,则假定图像为 YCbCrA。如果这些值大于 4,则根据“R”、“G”、“B”、“A”、“C”、“c”的 ASCII 代码检查它们。这些可以编码以下色彩空间:


    RGB
    RGBA
    YCC(如'Y','C','c'),假设为PhotoYCC
    YCCA(如'Y'、'C'、'c'、'A'),假定为 PhotoYCCA

    否则,假定 3 通道二次采样图像为 YCbCr,假定 3 通道非二次采样图像为 RGB,假定 4 通道二次采样图像为 YCCK,并假定 4 通道非二次采样图像为三色。

  • 所有其他图像都被声明为不可解释,并且如果尝试将其中一个图像读取为 BufferedImage 则会抛出异常。这样的图像只能作为 Raster 读取。如果图像是可解释的,但没有对应于编码颜色空间的 Java ColorSpace 可用(例如YCbCr),那么 ImageReader.getRawImageType 将返回 null
一旦确定了编码色彩空间,目标色彩空间就确定如下:
  • 如果未设置目标类型,则在上采样后会发生以下默认转换:YCbCr(和 YCbCrA)图像使用底层 IJG 库提供的转换和内置 sRGB ColorSpace 或基于嵌入式 ICC 配置文件的自定义 RGB ColorSpace 对象用于创建输出 ColorModel。 PhotoYCC 和 PhotoYCCA 图像不会被转换。目前不支持 CMYK 和 YCCK 图像。
  • 如果设置了目标图像或类型,则按如下方式使用:如果 IJG 库提供适当的转换,则使用它。否则,默认库转换之后是 Java 中的色彩空间转换。
  • 在任何库色彩空间转换之后选择波段。如果使用源或目标 band 的子集,则使用默认库转换而不会在 Java 中进行进一步转换,而不管任何目标类型。
  • 如果尝试将不受支持的 jpeg 色彩空间中的图像读取为 BufferedImage (例如三色)。这样的图像可以读作Rasters。如果图像色彩空间不受支持或无法解释,则 ImageReader.getImageTypes 将返回一个空的 Iterator 。如果需要原始波段的子集,则必须先获取 Raster,然后从中获取波段。

对于写作,要应用的颜色变换确定如下:

如果要写入源波段的子集,则不执行颜色转换。任何目标(如果已设置)必须与将写入的波段数相匹配,并用作对所选波段的解释,而不是转换请求。此行为与 Raster s 的行为相同。如果要写入所有 band 并且正在写入图像(而不是 Raster ),则忽略任何目标类型并向任何监听器发送警告。

如果使用目标类型并且元数据对象的任何方面(如果有)与该类型不兼容,则使用目标类型,根据提供的元数据修改写入的元数据,并向监听器发送警告。这包括 app0JFIFapp14Adobe 节点。 sofsos 节点中的组件 ID 未被修改,但是,除非存在 app0JFIF 节点,否则可以使用任何值。

写入完整图像时,将根据图像内容和元数据设置,根据以下算法选择目标色彩空间:

如果未指定元数据对象,则应用以下默认值:

  • 灰度图像使用 JFIF APP0 标记段写入。带有 alpha 的灰度图像没有特殊标记。按照JFIF的要求,frame和scan header中的component ids设置为1。
  • RGB 图像被转换为 YCbCr,在色度通道中垂直和水平二次采样,并写入 JFIF APP0 标记段。如果图像的 ColorSpace 基于 ICCProfile(它是 ICC_ColorSpace 的实例,但不是标准内置 ColorSpaces 之一),那么该配置文件将嵌入到 APP2 标记段中。根据 JFIF 的要求,帧和扫描头中的组件 ID 设置为 1、2 和 3。
  • 根据上述 可选的库支持,RGBA 图像被转换为 YCbCrA,在色度通道中垂直和水平二次采样,并且在没有任何特殊标记段的情况下写入。帧和扫描标头中的组件 ID 设置为 1、2、3 和 4。
  • 根据上述可选的库支持,PhotoYCC 和 YCCA 图像在垂直和水平方向的色度通道中被二次采样,并使用 Adobe APP14 标记段和“Y”、“C”和“c”(如果alpha 通道)作为帧和扫描标头中的组件 ID。
这些图像类型的默认元数据对象将反映这些设置。

如果指定了元数据对象,则帧和扫描标头中的通道数必须始终与要写入的波段数匹配,否则会抛出异常。 app0JFIFapp14Adobe 节点只有在 app14Adobe 节点指示 YCbCr 且组件 ID 与 JFIF 兼容 (0-2) 时才会出现在同一元数据对象中。各种图像类型按以下方式处理:
(所有多通道图像都根据元数据对象的帧头节点中的采样因子进行二次采样,而不考虑颜色空间。)

  • 灰度图像:
    • 如果元数据对象中存在 app0JFIF 节点,则写入 JFIF APP0 标记段。
    • 如果 app14Adobe 节点存在于元数据对象中,则检查其有效性(transform 必须是 UNKNOWN )并写入。
    • 如果两个节点都不存在于元数据对象中,则不写入特殊标记段。
  • 带 Alpha 通道的灰度图像:
    • 如果元数据对象中存在 app0JFIF 节点,则会忽略它并向监听器发送警告,因为 JFIF 不支持 2 通道图像。
    • 如果 app14Adobe 节点存在于元数据对象中,则检查其有效性(transform 必须是 UNKNOWN )并写入。如果 transform 不是 UNKNOWN ,则会向监听器发送警告并写入正确的转换。
    • 如果两个节点都不存在于元数据对象中,则不写入特殊标记段。
  • RGB 图像:
    • 如果元数据对象中存在 app0JFIF 节点,则图像将转换为 YCbCr 并写入 JFIF APP0 标记段。如果图像的 ColorSpace 基于非标准 ICC 配置文件,则该配置文件将嵌入到 APP2 标记段中。如果 ColorSpace 不是基于非标准 ICC 配置文件,但元数据中出现了 app2ICC 节点,则将使用适当的标准配置文件写入 APP2 标记段。请注意,配置文件必须指定 RGB 颜色空间,因为文件必须符合 JFIF。
    • 如果元数据对象中存在 app14Adobe 节点,则根据颜色变换设置转换图像并使用 Adobe APP14 标记段写入。组件 ID 就像它们出现在框架和扫描标题中一样被写入。颜色变换必须是 YCbCr 或 UNKNOWN 。如果是 UNKNOWN ,则图像未进行颜色转换。
    • 如果两个节点都不存在,则查询帧头中的组件 ID。如果这些指示如上所述的颜色空间,则图像将尽可能转换为该颜色空间。如果组件 id 不指示颜色空间,则参考采样因子。如果要对图像进行二次采样,则首先将其转换为 YCbCr。如果不对图像进行二次采样,则不应用任何转换。没有写入特殊的标记段。
  • RGBA 图像:根据上述可选的库支持
    • 如果元数据对象中存在 app0JFIF 节点,则会忽略它并向监听器发送警告,因为 JFIF 不支持 4 通道图像。
    • 如果元数据对象中存在 app14Adobe 节点,则图像将使用 Adobe APP14 标记段写入。不执行色彩空间转换。组件 ID 就像它们出现在框架和扫描标题中一样被写入。颜色变换必须是 UNKNOWN 。如果不是,则向听众发送警告。
    • 如果不存在 app14Adobe 节点,则查询帧头中的组件 ID。如果这些指示如上所述的颜色空间,则图像将尽可能转换为该颜色空间。如果组件 id 不指示颜色空间,则参考采样因子。如果要对图像进行二次采样,则将其转换为 YCbCrA。如果不对图像进行二次采样,则不应用任何转换。没有写入特殊的标记段。
  • PhotoYCC 图片:根据上述 可选的库支持
    • 如果app0JFIF节点存在于元数据对象中,则图像被转换为sRGB,然后在编码期间转换为YCbCr,并写入JFIF APP0标记段。
    • 如果元数据对象中存在 app14Adobe 节点,则不应用任何转换,并写入 Adobe APP14 标记段。颜色变换必须是 YCC。如果不是,则向听众发送警告。
    • 如果两个节点都不存在于元数据对象中,则不应用转换,也不写入特殊标记段。
  • PhotoYCCA 图片:根据上述 可选的库支持
    • 如果元数据对象中存在 app0JFIF 节点,则会忽略它并向监听器发送警告,因为 JFIF 不支持 4 通道图像。
    • 如果元数据对象中存在 app14Adobe 节点,则不应用任何转换,并写入 Adobe APP14 标记段。颜色变换必须是 UNKNOWN 。如果不是,则向听众发送警告。
    • 如果两个节点都不存在于元数据对象中,则不应用转换,也不写入特殊标记段。

缩略图

使用 JFIF 和 JFIF 扩展标记段支持缩略图。 write 方法中提供的缩略图决定了将包含的缩略图。元数据中的 app0JFIFapp0JFXX 节点不包含任何缩略图像素数据。然而,写入的缩略图的种类取决于元数据对象的内容,如下所示。任何要写成索引或 RGB 图像且大于 255 x 255 的缩略图将被裁剪,而不是缩放到 255 x 255。写成 JPEG 图像的缩略图可以是任何大小。每当裁剪缩略图时,都会向所有听众发送警告。
  • 如果只有一个缩略图,则处理如下:
    • 如果缩略图是 RGB 调色板图像,则按如下方式处理:
      • 如果元数据中不存在 app0JFXX 节点,或者元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbPalette 元素,则调色板缩略图将写入 JFXX APP0 标记段。
      • 如果元数据中的第一个 app0JFXX 节点包含另一种缩略图形式(RGB 或 JPEG),则调色板图像将扩展为 RGB 并写入指示的缩略图形式。
    • 如果缩略图是RGB图片,则进行如下处理:
      • 如果元数据中不存在 app0JFXX 节点,则缩略图将作为 JFIF APP0 标记段的一部分写入。
      • 如果元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbRGB 元素,则 RGB 缩略图将写入 JFXX APP0 标记段。
      • 如果元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbJPEG 元素,则 JPEG 缩略图将写入 JFXX APP0 标记段中。
      • 如果元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbPalette 元素,则将在 JFXX APP0 标记段中写入 RGB 缩略图,并向所有监听器发送警告。
    • 如果缩略图是灰度图,则进行如下处理:
      • 如果元数据中不存在 app0JFXX 节点,则缩略图将扩展为 RGB 并作为 JFIF APP0 标记段的一部分写入。
      • 如果元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbRGB 元素,则缩略图将扩展为 RGB 并写入单独的 JFXX RGB 标记段。
      • 如果元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbJPEG 元素,则 JPEG 缩略图将写入 JFXX APP0 标记段中。
      • 如果元数据中存在的第一个 app0JFXX 节点包含一个 JFIFthumbPalette 元素,则 JPEG 缩略图将写入 JFXX APP0 标记段中,并向所有监听器发送警告。
    • 忽略任何其他缩略图图像类型,并向所有听众发送警告。
  • 如果有多个缩略图,则每个都按上述方法处理,只是JFIFAPP0段中没有放置缩略图,每个缩略图查询的app0JFXX节点是元数据中与缩略图出现顺序相同的app0JFXX节点。IE第一个 app0JFXX 节点应用于第一个缩略图,第二个节点应用于第二个缩略图,依此类推。如果元数据中的 app0JFXX 节点少于缩略图,则认为这些缩略图没有匹配的 app0JFXX 节点。没有匹配 app0JFXX 节点的 RGB 缩略图写入 JFXX APP0 标记段。没有匹配 app0JFXX 节点的灰度缩略图作为 JPEG 图像写入 JFXX APP0 标记段。

请注意,由于存储缩略图的唯一机制是通过 JFIF 或 JFIF 扩展标记段,因此只有灰度或 RGB 图像可能具有缩略图。如果在写入任何其他类型的图像时存在缩略图,则忽略缩略图并向任何警告监听发送警告。

渐进式编码

必须在传入写入操作的 ImageWriteParam 上启用渐进式编码,否则将按顺序写入图像,而不管元数据对象中包含扫描标头。如果启用渐进式编码并将其设置为从元数据复制,则使用元数据中的扫描标头序列来写入图像。如果启用渐进式编码并设置为使用默认值,则忽略元数据中的扫描并使用默认扫描集。渐进式编码总是强制使用优化的霍夫曼表。元数据中存在的任何霍夫曼表都将被忽略,并且警告将发送给任何警告监听器。如果在 ImageWriteParam 上请求霍夫曼表优化,元数据或 ImageWriteParam 本身中的所有霍夫曼表都将被忽略,如果存在任何此类表,则会向任何警告监听器发送警告。

本机元数据格式树结构和编辑

下面的 DTD 仅描述了 IIOMetadata 对象实际返回的元数据对象树。它们不包括对应于 SOIEOIRST 标记的节点,因为这些解析定界符不携带任何有意义的元数据。

第一个节点始终是 JPEGvariety 节点。在javax_imageio_jpeg_image_1.0版本的JPEG元数据格式中,这个节点可能有一个子节点,一个app0JFIF节点,表示JPEG流包含一个JFIF标记段和相关数据,或者没有子节点,表示该流不包含JFIF标记。在 JPEG 元数据格式的未来版本中,可以通过定义可能显示为 JPEGvariety 节点的子节点的其他类型的节点来支持其他种类的 JPEG 元数据(例如 Exif)。

(请注意,应用程序希望在给定 javax_imageio_jpeg_image_1.0 格式的元数据树结构的情况下解释 Exif 元数据,必须检查带有指示 APP1 标记的标记并包含将其标识为 Exif 标记段的数据的 unknown 标记段。然后它可以使用 application-用于解释标记段中数据的特定代码。如果此类应用程序遇到根据 JPEG 元数据格式的未来版本格式化的元数据树,则 Exif 标记段可能不是该格式的 unknown - 它可能被构造为JPEGvariety 节点的子节点。因此,应用程序通过将标识版本的字符串传递给用于获取 IIOMetadata 对象的方法/构造函数来指定要使用的版本很重要。)

在读取时,JFXXapp2ICC 节点作为 app0JFIF 节点的子节点出现。无论 JFXX APP0APP2 标记段实际出现在流中的什么位置,都是如此。 markerSequence 节点内的节点排序对应于 JPEG 流中找到的标记段的排序。

在写入时,任何 JFXXapp2ICC 节点必须作为 app0JFIF 节点的子节点出现,它本身是 JPEGvariety 节点的子节点,它必须始终是第一个节点。 (如果流不符合 JFIF,则不应提供 app0JFIF 节点,并且 JPEGvariety 节点不应有子节点。)首先写入任何 JFIF APP0、JFXX APP0APP2 标记段,然后是所有 Adobe APP14APPn , COM 和未知段按照其相应节点出现在 markerSequence 节点中的顺序排列,然后是 DQT(和 DHT 用于非渐进式写入)标记段,然后是 SOFSOS 标记段。对于使用元数据控制进度的渐进式写入,SOS 段按照其对应节点在 markerSequence 节点中出现的顺序使用。

resetmergeTreesetFromTree 操作对于 JPEG 插件元数据对象具有以下语义:

reset - 调用 reset 会将元数据对象恢复到创建后的状态,无论这是通过读取流还是通过从 ImageWriter 获取默认对象实现的。无论元数据对象自创建以来被修改了多少次都是如此。

mergeTree - 本机格式
mergeTree 操作接受符合以下 DTD 的有效树,并使用以下排序规则合并节点。在所有情况下,只有存在于新节点中的数据在对应的现有节点中被改变,如果有的话。这意味着不能使用 mergeTree 删除节点。要删除节点,请使用 setFromTree 。树必须由 IIOMetadataNode 组成。

  • app0JFIF
    • 如果 app0JFIF 节点已经存在,则新节点的内容会修改现有节点。
    • 如果没有这样的节点,则创建一个新节点并将其插入适当的位置。
  • dqt
    • 如果序列中已经存在 dqt 个节点,则该节点中的每个表都会用相同的表 ID 替换任何 dqt 节点中的第一个表。
    • 如果现有的 dqt 节点都不包含具有相同 ID 的表,则该表将添加到最后一个现有的 dqt 节点。
    • 如果没有 dqt 节点,则创建并添加一个新节点,如下所示:
      • 如果有 dht 个节点,则将新的 dqt 节点插入到第一个节点之前。
      • 如果没有 dht 节点,则将新的 dqt 节点插入到 sof 节点之前(如果有的话)。
      • 如果没有 sof 节点,新的 dqt 节点插入到第一个 sos 节点之前,如果有的话。
      • 如果没有 sos 节点,则将新的 dqt 节点添加到序列的末尾。
  • dht
    • 如果序列中已经存在 dht 个节点,则该节点中的每个表都会用相同的表类和表 ID 替换任何 dht 节点中的第一个表。
    • 如果现有的 dht 节点都不包含具有相同类和 ID 的表,则该表将添加到最后一个现有的 dht 节点。
    • 如果没有 dht 节点,则创建并添加一个新节点,如下所示:
      • 如果有 dqt 个节点,则新的 dht 节点会立即插入到最后一个 dqt 节点之后。
      • 如果没有 dqt 节点,则将新的 dht 节点插入到 sof 节点之前(如果有的话)。
      • 如果没有 sof 节点,新的 dht 节点插入到第一个 sos 节点之前,如果有的话。
      • 如果没有 sos 节点,则将新的 dht 节点添加到序列的末尾。
  • dri
    • 如果已经存在 dri 节点,则更新重启间隔值。
    • 如果没有dri节点,则创建一个新节点并添加如下:
      • 如果有 sof 节点,则在它之前插入新的 dri 节点。
      • 如果没有 sof 节点,新的 dri 节点插入到第一个 sos 节点之前,如果有的话。
      • 如果没有 sos 节点,则将新的 dri 节点添加到序列的末尾。
  • com
    创建并插入一个新的 com 节点,如下所示:
    • 如果已经存在 com 个节点,则将新节点插入到最后一个节点之后。
    • 如果没有 com 节点,则在 app14Adobe 节点之后插入新的 com 节点(如果有的话)。
    • 如果没有 app14Adobe 节点,则将新的 com 节点插入到序列的开头。
  • app14Adobe
    • 如果已经存在 app14Adobe 节点,则从该节点更新其属性。
    • 如果没有app14Adobe节点,则创建一个新节点并添加如下:
      • 新的 app14Adobe 节点插入到最后一个 unknown 节点之后,如果有的话。
      • 如果没有 unknown 节点,则新的 app14Adobe 节点被插入到序列的开头。
  • unknown
    创建一个新的 unknown 节点并将其添加到序列中,如下所示:
    • 如果已经存在 unknown 个标记节点,则将新的插入到最后一个标记节点之后。
    • 如果没有 unknown 节点,则将新的 unknown 节点插入到 app14Adobe 节点之前(如果有的话)。
    • 如果没有 app14Adobe 节点,则将新的 unknown 节点插入到序列的开头。
  • sof
    • 如果序列中已经存在 sof 节点,则从该节点更新其值。
    • 如果没有 sof 节点,则创建一个新节点并添加如下:
      • 如果有任何 sos 节点,新的 sof 节点将插入到第一个节点之前。
      • 如果没有 sos 节点,则将新的 sof 节点添加到序列的末尾。
  • sos
    • 如果已经存在单个 sos 节点,则从该节点更新值。
    • 如果存在多个 sos 节点,则抛出 IIOInvalidTreeException,因为 sos 节点无法合并到一组渐进式扫描中。
    • 如果没有 sos 个节点,则会创建一个新节点并将其添加到序列的末尾。

mergeTree - 标准格式
mergeTree 操作,当给定标准格式的树时,将通过以下方式修改原生树:

  • Chroma - Chroma 节点的 ColorSpaceType 子节点可能会更改压缩图像的目标色彩空间。选择一个新的色彩空间可能会导致一些变化,与上述算法保持一致:可以添加或删除app0JFIFapp14Adobe节点,可以添加或删除子采样,可以更改组件ID,并且sofsos节点将相应更新。如有必要,添加额外的量化和霍夫曼表。在量化表的情况下,默认值将被缩放以匹配任何现有表的质量级别。没有表被添加到尚未包含表的元数据中。如果现有元数据指定渐进式编码,则通道数不得改变。还考虑了任何 Transparency 节点,因为 Alpha 子节点的 none 的显式值可能会导致删除 alpha 通道,而 none 以外的任何其他节点都会导致添加 alpha 通道。
  • Dimension - PixelAspectRatio 规范可以导致 app0JFIF 节点的内容发生变化(如果存在的话),或者添加包含适当值的 app0JFIF 节点(如果可以的话)。根据浮点比率计算出一对合适的整数以包含在节点中。
  • Text - 每个未压缩的文本项都被转换为一个 com 节点,并根据上述规则插入以合并 com 节点。

setFromTree - 本机格式
setFromTree 操作,当以下面描述的本机格式给出一棵树时,将简单地用新树替换整个现有树。树必须由 IIOMetadataNode 组成。

setFromTree - 标准格式
setFromTree 操作,当给定标准格式的树时,执行 reset,然后合并新树。

图像元数据 DTD

<!DOCTYPE "javax_imageio_jpeg_image_1.0" [

 <!ELEMENT "javax_imageio_jpeg_image_1.0" (JPEGvariety, markerSequence)>

  <!ELEMENT "JPEGvariety" (app0JFIF)>
   <!-- A node grouping all marker segments specific to the variety of
       stream being read/written (e.g. JFIF) - may be empty -->

   <!ELEMENT "app0JFIF" (JFXX?, app2ICC?)>
    <!ATTLIST "app0JFIF" "majorVersion" #CDATA "1">
     <!-- The major JFIF version number -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 255 (inclusive) -->
    <!ATTLIST "app0JFIF" "minorVersion" #CDATA "2">
     <!-- The minor JFIF version number -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 255 (inclusive) -->
    <!ATTLIST "app0JFIF" "resUnits" ("0" | "1" | "2") "0">
     <!-- The resolution units for Xdensisty and Ydensity (0 = no
        units, just aspect ratio; 1 = dots/inch; 2 = dots/cm) -->
    <!ATTLIST "app0JFIF" "Xdensity" #CDATA "1">
     <!-- The horizontal density or aspect ratio numerator -->
     <!-- Data type: Integer -->
     <!-- Min value: 1 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->
    <!ATTLIST "app0JFIF" "Ydensity" #CDATA "1">
     <!-- The vertical density or aspect ratio denominator -->
     <!-- Data type: Integer -->
     <!-- Min value: 1 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->
    <!ATTLIST "app0JFIF" "thumbWidth" #CDATA "0">
     <!-- The width of the thumbnail, or 0 if there isn't one -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 255 (inclusive) -->
    <!ATTLIST "app0JFIF" "thumbHeight" #CDATA "0">
     <!-- The height of the thumbnail, or 0 if there isn't one -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 255 (inclusive) -->

    <!ELEMENT "JFXX" (app0JFXX)*>
     <!-- Min children: 1 -->

    <!ELEMENT "app0JFXX" (JFIFthumbJPEG | JFIFthumbPalette |
     JFIFthumbRGB)>
     <!-- A JFIF extension marker segment -->
     <!ATTLIST "app0JFXX" "extensionCode" ("16" | "17" | "19")
       #IMPLIED>
      <!-- The JFXX extension code identifying thumbnail type: (16 =
         JPEG, 17 = indexed, 19 = RGB -->

     <!ELEMENT "JFIFthumbJPEG" (markerSequence?)>
      <!-- A JFIF thumbnail in JPEG format (no JFIF segments
         permitted) -->

     <!ELEMENT "JFIFthumbPalette" EMPTY>
      <!-- A JFIF thumbnail as an RGB indexed image -->
      <!ATTLIST "JFIFthumbPalette" "thumbWidth" #CDATA #IMPLIED>
       <!-- The width of the thumbnail -->
       <!-- Data type: Integer -->
       <!-- Min value: 0 (inclusive) -->
       <!-- Max value: 255 (inclusive) -->
      <!ATTLIST "JFIFthumbPalette" "thumbHeight" #CDATA #IMPLIED>
       <!-- The height of the thumbnail -->
       <!-- Data type: Integer -->
       <!-- Min value: 0 (inclusive) -->
       <!-- Max value: 255 (inclusive) -->

     <!ELEMENT "JFIFthumbRGB" EMPTY>
      <!-- A JFIF thumbnail as an RGB image -->
      <!ATTLIST "JFIFthumbRGB" "thumbWidth" #CDATA #IMPLIED>
       <!-- The width of the thumbnail -->
       <!-- Data type: Integer -->
       <!-- Min value: 0 (inclusive) -->
       <!-- Max value: 255 (inclusive) -->
      <!ATTLIST "JFIFthumbRGB" "thumbHeight" #CDATA #IMPLIED>
       <!-- The height of the thumbnail -->
       <!-- Data type: Integer -->
       <!-- Min value: 0 (inclusive) -->
       <!-- Max value: 255 (inclusive) -->

    <!ELEMENT "app2ICC" EMPTY>
     <!-- An ICC profile APP2 marker segment -->
     <!-- Optional User object: java.awt.color.ICC_Profile -->

  <!ELEMENT "markerSequence" (dqt | dht | dri | com | unknown |
   app14Adobe | sof | sos)*>
   <!-- A node grouping all non-jfif marker segments -->

   <!ELEMENT "dqt" (dqtable)*>
    <!-- A Define Quantization Table(s) marker segment -->
    <!-- Min children: 1 -->
    <!-- Max children: 4 -->

    <!ELEMENT "dqtable" EMPTY>
     <!-- A single quantization table -->
     <!-- User object: javax.imageio.plugins.jpeg.JPEGQTable -->
     <!ATTLIST "dqtable" "elementPrecision" #CDATA "0">
      <!-- The number of bits in each table element (0 = 8, 1 = 16)
         -->
      <!-- Data type: Integer -->
     <!ATTLIST "dqtable" "qtableId" ("0" | "1" | "2" | "3") #REQUIRED>

   <!ELEMENT "dht" (dhtable)*>
    <!-- A Define Huffman Table(s) marker segment -->
    <!-- Min children: 1 -->
    <!-- Max children: 4 -->

    <!ELEMENT "dhtable" EMPTY>
     <!-- A single Huffman table -->
     <!-- User object: javax.imageio.plugins.jpeg.JPEGHuffmanTable -->
     <!ATTLIST "dhtable" "class" ("0" | "1") #REQUIRED>
      <!-- Indicates whether this is a DC (0) or an AC (1) table -->
     <!ATTLIST "dhtable" "htableId" ("0" | "1" | "2" | "3") #REQUIRED>
      <!-- The table id -->

   <!ELEMENT "dri" EMPTY>
    <!-- A Define Restart Interval marker segment -->
    <!ATTLIST "dri" "interval" #CDATA #REQUIRED>
     <!-- The restart interval in MCUs -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->

   <!ELEMENT "com" EMPTY>
    <!-- A Comment marker segment. The user object contains the actual
       bytes. -->
    <!-- User object: array of [B -->
    <!-- Min length: 1 -->
    <!-- Max length: 65533 -->
    <!ATTLIST "com" "comment" #CDATA #IMPLIED>
     <!-- The comment as a string (used only if user object is null)
        -->
     <!-- Data type: String -->

   <!ELEMENT "unknown" EMPTY>
    <!-- An unrecognized marker segment. The user object contains the
       data not including length. -->
    <!-- User object: array of [B -->
    <!-- Min length: 1 -->
    <!-- Max length: 65533 -->
    <!ATTLIST "unknown" "MarkerTag" #CDATA #REQUIRED>
     <!-- The tag identifying this marker segment -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 255 (inclusive) -->

   <!ELEMENT "app14Adobe" EMPTY>
    <!-- An Adobe APP14 marker segment -->
    <!ATTLIST "app14Adobe" "version" #CDATA "100">
     <!-- The version of Adobe APP14 marker segment -->
     <!-- Data type: Integer -->
     <!-- Min value: 100 (inclusive) -->
     <!-- Max value: 255 (inclusive) -->
    <!ATTLIST "app14Adobe" "flags0" #CDATA "0">
     <!-- The flags0 variable of an APP14 marker segment -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->
    <!ATTLIST "app14Adobe" "flags1" #CDATA "0">
     <!-- The flags1 variable of an APP14 marker segment -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->
    <!ATTLIST "app14Adobe" "transform" ("0" | "1" | "2") #REQUIRED>
     <!-- The color transform applied to the image (0 = Unknown, 1 =
        YCbCr, 2 = YCCK) -->

   <!ELEMENT "sof" (componentSpec)*>
    <!-- A Start Of Frame marker segment -->
    <!-- Min children: 1 -->
    <!-- Max children: 4 -->
    <!ATTLIST "sof" "process" ("0" | "1" | "2") #IMPLIED>
     <!-- The JPEG process (0 = Baseline sequential, 1 = Extended
        sequential, 2 = Progressive) -->
    <!ATTLIST "sof" "samplePrecision" #CDATA "8">
     <!-- The number of bits per sample -->
     <!-- Data type: Integer -->
    <!ATTLIST "sof" "numLines" #CDATA #IMPLIED>
     <!-- The number of lines in the image -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->
    <!ATTLIST "sof" "samplesPerLine" #CDATA #IMPLIED>
     <!-- The number of samples per line -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 65535 (inclusive) -->
    <!ATTLIST "sof" "numFrameComponents" ("1" | "2" | "3" | "4")
      #IMPLIED>
     <!-- The number of components in the image -->

    <!ELEMENT "componentSpec" EMPTY>
     <!-- A component specification for a frame -->
     <!ATTLIST "componentSpec" "componentId" #CDATA #REQUIRED>
      <!-- The id for this component -->
      <!-- Data type: Integer -->
      <!-- Min value: 0 (inclusive) -->
      <!-- Max value: 255 (inclusive) -->
     <!ATTLIST "componentSpec" "HsamplingFactor" #CDATA #REQUIRED>
      <!-- The horizontal sampling factor for this component -->
      <!-- Data type: Integer -->
      <!-- Min value: 1 (inclusive) -->
      <!-- Max value: 255 (inclusive) -->
     <!ATTLIST "componentSpec" "VsamplingFactor" #CDATA #REQUIRED>
      <!-- The vertical sampling factor for this component -->
      <!-- Data type: Integer -->
      <!-- Min value: 1 (inclusive) -->
      <!-- Max value: 255 (inclusive) -->
     <!ATTLIST "componentSpec" "QtableSelector" ("0" | "1" | "2" |
      "3") #REQUIRED>
      <!-- The quantization table to use for this component -->

   <!ELEMENT "sos" (scanComponentSpec)*>
    <!-- A Start Of Scan marker segment -->
    <!-- Min children: 1 -->
    <!-- Max children: 4 -->
    <!ATTLIST "sos" "numScanComponents" ("1" | "2" | "3" | "4")
      #REQUIRED>
     <!-- The number of components in the scan -->
    <!ATTLIST "sos" "startSpectralSelection" #CDATA "0">
     <!-- The first spectral band included in this scan -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 63 (inclusive) -->
    <!ATTLIST "sos" "endSpectralSelection" #CDATA "63">
     <!-- The last spectral band included in this scan -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 63 (inclusive) -->
    <!ATTLIST "sos" "approxHigh" #CDATA "0">
     <!-- The highest bit position included in this scan -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 15 (inclusive) -->
    <!ATTLIST "sos" "approxLow" #CDATA "0">
     <!-- The lowest bit position included in this scan -->
     <!-- Data type: Integer -->
     <!-- Min value: 0 (inclusive) -->
     <!-- Max value: 15 (inclusive) -->

    <!ELEMENT "scanComponentSpec" EMPTY>
     <!-- A component specification for a scan -->
     <!ATTLIST "scanComponentSpec" "componentSelector" #CDATA
       #REQUIRED>
      <!-- The id of this component -->
      <!-- Data type: Integer -->
      <!-- Min value: 0 (inclusive) -->
      <!-- Max value: 255 (inclusive) -->
     <!ATTLIST "scanComponentSpec" "dcHuffTable" ("0" | "1" | "2" |
      "3") #REQUIRED>
      <!-- The huffman table to use for encoding DC coefficients -->
     <!ATTLIST "scanComponentSpec" "acHuffTable" ("0" | "1" | "2" |
      "3") #REQUIRED>
      <!-- The huffman table to use for encoding AC coefficients -->
]>

流式元数据 DTD

<!DOCTYPE "javax_imageio_jpeg_stream_1.0" [
 <!ELEMENT "javax_imageio_jpeg_stream_1.0" (dqt |
           dht |
           dri |
           com |
           unknown)*>

 <!-- All elements are as defined above for image metadata -->
]>