dartterm/lib/bitmap.dart

153 lines
3.9 KiB
Dart
Raw Normal View History

2023-09-21 03:56:30 +00:00
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:dartterm/algorithms/geometry.dart' as geo;
2023-09-21 03:56:30 +00:00
import 'package:flutter/services.dart';
class Bitmap<T> {
// This idiosyncratic usage of "bitmap" comes from some other technology.
// What I'm saying is "don't blame me"
final geo.Size size;
2023-09-21 03:56:30 +00:00
final List<T> data;
geo.Rect get rect => geo.Rect(0, 0, size.dx, size.dy);
Bitmap(this.size, this.data) {
assert(data.length == size.dx * size.dy);
}
static Bitmap<T> blankWith<T>(int dx, int dy, T Function(int, int) lt) {
var data = [
for (var y = 0; y < dy; y++)
for (var x = 0; x < dx; x++) lt(x, y)
];
return Bitmap(geo.Size(dx, dy), data);
}
static Bitmap<T> blank<T>(int dx, int dy, T lt) {
var data = [
for (var y = 0; y < dy; y++)
for (var x = 0; x < dx; x++) lt
];
return Bitmap(geo.Size(dx, dy), data);
2023-09-21 03:56:30 +00:00
}
static Future<Bitmap<T>> load<T>(String name, T Function(int) cb) async {
final assetImageByteData = await rootBundle.load(name);
final codec =
await ui.instantiateImageCodec(assetImageByteData.buffer.asUint8List());
final image = (await codec.getNextFrame()).image;
final bytedata =
(await image.toByteData(format: ui.ImageByteFormat.rawStraightRgba))!;
final sx = image.width;
final sy = image.height;
final List<T> data = [];
for (var i = 0; i < sx * sy; i++) {
var pixel = bytedata.getUint32(i * 4, Endian.big);
data.add(cb(pixel));
}
return Bitmap(geo.Size(sx, sy), data);
}
void clearWith(T Function(int, int) t) {
for (var y = 0; y < size.dy; y++) {
for (var x = 0; x < size.dx; x++) {
data[y * size.dx + x] = t(x, y);
}
}
}
void clear(T t) {
for (var y = 0; y < size.dy; y++) {
for (var x = 0; x < size.dx; x++) {
data[y * size.dx + x] = t;
}
}
2023-09-21 03:56:30 +00:00
}
T? get(int x, int y) {
if (x < 0 || y < 0 || x >= size.dx || y >= size.dy) {
2023-09-21 03:56:30 +00:00
return null;
}
return data[y * size.dx + x];
}
T unsafeGet(int x, int y) {
assert(x < 0 || y < 0 || x >= size.dx || y >= size.dy);
return data[y * size.dx + x];
}
void set(int x, int y, T value) {
assert(!(x < 0 || y < 0 || x >= size.dx || y >= size.dy));
data[y * size.dx + x] = value;
}
void blitFrom(Bitmap<T> other, int dx, int dy) {
assert(rect.containsRect(geo.Rect(dx, dy, other.size.dx, other.size.dy)));
var x0 = other.rect.x0;
var y0 = other.rect.x0;
var x1 = other.rect.x1;
var y1 = other.rect.y1;
var myW = size.dx;
var otW = other.size.dx;
for (var x = x0; x < x1; x++) {
for (var y = y0; y < y1; y++) {
data[(y + dy) * myW + (x + dx)] = other.data[y * otW + x];
}
}
}
void blitFromWith(Bitmap<T> other, int dx, int dy, T Function(T, T) merge) {
assert(rect.containsRect(geo.Rect(dx, dy, other.size.dx, other.size.dy)));
var x0 = other.rect.x0;
var y0 = other.rect.x0;
var x1 = other.rect.x1;
var y1 = other.rect.y1;
var myW = size.dx;
var otW = other.size.dx;
for (var x = x0; x < x1; x++) {
for (var y = y0; y < y1; y++) {
data[(y + dy) * myW + (x + dx)] =
merge(data[(y + dy) * myW + (x + dx)], other.data[y * otW + x]);
}
}
}
Bitmap<T> flip() {
var geo.Size(:dx, :dy) = size;
List<T> data2 = [
for (var y = 0; y < size.dy; y++)
for (var x = size.dx - 1; x >= 0; x--) data[y * dx + x]
];
return Bitmap(geo.Size(dx, dy), data2);
}
Bitmap<T> rotateRight() {
var geo.Size(:dx, :dy) = size;
List<T> data2 = [
for (var x = 0; x < dx; x++)
for (var y = 0; y < dy; y++) data[(dy - 1 - y) * dx + x]
];
return Bitmap(geo.Size(dy, dx), data2);
}
Bitmap<T> rotateLeft() {
var geo.Size(:dx, :dy) = size;
List<T> data2 = [
for (var x = dx - 1; x >= 0; x++)
for (var y = dy - 1; y >= 0; y++) data[y * dx + (dx - 1 - x)]
];
return Bitmap(geo.Size(dy, dx), data2);
2023-09-21 03:56:30 +00:00
}
}