File size: 4,500 Bytes
ebd87b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""
tests/test_background.py
--------------------------
Tests for border-connected background detection.
Run with: pytest tests/test_background.py -v
"""

import sys
from pathlib import Path

import numpy as np

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from background import apply_transparency, detect_background


def test_black_background_detected():
    rgba = np.zeros((20, 20, 4), dtype=np.uint8)
    rgba[:, :, 3] = 255
    bg = detect_background(rgba)
    assert bg.color[:3] == (0, 0, 0)
    assert bg.is_transparent is False


def test_interior_black_pixel_is_not_marked_background():
    """
    CRITICAL RULE: a sprite drawn on a black background can have its own
    black pixels (e.g. an outline). Only background pixels reachable from
    the sheet border are background -- an enclosed black pixel inside a
    sprite must never be marked as background.
    """
    rgba = np.zeros((20, 20, 4), dtype=np.uint8)
    rgba[:, :, 3] = 255
    rgba[5:15, 5:15] = [200, 50, 50, 255]  # red sprite body
    rgba[9, 9] = [0, 0, 0, 255]            # black pixel INSIDE the sprite

    bg = detect_background(rgba)
    assert bg.mask[9, 9] == False  # noqa: E712 -- must not be flagged as background
    assert bg.mask[0, 0] == True   # noqa: E712 -- the actual border IS background


def test_apply_transparency_preserves_interior_pixel():
    rgba = np.zeros((20, 20, 4), dtype=np.uint8)
    rgba[:, :, 3] = 255
    rgba[5:15, 5:15] = [200, 50, 50, 255]
    rgba[9, 9] = [0, 0, 0, 255]

    bg = detect_background(rgba)
    out = apply_transparency(rgba, bg)

    assert out[9, 9, 3] == 255   # interior black pixel: untouched
    assert out[0, 0, 3] == 0     # border background pixel: made transparent
    assert tuple(out[9, 9, :3]) == (0, 0, 0)  # color also untouched


def test_already_transparent_sheet_detected_as_transparent_background():
    rgba = np.zeros((20, 20, 4), dtype=np.uint8)  # fully transparent
    rgba[5:15, 5:15] = [80, 180, 90, 255]  # opaque sprite in the middle
    bg = detect_background(rgba)
    assert bg.is_transparent is True
    assert bg.mask[0, 0] == True  # noqa: E712


def test_white_background_detected():
    rgba = np.full((20, 20, 4), 255, dtype=np.uint8)
    rgba[5:15, 5:15] = [10, 10, 200, 255]  # blue sprite
    bg = detect_background(rgba)
    assert bg.color[:3] == (255, 255, 255)
    assert bg.mask[9, 9] == False  # noqa: E712 -- sprite interior untouched


def test_colored_background_detected():
    rgba = np.zeros((20, 20, 4), dtype=np.uint8)
    rgba[:, :, 3] = 255
    rgba[:, :, :3] = [30, 200, 30]  # green background
    rgba[5:15, 5:15] = [200, 30, 30, 255]  # red sprite
    bg = detect_background(rgba)
    assert bg.color[:3] == (30, 200, 30)
    assert bg.mask[9, 9] == False  # noqa: E712


def test_tolerance_absorbs_near_background_noise():
    """
    Slightly-off-black border pixels (e.g. from lossy JPEG compression
    artifacts) should still be classified as background within tolerance.
    """
    rng = np.random.default_rng(0)
    rgba = np.zeros((20, 20, 4), dtype=np.uint8)
    rgba[:, :, 3] = 255
    jitter = rng.integers(-5, 6, size=(20, 20, 3))
    rgba[:, :, :3] = np.clip(0 + jitter, 0, 255).astype(np.uint8)
    rgba[5:15, 5:15] = [200, 50, 50, 255]
    bg = detect_background(rgba)
    # Most of the noisy near-black border should still register as background.
    assert bg.mask[0, 0] == True  # noqa: E712
    assert bg.mask.sum() > 200  # most of the 400px sheet minus the sprite


def test_two_sprites_near_black_border_both_preserved():
    """Two separate sprites each containing a black pixel of their own."""
    rgba = np.zeros((30, 30, 4), dtype=np.uint8)
    rgba[:, :, 3] = 255
    rgba[3:10, 3:10] = [80, 200, 90, 255]
    rgba[6, 6] = [0, 0, 0, 255]
    rgba[20:27, 20:27] = [90, 90, 220, 255]
    rgba[23, 23] = [0, 0, 0, 255]

    bg = detect_background(rgba)
    assert bg.mask[6, 6] == False  # noqa: E712
    assert bg.mask[23, 23] == False  # noqa: E712


if __name__ == "__main__":
    import traceback
    tests = [(name, fn) for name, fn in list(globals().items()) if name.startswith("test_") and callable(fn)]
    passed, failed = 0, 0
    for name, fn in tests:
        try:
            fn()
            print(f"PASS  {name}")
            passed += 1
        except Exception:
            print(f"FAIL  {name}")
            traceback.print_exc()
            failed += 1
    print(f"\n{passed} passed, {failed} failed")
    sys.exit(1 if failed else 0)