Add image atlas and nine-slice support
This commit is contained in:
@@ -17,6 +17,102 @@ Color composeRuntimeColorAlpha(Color color, double alpha) {
|
||||
return color.withValues(alpha: color.a * alpha.clamp(0.0, 1.0));
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
Rect runtimeImageSourceRect({
|
||||
required double imageWidth,
|
||||
required double imageHeight,
|
||||
double? sourceX,
|
||||
double? sourceY,
|
||||
double? sourceWidth,
|
||||
double? sourceHeight,
|
||||
}) {
|
||||
final x = (sourceX ?? 0).clamp(0.0, imageWidth).toDouble();
|
||||
final y = (sourceY ?? 0).clamp(0.0, imageHeight).toDouble();
|
||||
final maxWidth = imageWidth - x;
|
||||
final maxHeight = imageHeight - y;
|
||||
final width = (sourceWidth ?? maxWidth).clamp(0.0, maxWidth).toDouble();
|
||||
final height = (sourceHeight ?? maxHeight).clamp(0.0, maxHeight).toDouble();
|
||||
return Rect.fromLTWH(x, y, width, height);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
List<({Rect source, Rect destination})> runtimeNineSliceRects({
|
||||
required Rect source,
|
||||
required Rect destination,
|
||||
double sliceLeft = 0,
|
||||
double sliceTop = 0,
|
||||
double sliceRight = 0,
|
||||
double sliceBottom = 0,
|
||||
}) {
|
||||
if (source.width <= 0 ||
|
||||
source.height <= 0 ||
|
||||
destination.width <= 0 ||
|
||||
destination.height <= 0) {
|
||||
return const [];
|
||||
}
|
||||
final left = sliceLeft.clamp(0.0, source.width).toDouble();
|
||||
final top = sliceTop.clamp(0.0, source.height).toDouble();
|
||||
final right = sliceRight.clamp(0.0, source.width - left).toDouble();
|
||||
final bottom = sliceBottom.clamp(0.0, source.height - top).toDouble();
|
||||
final destLeft = left.clamp(0.0, destination.width).toDouble();
|
||||
final destTop = top.clamp(0.0, destination.height).toDouble();
|
||||
final destRight = right.clamp(0.0, destination.width - destLeft).toDouble();
|
||||
final destBottom = bottom
|
||||
.clamp(0.0, destination.height - destTop)
|
||||
.toDouble();
|
||||
|
||||
final sourceXs = [
|
||||
source.left,
|
||||
source.left + left,
|
||||
source.right - right,
|
||||
source.right,
|
||||
];
|
||||
final sourceYs = [
|
||||
source.top,
|
||||
source.top + top,
|
||||
source.bottom - bottom,
|
||||
source.bottom,
|
||||
];
|
||||
final destXs = [
|
||||
destination.left,
|
||||
destination.left + destLeft,
|
||||
destination.right - destRight,
|
||||
destination.right,
|
||||
];
|
||||
final destYs = [
|
||||
destination.top,
|
||||
destination.top + destTop,
|
||||
destination.bottom - destBottom,
|
||||
destination.bottom,
|
||||
];
|
||||
|
||||
final parts = <({Rect source, Rect destination})>[];
|
||||
for (var y = 0; y < 3; y++) {
|
||||
for (var x = 0; x < 3; x++) {
|
||||
final sourcePart = Rect.fromLTRB(
|
||||
sourceXs[x],
|
||||
sourceYs[y],
|
||||
sourceXs[x + 1],
|
||||
sourceYs[y + 1],
|
||||
);
|
||||
final destPart = Rect.fromLTRB(
|
||||
destXs[x],
|
||||
destYs[y],
|
||||
destXs[x + 1],
|
||||
destYs[y + 1],
|
||||
);
|
||||
if (sourcePart.width <= 0 ||
|
||||
sourcePart.height <= 0 ||
|
||||
destPart.width <= 0 ||
|
||||
destPart.height <= 0) {
|
||||
continue;
|
||||
}
|
||||
parts.add((source: sourcePart, destination: destPart));
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
class RuntimeComponent extends PositionComponent
|
||||
with HasVisibility, TapCallbacks {
|
||||
RuntimeComponent({
|
||||
@@ -431,12 +527,14 @@ class RuntimeComponent extends PositionComponent
|
||||
(_node.type == RuntimeNodeType.sprite ||
|
||||
_node.type == RuntimeNodeType.image ||
|
||||
_node.type == RuntimeNodeType.button)) {
|
||||
canvas.drawImageRect(
|
||||
image,
|
||||
Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()),
|
||||
rect,
|
||||
Paint()..color = composeRuntimeColorAlpha(Colors.white, renderAlpha),
|
||||
);
|
||||
final imagePaint = Paint()
|
||||
..color = composeRuntimeColorAlpha(Colors.white, renderAlpha);
|
||||
final source = _imageSourceRect(image);
|
||||
if (_usesNineSlice(source, rect)) {
|
||||
_drawNineSliceImage(canvas, image, source, rect, imagePaint);
|
||||
} else {
|
||||
canvas.drawImageRect(image, source, rect, imagePaint);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -448,6 +546,50 @@ class RuntimeComponent extends PositionComponent
|
||||
);
|
||||
}
|
||||
|
||||
Rect _imageSourceRect(ui.Image image) {
|
||||
return runtimeImageSourceRect(
|
||||
imageWidth: image.width.toDouble(),
|
||||
imageHeight: image.height.toDouble(),
|
||||
sourceX: _node.sourceX,
|
||||
sourceY: _node.sourceY,
|
||||
sourceWidth: _node.sourceWidth,
|
||||
sourceHeight: _node.sourceHeight,
|
||||
);
|
||||
}
|
||||
|
||||
bool _usesNineSlice(Rect source, Rect destination) {
|
||||
if (source.width <= 0 ||
|
||||
source.height <= 0 ||
|
||||
destination.width <= 0 ||
|
||||
destination.height <= 0) {
|
||||
return false;
|
||||
}
|
||||
return (_node.sliceLeft ?? 0) > 0 ||
|
||||
(_node.sliceTop ?? 0) > 0 ||
|
||||
(_node.sliceRight ?? 0) > 0 ||
|
||||
(_node.sliceBottom ?? 0) > 0;
|
||||
}
|
||||
|
||||
void _drawNineSliceImage(
|
||||
Canvas canvas,
|
||||
ui.Image image,
|
||||
Rect source,
|
||||
Rect destination,
|
||||
Paint paint,
|
||||
) {
|
||||
final parts = runtimeNineSliceRects(
|
||||
source: source,
|
||||
destination: destination,
|
||||
sliceLeft: _node.sliceLeft ?? 0,
|
||||
sliceTop: _node.sliceTop ?? 0,
|
||||
sliceRight: _node.sliceRight ?? 0,
|
||||
sliceBottom: _node.sliceBottom ?? 0,
|
||||
);
|
||||
for (final part in parts) {
|
||||
canvas.drawImageRect(image, part.source, part.destination, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void _applyBase(RuntimeNode node) {
|
||||
_syncVisibility();
|
||||
size = Vector2(node.width ?? 40, node.height ?? 40);
|
||||
|
||||
Reference in New Issue
Block a user