Line data Source code
1 : import 'dart:ui';
2 :
3 : import 'extensions/vector2.dart';
4 : import 'palette.dart';
5 : import 'sprite.dart';
6 :
7 : /// This allows you to create a rectangle textured with a 9-sliced image.
8 : ///
9 : /// How it works is that you have a template image in a 3x3 grid, made up of 9 tiles,
10 : /// and a new rectangle can be draw by keeping the 4 corners, expanding the 4 sides only
11 : /// in the direction in which they are located and expanding the center in both directions.
12 : /// That allows you to have non distorted borders.
13 : class NineTileBox {
14 0 : static final _whitePaint = BasicPalette.white.paint();
15 :
16 : /// The sprite used to render the box, must be a 3x3 grid of square tiles.
17 : Sprite sprite;
18 :
19 : /// The size of each tile in the source sprite image.
20 : int tileSize;
21 :
22 : /// The size each tile becomes when rendered (optionally used to scale the src image).
23 : late int destTileSize;
24 :
25 : /// Creates a nine-box instance.
26 : ///
27 : /// [sprite] is the 3x3 grid and [tileSize] is the size of each tile.
28 : /// The src sprite must a square of size 3*[tileSize].
29 : ///
30 : /// If [tileSize] is not provided, the width of the sprite is assumed as the size.
31 : /// Otherwise the width and height properties of the sprite are ignored.
32 : ///
33 : /// If [destTileSize] is not provided, the evaluated [tileSize] is used instead
34 : /// (so no scaling happens).
35 0 : NineTileBox(this.sprite, {int? tileSize, int? destTileSize})
36 0 : : tileSize = tileSize ?? sprite.src.width.toInt() {
37 0 : this.destTileSize = destTileSize ?? this.tileSize;
38 : }
39 :
40 : /// Renders this nine box with the dimensions provided by [rect].
41 0 : void drawRect(Canvas c, Rect rect) {
42 0 : final position = Vector2(rect.left, rect.top);
43 0 : final size = Vector2(rect.width, rect.height);
44 0 : draw(c, position, size);
45 : }
46 :
47 : /// Renders this nine box as a rectangle at [position] with size [size].
48 0 : void draw(Canvas c, Vector2 position, Vector2 size) {
49 : // corners
50 0 : _drawTile(c, _getDest(position), 0, 0);
51 0 : final bottomLeft = position + Vector2(0, size.y - destTileSize);
52 0 : _drawTile(c, _getDest(bottomLeft), 0, 2);
53 0 : final topRight = position + Vector2(size.x - destTileSize, 0);
54 0 : _drawTile(c, _getDest(topRight), 2, 0);
55 0 : final bottomRight = Vector2(topRight.x, bottomLeft.y);
56 0 : _drawTile(c, _getDest(bottomRight), 2, 2);
57 :
58 : // horizontal sides
59 0 : final mx = size.x - 2 * destTileSize;
60 0 : final middleLeft = position + Vector2Extension.fromInts(destTileSize, 0);
61 0 : _drawTile(c, _getDest(middleLeft, width: mx), 1, 0);
62 0 : final middleRight = middleLeft + Vector2(0, size.y - destTileSize);
63 0 : _drawTile(c, _getDest(middleRight, width: mx), 1, 2);
64 :
65 : // vertical sides
66 0 : final my = size.y - 2 * destTileSize;
67 0 : final topCenter = position + Vector2Extension.fromInts(0, destTileSize);
68 0 : _drawTile(c, _getDest(topCenter, height: my), 0, 1);
69 0 : final bottomCenter = topCenter + Vector2(size.x - destTileSize, 0);
70 0 : _drawTile(c, _getDest(bottomCenter, height: my), 2, 1);
71 :
72 : // center
73 0 : final center = position + Vector2.all(destTileSize.toDouble());
74 0 : _drawTile(c, _getDest(center, width: mx, height: my), 1, 1);
75 : }
76 :
77 0 : Rect _getDest(Vector2 position, {double? width, double? height}) {
78 0 : final w = width ?? _destTileSizeDouble;
79 0 : final h = height ?? _destTileSizeDouble;
80 0 : return Rect.fromLTWH(position.x, position.y, w, h);
81 : }
82 :
83 0 : double get _tileSizeDouble => tileSize.toDouble();
84 :
85 0 : double get _destTileSizeDouble => destTileSize.toDouble();
86 :
87 0 : void _drawTile(Canvas c, Rect dest, int i, int j) {
88 0 : final xSrc = sprite.src.left + _tileSizeDouble * i;
89 0 : final ySrc = sprite.src.top + _tileSizeDouble * j;
90 0 : final src = Rect.fromLTWH(xSrc, ySrc, _tileSizeDouble, _tileSizeDouble);
91 0 : c.drawImageRect(sprite.image, src, dest, _whitePaint);
92 : }
93 : }
|