Enum Class PixelFormat
- All Implemented Interfaces:
Serializable,Comparable<PixelFormat>,Constable
VideoFrame: how many planes it has, what each one
holds, and how each plane's sample grid is derived from the frame size. Every producer and
every consumer computes plane geometry from here, so an odd frame size cannot be rounded one
way by the code that fills a plane and another way by the code that reads it; that mismatch
renders as a coloured stripe down one edge, nowhere near whichever side rounded wrong.
Layout only. Nothing here says what the sample values mean: matrix and range live
on VideoColor, and the same I420 buffer is BT.601 or BT.709 depending on it.
Plane 0 is always full-resolution luma. Plane sizes are counted in samples of that plane's own grid; bytes are a separate question, because NV12 stores two components per chroma sample and a 10-bit sample occupies two bytes whatever the layout.
A code and its storage word are not the same number. Most layouts store a code
right-justified, so the two coincide; P010 stores it shifted up to the top of a 16-bit
word, which is what a hardware decoder produces. componentAt(java.nio.ByteBuffer, int) and putComponent(java.nio.ByteBuffer, int, int)
are the only place that difference is spelled, so a consumer that reads samples through them is
right for both and one that reads the bytes itself is right for one of them; see
codeShift().
Bit depth is not a detail of the samples, it is a property of the arithmetic. Every
size here is derived through bytesPerSample(int), so a consumer that comes through these
methods is depth-correct and one that multiplies a width by a component count is not. The
decode matrix is depth-dependent too, which is why VideoColor's coefficient accessors
take a bit depth rather than answering for eight bits and hoping.
-
Nested Class Summary
Nested classes/interfaces inherited from class java.lang.Enum
Enum.EnumDesc<E extends Enum<E>> -
Enum Constant Summary
Enum ConstantsEnum ConstantDescription8-bit 4:2:0 in three planes: Y, then Cb, then Cr, each planar, each chroma plane half resolution in both directions.10-bit 4:2:0 in three planes, laid out exactly asI420but with a sample occupying two bytes.8-bit 4:4:4 in three planes: Y, Cb, Cr, all full resolution.8-bit 4:2:0 in two planes: Y, then Cb and Cr interleaved in a single plane whose sample n is the byte pair (Cb, Cr), in that order.10-bit 4:2:0 in two planes:NV12's geometry with ten bits per component. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intLargest frame width or height any geometry method accepts, in pixels. -
Method Summary
Modifier and TypeMethodDescriptionintalignedStride(int plane, int frameWidth, int alignBytes) The plane's byte width rounded up to a multiple ofalignBytes: what a producer that allocates its own planes should use as its stride, so rows start where wide loads and GPU uploads want them.intbitDepth()intbytesPerSample(int plane) Bytes occupied by one sample ofplane.intintintHow far a code sits above the bottom of its storage word: 0 for every right-justified layout and 6 forP010, whose ten bits occupy the top of a 16-bit word.intcomponentAt(ByteBuffer plane, int byteIndex) Reads one component out of a plane's bytes, at an absolute byte index and without moving the buffer's position.intcomponentsPerSample(int plane) intmaxCode()The largest sample code this layout can carry: 255 at eight bits, 1023 at ten.longminPlaneBytes(int plane, int frameWidth, int frameHeight, int strideBytes) Smallest buffer that can holdplaneat this size and stride: every row but the last occupies a fullstrideBytes, and the last row needs only its byte width.intplaneByteWidth(int plane, int frameWidth) Bytes of picture in one row ofplane: the part of a row a consumer may read.intintplaneHeight(int plane, int frameHeight) intplaneWidth(int plane, int frameWidth) Samples across one row ofplanefor a frameframeWidthpixels wide.voidputComponent(ByteBuffer plane, int byteIndex, int code) Writes one component into a plane's bytes, at an absolute byte index and without moving the buffer's position: the inverse ofcomponentAt(java.nio.ByteBuffer, int), and the only place a producer needs to know how wide a sample is.static PixelFormatReturns the enum constant of this class with the specified name.static PixelFormat[]values()Returns an array containing the constants of this enum class, in the order they are declared.
-
Enum Constant Details
-
I420
8-bit 4:2:0 in three planes: Y, then Cb, then Cr, each planar, each chroma plane half resolution in both directions. What a software H.264 / VP9 / AV1 decode hands back. -
NV12
8-bit 4:2:0 in two planes: Y, then Cb and Cr interleaved in a single plane whose sample n is the byte pair (Cb, Cr), in that order. That plane is 2 bytes per sample, so its byte width is the frame width rounded up to an even number: for an odd width it is one byte wider than the luma plane's, which is the arithmetic that defeats code assuming "chroma rows are smaller". What hardware decoders and capture devices produce. -
I444
8-bit 4:4:4 in three planes: Y, Cb, Cr, all full resolution. Every plane's geometry equals the frame's, so chroma subsampling never applies. Screen capture and lossless intermediates. -
I420_10LE
10-bit 4:2:0 in three planes, laid out exactly asI420but with a sample occupying two bytes. What HEVC Main 10, VP9 Profile 2 and AV1 Main 10 hand back, and by a wide margin the commonest way a file carries more than eight bits.A sample is a code in
[0..1023]stored right-justified in a little-endian 16-bit word: low byte first, and the top six bits zero. It is not a normalized 16-bit value, so reading one as though the plane were 16-bit content gives a picture 64 times too dark, and normalizing by 65535 instead of bymaxCode()gives one 64 times too bright. Both look like a broken shader rather than like an off-by-a-factor. -
I444_10LE
-
P010
10-bit 4:2:0 in two planes:NV12's geometry with ten bits per component. What every hardware decoder produces for 10-bit content, and the only layout here whose samples a producer other than this repository's own decoders creates.Its codes are left-justified, which is the whole reason it is a layout of its own rather than a wider NV12: the ten bits live in the top ten of the 16-bit word and the bottom six are zero, where
I420_10LEandI444_10LEput them in the bottom ten. So the same 16-bit word means two different codes depending on the layout, and reading a P010 word as though it were an I420_10LE one is a picture 64 times too bright. Nothing in the geometry says so (the sizes, the strides and the plane count are NV12's doubled), which is why the justification is carried oncodeShift()and applied bycomponentAt(java.nio.ByteBuffer, int)andputComponent(java.nio.ByteBuffer, int, int)rather than left to each reader.
-
-
Field Details
-
MAX_DIMENSION
public static final int MAX_DIMENSIONLargest frame width or height any geometry method accepts, in pixels. Above every coded size in use, and low enough that the rounding-up arithmetic inside those methods cannot wrap anint, which is the reason this is enforced rather than assumed.- See Also:
-
-
Method Details
-
values
Returns an array containing the constants of this enum class, in the order they are declared.- Returns:
- an array containing the constants of this enum class, in the order they are declared
-
valueOf
Returns the enum constant of this class with the specified name. The string must match exactly an identifier used to declare an enum constant in this class. (Extraneous whitespace characters are not permitted.)- Parameters:
name- the name of the enum constant to be returned.- Returns:
- the enum constant with the specified name
- Throws:
IllegalArgumentException- if this enum class has no constant with the specified nameNullPointerException- if the argument is null
-
planeCount
public int planeCount()- Returns:
- separately addressed planes: 3 for I420 and I444, 2 for NV12
-
bitDepth
public int bitDepth()- Returns:
- valid bits per component: 8 or 10
-
maxCode
public int maxCode()The largest sample code this layout can carry: 255 at eight bits, 1023 at ten. It is the divisor that turns a code into a normalized value, and it is not the largest value the sample's storage can hold: a 10-bit sample lives in a 16-bit word whose upper six bits are always zero, so normalizing by 65535 is the mistake this accessor exists to prevent.- Returns:
(1 << bitDepth()) - 1
-
codeShift
public int codeShift()How far a code sits above the bottom of its storage word: 0 for every right-justified layout and 6 forP010, whose ten bits occupy the top of a 16-bit word.Two consumers need it and no third one should.
componentAt(java.nio.ByteBuffer, int)andputComponent(java.nio.ByteBuffer, int, int)apply it, so every reader that comes through them works in codes and never meets the shift at all. A device sampler does not come through them: it normalizes by the texel's width, so a sampled value must be scaled by((1 << storageBits) - 1) >> codeShift()rather than by the storage maximum, and using the storage maximum is a picture 64 times too dark.- Returns:
- bits the code is shifted left inside its storage word; 0 unless the layout says otherwise
-
chromaShiftX
public int chromaShiftX()- Returns:
- log2 of horizontal chroma subsampling: 1 for 4:2:0, 0 for 4:4:4; luma is never subsampled
-
chromaShiftY
public int chromaShiftY()- Returns:
- log2 of vertical chroma subsampling: 1 for 4:2:0, 0 for 4:4:4
-
componentsPerSample
public int componentsPerSample(int plane) - Returns:
- components stored per sample of
plane: 1 everywhere except NV12's plane 1, which is 2 (Cb then Cr, adjacent) - Throws:
IndexOutOfBoundsException- ifplaneis negative or at leastplaneCount()
-
bytesPerSample
public int bytesPerSample(int plane) Bytes occupied by one sample ofplane. The only place bit depth turns into bytes, and the reason to come through it is that the two things it multiplies together stopped agreeing once a sample could be ten bits wide: a 10-bit luma sample is two bytes and one component, where NV12's chroma sample is two bytes and two components. Anything that derives a channel count from this number is right for one of those and wrong for the other.- Returns:
componentsPerSample(int)times the bytes one component occupies- Throws:
IndexOutOfBoundsException- ifplaneis not a plane of this format
-
componentAt
Reads one component out of a plane's bytes, at an absolute byte index and without moving the buffer's position. The only place this project spells the byte order of a wide sample: a 10-bit component is little-endian, so a reader that assembles it the other way round turns a dark grey into a bright noise pattern, everywhere at once, in a way that looks like the plane pointer being wrong. It is also the only placecodeShift()is applied, so what comes back is a code and never a storage word.- Parameters:
plane- the plane's bytes, at leastbyteIndex + bytesPerSamplelongbyteIndex- byte offset of the component withinplane- Returns:
- the code, in
[0..maxCode()] - Throws:
IndexOutOfBoundsException- if the component does not lie inside the bufferNullPointerException- ifplaneis null
-
putComponent
Writes one component into a plane's bytes, at an absolute byte index and without moving the buffer's position: the inverse ofcomponentAt(java.nio.ByteBuffer, int), and the only place a producer needs to know how wide a sample is.- Parameters:
code- a value in[0..maxCode()]; higher bits are dropped rather than checked, because this runs once per sample of every picture- Throws:
IndexOutOfBoundsException- if the component does not lie inside the bufferReadOnlyBufferException- ifplaneis a read-only viewNullPointerException- ifplaneis null
-
planeWidth
public int planeWidth(int plane, int frameWidth) Samples across one row ofplanefor a frameframeWidthpixels wide. Chroma rounds up: a 5-pixel-wide 4:2:0 frame has 3 chroma samples per row, not 2, because the last luma column still needs a chroma sample to pair with. This is a sample count, not a byte count.- Throws:
IllegalArgumentException- ifframeWidthis outside[1..MAX_DIMENSION]IndexOutOfBoundsException- ifplaneis not a plane of this format
-
planeHeight
public int planeHeight(int plane, int frameHeight) - Returns:
- rows in
planefor a frameframeHeightpixels tall, chroma rounding up - Throws:
IllegalArgumentException- ifframeHeightis outside[1..MAX_DIMENSION]IndexOutOfBoundsException- ifplaneis not a plane of this format
-
planeByteWidth
public int planeByteWidth(int plane, int frameWidth) Bytes of picture in one row ofplane: the part of a row a consumer may read. A row occupies a stride of bytes, of which only these carry samples; the rest is padding whose contents are undefined and which must never be uploaded, compared or hashed.- Returns:
planeWidth(int, int)timesbytesPerSample(int)- Throws:
IllegalArgumentException- ifframeWidthis outside[1..MAX_DIMENSION]IndexOutOfBoundsException- ifplaneis not a plane of this format
-
minPlaneBytes
public long minPlaneBytes(int plane, int frameWidth, int frameHeight, int strideBytes) Smallest buffer that can holdplaneat this size and stride: every row but the last occupies a fullstrideBytes, and the last row needs only its byte width. Trailing padding after the final sample is deliberately not required; a producer whose plane ends exactly at the end of its last row is handing over a valid buffer, and demandingstride × rowswould force a copy on the one path that exists to avoid copies.The consequence, which is the wrong edit this text exists to prevent: a plane may not be bulk-copied as one
stride × rowsblock. Copy row by row.Returned as a
longbecause at the maximum dimension a single plane exceeds the range of anint, and a size check that overflowed to a negative number would accept every buffer handed to it.- Parameters:
strideBytes- distance in bytes between the starts of consecutive rows- Throws:
IllegalArgumentException- ifstrideBytesis below the plane's byte width, or a dimension is outside[1..MAX_DIMENSION]IndexOutOfBoundsException- ifplaneis not a plane of this format
-
alignedStride
public int alignedStride(int plane, int frameWidth, int alignBytes) The plane's byte width rounded up to a multiple ofalignBytes: what a producer that allocates its own planes should use as its stride, so rows start where wide loads and GPU uploads want them.- Parameters:
alignBytes- a power of two in[1..4096]- Throws:
IllegalArgumentException- ifalignBytesis not a power of two in that range, orframeWidthis outside[1..MAX_DIMENSION]IndexOutOfBoundsException- ifplaneis not a plane of this format
-