dartterm/lib/terminal_painter.dart

139 lines
4.0 KiB
Dart
Raw Normal View History

2023-09-10 01:58:31 +00:00
import 'dart:developer';
2023-09-06 03:11:15 +00:00
import 'dart:ui' as ui;
import 'dart:ui';
2023-09-06 03:11:15 +00:00
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;
static const int cellW = 8;
static const int cellH = 8;
2023-09-06 03:11:15 +00:00
TerminalCustomPainter(this.t, this.scalingFactor);
@override
bool shouldRepaint(TerminalCustomPainter oldDelegate) {
return true;
}
@override
void paint(Canvas canvas, Size size) {
var pr = PictureRecorder();
var smallSize = Size((cellW * Terminal.width).toDouble(),
(cellH * Terminal.height).toDouble());
var virtualCanvas = Canvas(pr, const Offset(0, 0) & smallSize);
virtualPaint(virtualCanvas, smallSize);
var image = pr
.endRecording()
// Renderer does something bizarre if I don't multiply by something greater than 1
2023-09-10 01:58:31 +00:00
.toImageSync(cellW * Terminal.width * 1, cellH * Terminal.height * 1);
double x0, y0, xSz, ySz;
(x0, y0) = (0.0, 0.0);
(xSz, ySz) = (size.width, size.height);
for (var multiplierBase = 16.0; multiplierBase >= 1; multiplierBase -= 1) {
final multiplier = multiplierBase / scalingFactor;
final candidateXSz = (multiplier * cellW * Terminal.width).toDouble();
final candidateYSz = (multiplier * cellH * Terminal.height).toDouble();
if (candidateXSz <= size.width && candidateYSz <= size.height) {
xSz = candidateXSz;
ySz = candidateYSz;
x0 = (size.width - xSz) / 2;
y0 = (size.height - ySz) / 2;
x0 = (x0 * scalingFactor).floor() / scalingFactor;
y0 = (y0 * scalingFactor).floor() / scalingFactor;
break;
}
}
canvas.drawImageRect(
image,
const Offset(0, 0) & smallSize,
Offset(x0, y0) & Size(xSz, ySz),
Paint()..filterQuality = FilterQuality.none);
2023-09-10 03:26:30 +00:00
notifyScreenDimensions(
ScreenDimensions(x0: x0, y0: y0, xSz: xSz, ySz: ySz));
image.dispose();
}
2023-09-06 03:11:15 +00:00
void virtualPaint(Canvas canvas, Size size) {
final paint = Paint();
2023-09-06 03:11:15 +00:00
var todos = Todos();
// == Draw the background and foreground of every tile ==
for (var i = 0; i < nTiles; i++) {
2023-09-10 01:40:56 +00:00
final (pcxDst, pcyDst) = t.toXY(i) ?? (0, 0);
final (pxDst, pyDst) = (pcxDst * cellW, pcyDst * cellH);
final rectDst = Rect.fromLTWH(pxDst.toDouble(), pyDst.toDouble(),
cellW.toDouble(), cellH.toDouble());
2023-09-06 03:11:15 +00:00
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> imageTodos = [];
Map<String, int> 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<RSTransform> transforms = [];
List<Rect> rects = [];
List<Color> colors = [];
ImageTodos(this.atlas);
void add(Rect src, Rect dst, Color color) {
transforms.add(RSTransform.fromComponents(
rotation: 0.0,
scale: 1.0,
2023-09-06 03:11:15 +00:00
anchorX: 0.0,
anchorY: 0.0,
translateX: dst.left,
translateY: dst.top,
));
rects.add(src);
colors.add(color);
}
}