of static method

StripBatchData? of(
  1. StripBuffer strips,
  2. int flushOrdinal
)

Snapshots strips (the generator reuses its buffers, so all data is copied) into draw-ready form, or null when the buffer is empty.

The per-strip color word is passed through to the vertex colors unchanged and must therefore be straight (non-premultiplied) ARGB - Skia premultiplies vertex colors before BlendMode.modulate multiplies them with the shader's coverage.

Implementation

static StripBatchData? of(StripBuffer strips, int flushOrdinal) {
  final n = strips.length;
  if (n == 0) return null;

  final chunks = <StripChunkData>[];
  var start = 0;
  while (start < n) {
    // Chunk end: capped by quad count (Uint16 indices) and by the alpha
    // span of the chunk's MIXED strips (Impeller snapshot limit; alpha
    // offsets are appended monotonically, so a streaming scan suffices).
    // Solid strips carry constant u = 0 (their sample is ignored) and
    // never widen the span.
    var base = -1;
    var end = start;
    while (end < n && end - start < stripMaxQuadsPerDraw) {
      final wf = strips.widthFlags[end];
      if (wf & stripFlagSolid == 0) {
        final off = strips.alphaOffset[end];
        final w = wf & 0xFFFF;
        if (base < 0) base = off;
        if (off + w - base > stripMaxAlphaColumnsPerDraw && end > start) {
          break;
        }
      }
      end++;
    }
    if (end == start) end = start + 1; // lone over-wide strip: own chunk
    if (base < 0) base = 0; // solid-only chunk

    final count = end - start;
    final positions = Float32List(count * 8);
    final texs = Float32List(count * 8);
    final colors = Int32List(count * 4);
    final indices = Uint16List(count * 6);
    for (var k = 0; k < count; k++) {
      final i = start + k;
      final xy = strips.xy[i];
      final x = (xy & 0xFFFF).toDouble();
      final y = (xy >> 16).toDouble();
      final wf = strips.widthFlags[i];
      final w = (wf & 0xFFFF).toDouble();
      final solid = wf & stripFlagSolid != 0;

      final p = k * 8;
      positions[p] = x;
      positions[p + 1] = y;
      positions[p + 2] = x + w;
      positions[p + 3] = y;
      positions[p + 4] = x;
      positions[p + 5] = y + 4;
      positions[p + 6] = x + w;
      positions[p + 7] = y + 4;

      // u: chunk-relative texel index range (the shader adds the chunk's
      // alphaBase back); v: [0,4) mixed, [4,8) solid. Solid quads carry
      // constant u = 0 - the sample is ignored (cov = 1) and the atlas
      // always has a texel 0, so the dead fetch stays in-atlas without
      // widening the chunk's texcoord bounds.
      final u0 = solid ? 0.0 : (strips.alphaOffset[i] - base).toDouble();
      final u1 = solid ? 0.0 : u0 + w;
      final v0 = solid ? 4.0 : 0.0;
      final v1 = v0 + 4;
      texs[p] = u0;
      texs[p + 1] = v0;
      texs[p + 2] = u1;
      texs[p + 3] = v0;
      texs[p + 4] = u0;
      texs[p + 5] = v1;
      texs[p + 6] = u1;
      texs[p + 7] = v1;

      final color = strips.color[i];
      final c = k * 4;
      colors[c] = color;
      colors[c + 1] = color;
      colors[c + 2] = color;
      colors[c + 3] = color;

      final vBase = k * 4;
      final ix = k * 6;
      indices[ix] = vBase;
      indices[ix + 1] = vBase + 1;
      indices[ix + 2] = vBase + 2;
      indices[ix + 3] = vBase + 1;
      indices[ix + 4] = vBase + 3;
      indices[ix + 5] = vBase + 2;
    }
    chunks.add(StripChunkData(base, positions, texs, colors, indices));
    start = end;
  }

  // Alpha atlas: column texels row-major by global index, final row
  // zero-padded. Always at least one texel so solid-only batches still
  // have a valid sampler target.
  final cols = strips.alphaColumns;
  final rows =
      cols == 0 ? 1 : (cols + stripAtlasWidth - 1) ~/ stripAtlasWidth;
  final pixels = Uint8List(stripAtlasWidth * rows * 4);
  if (cols > 0) {
    pixels.setRange(0, cols * 4, strips.alphas);
  }
  return StripBatchData._(
      flushOrdinal, chunks, pixels, stripAtlasWidth, rows, n);
}