rasterize method

Future<void> rasterize()

Rasterizes the measured lines into a list of TxSprite objects. Each sprite represents one line of text.

Implementation

Future<void> rasterize() async {
  // If rasterization is already complete or is currently in progress, do nothing.
  // This prevents race conditions from concurrent calls.
  if (isRasterized || _isRasterizing) return;

  _isRasterizing = true;
  try {
    // This check is now mostly for safety; measureNextPage should prevent empty lines.
    for (final lineData in _lines) {
      if (lineData.text.isEmpty || lineData.lineHeight <= 0) {
        continue; // Skip invalid lines.
      }

      final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle(
        textAlign: layout.textAlign,
        fontFamily: layout.fontFamily,
        fontSize: layout.fontSize.toDouble(),
      ));
      paragraphBuilder.addText(lineData.text);
      final paragraph = paragraphBuilder.build();
      // Layout with the specific width calculated for this line.
      paragraph.layout(ui.ParagraphConstraints(width: lineData.width.toDouble()));

      final lineMetrics = paragraph.computeLineMetrics();
      if (lineMetrics.isEmpty) {
        continue; // No line metrics available, skip.
      }
      final firstLineMetrics = lineMetrics.first;

      // The canvas for the sprite should span the full width of the layout
      // to ensure consistent sprite dimensions.
      final int spriteWidth = firstLineMetrics.width.ceil();
      final int spriteHeight = lineData.lineHeight;

      if (spriteWidth <= 0 || spriteHeight <= 0) {
        continue; // Skip invalid sprite sizes.
      }

      final recorder = ui.PictureRecorder();
      final canvas = ui.Canvas(recorder);

      // Draw the paragraph at its calculated horizontal offset.
      canvas.drawParagraph(paragraph, ui.Offset(-firstLineMetrics.left, 0));

      final picture = recorder.endRecording();
      final image = await picture.toImage(spriteWidth, spriteHeight);
      final byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba);

      if (byteData == null) continue;

      // Convert the 32-bit RGBA image to a 1-bit monochrome sprite.
      final pixels = Uint8List(spriteWidth * spriteHeight);
      final rgba = byteData.buffer.asUint8List();
      for (int i = 0; i < pixels.length; ++i) {
        // Use the red channel to determine color (assuming grayscale text).
        // A threshold of 128 determines black or white.
        pixels[i] = rgba[i * 4] >= 128 ? 1 : 0;
      }

      // After rasterizing, update the lineData with the final sprite geometry
      lineData.xOffset = (lineData.xOffset + firstLineMetrics.left).toInt();
      lineData.width = spriteWidth;

      _sprites.add(TxSprite(
        width: spriteWidth,
        height: spriteHeight,
        numColors: 2,
        paletteData: TxTextPage._getPalette().data,
        pixelData: pixels,
      ));
    }
  } finally {
    // Ensure the flag is always reset, even if an error occurs.
    _isRasterizing = false;
  }
}