From c2c5329ef1650b66644292de9afbc7c15e129b1e Mon Sep 17 00:00:00 2001 From: Benito Palacios Sanchez Date: Tue, 19 Sep 2023 15:24:36 +0200 Subject: [PATCH] :sparkles: Improve nitro segmentation, layer reduction and fix copying OAM metadata --- src/Texim.Games/Nitro/FullImage2NitroCell.cs | 11 ++-- .../Nitro/NitroImageSegmentation.cs | 51 ++++++++++++------- src/Texim/Formats/Sprite2Tiff.cs | 31 ++++++----- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/Texim.Games/Nitro/FullImage2NitroCell.cs b/src/Texim.Games/Nitro/FullImage2NitroCell.cs index d2cadd5..976cf7e 100644 --- a/src/Texim.Games/Nitro/FullImage2NitroCell.cs +++ b/src/Texim.Games/Nitro/FullImage2NitroCell.cs @@ -32,7 +32,8 @@ public class FullImage2NitroCell : { private FullImage2NitroCellParams parameters; - private int rotationScalingGroup; + private bool hasRotationScaling; + private byte rotationScalingGroup; private bool hasDoubleSize; private bool isDisabled; private bool isMosaic; @@ -48,8 +49,10 @@ public void Initialize(FullImage2NitroCellParams parameters) // We can only guess the original metadata if every original OAMs have it // otherwise, as original and new OAMs may differ, it's hard to know. var originalOams = parameters.ReferenceCell.Segments.Cast().ToArray(); + hasRotationScaling = originalOams.DistinctBy(o => o.HasRotationOrScaling).Count() == 1 + && originalOams[0].HasRotationOrScaling; rotationScalingGroup = originalOams.DistinctBy(o => o.RotationOrScalingGroup).Count() == 1 - ? originalOams[0].RotationOrScalingGroup : -1; + ? originalOams[0].RotationOrScalingGroup : (byte)0; hasDoubleSize = originalOams.DistinctBy(obj => obj.HasDoubleSize).Count() == 1 && originalOams[0].HasDoubleSize; isDisabled = originalOams.DistinctBy(obj => obj.IsDisabled).Count() == 1 @@ -86,8 +89,8 @@ protected override IImageSegment AssignImageToSegment(IImageSegment segmentStruc ? NitroPaletteMode.Palette256x1 : NitroPaletteMode.Palette16x16; - nitroCell.RotationOrScalingGroup = (rotationScalingGroup != -1) ? (byte)rotationScalingGroup : (byte)0; - nitroCell.HasRotationOrScaling = rotationScalingGroup != -1; + nitroCell.HasRotationOrScaling = hasRotationScaling; + nitroCell.RotationOrScalingGroup = rotationScalingGroup; nitroCell.HasDoubleSize = hasDoubleSize; nitroCell.IsMosaic = isMosaic; nitroCell.IsDisabled = isDisabled; diff --git a/src/Texim.Games/Nitro/NitroImageSegmentation.cs b/src/Texim.Games/Nitro/NitroImageSegmentation.cs index 15c634f..90b5f96 100644 --- a/src/Texim.Games/Nitro/NitroImageSegmentation.cs +++ b/src/Texim.Games/Nitro/NitroImageSegmentation.cs @@ -31,8 +31,11 @@ namespace Texim.Games.Nitro; /// public class NitroImageSegmentation : IImageSegmentation { + // We define two modes but only use the second one so far (75% non-transparent) + // For each mode there is a list of tries for width and height. // First value is the limit and the second is the side. // From limit to side there must be non-transparent pixels to set it. + // So that it's worthier a bigger cell than two small ones. private static readonly int[][,] Modes = { new int[,] { { 32, 64 }, { 16, 32 }, { 8, 16 }, { 0, 8 } }, // 50% new int[,] { { 48, 64 }, { 24, 32 }, { 8, 16 }, { 0, 8 } }, // 75% @@ -108,7 +111,15 @@ private List CreateObjects(FullImage frame, int startX, int start return segments; } - (int width, int height) = GetObjectSize(frame, x, y, frame.Width, maxHeight - diffY); + int width, height; + + // If our cell is already valid, do not split further. + if (IsValidSize(frame.Width, maxHeight - diffY)) { + width = frame.Width; + height = maxHeight - diffY; + } else { + (width, height) = GetObjectSize(frame, x, y, frame.Width, maxHeight - diffY); + } if (width != 0 && height != 0) { var segment = new ImageSegment { @@ -205,23 +216,27 @@ private List CreateObjects(FullImage frame, int startX, int start Justification = "Readability of the algorithm")] private static bool IsValidSize(int width, int height) { - if (width < 0 || width > 64 || width % 8 != 0) { - return false; - } - - if (height < 0 || height > 64 || height % 8 != 0) { - return false; - } - - if (width == 64 && (height == 8 || height == 16)) { - return false; - } - - if ((width == 8 || width == 16) && height == 64) { - return false; - } - - return true; + return (width, height) switch { + // Square mode + (8, 8) => true, + (16, 16) => true, + (32, 32) => true, + (64, 64) => true, + + // Rectangle horizontal + (16, 8) => true, + (32, 8) => true, + (32, 16) => true, + (64, 32) => true, + + // Rectangle vertical + (8, 16) => true, + (8, 32) => true, + (16, 32) => true, + (32, 64) => true, + + _ => false, + }; } private static (int X, int Y, FullImage Trimmed) TrimImage(FullImage image) diff --git a/src/Texim/Formats/Sprite2Tiff.cs b/src/Texim/Formats/Sprite2Tiff.cs index 6edbfed..52cdb9d 100644 --- a/src/Texim/Formats/Sprite2Tiff.cs +++ b/src/Texim/Formats/Sprite2Tiff.cs @@ -136,12 +136,12 @@ private bool AddSegmentIfSameLayer(IImageSegment segment) return false; } - var segmentBounds = new Rectangle(segment.CoordinateX, segment.CoordinateY, segment.Width, segment.Height); - if (IntersectsWithLayer(segment)) { + var segmentBounds = CreateRectangle(segment); + if (IntersectsWithLayer(segmentBounds)) { return false; } - if (!IsAdjacentWithLayer(segment)) { + if (!IsAdjacentWithLayer(segmentBounds)) { return false; } @@ -156,21 +156,28 @@ private bool AddSegmentIfSameLayer(IImageSegment segment) return true; } - private bool IntersectsWithLayer(IImageSegment segment) + private bool IntersectsWithLayer(Rectangle segmentBounds) { - var segmentBounds = CreateRectangle(segment); return layerSegments.Exists(s => CreateRectangle(s).IntersectsWith(segmentBounds)); } - private bool IsAdjacentWithLayer(IImageSegment segment) + private bool IsAdjacentWithLayer(Rectangle segmentBounds) { - static bool IsAdjacent(Rectangle rect1, Rectangle rect2) => - (rect1.Left == rect2.Right) - || (rect1.Top == rect2.Bottom) - || (rect1.Right == rect2.Left) - || (rect1.Bottom == rect2.Top); + static bool IsAdjacent(Rectangle rect1, Rectangle rect2) + { + if ((rect1.Left == rect2.Right) || (rect1.Right == rect2.Left)) { + return ((rect1.Top <= rect2.Top) && (rect1.Bottom >= rect2.Bottom)) || + ((rect2.Top <= rect1.Top) && (rect2.Bottom >= rect1.Bottom)); + } + + if ((rect1.Top == rect2.Bottom) || (rect1.Bottom == rect2.Top)) { + return ((rect1.Left <= rect2.Left) && (rect1.Right >= rect2.Right)) || + ((rect2.Left <= rect1.Left) && (rect2.Right >= rect1.Right)); + } + + return false; + } - var segmentBounds = CreateRectangle(segment); return layerSegments.Exists(s => IsAdjacent(CreateRectangle(s), segmentBounds)); } }