155 lines
4.2 KiB
Dart
155 lines
4.2 KiB
Dart
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> 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) {
|
|
// 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);
|
|
}
|
|
}
|