Package limn.video

Class YuvConverter

java.lang.Object
limn.video.YuvConverter

public final class YuvConverter extends Object
Converts a decoded picture to 8-bit RGBA on the CPU: the reference implementation of the colour arithmetic, and the path used where no device conversion is available.

Any bit depth in, eight bits out. The matrix is worked in the picture's own code space, which is what VideoColor's depth-taking accessors describe, and the result is scaled to [0..255] at the very end, so a 10-bit picture is decoded at 10 bits and quantized once, rather than being quantized to eight and then decoded. The output is deliberately eight bits because this path exists to produce an ordinary RGBA image; a consumer that wants the extra precision uploads the planes to a device instead.

Allocation-free: the destination is supplied by the caller and reused, and the conversion itself creates nothing. A 1080p picture is about 8 MB of output, so allocating one per picture is roughly 250 MB a second at 30 per second.

Chroma is upsampled by replication: the chroma sample for a pixel is the one at that pixel's coordinates shifted down by the format's subsampling, with no interpolation. That is exact for odd sizes by construction, because a chroma plane's dimensions are the frame's rounded up. It is also reproducible elsewhere: a device sampler in nearest-neighbour mode produces identical results, whereas interpolation weights differ between implementations and could not be matched exactly. Interpolating additionally requires knowing where chroma samples sit relative to luma, which a stream signals and which nothing here carries.

Output is straight (non-premultiplied) RGBA, row-major, row 0 at the top, alpha a constant 255. Each channel is rounded half-up and then clamped to [0..255]; the clamp is not defensive, because legal studio-range codes decode outside that interval by design and a cast without it wraps a highlight to a dark speckle.

  • Method Details

    • toRgba8

      public static void toRgba8(VideoFrame frame, byte[] dst, int dstOffset, int dstStride)
      Converts the whole of frame into dst.

      Sample codes are read unsigned; the frame's own layout supplies the plane geometry, so a caller never re-derives a chroma size and cannot round it differently from the producer that filled it. The planes are read by absolute index, so their position and limit are left exactly as they were and a caller may convert a frame whose planes it is also reading relatively.

      Parameters:
      dst - destination, at least dstOffset + dstStride * (height - 1) + width * 4 bytes; never allocated here and never resized
      dstOffset - index in dst of the first byte of row 0, at least 0
      dstStride - bytes between the starts of consecutive destination rows, at least width * 4
      Throws:
      IllegalStateException - if the frame has been released
      IllegalArgumentException - if the stride is below the row's byte width, the offset is negative, or the destination is too small
      NullPointerException - if frame or dst is null
    • convertPixel

      public static void convertPixel(VideoColor color, int bitDepth, int y, int cb, int cr, int[] out)
      Converts one YCbCr triple, for a test or a single-pixel probe. Codes are taken as unsigned values in [0..(1 << bitDepth) - 1]; results outside that range are clamped, so footroom and headroom codes give clamped black and white rather than wrapping.

      The output stays in the picture's code space rather than being brought to eight bits: this is the matrix alone, which is what makes it the reference toRgba8(VideoFrame, byte[], int, int) is asserted against; that method is this arithmetic followed by one scale to eight bits.

      Parameters:
      bitDepth - valid bits per component, in [8..16] (PixelFormat.bitDepth())
      out - a four-element array receiving red, green, blue and an opaque alpha, each in [0..(1 << bitDepth) - 1]
      Throws:
      IllegalArgumentException - if any code is outside the depth's range, bitDepth is outside [8..16], or out is shorter than four
      NullPointerException - if color or out is null