function unpackComponentsBits()

in src/webgpu/util/texture/texel_data.ts [286:369]


function unpackComponentsBits(
  componentOrder: TexelComponent[],
  byteView: Uint8Array,
  bitLengths: number | PerTexelComponent<number>
): PerTexelComponent<number> {
  const components = makePerTexelComponent(componentOrder, 0);

  let bitLengthMap;
  let totalBitLength;
  if (typeof bitLengths === 'number') {
    let index = 0;
    // Optimized cases for when the bit lengths are all a well aligned value.
    switch (bitLengths) {
      case 8:
        for (const c of componentOrder) {
          components[c] = byteView[index++];
        }
        return components;
      case 16: {
        const shortView = new Uint16Array(byteView.buffer, byteView.byteOffset);
        for (const c of componentOrder) {
          components[c] = shortView[index++];
        }
        return components;
      }
      case 32: {
        const longView = new Uint32Array(byteView.buffer, byteView.byteOffset);
        for (const c of componentOrder) {
          components[c] = longView[index++];
        }
        return components;
      }
    }

    bitLengthMap = makePerTexelComponent(componentOrder, bitLengths);
    totalBitLength = bitLengths * componentOrder.length;
  } else {
    bitLengthMap = bitLengths;
    totalBitLength = Object.entries(bitLengthMap).reduce((acc, [, value]) => {
      assert(value !== undefined);
      return acc + value;
    }, 0);
  }

  assert(totalBitLength % 8 === 0);

  const dataView = new DataView(byteView.buffer, byteView.byteOffset, byteView.byteLength);
  let bitOffset = 0;
  for (const c of componentOrder) {
    const bitLength = bitLengthMap[c];
    assert(bitLength !== undefined);

    let value: number;

    const byteOffset = Math.floor(bitOffset / 8);
    const byteLength = Math.ceil(bitLength / 8);
    if (byteOffset === bitOffset / 8 && byteLength === bitLength / 8) {
      switch (byteLength) {
        case 1:
          value = dataView.getUint8(byteOffset);
          break;
        case 2:
          value = dataView.getUint16(byteOffset, true);
          break;
        case 4:
          value = dataView.getUint32(byteOffset, true);
          break;
        default:
          unreachable();
      }
    } else {
      // Packed representations are all 32-bit and use Uint as the data type.
      // ex.) rg10b11float, rgb10a2unorm
      assert(dataView.byteLength === 4);
      const word = dataView.getUint32(0, true);
      value = (word >>> bitOffset) & ((1 << bitLength) - 1);
    }

    bitOffset += bitLength;
    components[c] = value;
  }

  return components;
}