of static method
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);
}