summaryrefslogtreecommitdiff
path: root/subprojects/pixpat/pixpat-python/tests/test_numpy.py
blob: 6dbd7bb4993b128570bad45914207b5468faefbd (plain)
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
"""Empirical check that pixpat works correctly with numpy.

pixpat does not depend on numpy. This file proves that ndarrays work
as ``Buffer`` planes through the buffer protocol, in the shapes a
caller would typically use. Skipped when numpy is missing.
"""

import pixpat
import pytest

np = pytest.importorskip('numpy')


def test_xrgb8888_into_uint8_3d_ndarray():
    w, h = 64, 32
    arr = np.zeros((h, w, 4), dtype=np.uint8)
    pixpat.draw_pattern(pixpat.Buffer([arr], 'XRGB8888', w, h, [arr.strides[0]]), 'smpte')
    assert arr.any()


def test_abgr16161616_into_uint16_3d_ndarray():
    w, h = 64, 32
    arr = np.zeros((h, w, 4), dtype=np.uint16)
    pixpat.draw_pattern(pixpat.Buffer([arr], 'ABGR16161616', w, h, [arr.strides[0]]), 'smpte')
    assert arr.any()
    assert arr.dtype == np.uint16


def test_nv12_into_two_ndarrays():
    w, h = 64, 32
    y = np.zeros((h, w), dtype=np.uint8)
    uv = np.zeros((h // 2, w), dtype=np.uint8)  # interleaved U,V at half-height
    pixpat.draw_pattern(
        pixpat.Buffer(
            planes=[y, uv],
            fmt='NV12',
            width=w,
            height=h,
            strides=[y.strides[0], uv.strides[0]],
        ),
        'smpte',
    )
    assert y.any()
    assert uv.any()


def test_convert_with_readonly_ndarray_src():
    """A numpy view with writeable=False must work as the convert source."""
    w, h = 64, 32
    src = np.zeros((h, w, 4), dtype=np.uint8)
    pixpat.draw_pattern(pixpat.Buffer([src], 'XRGB8888', w, h, [src.strides[0]]), 'smpte')
    src.flags.writeable = False

    dst = np.zeros((h, w, 4), dtype=np.uint16)
    pixpat.convert(
        pixpat.Buffer([dst], 'ABGR16161616', w, h, [dst.strides[0]]),
        pixpat.Buffer([src], 'XRGB8888', w, h, [src.strides[0]]),
    )
    assert dst.any()


def test_roundtrip_ndarrays_byte_for_byte():
    """End-to-end XRGB8888 -> ABGR16161616 -> XRGB8888 with ndarrays only."""
    w, h = 64, 32
    src = np.zeros((h, w, 4), dtype=np.uint8)
    mid = np.zeros((h, w, 4), dtype=np.uint16)
    dst = np.zeros((h, w, 4), dtype=np.uint8)

    pixpat.draw_pattern(pixpat.Buffer([src], 'XRGB8888', w, h, [src.strides[0]]), 'smpte')
    pixpat.convert(
        pixpat.Buffer([mid], 'ABGR16161616', w, h, [mid.strides[0]]),
        pixpat.Buffer([src], 'XRGB8888', w, h, [src.strides[0]]),
    )
    pixpat.convert(
        pixpat.Buffer([dst], 'XRGB8888', w, h, [dst.strides[0]]),
        pixpat.Buffer([mid], 'ABGR16161616', w, h, [mid.strides[0]]),
    )
    assert np.array_equal(src, dst)


def test_writable_view_via_view_method():
    """`arr.view()` shares memory and is writable by default — drawing into
    it must update the original."""
    w, h = 64, 32
    arr = np.zeros((h, w, 4), dtype=np.uint8)
    view = arr.view()
    pixpat.draw_pattern(pixpat.Buffer([view], 'XRGB8888', w, h, [view.strides[0]]), 'smpte')
    assert arr.any()  # the original sees the writes


def test_noncontiguous_source_smoke():
    """Discovery test: what happens if the caller hands us a non-C-contiguous
    array? pixpat reads ``stride`` bytes per row, so any row-contiguous
    layout where ``arr.strides[0]`` is the row stride should work; a
    transposed array (where rows aren't even contiguous) is misuse.

    This test passes a *valid* row-contiguous slice — the top half of a
    bigger image, taken via slicing — and expects success. It exists to
    document the contract: 'rows must be contiguous in memory; pass
    arr.strides[0] as the stride'.
    """
    big = np.zeros((64, 64, 4), dtype=np.uint8)
    top_half = big[:32]  # shape (32, 64, 4); rows still C-contiguous
    assert top_half.strides[0] == 64 * 4
    pixpat.draw_pattern(
        pixpat.Buffer([top_half], 'XRGB8888', 64, 32, [top_half.strides[0]]),
        'smpte',
    )
    assert big[:32].any()
    assert not big[32:].any()  # the other half stayed untouched