Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Created September 21, 2020 21:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PlugFox/b1b41b17b3988394f8cec357e1faef0e to your computer and use it in GitHub Desktop.
Save PlugFox/b1b41b17b3988394f8cec357e1faef0e to your computer and use it in GitHub Desktop.
Atlas canvas drawer
import 'dart:async';
import 'dart:collection' show IterableBase;
import 'dart:typed_data' show Uint8List, ByteData;
import 'dart:ui' as ui;
import 'package:flutter/services.dart' show rootBundle;
import 'package:meta/meta.dart' show immutable, required;
/// Атлас изображений
///
/// Atlas.init(
/// path: 'assets/images/atlas.png',
/// spriteWidth: 32,
/// spriteHeight: 32,
/// )
///
@immutable
class Atlas extends IterableBase<Sprite> implements ui.Image {
final List<Sprite> sprites;
final double spriteWidth;
final double spriteHeight;
final ui.Image _spriteAtlas;
/// Assume spriteAtlas contains
/// N [spriteWidth] x [spriteHeight]
/// sprites side by side in a image
///
/// Indexing order:
/// 0 0x0 1 1x0 2 2x0
/// 3 1x0 4 1x1 5 2x1
/// 6 2x0 7 2x1 8 2x2
///
static Future<Atlas> init({
@required String path,
@required double spriteWidth,
@required double spriteHeight,
}) async {
final data = await rootBundle.load(path);
final codec = await ui.instantiateImageCodec(
Uint8List.view(data.buffer),
targetWidth: null,
targetHeight: null,
allowUpscaling: false,
);
final spriteAtlas = (await codec.getNextFrame()).image;
final numberOfHorizontal = spriteAtlas.width ~/ spriteWidth;
final numberOfVertical = spriteAtlas.height ~/ spriteHeight;
final sprites = <Sprite>[];
for (var positionX = 0; positionX < numberOfHorizontal; positionX++) {
for (var positionY = 0; positionY < numberOfVertical; positionY++) {
sprites.add(
Sprite(
positionX: positionX,
positionY: positionY,
),
);
}
}
return Atlas._(spriteAtlas, sprites, spriteWidth, spriteHeight);
}
Atlas._(ui.Image spriteAtlas, List<Sprite> sprites, this.spriteWidth, this.spriteHeight)
: _spriteAtlas = spriteAtlas
, sprites = List.unmodifiable(sprites);
static ui.RSTransform transform() =>
ui.RSTransform.fromComponents(
rotation: 0,
scale: 1,
anchorX: 0,
anchorY: 0,
translateX: 0,
translateY: 0,
);
void drawIndex(
ui.Canvas canvas,
ui.Paint paint,
int index,
[ui.Offset offset = const ui.Offset(0, 0)]) =>
drawSprite(canvas, paint, sprites[index], offset);
void drawSprite(
ui.Canvas canvas,
ui.Paint paint,
Sprite sprite,
[ui.Offset offset = const ui.Offset(0, 0)]) =>
canvas.drawAtlas(
_spriteAtlas,
<ui.RSTransform>[
_rsTransform(offset),
],
<ui.Rect>[
_rect(sprite),
],
null,
null,
null,
paint,
);
ui.RSTransform _rsTransform(ui.Offset offset) =>
ui.RSTransform(1, 0, offset.dx, offset.dy);
ui.Rect _rect(Sprite sprite) =>
ui.Rect.fromLTWH(
sprite.positionX * spriteWidth,
sprite.positionY * spriteHeight,
spriteWidth,
spriteHeight,
);
Sprite operator [](int index) =>
sprites[index];
@override
int get width => _spriteAtlas.width;
@override
int get height => _spriteAtlas.height;
@override
Future<ByteData> toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) =>
_spriteAtlas.toByteData(format: format ?? ui.ImageByteFormat.rawRgba);
@override
void dispose() =>
_spriteAtlas.dispose();
@override
int get length =>
sprites.length;
@override
Iterator<Sprite> get iterator =>
sprites.iterator;
@override
String toString() =>
IterableBase.iterableToFullString(sprites);
}
@immutable
class Sprite {
final int positionX;
final int positionY;
const Sprite({
@required this.positionX,
@required this.positionY,
});
@override
int get hashCode => super.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other)
|| (other is Sprite
&& positionX == other.positionX
&& positionY == other.positionY);
@override
String toString() =>
'<Sprite $positionX X $positionY>';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment