import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:dartterm/algorithms/geometry.dart' as geo; import 'package:flutter/services.dart'; class Bitmap { // This idiosyncratic usage of "bitmap" comes from some other technology. // What I'm saying is "don't blame me" final geo.Size size; final List 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 blankWith(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 blank(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); } static Future> load(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 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; } } } T? get(int x, int y) { if (x < 0 || y < 0 || x >= size.dx || y >= size.dy) { 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 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 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 flip() { var geo.Size(:dx, :dy) = size; List 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 rotateRight() { var geo.Size(:dx, :dy) = size; List 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 rotateLeft() { var geo.Size(:dx, :dy) = size; List 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); } }