render method

void render(
  1. Buffer backBuffer,
  2. StringSink out
)

Diffs backBuffer against _frontBuffer and writes minimal ANSI escape sequences to out.

Implementation

void render(Buffer backBuffer, StringSink out) {
  Tracer.record(_traceRenderId, Phase.begin, TraceCategory.paint);
  try {
    bool sizeChanged = _firstFrame;
    _firstFrame = false;

    if (mode == RenderingMode.inline) {
      if (_lastHeight > 0) {
        // Move cursor back up to the top-left of the inline block
        out.write('\x1b[${_lastHeight}F');
      } else {
        // First frame: move to the next line to avoid printing TUI inline on the command prompt line
        out.write('\n\r');
      }
      if (backBuffer.width != _frontBuffer.width ||
          backBuffer.height != _frontBuffer.height) {
        _frontBuffer.resize(backBuffer.width, backBuffer.height);
        sizeChanged = true;
      }
    } else {
      if (backBuffer.width != _frontBuffer.width ||
          backBuffer.height != _frontBuffer.height ||
          sizeChanged) {
        _frontBuffer.resize(backBuffer.width, backBuffer.height);
        sizeChanged = true;
        // Clear screen and reset cursor position to top-left
        out.write('\x1b[2J\x1b[H');
      }
    }

    int activeFg = 0;
    int activeBg = 0;
    int activeMod = 0;
    int cursorX = 0;
    int cursorY = 0;

    final width = backBuffer.width;
    final height = backBuffer.height;

    final backChars = backBuffer.characters;
    final backAttr = backBuffer.attributes;

    final frontChars = _frontBuffer.characters;
    final frontAttr = _frontBuffer.attributes;

    for (var y = 0; y < height; y++) {
      var x = 0;
      final rowOffset = y * width;
      while (x < width) {
        final idx = rowOffset + x;
        final attrIdx = idx * 3;

        final changed =
            sizeChanged ||
            (backChars[idx] != frontChars[idx] ||
                backAttr[attrIdx + 0] != frontAttr[attrIdx + 0] ||
                backAttr[attrIdx + 1] != frontAttr[attrIdx + 1] ||
                backAttr[attrIdx + 2] != frontAttr[attrIdx + 2]);

        if (changed) {
          final runStart = x;
          var runEnd = x;
          // Find the end of the contiguous run of changed cells in the current row
          while (runEnd < width) {
            final ridx = rowOffset + runEnd;
            final rattrIdx = ridx * 3;
            if (sizeChanged ||
                backChars[ridx] != frontChars[ridx] ||
                backAttr[rattrIdx + 0] != frontAttr[rattrIdx + 0] ||
                backAttr[rattrIdx + 1] != frontAttr[rattrIdx + 1] ||
                backAttr[rattrIdx + 2] != frontAttr[rattrIdx + 2]) {
              runEnd++;
            } else {
              break;
            }
          }

          // Move cursor to (runStart, y) relative or absolute
          if (mode == RenderingMode.inline) {
            _moveCursorRelative(out, cursorX, cursorY, runStart, y);
            cursorX = runStart;
            cursorY = y;
          } else {
            out.write('\x1b[${y + 1};${runStart + 1}H');
          }

          // Render each cell in the run
          for (var rx = runStart; rx < runEnd; rx++) {
            final rxIdx = rowOffset + rx;
            final rxAttrIdx = rxIdx * 3;
            final cellFg = backAttr[rxAttrIdx + 0];
            final cellBg = backAttr[rxAttrIdx + 1];
            final cellMod = backAttr[rxAttrIdx + 2];

            _writeStyleTransitionPrims(
              out,
              activeFg,
              activeBg,
              activeMod,
              cellFg,
              cellBg,
              cellMod,
            );
            activeFg = cellFg;
            activeBg = cellBg;
            activeMod = cellMod;

            out.write(backChars[rxIdx]);

            frontChars[rxIdx] = backChars[rxIdx];
            frontAttr[rxAttrIdx + 0] = backAttr[rxAttrIdx + 0];
            frontAttr[rxAttrIdx + 1] = backAttr[rxAttrIdx + 1];
            frontAttr[rxAttrIdx + 2] = backAttr[rxAttrIdx + 2];

            if (mode == RenderingMode.inline) {
              cursorX++;
            }
          }
          x = runEnd;
        } else {
          x++;
        }
      }
    }

    if (activeFg != 0 || activeBg != 0 || activeMod != 0) {
      out.write('\x1b[0m');
    }

    if (mode == RenderingMode.inline) {
      // Position cursor at the beginning of the line immediately following the inline block
      _moveCursorRelative(out, cursorX, cursorY, 0, backBuffer.height);
      _lastHeight = backBuffer.height;
    }
  } finally {
    Tracer.record(_traceRenderId, Phase.end, TraceCategory.paint);
  }
}