summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/LivingVillage.Desktop.Tests/PrototypeTests.fs53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
index bc3425a..7f67280 100644
--- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
+++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
@@ -1171,6 +1171,59 @@ type PrototypeTests () =
Assert.IsTrue((atlasWidth = VillageArt.worldAtlasWidth), "world atlas width must match 32 x 32px slots")
Assert.IsTrue((atlasHeight = VillageArt.worldAtlasHeight), "world atlas height must stay 32px")
+ /// P28:防复发。第二变体(P26)在 512 整图 ~2px/tile 下不可辨,根因是挑檐与第一变体同为
+ /// 近黑黛瓦。此回归直接解码 world 图集,断言变体 B 的挑檐(slot 28)平均亮度显著高于
+ /// 变体 A 的屋脊(slot 8),保证「配色差异」不会在后续编辑中被悄悄抹平。
+ [<TestMethod>]
+ member _.VariantBRoofPaletteIsClearlyLighterThanVariantA () =
+ let worldPath = Path.Combine(AppContext.BaseDirectory, "Assets/jiangnan-world.png")
+ let bytes = File.ReadAllBytes worldPath
+ let be32 offset =
+ (int bytes.[offset] <<< 24)
+ ||| (int bytes.[offset + 1] <<< 16)
+ ||| (int bytes.[offset + 2] <<< 8)
+ ||| int bytes.[offset + 3]
+ Assert.AreEqual<int>(0x89504E47, be32 0)
+ let width = be32 16
+ let height = be32 20
+ Assert.AreEqual<int>(VillageArt.worldAtlasWidth, width)
+ Assert.AreEqual<int>(VillageArt.worldAtlasHeight, height)
+ // Collect IDAT and inflate; the baked atlas is 8-bit RGBA, non-interlaced, filter 0.
+ let mutable offset = 8
+ use idat = new MemoryStream()
+ while offset < bytes.Length do
+ let length = be32 offset
+ let tag = System.Text.Encoding.ASCII.GetString(bytes, offset + 4, 4)
+ if tag = "IDAT" then idat.Write(bytes, offset + 8, length)
+ offset <- offset + 12 + length
+ idat.Position <- 0L
+ use inflater = new System.IO.Compression.ZLibStream(idat, System.IO.Compression.CompressionMode.Decompress)
+ use raw = new MemoryStream()
+ inflater.CopyTo raw
+ let data = raw.ToArray()
+ let stride = width * 4 + 1
+ let pixel (x: int) (y: int) =
+ let rowStart = y * stride
+ Assert.AreEqual<byte>(0uy, data.[rowStart])
+ let p = rowStart + 1 + x * 4
+ (int data.[p], int data.[p + 1], int data.[p + 2], int data.[p + 3])
+ let slotMeanBrightness (slot: int) =
+ let mutable sum = 0.0
+ let mutable count = 0
+ for y in 0 .. height - 1 do
+ for x in slot * 32 .. slot * 32 + 31 do
+ let (r, g, b, a) = pixel x y
+ if a > 0 then
+ sum <- sum + float (r + g + b) / 3.0
+ count <- count + 1
+ Assert.IsTrue(count > 0, sprintf "slot %d must have opaque pixels" slot)
+ sum / float count
+ let roofA = slotMeanBrightness 8 // RoofRidgeSprite: variant A near-black 黛瓦
+ let roofB = slotMeanBrightness 28 // RoofEaveOverhangSprite: variant B light blue-grey 瓦
+ Assert.IsTrue(
+ roofB > roofA + 15.0,
+ sprintf "variant B roof must be clearly lighter than variant A (A=%.1f B=%.1f)" roofA roofB)
+
[<TestMethod>]
member _.CjkGlyphManifestIsUniqueAndMatchesAtlasGrid () =
// 防复发:图集用字增长时,清单必须非空、每行恰 1 个字符、字符唯一,