using System; using System.Drawing; namespace Blacklight.Core.UnitTests { public class SpriteSheetTests : UnitTestSharp.TestFixture { public static readonly byte[] Transparent = MipChainTests.Pixel(0, 0, 0, 0); public static readonly byte[] Red = MipChainTests.Pixel(255, 0, 0, 255); public static readonly byte[] Green = MipChainTests.Pixel(0, 255, 0, 255); public static readonly byte[] Blue = MipChainTests.Pixel(0, 0, 255, 255); public static readonly byte[] White = MipChainTests.Pixel(255, 255, 255, 255); public class RecordingWindowHandle : NullWindowHandle { public DecodedImage[] Levels { get; private set; } protected override Texture CreateTextureFromLevels(DecodedImage[] levels) { Levels = levels; return base.CreateTextureFromLevels(levels); } } public class OwnedTexture : Texture { } public class StrideForTests : UnitTestSharp.TestFixture { public void SizeEveryLevelHalvesEvenly_IsUnchanged() { CheckEqual(new Size(16, 16), SpriteSheet.StrideFor(new Size(16, 16))); CheckEqual(new Size(8, 4), SpriteSheet.StrideFor(new Size(8, 4))); } public void SizeThatDoesntHalveEvenly_RoundsUpToWhatEveryLevelNeeds() { CheckEqual(new Size(16, 16), SpriteSheet.StrideFor(new Size(12, 12))); CheckEqual(new Size(4, 4), SpriteSheet.StrideFor(new Size(3, 3))); } public void NonSquareSize_RoundsEachSideForTheSharedLevelCount() { CheckEqual(new Size(16, 8), SpriteSheet.StrideFor(new Size(12, 8))); } public void OnePixelSprite_IsOnePixel() { CheckEqual(new Size(1, 1), SpriteSheet.StrideFor(new Size(1, 1))); } public void EmptySprite_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); SpriteSheet.StrideFor(new Size(0, 8)); } } public class PackTests : UnitTestSharp.TestFixture { public void DropsTheBorderBetweenFrames() { DecodedImage image = MipChainTests.Image(new Size(5, 2), (x, y) => { if (x < 2) { return Red; } return x == 2 ? Green : Blue; }); DecodedImage packed = SpriteSheet.Pack(image, new Size(2, 2), borderSize: 1, stride: new Size(2, 2)); CheckEqual(new Size(4, 2), packed.Size); CheckEqual(Red, MipChainTests.PixelAt(packed, 1, 1)); CheckEqual(Blue, MipChainTests.PixelAt(packed, 2, 0)); } public void LeavesClearSpaceBetweenFramesWhenTheStrideIsLarger() { DecodedImage image = MipChainTests.Image(new Size(6, 3), (x, y) => x < 3 ? Red : Blue); DecodedImage packed = SpriteSheet.Pack(image, new Size(3, 3), borderSize: 0, stride: new Size(4, 4)); CheckEqual(new Size(8, 4), packed.Size); CheckEqual(Red, MipChainTests.PixelAt(packed, 2, 2)); CheckEqual(Transparent, MipChainTests.PixelAt(packed, 3, 0)); CheckEqual(Transparent, MipChainTests.PixelAt(packed, 0, 3)); CheckEqual(Blue, MipChainTests.PixelAt(packed, 4, 0)); CheckEqual(Blue, MipChainTests.PixelAt(packed, 6, 2)); CheckEqual(Transparent, MipChainTests.PixelAt(packed, 7, 3)); } public void RowsOfFrames_LandOnTheStrideBothAcrossAndDown() { DecodedImage image = MipChainTests.Image(new Size(4, 4), (x, y) => { if (y < 2) { return x < 2 ? Red : Green; } return x < 2 ? Blue : White; }); DecodedImage packed = SpriteSheet.Pack(image, new Size(2, 2), borderSize: 0, stride: new Size(4, 4)); CheckEqual(new Size(8, 8), packed.Size); CheckEqual(Red, MipChainTests.PixelAt(packed, 1, 1)); CheckEqual(Green, MipChainTests.PixelAt(packed, 5, 1)); CheckEqual(Blue, MipChainTests.PixelAt(packed, 1, 5)); CheckEqual(White, MipChainTests.PixelAt(packed, 5, 5)); CheckEqual(Transparent, MipChainTests.PixelAt(packed, 2, 2)); } public void KeepsTheAlphaModeAndColorSpace() { DecodedImage image = MipChainTests.Solid( new Size(2, 2), Red, DecodedImage.AlphaMode.Premultiplied, isLinear: true); DecodedImage packed = SpriteSheet.Pack(image, new Size(2, 2), borderSize: 0, stride: new Size(2, 2)); CheckEqual(DecodedImage.AlphaMode.Premultiplied, packed.Alpha); CheckTrue(packed.IsLinear); } public void NullImage_Throws() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Pack(null, new Size(2, 2), borderSize: 0, stride: new Size(2, 2)); } public void NegativeBorder_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new Size(2, 2), borderSize: -1, stride: new Size(2, 2)); } public void EmptySprite_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new Size(0, 2), borderSize: 0, stride: new Size(2, 2)); } public void StrideSmallerThanTheSprite_Throws() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new Size(2, 2), borderSize: 0, stride: new Size(2, 1)); } public void ImageThatIsntAWholeGrid_Throws() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(5, 2), Red), new Size(2, 2), borderSize: 0, stride: new Size(2, 2)); } public void ImageSmallerThanOneSprite_Throws() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(1, 1), Red), new Size(2, 2), borderSize: 0, stride: new Size(2, 2)); } public void Rects_CopyEachSourceToItsDestination() { DecodedImage image = MipChainTests.Image(new Size(4, 2), (x, y) => x < 2 ? Red : Blue); DecodedImage packed = SpriteSheet.Pack( image, new[] { new Rectangle(0, 0, 2, 2), new Rectangle(2, 0, 2, 2) }, new[] { new Point(2, 0), new Point(0, 0) }, new Size(4, 2)); CheckEqual(Blue, MipChainTests.PixelAt(packed, 0, 0)); CheckEqual(Blue, MipChainTests.PixelAt(packed, 1, 1)); CheckEqual(Red, MipChainTests.PixelAt(packed, 2, 0)); CheckEqual(Red, MipChainTests.PixelAt(packed, 3, 1)); } public void Rects_LeaveEverythingElseClear() { DecodedImage image = MipChainTests.Solid(new Size(2, 2), Red); DecodedImage packed = SpriteSheet.Pack( image, new[] { new Rectangle(0, 0, 2, 2) }, new[] { new Point(1, 1) }, new Size(6, 3)); CheckEqual(new Size(6, 3), packed.Size); CheckEqual(Transparent, MipChainTests.PixelAt(packed, 0, 0)); CheckEqual(Transparent, MipChainTests.PixelAt(packed, 3, 1)); CheckEqual(Red, MipChainTests.PixelAt(packed, 1, 1)); CheckEqual(Red, MipChainTests.PixelAt(packed, 2, 2)); } public void Rects_KeepTheAlphaModeAndColorSpace() { DecodedImage image = MipChainTests.Solid( new Size(2, 2), Red, DecodedImage.AlphaMode.Premultiplied, isLinear: true); DecodedImage packed = SpriteSheet.Pack( image, new[] { new Rectangle(0, 0, 2, 2) }, new[] { new Point(0, 0) }, new Size(2, 2)); CheckEqual(DecodedImage.AlphaMode.Premultiplied, packed.Alpha); CheckTrue(packed.IsLinear); } public void Rects_NullImage_Throws() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Pack( null, new[] { new Rectangle(0, 0, 2, 2) }, new[] { new Point(0, 0) }, new Size(2, 2)); } public void Rects_NullSources_Throw() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), null, new[] { new Point(0, 0) }, new Size(2, 2)); } public void Rects_NullDestinations_Throw() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new[] { new Rectangle(0, 0, 2, 2) }, null, new Size(2, 2)); } public void Rects_WithFewerDestinationsThanSources_Throw() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new[] { new Rectangle(0, 0, 2, 2) }, new Point[0], new Size(2, 2)); } public void Rects_SourceOutsideTheImage_Throws() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new[] { new Rectangle(1, 0, 2, 2) }, new[] { new Point(0, 0) }, new Size(4, 4)); } public void Rects_DestinationOutsideTheAtlas_Throws() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Pack( MipChainTests.Solid(new Size(2, 2), Red), new[] { new Rectangle(0, 0, 2, 2) }, new[] { new Point(3, 0) }, new Size(4, 4)); } } public class LayOutTests : UnitTestSharp.TestFixture { private static int AlignmentOf(Size size) { return 1 << (MipChain.LevelCountFor(size) - 1); } private static int Halve(int size) { return (size + 1) / 2; } public void OneSprite_SitsInTheCornerOfAnAtlasItFills() { Point[] origins = SpriteSheet.LayOut(new[] { new Size(8, 8) }, out Size atlasSize); CheckEqual(new Point(0, 0), origins[0]); CheckEqual(new Size(8, 8), atlasSize); } public void OneSprite_RoundsItsAtlasUpToWhatEveryLevelNeeds() { SpriteSheet.LayOut(new[] { new Size(5, 5) }, out Size atlasSize); CheckEqual(new Size(8, 8), atlasSize); } public void NoSprites_NeedNoSpace() { Point[] origins = SpriteSheet.LayOut(new Size[0], out Size atlasSize); CheckEqual(0, origins.Length); CheckEqual(new Size(0, 0), atlasSize); } public void Origins_ComeBackInTheOrderTheSizesWentIn() { Point[] origins = SpriteSheet.LayOut(new[] { new Size(2, 2), new Size(8, 8) }, out Size atlasSize); CheckEqual(new Point(8, 0), origins[0]); CheckEqual(new Point(0, 0), origins[1]); CheckEqual(new Size(16, 8), atlasSize); } public void SpritesOfTheSameSize_FillRowsInTheOrderTheyWereGiven() { Point[] origins = SpriteSheet.LayOut( new[] { new Size(8, 8), new Size(8, 8), new Size(8, 8), new Size(8, 8) }, out Size atlasSize); CheckEqual(new Point(0, 0), origins[0]); CheckEqual(new Point(8, 0), origins[1]); CheckEqual(new Point(0, 8), origins[2]); CheckEqual(new Point(8, 8), origins[3]); CheckEqual(new Size(16, 16), atlasSize); } public void ASpriteWiderThanTheRest_GetsAWholeRow() { Point[] origins = SpriteSheet.LayOut(new[] { new Size(32, 2), new Size(2, 2) }, out Size atlasSize); CheckEqual(new Point(0, 0), origins[0]); CheckEqual(new Point(0, 2), origins[1]); CheckEqual(new Size(32, 4), atlasSize); } public void SpritesOfManySizes_StartOnAWholePixelAtEveryLevelAndNeverShareOne() { var sizes = new[] { new Size(16, 16), new Size(12, 12), new Size(3, 3), new Size(4, 2), new Size(1, 1), new Size(7, 5), new Size(1, 8), new Size(8, 4), new Size(13, 7), new Size(2, 30), }; Point[] origins = SpriteSheet.LayOut(sizes, out Size atlasSize); int mostLevels = 0; foreach (Size size in sizes) { mostLevels = Math.Max(mostLevels, MipChain.LevelCountFor(size)); } CheckEqual(0, atlasSize.Width % (1 << (mostLevels - 1))); CheckEqual(0, atlasSize.Height % (1 << (mostLevels - 1))); for (int level = 0; level < mostLevels; ++level) { var rectangles = new System.Collections.Generic.List(); for (int i = 0; i < sizes.Length; ++i) { if (level >= MipChain.LevelCountFor(sizes[i])) { continue; } CheckEqual(0, origins[i].X % AlignmentOf(sizes[i])); CheckEqual(0, origins[i].Y % AlignmentOf(sizes[i])); Size levelSize = sizes[i]; for (int halving = 0; halving < level; ++halving) { levelSize = new Size(Halve(levelSize.Width), Halve(levelSize.Height)); } var rectangle = new Rectangle( origins[i].X >> level, origins[i].Y >> level, levelSize.Width, levelSize.Height); foreach (Rectangle other in rectangles) { CheckFalse(rectangle.IntersectsWith(other)); } CheckTrue(rectangle.Right <= atlasSize.Width >> level); CheckTrue(rectangle.Bottom <= atlasSize.Height >> level); rectangles.Add(rectangle); } } } public void NullSizes_Throw() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.LayOut(null, out Size _); } public void ZeroSize_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); SpriteSheet.LayOut(new[] { new Size(0, 4) }, out Size _); } } public class CreateTests : UnitTestSharp.TestFixture { public void CreatesTheTextureOnTheWindow() { var window = new RecordingWindowHandle(); SpriteSheet sheet = SpriteSheet.Create( window, MipChainTests.Solid(new Size(4, 4), Red), new Size(4, 4), borderSize: 0); CheckNotNull(sheet.Texture); CheckEqual(3, window.Levels.Length); } public void RemembersTheSpriteSize() { SpriteSheet sheet = SpriteSheet.Create(new RecordingWindowHandle(), MipChainTests.Solid(new Size(4, 4), Red), new Size(4, 4), borderSize: 0); CheckEqual(new Size(4, 4), sheet.SpriteSize); } public void StridesByWhatEveryMipLevelNeeds() { var window = new RecordingWindowHandle(); SpriteSheet sheet = SpriteSheet.Create( window, MipChainTests.Solid(new Size(12, 12), Red), new Size(12, 12), borderSize: 0); CheckEqual(new Size(16, 16), sheet.Stride); CheckEqual(new Size(16, 16), window.Levels[0].Size); } public void FiltersEachFrameOnItsOwn() { var window = new RecordingWindowHandle(); DecodedImage image = MipChainTests.Image(new Size(4, 2), (x, y) => x < 2 ? Red : Blue); SpriteSheet.Create(window, image, new Size(2, 2), borderSize: 0); CheckEqual(Red, MipChainTests.PixelAt(window.Levels[1], 0, 0)); CheckEqual(Blue, MipChainTests.PixelAt(window.Levels[1], 1, 0)); } public void NullWindow_Throws() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Create(null, MipChainTests.Solid(new Size(2, 2), Red), new Size(2, 2), borderSize: 0); } public void ImageThatIsntAWholeGrid_Throws() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(5, 2), Red), new Size(2, 2), borderSize: 0); } private static readonly Rectangle RedBlock = new Rectangle(0, 0, 4, 4); private static readonly Rectangle BlueBlock = new Rectangle(8, 2, 2, 2); private static DecodedImage TwoBlocks() { return MipChainTests.Image(new Size(12, 6), (x, y) => { if (RedBlock.Contains(x, y)) { return Red; } return BlueBlock.Contains(x, y) ? Blue : Transparent; }); } public void Rects_CreateTheTextureOnTheWindow() { var window = new RecordingWindowHandle(); SpriteSheet sheet = SpriteSheet.Create(window, TwoBlocks(), new[] { RedBlock, BlueBlock }); CheckNotNull(sheet.Texture); CheckEqual(3, window.Levels.Length); } public void Rects_PutEachSpriteWhereItsFrameIs() { var window = new RecordingWindowHandle(); SpriteSheet sheet = SpriteSheet.Create(window, TwoBlocks(), new[] { RedBlock, BlueBlock }); Point red = sheet.Frame(RedBlock).Origin; Point blue = sheet.Frame(BlueBlock).Origin; CheckEqual(Red, MipChainTests.PixelAt(window.Levels[0], red.X + 3, red.Y + 3)); CheckEqual(Blue, MipChainTests.PixelAt(window.Levels[0], blue.X + 1, blue.Y + 1)); CheckEqual(Transparent, MipChainTests.PixelAt(window.Levels[0], blue.X + 2, blue.Y)); } public void Rects_FilterEachSpriteOnItsOwn() { var window = new RecordingWindowHandle(); SpriteSheet sheet = SpriteSheet.Create(window, TwoBlocks(), new[] { RedBlock, BlueBlock }); Point red = sheet.Frame(RedBlock).Origin; Point blue = sheet.Frame(BlueBlock).Origin; CheckEqual(Red, MipChainTests.PixelAt(window.Levels[1], red.X / 2 + 1, red.Y / 2 + 1)); CheckEqual(Red, MipChainTests.PixelAt(window.Levels[2], red.X / 4, red.Y / 4)); CheckEqual(Blue, MipChainTests.PixelAt(window.Levels[1], blue.X / 2, blue.Y / 2)); CheckEqual(Transparent, MipChainTests.PixelAt(window.Levels[1], blue.X / 2 + 1, blue.Y / 2)); } public void Rects_ListedTwice_AreOneSprite() { var once = new RecordingWindowHandle(); var twice = new RecordingWindowHandle(); SpriteSheet.Create(once, TwoBlocks(), new[] { RedBlock }); SpriteSheet sheet = SpriteSheet.Create(twice, TwoBlocks(), new[] { RedBlock, RedBlock }); CheckEqual(once.Levels[0].Size, twice.Levels[0].Size); CheckEqual(new Point(0, 0), sheet.Frame(RedBlock).Origin); } public void Rects_ThatOverlapInTheImage_GetTheirOwnPlaces() { var first = new Rectangle(0, 0, 4, 4); var second = new Rectangle(2, 2, 4, 4); SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), TwoBlocks(), new[] { first, second }); var firstFrame = new Rectangle(sheet.Frame(first).Origin, first.Size); var secondFrame = new Rectangle(sheet.Frame(second).Origin, second.Size); CheckFalse(firstFrame.IntersectsWith(secondFrame)); } public void Rects_NullWindow_Throws() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Create(null, TwoBlocks(), new[] { RedBlock }); } public void Rects_NullImage_Throws() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Create(new RecordingWindowHandle(), null, new[] { RedBlock }); } public void Rects_Null_Throw() { CheckThrow(typeof(ArgumentNullException)); SpriteSheet.Create(new RecordingWindowHandle(), TwoBlocks(), (Rectangle[])null); } public void Rects_None_Throw() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Create(new RecordingWindowHandle(), TwoBlocks(), new Rectangle[0]); } public void Rects_OutsideTheImage_Throw() { CheckThrow(typeof(ArgumentException)); SpriteSheet.Create(new RecordingWindowHandle(), TwoBlocks(), new[] { new Rectangle(10, 0, 4, 4) }); } public void Rects_OfZeroSize_Throw() { CheckThrow(typeof(ArgumentOutOfRangeException)); SpriteSheet.Create(new RecordingWindowHandle(), TwoBlocks(), new[] { new Rectangle(0, 0, 0, 4) }); } } public class FrameTests : UnitTestSharp.TestFixture { public void OriginStepsByTheStride() { var sheet = new SpriteSheet { SpriteSize = new Size(8, 4), Stride = new Size(10, 6) }; CheckEqual(new Point(20, 18), sheet.Frame(new Point(2, 3)).Origin); } [UnitTestSharp.IgnoreTest] void CheckEveryOriginIsAlignedForEveryMipLevel(Size spriteSize, int alignment) { const int columns = 3; const int rows = 2; SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(spriteSize.Width * columns, spriteSize.Height * rows), Red), spriteSize, borderSize: 0); for (int y = 0; y < rows; ++y) { for (int x = 0; x < columns; ++x) { Point origin = sheet.Frame(new Point(x, y)).Origin; CheckEqual(0, origin.X % alignment); CheckEqual(0, origin.Y % alignment); } } } public void CreatedSheetWithEvenlyHalvingFrames_PutsEveryOriginOnAWholePixelAtEveryLevel() { CheckEveryOriginIsAlignedForEveryMipLevel(new Size(16, 16), alignment: 16); } public void CreatedSheetWithOddFrames_PutsEveryOriginOnAWholePixelAtEveryLevel() { CheckEveryOriginIsAlignedForEveryMipLevel(new Size(3, 3), alignment: 2); CheckEveryOriginIsAlignedForEveryMipLevel(new Size(12, 8), alignment: 8); CheckEveryOriginIsAlignedForEveryMipLevel(new Size(13, 7), alignment: 4); } public void SizeIsTheSpriteSize() { var sheet = new SpriteSheet { SpriteSize = new Size(8, 4), Stride = new Size(10, 6) }; CheckEqual(new Size(8, 4), sheet.Frame(new Point(2, 3)).Size); } public void DrawsFromTheSheetsTexture() { var texture = new OwnedTexture(); var sheet = new SpriteSheet { SpriteSize = new Size(8, 4), Stride = new Size(10, 6), Texture = texture, }; CheckTrue(ReferenceEquals(texture, sheet.Frame(new Point(0, 0)).Texture)); } public void Rect_IsTheSizeOfWhatItWasCreatedFrom() { SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(16, 16), Red), new[] { new Rectangle(2, 3, 5, 7) }); CheckEqual(new Size(5, 7), sheet.Frame(new Rectangle(2, 3, 5, 7)).Size); } public void Rect_DrawsFromTheSheetsTexture() { SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(4, 4), Red), new[] { new Rectangle(0, 0, 4, 4) }); CheckTrue(ReferenceEquals(sheet.Texture, sheet.Frame(new Rectangle(0, 0, 4, 4)).Texture)); } public void Rect_NotCreatedWith_Throws() { SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(4, 4), Red), new[] { new Rectangle(0, 0, 4, 4) }); CheckThrow(typeof(ArgumentException)); sheet.Frame(new Rectangle(0, 0, 2, 2)); } public void Rect_OnAGridSheet_Throws() { var sheet = new SpriteSheet { SpriteSize = new Size(8, 4), Stride = new Size(10, 6) }; CheckThrow(typeof(InvalidOperationException)); sheet.Frame(new Rectangle(0, 0, 8, 4)); } public void Cell_OnAFreeRectSheet_Throws() { SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(4, 4), Red), new[] { new Rectangle(0, 0, 4, 4) }); CheckThrow(typeof(InvalidOperationException)); sheet.Frame(new Point(0, 0)); } public void CreatedFromRectsOfManySizes_PutsEveryOriginOnAWholePixelAtEveryLevel() { var sizes = new[] { new Size(16, 16), new Size(12, 12), new Size(3, 3), new Size(4, 2), new Size(1, 1), new Size(7, 5), new Size(1, 8), new Size(8, 4), new Size(13, 7), new Size(2, 30), }; var rectangles = new Rectangle[sizes.Length]; int top = 0; for (int i = 0; i < sizes.Length; ++i) { rectangles[i] = new Rectangle(new Point(0, top), sizes[i]); top += sizes[i].Height; } SpriteSheet sheet = SpriteSheet.Create( new RecordingWindowHandle(), MipChainTests.Solid(new Size(16, top), Red), rectangles); for (int i = 0; i < sizes.Length; ++i) { int alignment = 1 << (MipChain.LevelCountFor(sizes[i]) - 1); Point origin = sheet.Frame(rectangles[i]).Origin; CheckEqual(0, origin.X % alignment); CheckEqual(0, origin.Y % alignment); } } } } }