import 'dart:ui' as ui; import 'package:dartterm/assets.dart'; import 'package:dartterm/fonts.dart'; import 'package:dartterm/terminal.dart'; import 'package:flutter/material.dart'; class TerminalCustomPainter extends CustomPainter { Terminal t; double scalingFactor; TerminalCustomPainter(this.t, this.scalingFactor); @override bool shouldRepaint(TerminalCustomPainter oldDelegate) { return true; } @override void paint(Canvas canvas, Size size) { final paint = Paint()..filterQuality = FilterQuality.none; // == Fill backdrop == final paint2 = Paint() ..color = Colors.black ..style = PaintingStyle.fill; canvas.drawRect(const Offset(0, 0) & size, paint2); // == Estimate dimensions for scaling == var sx = size.width / Terminal.width; var sy = size.height / Terminal.height; var dpx = 0.0; var dpy = 0.0; var invScalingFactor = 1 / scalingFactor; for (double preferredSx in [64, 56, 48, 40, 32, 24, 16, 8]) { preferredSx *= invScalingFactor; var preferredSy = preferredSx; if (preferredSx < sx && preferredSy < sy) { sx = preferredSx.toDouble(); sy = preferredSy.toDouble(); dpx = ((size.width - (sx * Terminal.width)) / 2); dpy = ((size.height - (sy * Terminal.height)) / 2); dpx /= invScalingFactor; dpx = dpx.floorToDouble(); dpx *= invScalingFactor; dpy /= invScalingFactor; dpy = dpy.floorToDouble(); dpy *= invScalingFactor; break; } } for (var i = 0; i < nTiles; i++) { final tile = t.tiles[i]; final (pcxDst, pcyDst) = terminal.toXY(i) ?? (0, 0); final (pxDst, pyDst) = (dpx + pcxDst * sx, dpy + pcyDst * sy); final rectDst = Rect.fromLTWH(pxDst, pyDst, sx, sy); var content = tile.content; if (content != null) { var source = content.sourceImage; var image = assets.getImageIfAvailable(source); if (image != null) { var fgRect = Rect.fromLTWH( content.sourceCx.toDouble() * cellW, content.sourceCy.toDouble() * cellH, cellW.toDouble(), cellH.toDouble()); canvas.drawImageRect( image, fgRect, rectDst, paint, ); } } } var todos = Todos(); // == Draw the background and foreground of every tile == for (var i = 0; i < nTiles; i++) { final (pcxDst, pcyDst) = terminal.toXY(i) ?? (0, 0); final (pxDst, pyDst) = (dpx + pcxDst * sx, dpy + pcyDst * sy); final rectDst = Rect.fromLTWH(pxDst, pyDst, sx, sy); final tile = t.tiles[i]; var bgRect = Rect.fromLTWH(0, 0, cellW.toDouble(), cellH.toDouble()); todos.add(Font.bg.imageName, bgRect, rectDst, tile.bg); var c = tile.content; if (c != null) { var fgRect = Rect.fromLTWH(c.sourceCx.toDouble() * cellW, c.sourceCy.toDouble() * cellH, cellW.toDouble(), cellH.toDouble()); todos.add(c.sourceImage, fgRect, rectDst, tile.fg); } } for (var t in todos.imageTodos) { final atlas = t.atlas; if (atlas != null) { canvas.drawAtlas(atlas, t.transforms, t.rects, t.colors, BlendMode.modulate, null, paint); } } } } class Todos { List imageTodos = []; Map imageTodoIx = {}; void add(String source, Rect src, Rect dst, Color color) { var ix = imageTodoIx[source]; if (ix == null) { ix = imageTodos.length; var image = assets.getImageIfAvailable(source); imageTodos.add(ImageTodos(image)); imageTodoIx[source] = ix; } imageTodos[ix].add(src, dst, color); } } class ImageTodos { ui.Image? atlas; List transforms = []; List rects = []; List colors = []; ImageTodos(this.atlas); void add(Rect src, Rect dst, Color color) { // NOTE: Because main.dart uses aspectRatio, scaleX and scaleY should be close to the same var scaleX = dst.width / src.width; transforms.add(RSTransform.fromComponents( rotation: 0.0, scale: scaleX, anchorX: 0.0, anchorY: 0.0, translateX: dst.left, translateY: dst.top, )); rects.add(src); colors.add(color); } }