pub enum EncodePixelFormat<'a> {
Bilevel {
pixels: &'a [u8],
},
TransparencyMask {
pixels: &'a [u8],
},
Gray8 {
pixels: &'a [u8],
},
Gray16Le {
pixels: &'a [u8],
},
Rgb24 {
pixels: &'a [u8],
},
Palette8 {
indices: &'a [u8],
palette: &'a [RgbColor],
},
CieLab8 {
pixels: &'a [u8],
},
CieLabL8 {
pixels: &'a [u8],
},
Cmyk32 {
pixels: &'a [u8],
},
YCbCr24 {
pixels: &'a [u8],
},
YCbCrSubsampled24 {
pixels: &'a [u8],
subsampling: (u16, u16),
},
}Expand description
Pixel layouts the encoder knows how to write.
Variants§
Bilevel
1-bit bilevel (1 sample per pixel, MSB-first byte packing).
pixels must contain ceil(width / 8) * height bytes. The
bit convention follows §10 / §11: bit value 0 = white,
1 = black. The WhiteIsZero (default) PhotometricInterpretation
reads those bits straight through; combine with
BlackIsZero by inverting the input first. Required for
CCITT compression schemes (TiffCompression::CcittRle,
TiffCompression::CcittT4OneD).
TransparencyMask
1-bit transparency mask (TIFF 6.0 §“PhotometricInterpretation”
value 4, page 37). 1 sample per pixel, MSB-first byte packing —
the on-disk layout is identical to Self::Bilevel, but the
encoder writes PhotometricInterpretation = 4 (Transparency
Mask) and sets bit 2 of NewSubfileType (tag 254), which the
spec defines as “1 if the image defines a transparency mask
for another image in this TIFF file. The PhotometricInterpretation
value must be 4”. pixels must contain ceil(width / 8) * height
bytes; the bit convention is fixed by spec (§Photometric-
Interpretation page 37: “The 1-bits define the interior of the
region; the 0-bits define the exterior of the region”). The
spec recommends PackBits but does not forbid the other
compressions; this encoder accepts None / PackBits / LZW /
Deflate / Zstd / CCITT-MH / CCITT-T.4-1D, the same compressor
set Self::Bilevel accepts.
Gray8
8-bit greyscale (BlackIsZero, 1 sample per pixel).
pixels.len() == width * height.
Gray16Le
16-bit greyscale (BlackIsZero, 1 sample per pixel,
little-endian on disk). pixels.len() == width * height * 2.
Rgb24
8-bit packed RGB. pixels.len() == width * height * 3.
Palette8
8-bit indexed palette. indices.len() == width * height,
palette.len() <= 256 (any extras are ignored).
CieLab8
8-bit chunky 1976 CIE L*a*b* (PhotometricInterpretation = 8,
SamplesPerPixel = 3, BitsPerSample = 8 / 8 / 8) per TIFF 6.0
§23 “CIE L*a*b* Images” (page 110). pixels is row-major
interleaved (L*, a*, b*) triples — pixels.len() == width * height * 3. The on-disk bit interpretation is fixed by §23: L*
is unsigned 0..255 mapping linearly to the perceptual 0..100
lightness scale, and a* / b* are two’s-complement signed bytes
in -128..127 representing the red/green and yellow/blue chrominance
channels (§23: “The a* and b* ranges will be represented as
signed 8 bit values”). The encoder writes these bytes through to
the strip / tile / plane payload verbatim — the caller owns the
colourimetric encoding, exactly as the decoder takes them
verbatim back off disk. Compressors accepted: None / PackBits /
LZW / Deflate / Zstd (the byte-aligned, photometric-agnostic set
the other multi-bit photometric paths use); CCITT is bilevel-only
per §10 / §11 and rejected here. Predictor = 2 (TIFF 6.0 §14
horizontal differencing, per-component on chunky multi-sample
data) composes; PlanarConfiguration = 2 (separate L* / a* /
b* component planes) composes (§14 says differencing in planar
“works the same as it does for grayscale data” — each plane is
differenced independently with an offset of one sample); tiled
layout (§15) composes for both chunky and planar.
CieLabL8
8-bit 1-sample CIE L* monochrome (PhotometricInterpretation = 8, SamplesPerPixel = 1, BitsPerSample = 8) per TIFF 6.0 §23
page 110 “Usage of other Fields”: “SamplesPerPixel - ExtraSamples:
3 for L*a*b*, 1 implies L* only, for monochrome data”.
pixels.len() == width * height. Each byte is L* on the
0..255-maps-to-0..100 scale. As with Self::CieLab8, the bytes
are written through verbatim. Compressors accepted: None /
PackBits / LZW / Deflate / Zstd. Predictor = 2 composes (single-sample
chunky path with offset = 1); PlanarConfiguration = 2 is
rejected per §“PlanarConfiguration” “irrelevant” for
SamplesPerPixel = 1; tiled layout composes.
Cmyk32
8-bit chunky CMYK per TIFF 6.0 §16 “CMYK Images” (page 69):
PhotometricInterpretation = 5, SamplesPerPixel = 4,
BitsPerSample = 8, 8, 8, 8. pixels is row-major interleaved
cyan, magenta, yellow, black quadruples — its length must equal
width * height * 4. The on-disk bit interpretation is fixed
by §16: each component is the amount of that ink at the pixel
on the canonical InkSet = 1 CMYK ordering, where 0 means no
ink and 255 means full coverage (§16 InkSet page 70: “Usually,
a value of 0 represents 0 % ink coverage and a value of 255
represents 100 % ink coverage for that component”). The encoder
writes the caller-supplied bytes through to the strip, tile, or
plane payload verbatim — the caller owns the colourimetric
encoding, exactly as the decoder takes them verbatim off disk
and collapses them into additive RGB by the §16 “amount of dye”
convention. Alongside the §16-required Baseline tags
SamplesPerPixel, BitsPerSample, and PhotometricInterpretation,
the encoder also writes the two optional §16 separated-image
tags InkSet = 1 (tag 332, the “CMYK” InkSet value) and
NumberOfInks = 4 (tag 334). Both match their §16 defaults,
but emitting them explicitly makes the written file
self-describing to readers that key on those fields. Compressors
accepted: the byte-aligned, photometric-agnostic set the other
multi-bit photometric paths use (None, PackBits, LZW, Deflate,
Zstd).
CCITT is bilevel-only per §10 and §11 and rejected here.
Predictor = 2 (TIFF 6.0 §14 horizontal differencing,
per-component on chunky multi-sample data with offset
SamplesPerPixel = 4) composes. PlanarConfiguration = 2
composes too: each of the four component planes is written as
its own strip per §“PlanarConfiguration”, and the §14 predictor
differences each plane independently with an offset of one
sample (§14: “Differencing works the same as it does for
grayscale data” when PlanarConfiguration is 2). Tiled layout
(§15) composes for both chunky and planar.
YCbCr24
8-bit chunky (Y, Cb, Cr) per TIFF 6.0 §21 “YCbCr Images”
(page 89), PhotometricInterpretation = 6, SamplesPerPixel = 3,
BitsPerSample = 8, 8, 8 at the §21 chroma-subsampling-1:1
(YCbCrSubSampling = [1, 1]) layout. pixels is row-major,
interleaved (Y, Cb, Cr) triples — pixels.len() == width * height * 3. At YCbCrSubSampling = [1, 1] the §21 “Ordering
of Component Samples” rule for PlanarConfiguration = 1
collapses to one Y sample per data unit (1×1 luminance grid)
followed by one Cb sample and one Cr sample — i.e. the bytes
are written through to the strip / tile payload exactly as the
caller supplied them, with no re-tiling. The caller owns the
RGB→YCbCr conversion; the encoder transports the supplied bytes
verbatim, matching the decoder’s build_rgb24_from_ycbcr path
which treats §21’s stated chunky data-unit layout as fact.
Alongside the §21 / Baseline tags (SamplesPerPixel,
BitsPerSample, PhotometricInterpretation), the encoder
emits the three §21 fields with their full-range
no-headroom/no-footroom values fixed by §20
“ReferenceBlackWhite” page 87 (“Useful ReferenceBlackWhite
values for YCbCr images are: [0, 255, 128, 255, 128, 255]
no headroom/footroom”):
YCbCrSubSampling = [1, 1](tag 530), the chunky-444 layout the encoder writes;YCbCrPositioning = 1(tag 531), §21 “centered” — the tag’s documented default and the only positioning that matters when both subsampling factors are 1;ReferenceBlackWhite = [0/1, 255/1, 128/1, 255/1, 128/1, 255/1](tag 532), the §20 page 87 “no headroom/footroom” reference coding range — §21 says this field “must be used explicitly” for Class Y images.
The §21 default YCbCrCoefficients (tag 529, CCIR
Recommendation 601-1’s {299/1000, 587/1000, 114/1000}
luma weights) are omitted: §21 explicitly defaults to those
values and the decoder’s ycbcr_to_rgb matrix is the Q16
inverse of the same {0.299, 0.587, 0.114} coefficients, so
emitting the tag would just restate the spec default. Future
rounds add chroma-subsampled (YCbCrSubSampling = [2, 1],
[2, 2], etc.) and PlanarConfiguration = 2 writers; the
1:1 chunky surface here is the minimal §21-conformant slice.
Compressors accepted: None / PackBits / LZW / Deflate / Zstd
(the byte-aligned photometric-agnostic set the other multi-bit
photometric paths use). CCITT is bilevel-only per §10 / §11
and rejected here. Predictor = 2,
PlanarConfiguration = 2, and tiled layout are deferred —
the §21 sample-ordering rule changes shape under non-1:1
subsampling, so the encoder pins those flags off in this
initial YCbCr round and rejects the combinations with a
precise error rather than emitting something the decoder
might mis-tile.
YCbCrSubsampled24
8-bit chroma-subsampled (Y, Cb, Cr) per TIFF 6.0 §21 “YCbCr
Images” (pages 90–94), PhotometricInterpretation = 6,
SamplesPerPixel = 3, BitsPerSample = [8, 8, 8],
PlanarConfiguration = 1 (chunky). pixels is the
full-resolution row-major interleaved (Y, Cb, Cr) raster
(pixels.len() == width * height * 3); the encoder performs the
chroma decimation and the §21 “Ordering of Component Samples”
data-unit packing.
subsampling is (ChromaSubsampleHoriz, ChromaSubsampleVert)
from the §21 YCbCrSubSampling field (tag 530). §21 page 90
restricts each factor to 1, 2, or 4 and requires
YCbCrSubsampleVert <= YCbCrSubsampleHoriz, so the supported
set is (1,1), (2,1), (2,2), (4,1), (4,2) — the same
configurations the decoder’s full-resolution chroma-splat path
reverses. (1,1) is accepted here too (it is the trivial
no-subsampling case) and behaves identically to
EncodePixelFormat::YCbCr24.
§21 page 90: “ImageWidth and ImageLength are constrained to be
integer multiples of YCbCrSubsampleHoriz and
YCbCrSubsampleVert respectively.” The encoder rejects a
width/height that is not a multiple of the corresponding
factor with a precise error.
Data-unit layout (§21 page 93): a data unit is
ChromaSubsampleVert rows of ChromaSubsampleHoriz Y samples
(row-major), then one Cb sample, then one Cr sample. For
(sh, sv) = (4, 2) the worked example on page 94 is
Y00, Y01, Y02, Y03, Y10, Y11, Y12, Y13, Cb00, Cr00, Y04, ….
The encoder visits data units left-to-right then top-to-bottom
and emits exactly this byte order, which is what the decoder’s
build_rgb24_from_ycbcr block walker consumes.
Chroma decimation: each sh × sv block’s Cb / Cr is the rounded
arithmetic mean of the block’s full-resolution Cb / Cr samples
(a box filter — the symmetric even-tap filter §21 page 92 calls
out for YCbCrPositioning = 1 centered subsampling). The Y
samples are transported full-resolution and unchanged, so the
luminance round-trips bit-exact; chroma round-trips exactly only
when it is already constant across each block (decode splats one
Cb / Cr back over the whole block).
The same §21 / §20 fields the 1:1 path emits are written, with
tag 530 carrying the actual [sh, sv]. YCbCrPositioning = 1
(centered) is emitted to match the box-filter decimation.
Compressors accepted: None / PackBits / LZW / Deflate / Zstd.
CCITT is bilevel-only and rejected. Predictor = 2,
PlanarConfiguration = 2, and tiled layout are rejected with a
precise error (the data-unit packing is single-strip chunky
only in this round).
Trait Implementations§
Source§impl<'a> Clone for EncodePixelFormat<'a>
impl<'a> Clone for EncodePixelFormat<'a>
Source§fn clone(&self) -> EncodePixelFormat<'a>
fn clone(&self) -> EncodePixelFormat<'a>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more