Skip to content

Instantly share code, notes, and snippets.

@Vanilagy
Last active October 27, 2022 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Vanilagy/7fe8f98cf1563c73a070657d922266b2 to your computer and use it in GitHub Desktop.
Save Vanilagy/7fe8f98cf1563c73a070657d922266b2 to your computer and use it in GitHub Desktop.
Canvas RGBA image data to packed planar YUV 4:2:0 using the BT.709 color matrix
const RGBAToYUV420 = ({ width, height, data }: ImageData) => {
// Assume both width and height are even
let yuv = new Uint8Array(width * height * 1.5);
// Use loop tiling as a cache optimization
const tileSize = 64;
for (let y0 = 0; y0 < height; y0 += tileSize) {
for (let x0 = 0; x0 < width; x0 += tileSize) {
let limitX = Math.min(width, x0 + tileSize);
let limitY = Math.min(height, y0 + tileSize);
for (let y = y0; y < limitY; y++) {
for (let x = x0; x < limitX; x++) {
let R = data[4*(y*width + x) + 0];
let G = data[4*(y*width + x) + 1];
let B = data[4*(y*width + x) + 2];
// Uses the matrix given in https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion, then adds 128 to the chroma channels, then remaps full range to the condensed, broadcast range (also explained in that article). This entire transformation is condensed into a single matrix, used here:
let Y = 0.182586*R + 0.614231*G + 0.0620071*B + 16;
let U = -0.100668*R - 0.338547*G + 0.439216*B + 128.439;
let V = 0.439216*R - 0.398984*G - 0.0426039*B + 128.439;
yuv[y*width + x] = Y;
if (x % 2 === 0 && y % 2 === 0) {
yuv[1*width*height + (y*width/4 + x/2)] = U;
yuv[1.25*width*height + (y*width/4 + x/2)] = V;
}
}
}
}
}
return yuv;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment