Improve nine-slice atlas sampling
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
## Unreleased
|
||||
|
||||
- Added TexturePacker frame, manual source-region, and nine-slice image rendering fields for image-capable nodes.
|
||||
- Fixed nine-slice image seams by overlapping destination slices during runtime rendering.
|
||||
- Fixed nine-slice image seams by overlapping destination slices and using inset source sampling during runtime rendering.
|
||||
- Fixed Runtime alpha inheritance so parent fade commands apply to the full child subtree.
|
||||
- Added Runtime text shadow fields for text-capable nodes.
|
||||
- Fixed Runtime node color alpha composition so `#AARRGGBB` alpha now multiplies with node/runtime alpha instead of being overwritten.
|
||||
|
||||
@@ -44,6 +44,7 @@ List<({Rect source, Rect destination})> runtimeNineSliceRects({
|
||||
double sliceRight = 0,
|
||||
double sliceBottom = 0,
|
||||
double destinationOverlap = 0,
|
||||
double sourceInset = 0,
|
||||
}) {
|
||||
if (source.width <= 0 ||
|
||||
source.height <= 0 ||
|
||||
@@ -88,7 +89,7 @@ List<({Rect source, Rect destination})> runtimeNineSliceRects({
|
||||
final parts = <({Rect source, Rect destination})>[];
|
||||
for (var y = 0; y < 3; y++) {
|
||||
for (var x = 0; x < 3; x++) {
|
||||
final sourcePart = Rect.fromLTRB(
|
||||
final rawSourcePart = Rect.fromLTRB(
|
||||
sourceXs[x],
|
||||
sourceYs[y],
|
||||
sourceXs[x + 1],
|
||||
@@ -100,12 +101,17 @@ List<({Rect source, Rect destination})> runtimeNineSliceRects({
|
||||
destXs[x + 1],
|
||||
destYs[y + 1],
|
||||
);
|
||||
if (sourcePart.width <= 0 ||
|
||||
sourcePart.height <= 0 ||
|
||||
if (rawSourcePart.width <= 0 ||
|
||||
rawSourcePart.height <= 0 ||
|
||||
rawDestPart.width <= 0 ||
|
||||
rawDestPart.height <= 0) {
|
||||
continue;
|
||||
}
|
||||
final sourcePart = _insetNineSliceSourceRect(
|
||||
rawSourcePart,
|
||||
bounds: source,
|
||||
inset: sourceInset,
|
||||
);
|
||||
final destPart = _overlapNineSliceDestinationRect(
|
||||
rawDestPart,
|
||||
x: x,
|
||||
@@ -119,6 +125,22 @@ List<({Rect source, Rect destination})> runtimeNineSliceRects({
|
||||
return parts;
|
||||
}
|
||||
|
||||
Rect _insetNineSliceSourceRect(
|
||||
Rect rect, {
|
||||
required Rect bounds,
|
||||
required double inset,
|
||||
}) {
|
||||
if (inset <= 0 || rect.width <= inset * 2 || rect.height <= inset * 2) {
|
||||
return rect;
|
||||
}
|
||||
return Rect.fromLTRB(
|
||||
math.min(rect.right, math.max(bounds.left, rect.left + inset)),
|
||||
math.min(rect.bottom, math.max(bounds.top, rect.top + inset)),
|
||||
math.max(rect.left, math.min(bounds.right, rect.right - inset)),
|
||||
math.max(rect.top, math.min(bounds.bottom, rect.bottom - inset)),
|
||||
);
|
||||
}
|
||||
|
||||
Rect _overlapNineSliceDestinationRect(
|
||||
Rect rect, {
|
||||
required int x,
|
||||
@@ -575,7 +597,13 @@ class RuntimeComponent extends PositionComponent
|
||||
..color = composeRuntimeColorAlpha(Colors.white, renderAlpha);
|
||||
final source = _imageSourceRect(image, _currentImageFrame(_node));
|
||||
if (_usesNineSlice(source, rect)) {
|
||||
_drawNineSliceImage(canvas, image, source, rect, imagePaint);
|
||||
_drawNineSliceImage(
|
||||
canvas,
|
||||
image,
|
||||
source,
|
||||
rect,
|
||||
imagePaint..filterQuality = FilterQuality.none,
|
||||
);
|
||||
} else {
|
||||
canvas.drawImageRect(image, source, rect, imagePaint);
|
||||
}
|
||||
@@ -630,7 +658,8 @@ class RuntimeComponent extends PositionComponent
|
||||
final parts = runtimeNineSliceRects(
|
||||
source: source,
|
||||
destination: destination,
|
||||
destinationOverlap: 0.5,
|
||||
destinationOverlap: 1,
|
||||
sourceInset: 0.5,
|
||||
sliceLeft: _node.sliceLeft ?? 0,
|
||||
sliceTop: _node.sliceTop ?? 0,
|
||||
sliceRight: _node.sliceRight ?? 0,
|
||||
|
||||
@@ -284,6 +284,36 @@ void main() {
|
||||
expect(parts[4].source, const Rect.fromLTRB(10, 10, 20, 20));
|
||||
});
|
||||
|
||||
test('insets nine-slice source rects to avoid atlas edge sampling', () {
|
||||
final parts = runtimeNineSliceRects(
|
||||
source: const Rect.fromLTWH(10, 20, 30, 30),
|
||||
destination: const Rect.fromLTWH(0, 0, 90, 90),
|
||||
sliceLeft: 10,
|
||||
sliceTop: 10,
|
||||
sliceRight: 10,
|
||||
sliceBottom: 10,
|
||||
sourceInset: 0.5,
|
||||
);
|
||||
|
||||
expect(parts.first.source, const Rect.fromLTRB(10.5, 20.5, 19.5, 29.5));
|
||||
expect(parts[4].source, const Rect.fromLTRB(20.5, 30.5, 29.5, 39.5));
|
||||
expect(parts.last.source, const Rect.fromLTRB(30.5, 40.5, 39.5, 49.5));
|
||||
});
|
||||
|
||||
test('keeps tiny nine-slice source rects when inset would collapse them', () {
|
||||
final parts = runtimeNineSliceRects(
|
||||
source: const Rect.fromLTWH(0, 0, 3, 3),
|
||||
destination: const Rect.fromLTWH(0, 0, 30, 30),
|
||||
sliceLeft: 1,
|
||||
sliceTop: 1,
|
||||
sliceRight: 1,
|
||||
sliceBottom: 1,
|
||||
sourceInset: 0.5,
|
||||
);
|
||||
|
||||
expect(parts[4].source, const Rect.fromLTRB(1, 1, 2, 2));
|
||||
});
|
||||
|
||||
test('updates text alpha style without rebuilding text component', () {
|
||||
final component = RuntimeComponent(
|
||||
node: const RuntimeNode(
|
||||
|
||||
Reference in New Issue
Block a user