Skip to content

Commit

Permalink
refactor: Use canvas.drawImageNine in NineTileBox (#1314)
Browse files Browse the repository at this point in the history
`canvas.drawImageNine` is built-in now a lot of logic was removed from NineTileBox.
  • Loading branch information
spydon authored Jan 16, 2022
1 parent dc37053 commit d77e5ef
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 166 deletions.
Binary file modified examples/assets/images/nine-box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 13 additions & 13 deletions examples/lib/stories/rendering/nine_tile_box_example.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';

class NineTileBoxExample extends FlameGame with TapDetector, DoubleTapDetector {
static const String description = '''
Expand All @@ -10,30 +9,31 @@ class NineTileBoxExample extends FlameGame with TapDetector, DoubleTapDetector {
Tap to make the box bigger and double tap to make it smaller.
''';

late NineTileBox nineTileBox;
final Vector2 boxSize = Vector2.all(300);
late NineTileBoxComponent nineTileBoxComponent;

@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(await images.load('nine-box.png'));
nineTileBox = NineTileBox(sprite, tileSize: 8, destTileSize: 24);
}

@override
void render(Canvas canvas) {
super.render(canvas);
final position = (size - boxSize) / 2;
nineTileBox.draw(canvas, position, boxSize);
final boxSize = Vector2.all(300);
final nineTileBox = NineTileBox(sprite, destTileSize: 148);
add(
nineTileBoxComponent = NineTileBoxComponent(
nineTileBox: nineTileBox,
position: size / 2,
size: boxSize,
anchor: Anchor.center,
),
);
}

@override
void onTap() {
boxSize.scale(1.2);
nineTileBoxComponent.scale.scale(1.2);
}

@override
void onDoubleTap() {
boxSize.scale(0.8);
nineTileBoxComponent.scale.scale(0.8);
}
}
4 changes: 2 additions & 2 deletions examples/lib/stories/widgets/nine_tile_box_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Widget nineTileBoxBuilder(DashbookContext ctx) {
return Container(
width: ctx.numberProperty('width', 200),
height: ctx.numberProperty('height', 200),
child: NineTileBox.asset(
child: NineTileBoxWidget.asset(
path: 'nine-box.png',
tileSize: 8,
tileSize: 22,
destTileSize: 50,
child: const Center(
child: Text(
Expand Down
71 changes: 19 additions & 52 deletions packages/flame/lib/src/nine_tile_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,81 +15,48 @@ class NineTileBox {
static final _whitePaint = BasicPalette.white.paint();

/// The sprite used to render the box, must be a 3x3 grid of square tiles.
Sprite sprite;
final Sprite sprite;

/// The size of each tile in the source sprite image.
int tileSize;
final int tileSize;

/// The size each tile becomes when rendered
/// (optionally used to scale the src image).
late int destTileSize;

late final Rect _center;
late final Rect _dst;

/// Creates a nine-box instance.
///
/// [sprite] is the 3x3 grid and [tileSize] is the size of each tile.
/// The src sprite must a square of size 3*[tileSize].
/// The src sprite must be a square of size 3*[tileSize].
///
/// If [tileSize] is not provided, the width of the sprite is assumed as the
/// size. Otherwise the width and height properties of the sprite are ignored.
///
/// If [destTileSize] is not provided, the evaluated [tileSize] is used
/// instead (so no scaling happens).
NineTileBox(this.sprite, {int? tileSize, int? destTileSize})
: tileSize = tileSize ?? sprite.src.width.toInt() {
: tileSize = tileSize ?? sprite.src.width ~/ 3 {
this.destTileSize = destTileSize ?? this.tileSize;
final centerEdge = this.tileSize.toDouble();
_center = Rect.fromLTWH(centerEdge, centerEdge, centerEdge, centerEdge);
_dst = Rect.fromLTWH(0, 0, this.destTileSize * 3, this.destTileSize * 3);
}

/// Renders this nine box with the dimensions provided by [rect].
void drawRect(Canvas c, Rect rect) {
final position = Vector2(rect.left, rect.top);
final size = Vector2(rect.width, rect.height);
draw(c, position, size);
/// Renders this nine box with the dimensions provided by [dst].
void drawRect(Canvas c, [Rect? dst]) {
c.drawImageNine(sprite.image, _center, dst ?? _dst, _whitePaint);
}

/// Renders this nine box as a rectangle at [position] with size [size].
void draw(Canvas c, Vector2 position, Vector2 size) {
// corners
_drawTile(c, _getDest(position), 0, 0);
final bottomLeft = position + Vector2(0, size.y - destTileSize);
_drawTile(c, _getDest(bottomLeft), 0, 2);
final topRight = position + Vector2(size.x - destTileSize, 0);
_drawTile(c, _getDest(topRight), 2, 0);
final bottomRight = Vector2(topRight.x, bottomLeft.y);
_drawTile(c, _getDest(bottomRight), 2, 2);

// horizontal sides
final mx = size.x - 2 * destTileSize;
final middleLeft = position + Vector2Extension.fromInts(destTileSize, 0);
_drawTile(c, _getDest(middleLeft, width: mx), 1, 0);
final middleRight = middleLeft + Vector2(0, size.y - destTileSize);
_drawTile(c, _getDest(middleRight, width: mx), 1, 2);

// vertical sides
final my = size.y - 2 * destTileSize;
final topCenter = position + Vector2Extension.fromInts(0, destTileSize);
_drawTile(c, _getDest(topCenter, height: my), 0, 1);
final bottomCenter = topCenter + Vector2(size.x - destTileSize, 0);
_drawTile(c, _getDest(bottomCenter, height: my), 2, 1);

// center
final center = position + Vector2.all(destTileSize.toDouble());
_drawTile(c, _getDest(center, width: mx, height: my), 1, 1);
}

Rect _getDest(Vector2 position, {double? width, double? height}) {
final w = width ?? _destTileSizeDouble;
final h = height ?? _destTileSizeDouble;
return Rect.fromLTWH(position.x, position.y, w, h);
}

double get _tileSizeDouble => tileSize.toDouble();

double get _destTileSizeDouble => destTileSize.toDouble();

void _drawTile(Canvas c, Rect dest, int i, int j) {
final xSrc = sprite.src.left + _tileSizeDouble * i;
final ySrc = sprite.src.top + _tileSizeDouble * j;
final src = Rect.fromLTWH(xSrc, ySrc, _tileSizeDouble, _tileSizeDouble);
c.drawImageRect(sprite.image, src, dest, _whitePaint);
c.drawImageNine(
sprite.image,
_center,
Rect.fromLTWH(position.x, position.y, size.x, size.y),
_whitePaint,
);
}
}
114 changes: 15 additions & 99 deletions packages/flame/lib/src/widgets/nine_tile_box.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

import '../../assets.dart';
import '../../flame.dart';
import '../extensions/vector2.dart';
import '../nine_tile_box.dart' as non_widget;
import '../sprite.dart';
import 'base_future_builder.dart';

Expand All @@ -15,117 +16,32 @@ class _Painter extends CustomPainter {
final ui.Image image;
final double tileSize;
final double destTileSize;
late final non_widget.NineTileBox _nineTileBox;

_Painter({
required this.image,
required this.tileSize,
required this.destTileSize,
});

Sprite _getSpriteTile(double x, double y) =>
Sprite(image, srcPosition: Vector2(x, y), srcSize: Vector2.all(tileSize));
}) : _nineTileBox = non_widget.NineTileBox(
Sprite(image),
tileSize: tileSize.toInt(),
destTileSize: destTileSize.toInt(),
);

@override
void paint(Canvas canvas, Size size) {
final topLeftCorner = _getSpriteTile(0, 0);
final topRightCorner = _getSpriteTile(tileSize * 2, 0);

final bottomLeftCorner = _getSpriteTile(0, 2 * tileSize);
final bottomRightCorner = _getSpriteTile(tileSize * 2, 2 * tileSize);

final topSide = _getSpriteTile(tileSize, 0);
final bottomSide = _getSpriteTile(tileSize, tileSize * 2);

final leftSide = _getSpriteTile(0, tileSize);
final rightSide = _getSpriteTile(tileSize * 2, tileSize);

final middle = _getSpriteTile(tileSize, tileSize);

final horizontalWidget = size.width - destTileSize * 2;
final verticalHeight = size.height - destTileSize * 2;

void render(Sprite sprite, double x, double y, double w, double h) {
sprite.render(canvas, position: Vector2(x, y), size: Vector2(w, h));
}

// Middle
render(
middle,
destTileSize,
destTileSize,
horizontalWidget,
verticalHeight,
);

// Top and bottom side
render(
topSide,
destTileSize,
0,
horizontalWidget,
destTileSize,
);
render(
bottomSide,
destTileSize,
size.height - destTileSize,
horizontalWidget,
destTileSize,
);

// Left and right side
render(
leftSide,
0,
destTileSize,
destTileSize,
verticalHeight,
);
render(
rightSide,
size.width - destTileSize,
destTileSize,
destTileSize,
verticalHeight,
);

// Corners
render(
topLeftCorner,
0,
0,
destTileSize,
destTileSize,
);
render(
topRightCorner,
size.width - destTileSize,
0,
destTileSize,
destTileSize,
);
render(
bottomLeftCorner,
0,
size.height - destTileSize,
destTileSize,
destTileSize,
);
render(
bottomRightCorner,
size.width - destTileSize,
size.height - destTileSize,
destTileSize,
destTileSize,
);
_nineTileBox.drawRect(canvas, Offset.zero & size);
}

@override
bool shouldRepaint(_) => false;
}

@Deprecated('Renamed to [NineTileBoxWidget]')
typedef NineTileBox = NineTileBoxWidget;

/// A [StatelessWidget] that renders NineTileBox
class NineTileBox extends StatelessWidget {
class NineTileBoxWidget extends StatelessWidget {
final Future<ui.Image> Function() _imageFuture;

/// The size of the tile on the image
Expand All @@ -144,7 +60,7 @@ class NineTileBox extends StatelessWidget {
/// A builder function that is called while the loading is on the way
final WidgetBuilder? loadingBuilder;

NineTileBox({
NineTileBoxWidget({
required ui.Image image,
required this.tileSize,
required this.destTileSize,
Expand All @@ -157,7 +73,7 @@ class NineTileBox extends StatelessWidget {
}) : _imageFuture = (() => Future.value(image)),
super(key: key);

NineTileBox.asset({
NineTileBoxWidget.asset({
required String path,
required this.tileSize,
required this.destTileSize,
Expand Down

0 comments on commit d77e5ef

Please sign in to comment.