transform(chunk, controller) {
    let position = chunk.byteOffset;
    let length = chunk.byteLength;
    const source = new DataView(chunk.buffer, position, length);
    const buffer = new Uint8Array(length);
    const target = new DataView(buffer.buffer, position, length);

    while (position < length) {
      switch (this._mode) {
        case 'magic': {
          const magic1 = source.getUint32(position);
          target.setUint32(position, magic1);
          position += 4;

          const magic2 = source.getUint32(position);
          target.setUint32(position, magic2);
          position += 4;

          const magic = magic1.toString(16) + '0' + magic2.toString(16);
          console.log('%cPNG magic:     %c %o', 'font-weight: bold', '', magic);
          if (magic !== '89504e470d0a1a0a') {
            throw new TypeError('This is not a PNG');
          }

          this._mode = 'header';
          break;
        }
        case 'header': {
          // Read chunk info
          const chunkLength = source.getUint32(position);
          target.setUint32(position, chunkLength);
          position += 4;
          const chunkName = this.readString(source, position, 4);
          this.writeString(target, position, chunkName);
          position += 4;
          if (chunkName !== 'IHDR') {
            throw new TypeError('PNG is missing IHDR chunk');
          }

          // Read image dimensions
          this._width = source.getUint32(position);
          target.setUint32(position, this._width);
          position += 4;
          this._height = source.getUint32(position);
          target.setUint32(position, this._height);
          position += 4;
          console.log('%cPNG dimensions:%c %d x %d', 'font-weight: bold', '', this._width, this._height);

          this._bitDepth = source.getUint8(position);
          target.setUint8(position, this._bitDepth);
          position += 1;
          console.log('%cPNG bit depth: %c %d', 'font-weight: bold', '', this._bitDepth);

          this._colorType = source.getUint8(position);
          target.setUint8(position, this._colorType);
          position += 1;
          console.log('%cPNG color type:%c %s', 'font-weight: bold', '', this.colorType(this._colorType));

          const compression = source.getUint8(position);
          target.setUint8(position, compression);
          position += 1;
          console.log('%cPNG compressio:%c %d', 'font-weight: bold', '', compression);
          const filter = source.getUint8(position);
          target.setUint8(position, filter);
          position += 1;
          console.log('%cPNG filter:    %c %d', 'font-weight: bold', '', filter);
          const interlace = source.getUint8(position);
          target.setUint8(position, interlace);
          position += 1;
          console.log('%cPNG interlace: %c %d', 'font-weight: bold', '', interlace);

          const chunkCrc = source.getUint32(position);
          target.setUint32(position, chunkCrc);
          position += 4;

          this._mode = 'data';
          break;
        }
        case 'data': {
          // Read chunk info
          const dataSize = source.getUint32(position);
          console.log('%cPNG data size: %c %d', 'font-weight: bold', '', dataSize);

          const chunkName = this.readString(source, position + 4, 4);
          if (chunkName !== 'IDAT') {
            throw new TypeError('PNG is missing IDAT chunk');
          }

          const crcStart = position + 4;

          // Extract the data from the PNG stream
          const bytesPerCol = this.bytesPerPixel();
          const bytesPerRow = this._width * bytesPerCol + 1;
          let result = chunk.subarray(position + 8, position + 8 + dataSize);

          // Decompress the data
          result = pako.inflate(result);

          // Remove PNG filters from each scanline
          result = this.removeFilters(result, bytesPerCol, bytesPerRow);

          // Actually grayscale the image
          result = this.grayscale(result, bytesPerCol, bytesPerRow);

          // Compress with Deflate
          result = pako.deflate(result);

          // Write data to target
          target.setUint32(position, result.byteLength);
          this.writeString(target, position + 4, 'IDAT');
          buffer.set(result, position + 8);

          position += result.byteLength + 8;

          const chunkCrc = crc32(chunkName, result);
          target.setUint32(position, chunkCrc);
          position += 4;

          this._mode = 'end';
          break;
        }
        case 'end': {
          // Write IEND chunk
          target.setUint32(position, 0);
          position += 4;
          this.writeString(target, position, 'IEND');
          position += 4;
          target.setUint32(position, 2923585666);
          position += 4;

          controller.enqueue(buffer.subarray(0, position));
          return;
        }
      }
    }
  }
Called for every downloaded PNG data chunk to be grayscaled.

@param {Uint8Array} chunk The downloaded chunk.
@param {TransformStreamDefaultController} controller The controller to euqueue grayscaled data.