- 图像处理
- 图形学
【免费下载链接】ImageSharp
A modern, cross-platform, 2D Graphics library for .NET
TIFF 是一个极具包容性的图像容器格式:同一个.tif文件内部可能使用十几种不同的压缩算法、七八种不同的色彩解释方式,以及数百个不同的 IFD 标签。ImageSharp 仓库中的 TIFF codec 实现文档 以能力矩阵的形式,逐项列出了编码器(Encoder)与解码器(Decoder)对各种压缩格式、光度解释和标签的支持现状。本文将以此为骨架,结合src/ImageSharp/Formats/Tiff/目录下的源码实现与tests/ImageSharp.Tests/Formats/Tiff/下的测试用例,讲清 ImageSharp TIFF 编解码器“支持什么、不支持什么、底层如何实现、代码中如何配置”,帮助你在实际项目中避开兼容性陷阱。
一、TIFF 编解码器架构总览
在深入矩阵之前,先建立整体认知。ImageSharp 的 TIFF 模块遵循标准格式插件架构:
- TiffFormat.cs 定义格式本体:
Name为"TIFF",默认 MIME 为image/tiff,支持image/tiff、image/tiff-fx两种 MIME 与tiff、tif两种扩展名; - TiffConfigurationModule.cs 负责向
ImageFormatsManager注册TiffEncoder、TiffDecoder.Instance和TiffImageFormatDetector; - TiffImageFormatDetector.cs 通过前 8 字节识别文件:
0x49 0x49(小端)/0x4D 0x4D(大端)字节序标记,配合魔数42(经典 TIFF)或43(BigTIFF)。也就是说,ImageSharp 的 TIFF 检测器同时支持经典 TIFF 与 BigTIFF; - TiffEncoder.cs 与 TiffDecoder.cs 分别是编解码入口,最终由内部
TiffEncoderCore/TiffDecoderCore完成实际工作。
值得注意的一点是,TIFF 的 IFD(Image File Directory)结构天然支持多帧,ImageSharp 将每个 IFD 映射为ImageFrame,因此多帧 TIFF 的读写与 GIF 类似,都是通过image.Frames集合来操作。README 明确指出:解码器当前仅支持“所有帧尺寸相同”的多帧图像,任何帧尺寸大于根帧的多帧文件会直接抛出NotSupported(详见 TiffDecoderCore.cs 中DecodeFrame的尺寸校验逻辑)。
二、压缩格式支持矩阵与实现原理
README 的压缩能力表是选择 TIFF 编码参数时最重要的参考,完整继承如下:
| 压缩格式 | Encoder | Decoder | 说明 |
|---|---|---|---|
| None(无压缩) | Y | Y | 原样存储像素数据 |
| Ccitt1D | Y | Y | 1 维 Modified Huffman RLE(T.4 一维) |
| PackBits | Y | Y | TIFF 6.0 第 9 节的游程压缩 |
| CcittGroup3Fax | Y | Y | CCITT T.4 二值编码(Group 3) |
| CcittGroup4Fax | Y | Y | CCITT T.6 二值编码(Group 4) |
| Lzw | Y | Y | 基于 ImageSharp GIF LZW 实现,可进一步共享或按场景优化 |
| Old Jpeg | Y | 仅支持 chunky(连续)配置 | |
| Jpeg(Technote 2) | Y | Y | 新式 JPEG 压缩 |
| Deflate(Technote 2) | Y | Y | 基于 PNG Deflate 实现 |
| Old Deflate(Technote 2) | Y | 兼容旧式 Deflate(tag 32946) | |
| Webp | Y | 解码 WebP 压缩的 TIFF 像素数据 |
2.1 源码中的实现分布
压缩相关的全部实现集中在 Compression 目录:
- 压缩器(Compressors):
NoCompressor、PackBitsCompressor、LzwCompressor+TiffLzwEncoder、T4BitCompressor、T6BitCompressor、TiffCcittCompressor、DeflateCompressor、TiffJpegCompressor; - 解压器(Decompressors):
NoneTiffCompression、PackBitsTiffCompression、LzwTiffCompression、ModifiedHuffmanTiffCompression(CCITT 1D)、T4TiffCompression、T6TiffCompression、DeflateTiffCompression、JpegTiffCompression、OldJpegTiffCompression、WebpTiffCompression; - 选择逻辑由 TiffCompressorFactory.cs 与 TiffDecompressorsFactory.cs 承担。
从 TiffDecompressorsFactory.cs 可以看到两类重要约束:
- Predictor 只允许配合 LZW 与 Deflate——其余压缩路径中若出现 predictor 会触发
DebugGuard断言失败; - Fax 类压缩(T4/T6/HuffmanRLE)逐行解码,在平铺(tiled)场景下必须使用 tile 行宽计算缓冲,这也是工厂单独传入
tileWidth/tileHeight的原因。
2.2 底层依赖关系
README 特别注明了两处代码复用关系,这在源码中可以交叉印证:
- LZW 基于 ImageSharp GIF LZW 实现——TIFF 的 LZW 编码/解码器与 GIF 的 LZW 属于同一算法家族,ImageSharp 在
TiffLzwEncoder/TiffLzwDecoder中按 TIFF 规范做了适配(如 code size 与 early change 处理差异),README 也诚实指出该代码“可以被修改为(i)共享,或(ii)各自优化”; - Deflate 基于 PNG Deflate——TIFF Technote 2 的 Deflate 使用 zlib 数据格式,实现位于
DeflateCompressor/DeflateTiffCompression,与 PNG 解码路径共用底层的 zlib 读写基础设施(SixLabors.ImageSharp.Compression.Zlib)。
2.3 编码时的自动降级策略
你可能会好奇:如果编码时选择了“未实现”的压缩怎么办?TiffEncoderCore.cs 的SanitizeAndSetEncoderOptions给出了完整的兜底规则:
- 1-bit CCITT 压缩(Ccitt1D/Group3/Group4)只能用于 1 bpp 图像:若 bpp 不是 1,自动降级——
< 8 bpp时改为None,否则改为Deflate; - JPEG 压缩不支持 48/64/96/128 bpp 高精度整数样本:自动降级为
Deflate; - 浮点样本下,Fax 与 JPEG 压缩无法写入:自动降级为
Deflate。
这意味着编码器选项在“不合理”时会被静默修正,最终写入文件的参数以修正后的值为准(可以通过解码后的TiffFrameMetadata.Compression验证)。
三、高精度样本支持:16/32 位整数与 32 位浮点
README 中关于高精度样本的段落是本模块的一大亮点,原文要点如下:
对于高精度样本,编码器和解码器支持无符号 16 位与 32 位 RGB/RGBA 样本(48、64、96 或 128 bits per pixel)。同时支持 32 位浮点灰度、RGB、RGBA 样本(32、96 或 128 bits per pixel)。浮点编码支持 None、PackBits、Lzw 与 Deflate 压缩。浮点 predictor 不支持。
3.1 位深枚举完整梳理
TiffBitsPerPixel.cs 定义了从 1 到 128 的全部位深枚举,逐一说明:
| 枚举值 | 位深 | 语义与编码器行为 |
|---|---|---|
| Bit1 | 1 | 双色图(bilevel) |
| Bit4 | 4 | 调色板图像 |
| Bit6/10/12/14 | 6/10/12/14 | 编码器暂不支持,请求后自动降级为 24 bpp RGB |
| Bit8 | 8 | 灰度或调色板图像 |
| Bit16 | 16 | 灰度(L16) |
| Bit24 | 24 | 每通道 1 字节的 RGB |
| Bit30/36/42 | 30/36/42 | 每通道 10/12/14 位;编码器按48 bpp写入 |
| Bit32 | 32 | 四个 8 位整数样本,或一个 32 位浮点样本 |
| Bit48 | 48 | 三个 16 位整数样本 |
| Bit64 | 64 | 三个 16 位样本 + 16 位 alpha |
| Bit96 | 96 | 三个 32 位样本(整数或浮点) |
| Bit128 | 128 | 四个 32 位样本(整数或浮点) |
3.2 浮点样本的编码规则
从 TiffEncoderCore.cs 的浮点分支可以提炼出几条硬性规则:
- 浮点模式只接受
Bit32 / Bit96 / Bit128三种位深,其他值会被重新计算; Bit32一律按灰度写入(BlackIsZero或WhiteIsZero),Bit96/Bit128一律按Rgb写入;- 浮点压缩仅限
None / PackBits / Deflate / Lzw,其余压缩自动降级为Deflate; - 浮点 predictor(
TiffPredictor.FloatingPoint)不支持——编码端强制写入TiffPredictor.None,解码端在 TiffDecoderOptionsParser.cs 中遇到FloatingPointpredictor 直接抛出NotSupported。
对应地,TiffDecoder.cs 在解码时会根据元数据自动选择像素类型:浮点样本解码为RgbaVector(或带预乘 alpha 的RgbaVectorP);整数 RGB 样本则按位深匹配Rgb48/Rgba64/Rgb96/Rgba128,其余默认Rgba32。测试 TiffEncoderTests.cs 中的FloatSingleComponent_RoundTripsWithoutChangingIntensity、Rgb48_RoundTrips16BitSamples等用例专门验证了这些高精度路径的往返无损性。
四、光度解释(Photometric Interpretation)支持矩阵
光度解释决定像素数值如何映射为颜色,是 TIFF 兼容性的核心。README 的能力表完整继承如下:
| 光度解释 | Encoder | Decoder | 说明 |
|---|---|---|---|
| WhiteIsZero | Y | Y | 通用实现 + 1/4/8 位优化实现 |
| BlackIsZero | Y | Y | 通用实现 + 1/4/8 位优化实现 |
| Rgb(Chunky 连续) | Y | Y | 通用实现 + Rgb888 优化实现 |
| Rgb(Planar 平面) | Y | 仅通用实现 | |
| PaletteColor | Y | Y | 仅通用实现 |
| TransparencyMask | 不支持 | ||
| Separated(TIFF 扩展) | Y | 通常是 CMYK | |
| YCbCr(TIFF 扩展) | Y | 解码支持 | |
| CieLab(TIFF 扩展) | Y | 解码支持 | |
| IccLab(TechNote 1) | 不支持 | ||
| CMYK | Y | 解码支持 | |
| Tiled Images | Y | 平铺图像解码支持 |
4.1 编码端的光度解释自动映射
编码时若不显式指定PhotometricInterpretation,TiffEncoderCore.cs 会按位深自动选择最合适的解释方式:
- Bit1:使用 CCITT 压缩时强制
WhiteIsZero(二值 CCITT 数据的标准解释),否则BlackIsZero; - Bit4 / Bit8:
PaletteColor(允许显式指定为 BlackIsZero/WhiteIsZero); - Bit16:
BlackIsZero(等价于 L16 灰度); - Bit48 / Bit64 及以上:
Rgb。
4.2 解码端的色彩类型派发
解码端 TiffDecoderOptionsParser.cs 的ParseColorType是最复杂的派发逻辑,它会根据PhotometricInterpretation + BitsPerSample + PlanarConfiguration + SampleFormat组合出TiffColorType:
- WhiteIsZero / BlackIsZero:仅接受单通道,支持 1/4/8/16/24/32 位及 32 位浮点等细分类型;
- Rgb:仅接受 3 或 4 通道且各通道位深相等,Chunky 下支持 2~32 位(含浮点)十余种细分类型,Planar 下支持 8/16/24/32 位;
- PaletteColor:需要
ColorMap标签,缺失则抛异常; - YCbCr:仅 8 位/通道,需
YCbCrCoefficients、YCbCrSubsampling、ReferenceBlackWhite等参数配合,Chunky 与 Planar 各有独立类型; - Separated(CMYK):仅 8 位/通道、4 通道,且不支持自定义
InkNames; - CieLab:3 通道,Chunky/Planar 均可。
从源码结构看(如PhotometricInterpretation目录下的解码器文件),README 所述“1/4/8 位优化实现”与“Rgb888 优化实现”对应着WhiteIsZero1Bit、WhiteIsZero4Bit、WhiteIsZero8Bit、BlackIsZero*、Rgb888等专用解码器,通用实现则覆盖 16/24/32 位等高精度场景。
五、Baseline TIFF 标签支持矩阵
TIFF 6.0 基线标签共 28 个,README 的能力表完整继承如下(Y 表示支持,空表示不支持,-表示“不适用/特殊处理”):
| 基线标签 | Encoder | Decoder | 说明 |
|---|---|---|---|
| NewSubfileType | |||
| SubfileType | |||
| ImageWidth | Y | Y | |
| ImageLength | Y | Y | |
| BitsPerSample | Y | Y | |
| Compression | Y | Y | |
| PhotometricInterpretation | Y | Y | |
| Thresholding | |||
| CellWidth | |||
| CellLength | |||
| FillOrder | Y | ||
| ImageDescription | Y | Y | |
| Make | Y | Y | |
| Model | Y | Y | |
| StripOffsets | Y | Y | |
| Orientation | - | 忽略。许多读取器都忽略此标签 | |
| SamplesPerPixel | Y | - | 当前被忽略,因为可从 BitsPerSample 数量推断 |
| RowsPerStrip | Y | Y | |
| StripByteCounts | Y | Y | |
| MinSampleValue | |||
| MaxSampleValue | |||
| XResolution | Y | Y | |
| YResolution | Y | Y | |
| PlanarConfiguration | Y | Y | 编码器只写 chunky;解码器同时支持 chunky 与 planar |
| FreeOffsets | |||
| FreeByteCounts | |||
| GrayResponseUnit | |||
| GrayResponseCurve | |||
| ResolutionUnit | Y | Y | |
| Software | Y | Y | |
| DateTime | Y | Y | |
| Artist | Y | Y | |
| HostComputer | Y | Y | |
| ColorMap | Y | Y | |
| ExtraSamples | Y | Y | 编码器写入未关联整数 alpha 或关联/未关联浮点 alpha;未指定的 alpha 数据不支持 |
| Copyright | Y | Y |
5.1 几个值得展开的“特殊”标签
- Orientation 被“忽略”:README 注明“许多读取器都忽略此标签”,ImageSharp 解码器同样不处理旋转语义,这是与主流读取器保持一致的做法;
- SamplesPerSample 被忽略:通道数完全由
BitsPerSample数组的长度推断(见 TiffBitsPerSample.cs,其Channels属性统计非零通道数),因此无需再读SamplesPerPixel; - PlanarConfiguration 的不对称:编码器固定输出 chunky(连续)布局;解码器两条路径都实现——
DecodeStripsChunky/DecodeTilesChunky与DecodeStripsPlanar/DecodeTilesPlanar并存于 TiffDecoderCore.cs; - ExtraSamples 的 alpha 语义:解码端 TiffDecoderOptionsParser.cs 只消费第一个样本:
0(未关联 alpha)与1(关联 alpha)会被保留,其他值直接丢弃;同时专门兼容了 CorelDRAW 产生的非标准2(CorelDrawUnassociatedAlphaData),按 libtiff 的处理方式映射为未关联 alpha。
5.2 编码端 IFD 写入细节
编码器在 TiffEncoderCore.cs 的WriteIfd中按 tag 升序写入条目,长度小于等于 4 字节的值直接内联,否则写入独立数据块并在 IFD 中记录偏移;TiffEncoderEntriesCollector负责把图像元数据、帧元数据、格式信息(位深、压缩、光度解释、predictor 等)收集成 IFD 条目。IFD 偏移需要对齐到 2 字节边界,编码器在必要处会补写一个填充字节。
六、扩展 TIFF 标签支持矩阵
扩展标签覆盖 TIFF 6.0 之后的规范增补(Supplement 1/2、RFC 2301 等),完整继承如下:
| 扩展标签 | Encoder | Decoder | 说明 |
|---|---|---|---|
| NewSubfileType | |||
| DocumentName | Y | Y | |
| PageName | |||
| XPosition | |||
| YPosition | |||
| T4Options | Y | ||
| T6Options | |||
| PageNumber | |||
| TransferFunction | |||
| Predictor | Y | Y | 仅 Horizontal;不用于浮点样本 |
| WhitePoint | |||
| PrimaryChromaticities | |||
| HalftoneHints | |||
| TileWidth | - | ||
| TileLength | - | ||
| TileOffsets | - | ||
| TileByteCounts | - | ||
| BadFaxLines | |||
| CleanFaxData | |||
| ConsecutiveBadFaxLines | |||
| SubIFDs | - | ||
| InkSet | Y | CMYK | |
| InkNames | - | ||
| NumberOfInks | - | ||
| DotRange | |||
| TargetPrinter | |||
| SampleFormat | Y | Y | 编码器写入 IEEE 浮点;默认无符号整数。有符号与复数样本不支持 |
| SMinSampleValue | |||
| SMaxSampleValue | |||
| TransferRange | |||
| ClipPath | |||
| XClipPathUnits | |||
| YClipPathUnits | |||
| Indexed | |||
| JPEGTables | |||
| OPIProxy | |||
| GlobalParametersIFD | |||
| ProfileType | |||
| FaxProfile | |||
| CodingMethods | |||
| VersionYear | |||
| ModeNumber | |||
| Decode | |||
| DefaultImageColor | |||
| JPEGProc | |||
| JPEGInterchangeFormat | |||
| JPEGInterchangeFormatLength | |||
| JPEGRestartInterval | |||
| JPEGLosslessPredictors | |||
| JPEGPointTransforms | |||
| JPEGQTables | |||
| JPEGDCTables | |||
| JPEGACTables | |||
| YCbCrCoefficients | Y | ||
| YCbCrSubSampling | Y | ||
| YCbCrPositioning | |||
| ReferenceBlackWhite | Y | ||
| StripRowCounts | - | - | 见 RFC 2301(Internet Fax 文件格式) |
| XMP | Y | Y | |
| ImageID | |||
| ImageLayer |
6.1 Predictor 的实际约束
Predictor是扩展标签中最常用也最易误用的一个。README 明确:仅支持 Horizontal(水平差分),且不用于浮点样本。源码进一步印证:
- TiffPredictor.cs 定义
None=1、Horizontal=2、FloatingPoint=3,其中 FloatingPoint 注释为“编码器不支持,若选择将回退为 None”; - TiffEncoderCore.cs 的
HasPredictor方法限定只有 Deflate 与 Lzw 压缩才允许启用 predictor,其余压缩一律强制None; - 解码端 TiffDecoderOptionsParser.cs 遇到
FloatingPointpredictor 直接抛NotSupported。
6.2 SampleFormat 与 StripRowCounts 的细节
- SampleFormat:解码端只接受
UnsignedInteger(1)与Float(3)两种样本格式,出现有符号整数或复数格式直接抛异常;编码端写入 IEEE 浮点或默认无符号整数(TiffSampleFormat.cs); - StripRowCounts(RFC 2301 引入的可变长 strip 计数):README 两列均为
-;源码中 TiffDecoderOptionsParser.cs 检测到该标签存在时直接抛出NotSupported(“不支持可变大小 strip”); - Tile 系列标签:README 表格中 Decoder 列为
-,但从源码看,解码器实际上通过 TiffDecoderCore.cs 的DecodeImageWithTiles/DecodeTilesChunky/DecodeTilesPlanar完整消费了TileWidth、TileLength、TileOffsets、TileByteCounts。README 的-更应理解为“这些标签由解码流程内部消费、不对外暴露”;并且VerifyRequiredFieldsArePresent会强制校验 tile 场景下这些字段的完整性(对 planar 配置还要求TileOffsets必须存在,缺失即抛异常)。
七、私有 TIFF 标签支持矩阵
私有标签由厂商与行业组织定义(如 Adobe DNG、GeoTIFF、Photoshop、GPS 等),ImageSharp 的支持非常克制,完整继承如下:
| 私有标签 | Encoder | Decoder | 说明 |
|---|---|---|---|
| Wang Annotation | |||
| MD FileTag | |||
| MD ScalePixel | |||
| MD ColorTable | |||
| MD LabName | |||
| MD SampleInfo | |||
| MD PrepDate | |||
| MD PrepTime | |||
| MD FileUnits | |||
| ModelPixelScaleTag | |||
| IPTC | Y | Y | |
| INGR Packet Data Tag | |||
| INGR Flag Registers | |||
| IrasB Transformation Matrix | |||
| ModelTiepointTag | |||
| ModelTransformationTag | |||
| Photoshop | |||
| Exif IFD | - | 0x8769 SubExif | |
| ICC Profile | Y | Y | |
| GeoKeyDirectoryTag | |||
| GeoDoubleParamsTag | |||
| GeoAsciiParamsTag | |||
| GPS IFD | |||
| HylaFAX FaxRecvParams | |||
| HylaFAX FaxSubAddress | |||
| HylaFAX FaxRecvTime | |||
| ImageSourceData | |||
| Interoperability IFD | |||
| GDAL_METADATA | |||
| GDAL_NODATA | |||
| Oce Scanjob Description | |||
| Oce Application Selector | |||
| Oce Identification Number | |||
| Oce ImageLogic Characteristics | |||
| DNGVersion | |||
| DNGBackwardVersion | |||
| UniqueCameraModel | |||
| LocalizedCameraModel | |||
| CFAPlaneColor | |||
| CFALayout | |||
| LinearizationTable | |||
| BlackLevelRepeatDim | |||
| BlackLevel | |||
| BlackLevelDeltaH | |||
| BlackLevelDeltaV | |||
| WhiteLevel | |||
| DefaultScale | |||
| DefaultCropOrigin | |||
| DefaultCropSize | |||
| ColorMatrix1 | |||
| ColorMatrix2 | |||
| CameraCalibration1 | |||
| CameraCalibration2 | |||
| ReductionMatrix1 | |||
| ReductionMatrix2 | |||
| AnalogBalance | |||
| AsShotNeutral | |||
| AsShotWhiteXY | |||
| BaselineExposure | |||
| BaselineNoise | |||
| BaselineSharpness | |||
| BayerGreenSplit | |||
| LinearResponseLimit | |||
| CameraSerialNumber | |||
| LensInfo | |||
| ChromaBlurRadius | |||
| AntiAliasStrength | |||
| DNGPrivateData | |||
| MakerNoteSafety | |||
| CalibrationIlluminant1 | |||
| CalibrationIlluminant2 | |||
| BestQualityScale | |||
| Alias Layer Metadata |
从矩阵可以看出,私有标签中 ImageSharp 仅实现了IPTC与ICC Profile的读写,以及Exif IFD的部分解码(-表示有内部处理但非完整支持)。大量 DNG 相机原始数据标签(DNGVersion、ColorMatrix、BlackLevel 等)均不支持,这一点对处理数码相机 RAW/DNG 文件的开发者尤其重要——DNG 的完整解析不在 ImageSharp TIFF 编解码器的能力范围内。
八、编码实战:TiffEncoder 选项与使用示例
编码入口是SaveAsTiff系列扩展方法(由 ImageExtensions.Save.cs 生成),支持路径与流两种目标、同步与异步两种调用方式:
using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using Image<Rgba32> image = Image.Load<Rgba32>("input.png"); // 基础用法:使用默认 TiffEncoder image.SaveAsTiff("output.tif"); // 高级用法:显式配置编码参数 TiffEncoder encoder = new() { BitsPerPixel = TiffBitsPerPixel.Bit24, // 24 bpp RGB,默认值 Compression = TiffCompression.Deflate, // 压缩算法 CompressionLevel = DeflateCompressionLevel.DefaultCompression, // Deflate 级别 1-9 PhotometricInterpretation = TiffPhotometricInterpretation.Rgb, HorizontalPredictor = TiffPredictor.Horizontal, // 仅 Deflate/Lzw 时生效 }; image.SaveAsTiff("output-deflate.tif", encoder);8.1 编码器选项全解
TiffEncoder.cs 继承自QuantizingImageEncoder,构造时默认使用KnownQuantizers.Hexadecatree量化器(用于调色板类输出),其公开选项包括:
| 选项 | 类型 | 说明 |
|---|---|---|
BitsPerPixel | TiffBitsPerPixel? | 输出位深,不指定时取自帧元数据,再回退到默认Bit24 |
SampleFormat | TiffSampleFormat? | 样本格式;浮点样本固定 32 位/分量 |
Compression | TiffCompression? | 压缩算法,默认None |
CompressionLevel | DeflateCompressionLevel? | Deflate 压缩级别 1-9,默认DefaultCompression |
PhotometricInterpretation | TiffPhotometricInterpretation? | RGB、调色板、灰度或双色;不指定或编码器不支持时用 RGB |
HorizontalPredictor | TiffPredictor? | 水平预测,可提升 Deflate/Lzw 的压缩比 |
选项优先级遵循“EncoderOptions > 帧元数据 > 默认值”的规则(见 TiffEncoderCore.cs 中Encode方法),而SanitizeAndSetEncoderOptions会在写入前对组合做一致性修正(详见 2.3 与 4.1 节)。
8.2 多帧 TIFF 编码
由于每个ImageFrame会写成一个 IFD,多帧编码就是把多个帧放进同一个Image再保存:
using Image<Rgba32> image = new(100, 100); // 追加帧(尺寸必须与根帧一致) image.Frames.AddFrame(image.Frames.RootFrame.Clone()); image.Frames.AddFrame(image.Frames.RootFrame.Clone()); image.SaveAsTiff("multiframe.tif", new TiffEncoder { Compression = TiffCompression.Lzw });对应测试 TiffEncoderMultiframeTests.cs 覆盖了多帧编码、添加/移除帧后重编码、以及“不同尺寸变体多帧”不支持等场景。
九、解码实战:流程、校验与限制
解码入口为Image.Load/Image.LoadAsync/Image.Identify。TIFF 解码的核心链路在 TiffDecoderCore.cs:
DirectoryReader读取文件头与全部 IFD 目录(Ifd 目录),解析字节序与 BigTIFF 标记;- 每个 IFD 由
TiffFrameMetadata.Parse解析出位深、样本格式、压缩、光度解释、predictor、ink set 等字段(TiffFrameMetadata.cs); VerifyAndParse对压缩与色彩类型做合法性校验与派发(TiffDecoderOptionsParser.cs):FillOrder为LeastSignificantBitFirst时,只允许 1 bpp 双色 TIFF;- 浮点 predictor 不支持;
SampleFormat仅允许无符号整数与浮点;YCbCrSubsampling必须恰有 2 个值且垂直子采样不超过水平子采样;StripRowCounts(可变长 strip)不支持;
- 校验 tile/strip 必需字段(
StripOffsets/StripByteCounts或TileOffsets/TileWidth/TileLength等)是否齐全; - 依据
PhotometricInterpretation + BitsPerSample + PlanarConfiguration + SampleFormat派发颜色解码器(ParseColorType),依据压缩标签派发解压器(ParseCompression); - 按 chunky/planar × strip/tile 四种组合之一逐块解压、逐块解码;
- RGB 兼容类型(
PaletteColor到Rgba32323232Planar区间)在帧级执行 ICC Profile 颜色转换;YCbCr、CMYK、Lab 等类型不做 ICC 转换(TiffDecoderCore.csDecodeFrame中有明确注释说明)。
9.1 几个值得注意的解码限制
- 多帧尺寸限制:所有帧必须与根帧等宽等高,否则抛
NotSupported; - 超大 strip 限制:未压缩 strip 超过
Int32.MaxValue字节时,NoneTiffCompression路径会退化为逐行读取解码;压缩 strip 超过该阈值则直接抛NotSupported; - Old Jpeg 兼容细节:需要
JPEGInterchangeFormat提供 SOI 标记偏移,且不支持 planar 配置;YCbCr 光度解释的 Old Jpeg 会被改写为 RGB 交给 JPEG 解码器处理; - Jpeg 压缩细节:无论文件中
YCbCrSubsampling写什么,解码时统一重置为[1, 1](JPEG 解码器自行处理子采样);缺少JPEGTables的 YCbCr JPEG 同样改写为 RGB 路径(源码注释关联了对应 issue)。
十、测试验证与进一步阅读
TIFF 模块的测试集中在 tests/ImageSharp.Tests/Formats/Tiff:
- TiffDecoderTests.cs:多帧解码、压缩格式往返、tile 图像解码等;
- TiffEncoderTests.cs:高精度 16 位(
Rgb48/Rgba64)、浮点(FloatSingleComponent)、各种压缩与 predictor 组合的往返测试; - TiffEncoderMultiframeTests.cs:多帧编码与限制验证;
- Compression 子目录:None/PackBits/Lzw/Deflate/CCITT 各解压器的单元测试;
- PhotometricInterpretation 子目录:WhiteIsZero/BlackIsZero/Palette/RGB/RGBA/Planar 颜色解码器测试;
BigTiffDecoderTests.cs与BigTiffMetadataTests.cs:BigTIFF(魔数 43)解码验证。
如需深入底层,建议按此顺序阅读源码:TiffEncoder.cs → TiffEncoderCore.cs → TiffDecoder.cs → TiffDecoderCore.cs → TiffDecoderOptionsParser.cs → Compression 目录。README 中引用的 TIFF 6.0 规范、Supplement 1/2/3、TIFF-F/FX(RFC 2301)、CCITT T.4/T.6、EXIF 2.3、XMP 等参考资料原文,可在本仓库的 TIFF 模块目录 中查阅对应规范文档(仓库附带 TIFF-v6.pdf 等 PDF 原文与 T-REC-T.4/T.6 标准文本)。
十一、总结:选择 TIFF 参数时的决策要点
综合 README 能力矩阵与源码实现,在实际项目中可以按以下清单快速决策:
- 通用彩色照片/图形:
Rgb+Bit24,压缩可选None(最快)、PackBits(中等)、Lzw或Deflate(高压缩比,可配HorizontalPredictor); - 传真/文档扫描(二值):
WhiteIsZero+Bit1+CcittGroup3Fax/CcittGroup4Fax/Ccitt1D; - 高精度影像(医学、遥感):整数走
Bit48/64/96/128,浮点走SampleFormat.Float+Bit32/96/128,压缩限None/PackBits/Lzw/Deflate,且不要启用 predictor; - 兼容性红线:解码可放宽(支持 planar、tile、CMYK、YCbCr、CieLab、WebP 压缩、Old Jpeg 等),编码保守(仅 chunky、仅 WhiteIsZero/BlackIsZero/Rgb/PaletteColor、不支持 tiled 输出、DNG 系列私有标签全部不支持);
- 多帧:所有帧必须同尺寸,写多帧用
Frames.AddFrame,读多帧按Frames索引访问。
掌握这张“支持矩阵”与背后的降级规则,就能在 ImageSharp 生态中安全、高效地处理绝大多数 TIFF 场景。
- 图像处理
- 图形学
【免费下载链接】ImageSharp
A modern, cross-platform, 2D Graphics library for .NET
相关推荐
极速压缩新体验:Rufus工具Zstandard格式支持深度解析
极速压缩新体验:Rufus工具Zstandard格式支持深度解析 在日常使用USB设备时,你是否遇到过系统镜像体积过大导致传输缓慢的问题?是否因压缩格式不兼容而
桌面应用开发工具PeaZip 免费压缩文件管理器深度解析:200+ 格式支持、安全特性与源码构建指南
PeaZip 免费压缩文件管理器深度解析:200+ 格式支持、安全特性与源码构建指南 PeaZip 是一个面向 Linux、macOS 和 Windows 的免
桌面应用Rufus压缩解压:多种压缩格式支持
Rufus压缩解压:多种压缩格式支持 概述 Rufus作为一款功能强大的USB启动盘制作工具,其内置的BLED(Base Library for Easy De
桌面应用开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考